From 2a38bf29618a8ce163f9d6f83b7ae86924752e32 Mon Sep 17 00:00:00 2001 From: cyan Date: Wed, 4 Dec 2024 10:22:48 -0500 Subject: cleanup patches (and other stuff) (#79) * cleanup patches * fix polyseed patch * Fix iOS builds * fix polyseed dependencies * fix polyseed patch for macOS * update ledger patch * update wownero patches and version * update checksums * wip" * update gitmodules * update boost build script * update build_single.sh * vix verbosey_copy * fix __clear_cache bug on wownero * update randomwow * migrate build system * fix cross compilation issues * some more build issue * update polyseed * cleanup cmakelists * fix toolchain.cmake.in * add ssp * another attempt at building windows on CI * fix package name * migrate mirror to my own hosting * change download mirror priority (fallback first) * link ssp in monero module as well by using CMAKE_{C,CXX}_FLAGS * fix android builds * update polyseed source * 13 -> trixie * fix package name conflicts, update runner to sid * update boost to 1_84_0, disable patch that's no longer needed * switch to ubuntu:24.04 * add POLYSEED_STATIC to toolchain.cmake.in in order to properly link * drop patches * fixes to darwin * link missing wowner-seed library * a litte bit more of experiments * build locale only on windows * update iconv * update definitions * update ci builds * update my progress * ios fix, update depends, ci * multithread build system * fix android, mingw and linux build issues * remove dependency check * update Dockerfile to include pigz * show a message when pigz is missing * fix devcontainer mingw setup (missing ENV) * update android build runner * sailfishos dropped (you better go behave yourself and run actual linux programs) * fiz pigz issues * install llvm-ranlib for android * fix iOS build issues * fix dummy ledger patch * fix macos and darwin * fix macos ci * fix macos build command * install autoconf * add automake * add libtool * macos fixes, wownero fixes, idk what else, please help me * fix wownero iOS build * Cleanup patches * add try-catch into monero code * fix error handling * update checksums --- .devcontainer/Dockerfile | 4 +- .github/workflows/dependencies_matches.yaml | 74 - .github/workflows/full_check.yaml | 244 +- .gitmodules | 39 +- README.md | 2 +- build_single.sh | 354 +- contrib/depends/.gitignore | 10 + contrib/depends/Makefile | 240 + contrib/depends/README.md | 74 + contrib/depends/builders/darwin.mk | 23 + contrib/depends/builders/default.mk | 21 + contrib/depends/builders/linux.mk | 2 + contrib/depends/config.guess | 1815 ++++++ contrib/depends/config.site.in | 66 + contrib/depends/config.sub | 2354 +++++++ contrib/depends/description.md | 53 + contrib/depends/funcs.mk | 292 + contrib/depends/hosts/android.mk | 32 + contrib/depends/hosts/darwin.mk | 23 + contrib/depends/hosts/default.mk | 26 + contrib/depends/hosts/freebsd.mk | 18 + contrib/depends/hosts/ios.mk | 24 + contrib/depends/hosts/linux.mk | 32 + contrib/depends/hosts/mingw32.mk | 11 + contrib/depends/packages.md | 165 + contrib/depends/packages/android_ndk.mk | 36 + contrib/depends/packages/boost.mk | 56 + contrib/depends/packages/darwin_sdk.mk | 15 + contrib/depends/packages/eudev.mk | 30 + contrib/depends/packages/expat.mk | 28 + contrib/depends/packages/freebsd_base.mk | 23 + contrib/depends/packages/graphviz.mk | 30 + contrib/depends/packages/gtest.mk | 39 + contrib/depends/packages/hidapi.mk | 38 + contrib/depends/packages/icu4c.mk | 27 + contrib/depends/packages/libICE.mk | 23 + contrib/depends/packages/libSM.mk | 23 + contrib/depends/packages/libiconv.mk | 34 + contrib/depends/packages/libusb.mk | 39 + contrib/depends/packages/native_biplist.mk | 20 + contrib/depends/packages/native_ccache.mk | 25 + contrib/depends/packages/native_cctools.mk | 28 + contrib/depends/packages/native_cdrkit.mk | 26 + contrib/depends/packages/native_clang.mk | 29 + contrib/depends/packages/native_cmake-unused.mk | 23 + contrib/depends/packages/native_ds_store.mk | 17 + contrib/depends/packages/native_libdmg-hfsplus.mk | 22 + contrib/depends/packages/native_libtapi.mk | 28 + contrib/depends/packages/native_mac_alias.mk | 21 + contrib/depends/packages/native_protobuf.mk | 27 + contrib/depends/packages/ncurses.mk | 63 + contrib/depends/packages/openssl.mk | 68 + contrib/depends/packages/packages.mk | 39 + contrib/depends/packages/polyseed.mk | 22 + contrib/depends/packages/protobuf.mk | 37 + contrib/depends/packages/readline.mk | 29 + contrib/depends/packages/sodium.mk | 34 + contrib/depends/packages/unbound.mk | 36 + contrib/depends/packages/unwind.mk | 29 + contrib/depends/packages/xproto.mk | 21 + contrib/depends/packages/zeromq.mk | 40 + contrib/depends/patches/boost/fix_arm_arch.patch | 11 + contrib/depends/patches/boost/fix_aroptions.patch | 28 + .../depends/patches/boost/fix_io_control_hpp.patch | 11 + contrib/depends/patches/cmake/cmake-1-fixes.patch | 67 + .../darwin_sdk/fix_missing_definitions.patch | 18 + .../patches/hidapi/missing_win_include.patch | 21 + .../icu-001-dont-build-static-dynamic-twice.patch | 37 + .../depends/patches/libiconv/fix-whitespace.patch | 13 + .../patches/native_biplist/sorted_list.patch | 29 + .../native_cdrkit/cdrkit-deterministic.patch | 86 + .../depends/patches/native_mac_alias/python3.patch | 72 + contrib/depends/patches/ncurses/fallback.c | 6621 ++++++++++++++++++++ contrib/depends/patches/protobuf/visibility.patch | 159 + .../disable-glibc-getrandom-getentropy.patch | 25 + .../depends/patches/sodium/fix-whitespace.patch | 13 + .../unbound/disable-glibc-reallocarray.patch | 14 + contrib/depends/patches/unwind/fix_obj_order.patch | 11 + .../depends/patches/zeromq/fix_declaration.patch | 11 + contrib/depends/toolchain.cmake.in | 202 + external/Apple-Boost-BuildScript | 1 - external/OpenSSL-for-iPhone | 1 - external/alpine/libexecinfo/10-execinfo.patch | 64 - .../alpine/libexecinfo/20-define-gnu-source.patch | 24 - .../alpine/libexecinfo/30-linux-makefile.patch | 44 - external/alpine/libexecinfo/APKBUILD | 50 - external/ios-cmake | 1 - external/ios/build_boost.sh | 32 - external/ios/build_monero_all.sh | 18 - external/ios/build_openssl.sh | 28 - external/ios/build_sodium.sh | 28 - external/ios/build_unbound.sh | 38 - external/ios/build_wownero_seed.sh | 48 - external/ios/build_zmq.sh | 32 - external/ios/config.sh | 13 - external/ios/install_missing_headers.sh | 69 - external/ios/libsodium_apple-ios.sh | 147 - external/libexpat | 1 - external/libsodium | 1 - external/libzmq | 1 - external/macos/build_unbound.sh | 54 - external/macos/config.sh | 13 - external/polyseed | 1 - external/unbound | 1 - external/wownero-seed | 1 - ...nero-seed-0001-fix-duplicate-symbol-error.patch | 1 - impls/monero.dart/lib/monero.dart | 17 - impls/monero.dart/lib/src/checksum_monero.dart | 6 +- impls/monero.dart/lib/src/checksum_wownero.dart | 6 +- .../lib/src/generated_bindings_monero.g.dart | 33 - .../lib/src/generated_bindings_wownero.g.dart | 33 - impls/monero.dart/lib/wownero.dart | 18 - impls/monero.ts/checksum_monero.ts | 6 +- impls/monero.ts/checksum_wownero.ts | 6 +- impls/monero.ts/src/symbols.ts | 15 - monero_libwallet2_api_c/CMakeLists.txt | 397 +- .../monero_libwallet2_api_c.exp | 2 - monero_libwallet2_api_c/src/main/cpp/helpers.cpp | 4 +- monero_libwallet2_api_c/src/main/cpp/helpers.hpp | 18 + .../src/main/cpp/monero_checksum.h | 6 +- .../src/main/cpp/wallet2_api_c.cpp | 621 +- .../src/main/cpp/wallet2_api_c.h | 2 - patches/monero/0001-polyseed.patch | 1273 ---- ...et-background-sync-with-just-the-view-key.patch | 4317 +++++++++++++ ...issing-___clear_cache-when-targetting-iOS.patch | 34 + ...et-background-sync-with-just-the-view-key.patch | 4316 ------------- patches/monero/0003-airgap.patch | 195 - patches/monero/0003-store-crash-fix.patch | 208 + patches/monero/0004-coin-control.patch | 902 --- .../0004-uint64_t-missing-definition-fix.patch | 25 + patches/monero/0005-fix-build-issues.patch | 137 - ...005-use-proper-error-handling-in-get_seed.patch | 71 + patches/monero/0006-UR-functions.patch | 1010 +++ patches/monero/0006-macos-build-fix.patch | 114 - .../monero/0007-add-dummy-device-for-ledger.patch | 604 ++ .../monero/0007-fix-make-debug-test-target.patch | 39 - ...issing-___clear_cache-when-targetting-iOS.patch | 33 - patches/monero/0008-polyseed.patch | 1286 ++++ ...dd-recoverDeterministicWalletFromSpendKey.patch | 153 - patches/monero/0009-coin-control.patch | 979 +++ ...oding-and-tx-key-getter-for-PendingTransc.patch | 18 +- ...dd-recoverDeterministicWalletFromSpendKey.patch | 153 + patches/monero/0011-store-crash-fix.patch | 210 - patches/monero/0012-WIP-UR-functions.patch | 893 --- .../monero/0012-add-monero-submodule-support.patch | 65 + patches/monero/0013-android-ndk-version-bump.patch | 120 - patches/monero/0013-fix-iOS-depends-build.patch | 104 + ...-include-locale-only-when-targeting-WIN32.patch | 43 + patches/monero/0014-rename-arm-armv7a.patch | 94 - ...015-use-proper-error-handling-in-get_seed.patch | 68 - .../monero/0016-add-dummy-device-for-ledger.patch | 580 -- .../0016-uint64_t-missing-definition-fix.patch | 25 - patches/monero/0017-added-deps.patch | 29 - patches/monero/0018-fix-coin-control-patch.patch | 30 - .../monero/0019-fix-for-coin-control-patch.patch | 97 - ...0020-catch-exception-in-queryWalletDevice.patch | 35 - patches/monero/README.md | 43 - ...api-compilation-target-https-git.wownero..patch | 31 - ...et-background-sync-with-just-the-view-key.patch | 4318 +++++++++++++ ...issing-___clear_cache-when-targetting-iOS.patch | 19 + patches/wownero/0002-polyseed.patch | 1299 ---- .../wownero/0003-fix-is_trivially_copyable.patch | 32 + ...et-background-sync-with-just-the-view-key.patch | 4351 ------------- patches/wownero/0004-airgap.patch | 170 - patches/wownero/0004-store-crash-fix.patch | 210 + patches/wownero/0005-coin-control.patch | 225 - .../0005-uint64_t-missing-definition-fix.patch | 25 + patches/wownero/0006-fix-build.patch | 137 - ...006-use-proper-error-handling-in-get_seed.patch | 71 + patches/wownero/0007-UR-functions.patch | 1036 +++ patches/wownero/0007-macos-build-fix.patch | 114 - .../0008-FIX-wallet-listener-crashing.patch | 58 - .../wownero/0008-add-dummy-device-for-ledger.patch | 604 ++ ...issing-___clear_cache-when-targetting-iOS.patch | 33 - patches/wownero/0009-polyseed.patch | 1464 +++++ patches/wownero/0010-build-wownero-seed.patch | 613 -- patches/wownero/0010-coin-control.patch | 979 +++ ...oding-and-tx-key-getter-for-PendingTransc.patch | 68 + .../wownero/0011-android-ndk-version-bump.patch | 120 - ...dd-recoverDeterministicWalletFromSpendKey.patch | 153 + patches/wownero/0012-rename-arm-armv7a.patch | 94 - .../0013-add-monero-submodule-support.patch | 65 + ...013-use-proper-error-handling-in-get_seed.patch | 68 - patches/wownero/0014-fix-iOS-depends-build.patch | 104 + patches/wownero/0014-store-crash-fix.patch | 213 - ...-include-locale-only-when-targeting-WIN32.patch | 43 + .../0015-uint64_t-missing-definition-fix.patch | 25 - patches/wownero/README.md | 35 - wownero | 2 +- .../src/main/cpp/wallet2_api_c.cpp | 568 +- .../src/main/cpp/wallet2_api_c.h | 2 - .../src/main/cpp/wownero_checksum.h | 6 +- wownero_libwallet2_api_c/wownero-seed | 1 + .../wownero_libwallet2_api_c.exp | 2 - 194 files changed, 33235 insertions(+), 18900 deletions(-) delete mode 100644 .github/workflows/dependencies_matches.yaml create mode 100644 contrib/depends/.gitignore create mode 100644 contrib/depends/Makefile create mode 100644 contrib/depends/README.md create mode 100644 contrib/depends/builders/darwin.mk create mode 100644 contrib/depends/builders/default.mk create mode 100644 contrib/depends/builders/linux.mk create mode 100755 contrib/depends/config.guess create mode 100755 contrib/depends/config.site.in create mode 100755 contrib/depends/config.sub create mode 100644 contrib/depends/description.md create mode 100644 contrib/depends/funcs.mk create mode 100644 contrib/depends/hosts/android.mk create mode 100644 contrib/depends/hosts/darwin.mk create mode 100644 contrib/depends/hosts/default.mk create mode 100644 contrib/depends/hosts/freebsd.mk create mode 100644 contrib/depends/hosts/ios.mk create mode 100644 contrib/depends/hosts/linux.mk create mode 100644 contrib/depends/hosts/mingw32.mk create mode 100644 contrib/depends/packages.md create mode 100644 contrib/depends/packages/android_ndk.mk create mode 100644 contrib/depends/packages/boost.mk create mode 100644 contrib/depends/packages/darwin_sdk.mk create mode 100644 contrib/depends/packages/eudev.mk create mode 100644 contrib/depends/packages/expat.mk create mode 100644 contrib/depends/packages/freebsd_base.mk create mode 100644 contrib/depends/packages/graphviz.mk create mode 100644 contrib/depends/packages/gtest.mk create mode 100644 contrib/depends/packages/hidapi.mk create mode 100644 contrib/depends/packages/icu4c.mk create mode 100644 contrib/depends/packages/libICE.mk create mode 100644 contrib/depends/packages/libSM.mk create mode 100644 contrib/depends/packages/libiconv.mk create mode 100644 contrib/depends/packages/libusb.mk create mode 100644 contrib/depends/packages/native_biplist.mk create mode 100644 contrib/depends/packages/native_ccache.mk create mode 100644 contrib/depends/packages/native_cctools.mk create mode 100644 contrib/depends/packages/native_cdrkit.mk create mode 100644 contrib/depends/packages/native_clang.mk create mode 100644 contrib/depends/packages/native_cmake-unused.mk create mode 100644 contrib/depends/packages/native_ds_store.mk create mode 100644 contrib/depends/packages/native_libdmg-hfsplus.mk create mode 100644 contrib/depends/packages/native_libtapi.mk create mode 100644 contrib/depends/packages/native_mac_alias.mk create mode 100644 contrib/depends/packages/native_protobuf.mk create mode 100644 contrib/depends/packages/ncurses.mk create mode 100644 contrib/depends/packages/openssl.mk create mode 100644 contrib/depends/packages/packages.mk create mode 100644 contrib/depends/packages/polyseed.mk create mode 100644 contrib/depends/packages/protobuf.mk create mode 100644 contrib/depends/packages/readline.mk create mode 100644 contrib/depends/packages/sodium.mk create mode 100644 contrib/depends/packages/unbound.mk create mode 100644 contrib/depends/packages/unwind.mk create mode 100644 contrib/depends/packages/xproto.mk create mode 100644 contrib/depends/packages/zeromq.mk create mode 100644 contrib/depends/patches/boost/fix_arm_arch.patch create mode 100644 contrib/depends/patches/boost/fix_aroptions.patch create mode 100644 contrib/depends/patches/boost/fix_io_control_hpp.patch create mode 100644 contrib/depends/patches/cmake/cmake-1-fixes.patch create mode 100644 contrib/depends/patches/darwin_sdk/fix_missing_definitions.patch create mode 100644 contrib/depends/patches/hidapi/missing_win_include.patch create mode 100644 contrib/depends/patches/icu4c/icu-001-dont-build-static-dynamic-twice.patch create mode 100644 contrib/depends/patches/libiconv/fix-whitespace.patch create mode 100644 contrib/depends/patches/native_biplist/sorted_list.patch create mode 100644 contrib/depends/patches/native_cdrkit/cdrkit-deterministic.patch create mode 100644 contrib/depends/patches/native_mac_alias/python3.patch create mode 100644 contrib/depends/patches/ncurses/fallback.c create mode 100644 contrib/depends/patches/protobuf/visibility.patch create mode 100644 contrib/depends/patches/sodium/disable-glibc-getrandom-getentropy.patch create mode 100644 contrib/depends/patches/sodium/fix-whitespace.patch create mode 100644 contrib/depends/patches/unbound/disable-glibc-reallocarray.patch create mode 100644 contrib/depends/patches/unwind/fix_obj_order.patch create mode 100644 contrib/depends/patches/zeromq/fix_declaration.patch create mode 100644 contrib/depends/toolchain.cmake.in delete mode 160000 external/Apple-Boost-BuildScript delete mode 160000 external/OpenSSL-for-iPhone delete mode 100644 external/alpine/libexecinfo/10-execinfo.patch delete mode 100644 external/alpine/libexecinfo/20-define-gnu-source.patch delete mode 100644 external/alpine/libexecinfo/30-linux-makefile.patch delete mode 100644 external/alpine/libexecinfo/APKBUILD delete mode 160000 external/ios-cmake delete mode 100755 external/ios/build_boost.sh delete mode 100755 external/ios/build_monero_all.sh delete mode 100755 external/ios/build_openssl.sh delete mode 100755 external/ios/build_sodium.sh delete mode 100755 external/ios/build_unbound.sh delete mode 100755 external/ios/build_wownero_seed.sh delete mode 100755 external/ios/build_zmq.sh delete mode 100755 external/ios/config.sh delete mode 100755 external/ios/install_missing_headers.sh delete mode 100755 external/ios/libsodium_apple-ios.sh delete mode 160000 external/libexpat delete mode 160000 external/libsodium delete mode 160000 external/libzmq delete mode 100755 external/macos/build_unbound.sh delete mode 100755 external/macos/config.sh delete mode 160000 external/polyseed delete mode 160000 external/unbound delete mode 160000 external/wownero-seed delete mode 120000 external/wownero-seed-0001-fix-duplicate-symbol-error.patch delete mode 100644 patches/monero/0001-polyseed.patch create mode 100644 patches/monero/0001-wallet-background-sync-with-just-the-view-key.patch create mode 100644 patches/monero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch delete mode 100644 patches/monero/0002-wallet-background-sync-with-just-the-view-key.patch delete mode 100644 patches/monero/0003-airgap.patch create mode 100644 patches/monero/0003-store-crash-fix.patch delete mode 100644 patches/monero/0004-coin-control.patch create mode 100644 patches/monero/0004-uint64_t-missing-definition-fix.patch delete mode 100644 patches/monero/0005-fix-build-issues.patch create mode 100644 patches/monero/0005-use-proper-error-handling-in-get_seed.patch create mode 100644 patches/monero/0006-UR-functions.patch delete mode 100644 patches/monero/0006-macos-build-fix.patch create mode 100644 patches/monero/0007-add-dummy-device-for-ledger.patch delete mode 100644 patches/monero/0007-fix-make-debug-test-target.patch delete mode 100644 patches/monero/0008-fix-missing-___clear_cache-when-targetting-iOS.patch create mode 100644 patches/monero/0008-polyseed.patch delete mode 100644 patches/monero/0009-Add-recoverDeterministicWalletFromSpendKey.patch create mode 100644 patches/monero/0009-coin-control.patch create mode 100644 patches/monero/0011-Add-recoverDeterministicWalletFromSpendKey.patch delete mode 100644 patches/monero/0011-store-crash-fix.patch delete mode 100644 patches/monero/0012-WIP-UR-functions.patch create mode 100644 patches/monero/0012-add-monero-submodule-support.patch delete mode 100644 patches/monero/0013-android-ndk-version-bump.patch create mode 100644 patches/monero/0013-fix-iOS-depends-build.patch create mode 100644 patches/monero/0014-include-locale-only-when-targeting-WIN32.patch delete mode 100644 patches/monero/0014-rename-arm-armv7a.patch delete mode 100644 patches/monero/0015-use-proper-error-handling-in-get_seed.patch delete mode 100644 patches/monero/0016-add-dummy-device-for-ledger.patch delete mode 100644 patches/monero/0016-uint64_t-missing-definition-fix.patch delete mode 100644 patches/monero/0017-added-deps.patch delete mode 100644 patches/monero/0018-fix-coin-control-patch.patch delete mode 100644 patches/monero/0019-fix-for-coin-control-patch.patch delete mode 100644 patches/monero/0020-catch-exception-in-queryWalletDevice.patch delete mode 100644 patches/monero/README.md delete mode 100644 patches/wownero/0001-Fix-wallet_api-compilation-target-https-git.wownero..patch create mode 100644 patches/wownero/0001-wallet-background-sync-with-just-the-view-key.patch create mode 100644 patches/wownero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch delete mode 100644 patches/wownero/0002-polyseed.patch create mode 100644 patches/wownero/0003-fix-is_trivially_copyable.patch delete mode 100644 patches/wownero/0003-wallet-background-sync-with-just-the-view-key.patch delete mode 100644 patches/wownero/0004-airgap.patch create mode 100644 patches/wownero/0004-store-crash-fix.patch delete mode 100644 patches/wownero/0005-coin-control.patch create mode 100644 patches/wownero/0005-uint64_t-missing-definition-fix.patch delete mode 100644 patches/wownero/0006-fix-build.patch create mode 100644 patches/wownero/0006-use-proper-error-handling-in-get_seed.patch create mode 100644 patches/wownero/0007-UR-functions.patch delete mode 100644 patches/wownero/0007-macos-build-fix.patch delete mode 100644 patches/wownero/0008-FIX-wallet-listener-crashing.patch create mode 100644 patches/wownero/0008-add-dummy-device-for-ledger.patch delete mode 100644 patches/wownero/0009-fix-missing-___clear_cache-when-targetting-iOS.patch create mode 100644 patches/wownero/0009-polyseed.patch delete mode 100644 patches/wownero/0010-build-wownero-seed.patch create mode 100644 patches/wownero/0010-coin-control.patch create mode 100644 patches/wownero/0011-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch delete mode 100644 patches/wownero/0011-android-ndk-version-bump.patch create mode 100644 patches/wownero/0012-Add-recoverDeterministicWalletFromSpendKey.patch delete mode 100644 patches/wownero/0012-rename-arm-armv7a.patch create mode 100644 patches/wownero/0013-add-monero-submodule-support.patch delete mode 100644 patches/wownero/0013-use-proper-error-handling-in-get_seed.patch create mode 100644 patches/wownero/0014-fix-iOS-depends-build.patch delete mode 100644 patches/wownero/0014-store-crash-fix.patch create mode 100644 patches/wownero/0015-include-locale-only-when-targeting-WIN32.patch delete mode 100644 patches/wownero/0015-uint64_t-missing-definition-fix.patch delete mode 100644 patches/wownero/README.md create mode 160000 wownero_libwallet2_api_c/wownero-seed diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 37533b9..6dc59c4 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -11,7 +11,7 @@ RUN wget http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3- && rm libtinfo5.deb RUN apt update \ - && apt install -y wget build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo6 gperf gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 gcc-mingw-w64-i686 g++-mingw-w64-i686 build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf unzip python-is-python3 build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu gcc-i686-linux-gnu g++-i686-linux-gnu pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf python-is-python3 zsh llvm libclang1-18 libc++-18-dev clang + && apt install -y pigz wget build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo6 gperf gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 gcc-mingw-w64-i686 g++-mingw-w64-i686 build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf unzip python-is-python3 build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu gcc-i686-linux-gnu g++-i686-linux-gnu pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf python-is-python3 zsh llvm libclang1-18 libc++-18-dev clang RUN bash -c 'if [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ]] then sudo rm -rf /opt/android-sdk-linux/platform-tools; fi' @@ -31,3 +31,5 @@ USER ubuntu RUN git config --global --add safe.directory '*' RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v1.2.0/zsh-in-docker.sh)" \ -t robbyrussell + +ENV SUDO=sudo diff --git a/.github/workflows/dependencies_matches.yaml b/.github/workflows/dependencies_matches.yaml deleted file mode 100644 index 5552539..0000000 --- a/.github/workflows/dependencies_matches.yaml +++ /dev/null @@ -1,74 +0,0 @@ -name: Check if dependencies match -# Sometimes we have the libraries in two (or more) places -# at the same time, this check makes sure that we do use -# the same version of said libraries. -on: [push] - -jobs: - wownero-seed: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: recursive - - name: Patch sources - run: | - git config --global --add safe.directory '*' - git config --global user.email "ci@mrcyjanek.net" - git config --global user.name "CI mrcyjanek.net" - ./apply_patches.sh monero - ./apply_patches.sh wownero - - name: obtain hashes - run: | - set -x - pushd external/wownero-seed - echo "EXTERNAL_WOWNEROSEED=$(git rev-parse HEAD)" >> $GITHUB_ENV - popd - echo "WOWNERO_WOWNEROSEED=$(cat wownero/contrib/depends/packages/wownero_seed.mk | grep _download_file | tr '=.' '\n' | head -2 | tail -1)" >> $GITHUB_ENV - - name: compare hashes - run: | - if [[ "x$WOWNERO_WOWNEROSEED" == "x" ]]; - then - echo "Unable to obtain wownero seed from wownero repo" - exit 1 - fi - if [[ ! "x$EXTERNAL_WOWNEROSEED" == "x$WOWNERO_WOWNEROSEED" ]]; - then - echo "external/wownero-seed doesn't match wownero/contrib/depends/packages/wownero_seed.mk checksum" - exit 1 - fi - polyseed: - strategy: - matrix: - coin: [monero, wownero] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: recursive - - name: Patch sources - run: | - git config --global --add safe.directory '*' - git config --global user.email "ci@mrcyjanek.net" - git config --global user.name "CI mrcyjanek.net" - ./apply_patches.sh ${{ matrix.coin }} - - name: prepare - run: | - set -x - pushd ${{ matrix.coin }}/contrib/depends - make download # sorry, this is the easiest way - mkdir sources/polyseed - pushd sources/polyseed - tar xzf ../polyseed*.tar.gz - popd - popd - - name: diff - run: | - OUTPUT=$(diff -ra external/polyseed ${{ matrix.coin }}/contrib/depends/sources/polyseed/* | grep -v .git | wc -l) - if [[ ! "x$OUTPUT" == "x0" ]]; - then - diff -ra external/polyseed ${{ matrix.coin }}/contrib/depends/sources/polyseed/* - exit 1 - fi diff --git a/.github/workflows/full_check.yaml b/.github/workflows/full_check.yaml index 2beda33..2f73515 100644 --- a/.github/workflows/full_check.yaml +++ b/.github/workflows/full_check.yaml @@ -6,16 +6,17 @@ permissions: jobs: lib_mingw: strategy: + fail-fast: false matrix: coin: [monero, wownero] runs-on: ubuntu-latest container: - image: debian:bookworm + image: ubuntu:24.04 steps: - name: Install dependencies run: | apt update - apt install -y build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 gcc-mingw-w64-i686 g++-mingw-w64-i686 + apt install -y pigz build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 gperf gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 gcc-mingw-w64-i686 g++-mingw-w64-i686 - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -35,8 +36,8 @@ jobs: uses: actions/cache@v4 with: path: | - ${{ matrix.coin }}/contrib/depends/built/* - key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }} + contrib/depends/built/* + key: depends-${{ github.job }}-${{ hashFiles('contrib/depends/packages/*.mk') }} - name: ${{ matrix.coin }}/x86_64-w64-mingw32 run: ./build_single.sh ${{ matrix.coin }} x86_64-w64-mingw32 -j$(nproc) - name: ${{ matrix.coin }}/i686-w64-mingw32 @@ -61,9 +62,10 @@ jobs: path: release/${{ matrix.coin }} lib_android: strategy: + fail-fast: false matrix: coin: [monero, wownero] - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 # container: # image: debian:bookworm steps: @@ -81,7 +83,7 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt install -y build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf unzip python-is-python3 + sudo apt install -y llvm pigz build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo6 gperf unzip python-is-python3 - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -101,8 +103,8 @@ jobs: uses: actions/cache@v4 with: path: | - ${{ matrix.coin }}/contrib/depends/built/* - key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }} + contrib/depends/built/* + key: depends-${{ github.job }}-${{ hashFiles('contrib/depends/packages/*.mk') }} - name: ${{ matrix.coin }}/x86_64-linux-android run: ./build_single.sh ${{ matrix.coin }} x86_64-linux-android -j$(nproc) - name: ${{ matrix.coin }}/aarch64-linux-android @@ -129,10 +131,11 @@ jobs: path: release/${{ matrix.coin }} - name: remove android_ndk run: | - rm -rf ${{ matrix.coin }}/contrib/depends/built/*/android_ndk - rm -rf ${{ matrix.coin }}/contrib/depends/sources/android-ndk-r26d-linux.zip + rm -rf contrib/depends/built/*/android_ndk + rm -rf contrib/depends/sources/android-ndk-r26d-linux.zip lib_linux: strategy: + fail-fast: false matrix: coin: [monero, wownero] runs-on: ubuntu-latest @@ -142,7 +145,7 @@ jobs: - name: Install dependencies run: | apt update - apt install -y build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu gcc-i686-linux-gnu g++-i686-linux-gnu + apt install -y pigz build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf gcc-aarch64-linux-gnu g++-aarch64-linux-gnu gcc-i686-linux-gnu g++-i686-linux-gnu - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -162,8 +165,8 @@ jobs: uses: actions/cache@v4 with: path: | - ${{ matrix.coin }}/contrib/depends/built/* - key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }} + contrib/depends/built/* + key: depends-${{ github.job }}-${{ hashFiles('contrib/depends/packages/*.mk') }} - name: ${{ matrix.coin }}/x86_64-linux-gnu run: ./build_single.sh ${{ matrix.coin }} x86_64-linux-gnu -j$(nproc) - name: ${{ matrix.coin }}/aarch64-linux-gnu @@ -188,173 +191,9 @@ jobs: with: name: linux ${{ matrix.coin }} path: release/${{ matrix.coin }} - lib_sailfishos_aarch64: - strategy: - matrix: - coin: [monero, wownero] - runs-on: buildjet-2vcpu-ubuntu-2204-arm - steps: - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - uses: thejerrybao/setup-swap-space@v1 - with: - swap-space-path: /swapfile - swap-size-gb: 8 - remove-existing-swap-files: true - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: recursive - - name: Patch sources - run: | - git config --global --add safe.directory '*' - git config --global user.email "ci@mrcyjanek.net" - git config --global user.name "CI mrcyjanek.net" - ./apply_patches.sh ${{ matrix.coin }} - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.job }}-${{ matrix.coin }} - - name: Cache built - if: ${{ !startsWith(github.ref, 'refs/tags/v') }} - uses: actions/cache@v4 - with: - path: | - ${{ matrix.coin }}/contrib/depends/built/* - key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }} - - name: ${{ matrix.coin }}/aarch64-meego-linux-gnu - run: | - docker run --rm -v $PWD/.ccache:/root/.ccache -v $PWD:$PWD -w $PWD ghcr.io/mrcyjanek/sailfishos:4.6.0.11_target_aarch64 bash -c 'zypper in -f -y perl-IPC-Cmd curl ccache gperf cmake ffmpeg-tools git clang libxkbcommon-devel wayland-protocols-devel wayland-client wayland-egl-devel make glibc-static && git config --global --add safe.directory "*" && for i in gcc g++ ar ranlib; do ln -s /usr/bin/$i /usr/bin/aarch64-meego-linux-gnu-$i; ln -s /usr/bin/$i /usr/bin/aarch64-linux-gnu-$i; done && ./build_single.sh ${{ matrix.coin }} aarch64-meego-linux-gnu -j$(nproc)' - - name: rename artifacts - run: | - sudo mkdir release/gh/ - for i in release/${{ matrix.coin }}/* - do - sudo cp "$i" "release/gh/${{ matrix.coin }}_$(basename $i)" - done - - name: Release - uses: softprops/action-gh-release@v2 - if: startsWith(github.ref, 'refs/tags/') - with: - files: release/gh/* - token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} - - name: Upload lib - uses: actions/upload-artifact@v4 - with: - name: sfos aarch64 ${{ matrix.coin }} - path: release/${{ matrix.coin }} - lib_sailfishos_i486: - strategy: - matrix: - coin: [monero, wownero] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: recursive - - name: Patch sources - run: | - git config --global --add safe.directory '*' - git config --global user.email "ci@mrcyjanek.net" - git config --global user.name "CI mrcyjanek.net" - ./apply_patches.sh ${{ matrix.coin }} - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.job }}-${{ matrix.coin }} - - name: Cache built - if: ${{ !startsWith(github.ref, 'refs/tags/v') }} - uses: actions/cache@v4 - with: - path: | - ${{ matrix.coin }}/contrib/depends/built/* - key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }} - - name: ${{ matrix.coin }}/aarch64-meego-linux-gnu - run: | - docker run --rm -v $PWD/.ccache:/root/.ccache -v $PWD:$PWD -w $PWD ghcr.io/mrcyjanek/sailfishos:4.6.0.11_target_i486 bash -c 'zypper in -f -y perl-IPC-Cmd curl ccache gperf cmake ffmpeg-tools git clang libxkbcommon-devel wayland-protocols-devel wayland-client wayland-egl-devel make glibc-static && git config --global --add safe.directory "*" && for i in gcc g++ ar ranlib; do ln -s /usr/bin/$i /usr/bin/i686-linux-gnu-$i; ln -s /usr/bin/$i /usr/bin/i686-meego-linux-gnu-$i; done && ./build_single.sh ${{ matrix.coin }} i686-meego-linux-gnu -j$(nproc)' - - name: rename artifacts - run: | - sudo mkdir release/gh/ - for i in release/${{ matrix.coin }}/* - do - sudo cp "$i" "release/gh/${{ matrix.coin }}_$(basename $i)" - done - - name: Release - uses: softprops/action-gh-release@v2 - if: startsWith(github.ref, 'refs/tags/') - with: - files: release/gh/* - token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} - - name: Upload lib - uses: actions/upload-artifact@v4 - with: - name: sfos_i486 ${{ matrix.coin }} - path: release/${{ matrix.coin }} - lib_darwin: - strategy: - matrix: - coin: [monero, wownero] - runs-on: ubuntu-latest - container: - image: debian:bookworm - steps: - - name: Install dependencies - run: | - apt update - apt install -y build-essential pkg-config autoconf libtool ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf python-is-python3 - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: recursive - - name: Patch sources - run: | - git config --global --add safe.directory '*' - git config --global user.email "ci@mrcyjanek.net" - git config --global user.name "CI mrcyjanek.net" - ./apply_patches.sh monero - ./apply_patches.sh wownero - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.job }}-${{ matrix.coin }} - - name: Cache built - if: ${{ !startsWith(github.ref, 'refs/tags/v') }} - uses: actions/cache@v4 - with: - path: | - ${{ matrix.coin }}/contrib/depends/built/* - key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }} - - name: ${{ matrix.coin }}/x86_64-apple-darwin11 - run: ./build_single.sh ${{ matrix.coin }} x86_64-apple-darwin11 -j$(nproc) - - name: ${{ matrix.coin }}/aarch64-apple-darwin11 - run: ./build_single.sh ${{ matrix.coin }} aarch64-apple-darwin11 -j$(nproc) - - name: rename artifacts - run: | - mkdir release/gh/ - for i in release/${{ matrix.coin }}/* - do - cp "$i" "release/gh/${{ matrix.coin }}_$(basename $i)" - done - - name: Release - uses: softprops/action-gh-release@v2 - if: startsWith(github.ref, 'refs/tags/') - with: - files: release/gh/* - token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} - - name: Upload lib - uses: actions/upload-artifact@v4 - with: - name: darwin ${{ matrix.coin }} - path: release/${{ matrix.coin }} - - name: remove darwin sdk - run: | - rm -rf ${{ matrix.coin }}/contrib/depends/built/*/darwin_sdk - rm -rf ${{ matrix.coin }}/contrib/depends/built/*/native_clang - rm -rf ${{ matrix.coin }}/contrib/depends/sources/clang-llvm*.tar.xz - rm -rf ${{ matrix.coin }}/contrib/depends/sources/MacOSX*sdk.tar.xz lib_macos: strategy: + fail-fast: false matrix: coin: [monero, wownero] name: macos build @@ -371,7 +210,7 @@ jobs: xcode-version: '15.4' - name: install dependencies run: | - brew install ccache unbound boost@1.76 zmq autoconf automake libtool && brew link boost@1.76 + brew install ccache binutils pigz autoconf automake libtool - name: Patch sources run: | git config --global --add safe.directory '*' @@ -387,11 +226,14 @@ jobs: uses: actions/cache@v4 with: path: | - ${{ matrix.coin }}/contrib/depends/built/* - key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }} - - name: build + contrib/depends/built/* + key: depends-${{ github.job }}-${{ hashFiles('contrib/depends/packages/*.mk') }} + - name: build (aarch64-apple-darwin) + run: | + ./build_single.sh ${{ matrix.coin }} aarch64-apple-darwin -j$(sysctl -n hw.logicalcpu) + - name: build (x86_64-apple-darwin) run: | - ./build_single.sh ${{ matrix.coin }} aarch64-host-apple-darwin -j$(sysctl -n hw.logicalcpu) + ./build_single.sh ${{ matrix.coin }} x86_64-apple-darwin -j$(sysctl -n hw.logicalcpu) - name: rename artifacts run: | mkdir release/gh/ @@ -412,6 +254,7 @@ jobs: path: release/${{ matrix.coin }} lib_ios: strategy: + fail-fast: false matrix: coin: [monero, wownero] name: ios build @@ -444,11 +287,11 @@ jobs: uses: actions/cache@v4 with: path: | - ${{ matrix.coin }}/contrib/depends/built/* - key: depends-${{ github.job }}-${{ matrix.coin }}-${{ hashFiles('*/contrib/depends/packages/*.mk') }} + contrib/depends/built/* + key: depends-${{ github.job }}-${{ hashFiles('contrib/depends/packages/*.mk') }} - name: build run: | - ./build_single.sh ${{ matrix.coin }} host-apple-ios -j$(sysctl -n hw.logicalcpu) + ./build_single.sh ${{ matrix.coin }} aarch64-apple-ios -j$(sysctl -n hw.logicalcpu) - name: rename artifacts run: | mkdir release/gh/ @@ -471,7 +314,7 @@ jobs: name: create single release file runs-on: ubuntu-latest needs: [ - lib_mingw, lib_android, lib_linux, lib_sailfishos_aarch64, lib_sailfishos_i486, lib_darwin, lib_macos, lib_ios + lib_mingw, lib_android, lib_linux, lib_macos, lib_ios ] steps: - uses: actions/download-artifact@v4 @@ -482,14 +325,6 @@ jobs: with: name: android wownero path: release/wownero - - uses: actions/download-artifact@v4 - with: - name: darwin monero - path: release/monero - - uses: actions/download-artifact@v4 - with: - name: darwin wownero - path: release/wownero - uses: actions/download-artifact@v4 with: name: ios monero @@ -522,22 +357,6 @@ jobs: with: name: mingw wownero path: release/wownero - - uses: actions/download-artifact@v4 - with: - name: sfos aarch64 monero - path: release/monero - - uses: actions/download-artifact@v4 - with: - name: sfos aarch64 wownero - path: release/wownero - - uses: actions/download-artifact@v4 - with: - name: sfos_i486 monero - path: release/monero - - uses: actions/download-artifact@v4 - with: - name: sfos_i486 wownero - path: release/wownero - name: zip release dir run: zip -r release-bundle.zip release - name: Release @@ -587,6 +406,7 @@ jobs: regression_check: strategy: + fail-fast: false matrix: coin: [monero, wownero] needs: [ @@ -646,7 +466,7 @@ jobs: name: comment on pr runs-on: ubuntu-latest needs: [ - lib_mingw, lib_android, lib_linux, lib_sailfishos_aarch64, lib_sailfishos_i486, lib_darwin, lib_macos, lib_ios, + lib_mingw, lib_android, lib_linux, lib_macos, lib_ios, ] steps: - uses: actions/github-script@v7 diff --git a/.gitmodules b/.gitmodules index a49395d..ecf86b2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,36 +2,13 @@ path = monero url = https://github.com/monero-project/monero branch = release-v0.18 + shallow = true [submodule "wownero"] path = wownero - url = https://github.com/wownero-mirror/wownero -[submodule "external/polyseed"] - path = external/polyseed - url = https://github.com/tevador/polyseed -[submodule "external/ios-cmake"] - path = external/ios-cmake - url = https://github.com/leetal/ios-cmake -[submodule "external/wownero-seed"] - path = external/wownero-seed - url = https://github.com/wownero-mirror/wownero-seed -[submodule "external/Apple-Boost-BuildScript"] - path = external/Apple-Boost-BuildScript - url = https://github.com/cake-tech/Apple-Boost-BuildScript.git - branch = build -[submodule "external/OpenSSL-for-iPhone"] - path = external/OpenSSL-for-iPhone - url = https://github.com/MrCyjaneK/OpenSSL-for-iPhone.git -[submodule "external/libsodium"] - path = external/libsodium - url = https://github.com/jedisct1/libsodium.git - branch = stable -[submodule "external/unbound"] - path = external/unbound - url = https://github.com/NLnetLabs/unbound.git - branch = release-1.16.2 -[submodule "external/libzmq"] - path = external/libzmq - url = https://github.com/zeromq/libzmq -[submodule "external/libexpat"] - path = external/libexpat - url = https://github.com/libexpat/libexpat.git + url = https://codeberg.org/wownero/wownero + shallow = true +[submodule "wownero_libwallet2_api_c/wownero-seed"] + path = wownero_libwallet2_api_c/wownero-seed + url = https://github.com/MrCyjaneK/wownero-seed + branch = cyjan-namespace2 + shallow = true diff --git a/README.md b/README.md index 63f3fce..4036888 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ TL;DR: ```bash $ rm -rf monero wownero release $ git submodule update --init --recursive --force -$ for coin in monero wownero; do ./apply_patches $coin; done +$ for coin in monero wownero; do ./apply_patches.sh $coin; done $ ./build_single monero $(gcc -dumpmachine) -j$(nproc) ``` diff --git a/build_single.sh b/build_single.sh index 4f0c5ad..f868f98 100755 --- a/build_single.sh +++ b/build_single.sh @@ -28,6 +28,7 @@ if [[ "x$repo" != "xwownero" && "x$repo" != "xmonero" ]]; then echo "Usage: $0 monero/wownero $(gcc -dumpmachine) -j$proccount" echo "Invalid target given, only monero and wownero are supported targets" + exit 1 fi if [[ ! -d "$repo" ]] @@ -53,354 +54,18 @@ then fi cd $(dirname $0) WDIR=$PWD -CC="" -CXX="" -case "$HOST_ABI" in - "x86_64-linux-gnu") - export CC="${HOST_ABI}-gcc" - export CXX="${HOST_ABI}-g++" - ;; - "i686-linux-gnu") - export CC="${HOST_ABI}-gcc" - export CXX="${HOST_ABI}-g++" - ;; - "i686-meego-linux-gnu") - # sanity checks, we should only run on native cpu - if [[ ! "$(uname -m)" == "x86_64" ]]; - then - echo "${HOST_ABI} builds are only supported on x86_64 host." - exit 1 - fi - # we also only support sailfishos linux - source /etc/os-release - if [[ ! "$ID" == "sailfishos" ]]; - then - echo "${HOST_ABI} builds are only supported on sailfishos host." - exit 1 - fi - export CC="${HOST_ABI}-gcc" - export CXX="${HOST_ABI}-g++" - ;; - "aarch64-linux-gnu") - export CC="${HOST_ABI}-gcc" - export CXX="${HOST_ABI}-g++" - ;; - "aarch64-meego-linux-gnu") - # sanity checks, we should only run on native cpu - if [[ ! "$(uname -m)" == "aarch64" ]]; - then - echo "${HOST_ABI} builds are only supported on aarch64 host." - exit 1 - fi - # we also only support sailfishos linux - source /etc/os-release - if [[ ! "$ID" == "sailfishos" ]]; - then - echo "${HOST_ABI} builds are only supported on sailfishos host." - exit 1 - fi - export CC="${HOST_ABI}-gcc" - export CXX="${HOST_ABI}-g++" - ;; - "x86_64-linux-android") - export PATH="$WDIR/$repo/contrib/depends/${HOST_ABI}/native/bin/:$PATH" - export CC=x86_64-linux-android21-clang - export CXX=x86_64-linux-android21-clang++ - ;; - "i686-linux-android") - export PATH="$WDIR/$repo/contrib/depends/${HOST_ABI}/native/bin/:$PATH" - export CC=i686-linux-android21-clang - export CXX=i686-linux-android21-clang++ - ;; - "aarch64-linux-android") - export PATH="$WDIR/$repo/contrib/depends/${HOST_ABI}/native/bin/:$PATH" - export CC=aarch64-linux-android21-clang - export CXX=aarch64-linux-android21-clang++ - ;; - "armv7a-linux-androideabi") - export PATH="$WDIR/$repo/contrib/depends/${HOST_ABI}/native/bin/:$PATH" - export CC=armv7a-linux-androideabi21-clang - export CXX=armv7a-linux-androideabi21-clang++ - ;; - "i686-w64-mingw32") - update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix - update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix - export CC=i686-w64-mingw32-gcc-posix - export CXX=i686-w64-mingw32-g++-posix - ;; - "x86_64-w64-mingw32") - $SUDO update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix - $SUDO update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix - export CC=x86_64-w64-mingw32-gcc-posix - export CXX=x86_64-w64-mingw32-g++-posix - ;; - "x86_64-apple-darwin11") - export PATH="$WDIR/$repo/contrib/depends/x86_64-apple-darwin11/native/bin:$PATH" - export CC="clang -stdlib=libc++ -target x86_64-apple-darwin11 -mmacosx-version-min=10.14 --sysroot $WDIR/$repo/contrib/depends/x86_64-apple-darwin11/native/SDK/ -mlinker-version=609 -B$WDIR/$repo/contrib/depends/x86_64-apple-darwin11/native/bin/x86_64-apple-darwin11-" - export CXX="clang++ -stdlib=libc++ -target x86_64-apple-darwin11 -mmacosx-version-min=10.14 --sysroot $WDIR/$repo/contrib/depends/x86_64-apple-darwin11/native/SDK/ -mlinker-version=609 -B$WDIR/$repo/contrib/depends/x86_64-apple-darwin11/native/bin/x86_64-apple-darwin11-" - ;; - "aarch64-apple-darwin11") - export PATH="$WDIR/$repo/contrib/depends/aarch64-apple-darwin11/native/bin:$PATH" - export CC="clang -stdlib=libc++ -target arm64-apple-darwin11 -mmacosx-version-min=10.14 --sysroot $WDIR/$repo/contrib/depends/aarch64-apple-darwin11/native/SDK/ -mlinker-version=609 -B$WDIR/$repo/contrib/depends/aarch64-apple-darwin11/native/bin/aarch64-apple-darwin11-" - export CXX="clang++ -stdlib=libc++ -target arm64-apple-darwin11 -mmacosx-version-min=10.14 --sysroot $WDIR/$repo/contrib/depends/aarch64-apple-darwin11/native/SDK/ -mlinker-version=609 -B$WDIR/$repo/contrib/depends/aarch64-apple-darwin11/native/bin/aarch64-apple-darwin11-" - ;; - "host-apple-darwin"|"x86_64-host-apple-darwin"|"aarch64-host-apple-darwin") - export CC="clang" - export CXX="clang++" - ;; - "host-apple-ios") - export IOS_CC="clang -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk" - export IOS_CXX="clang++ -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk" - ;; - *) - echo "Unsupported target." - exit 1 - ;; -esac -pushd $repo/contrib/depends - case "$HOST_ABI" in - "x86_64-linux-gnu" | "i686-linux-gnu" | "i686-meego-linux-gnu" | "aarch64-linux-gnu" | "aarch64-meego-linux-gnu" | "x86_64-linux-android" | "i686-linux-android" | "aarch64-linux-android" | "armv7a-linux-androideabi" | "i686-w64-mingw32" | "x86_64-w64-mingw32" | "x86_64-apple-darwin11" | "aarch64-apple-darwin11") - env -i PATH="$PATH" CC=gcc CXX=g++ make HOST="$HOST_ABI" "$NPROC" - ;; - "host-apple-darwin" | "x86_64-host-apple-darwin" | "aarch64-host-apple-darwin") - echo "====================================" - echo "= =" - echo "= CHECK README.md IF BUILD FAILS =" - echo "= =" - echo "====================================" - echo "WARN: using host dependencies on macos." - POLYSEED_DIR=../../../external/polyseed/build/${HOST_ABI} - rm -rf ${POLYSEED_DIR} - mkdir -p ${POLYSEED_DIR} - pushd ${POLYSEED_DIR} - CC="${CC}" CXX="${CXX}" cmake ../.. - make $NPROC - popd - if [[ "$repo" == "wownero" ]]; - then - WOWNEROSEED_DIR=../../../external/wownero-seed/build/${HOST_ABI} - rm -rf ${WOWNEROSEED_DIR} - mkdir -p ${WOWNEROSEED_DIR} - pushd ${WOWNEROSEED_DIR} - pushd ../.. - git reset --hard - patch -p1 < ../wownero-seed-0001-fix-duplicate-symbol-error.patch - popd - CC="${CC}" CXX="${CXX}" cmake ../.. - make $NPROC - popd - fi - MACOS_LIBS_DIR="${PWD}/${HOST_ABI}" - rm -rf ${MACOS_LIBS_DIR} - mkdir -p ${MACOS_LIBS_DIR}/lib - if [[ "$(uname -m)" == "arm64" ]]; - then - export HOMEBREW_PREFIX="/opt/homebrew" - elif [[ "$(uname -m)" == "x86_64" ]]; - then - export HOMEBREW_PREFIX="/usr/local" - fi - pushd ../../../external/macos - ./build_unbound.sh - popd - # NOTE: we can use unbound from brew but app store rejects the app because of nghttp2 symbols being included - # verbose_copy "${HOMEBREW_PREFIX}/lib/libunbound.a" ${MACOS_LIBS_DIR}/lib/libunbound.a - verbose_copy "../../../external/macos/build/MACOS/lib/libunbound.a" ${MACOS_LIBS_DIR}/lib/libunbound.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_chrono-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_chrono-mt.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_locale-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_locale-mt.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_date_time-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_date_time-mt.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_filesystem-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_filesystem-mt.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_program_options-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_program_options-mt.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_regex-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_regex-mt.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_serialization-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_serialization-mt.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_system-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_system-mt.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_thread-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_thread-mt.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libboost_wserialization-mt.a" ${MACOS_LIBS_DIR}/lib/libboost_wserialization-mt.a - verbose_copy "${POLYSEED_DIR}/libpolyseed.a" ${MACOS_LIBS_DIR}/lib/libpolyseed.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libssl.a" ${MACOS_LIBS_DIR}/lib/libssl.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libcrypto.a" ${MACOS_LIBS_DIR}/lib/libcrypto.a - verbose_copy "${HOMEBREW_PREFIX}/lib/libsodium.a" ${MACOS_LIBS_DIR}/lib/libsodium.a - if [[ "$repo" == "wownero" ]]; - then - verbose_copy "${WOWNEROSEED_DIR}/libwownero-seed.a" ${MACOS_LIBS_DIR}/lib/libwownero-seed.a - fi - ;; - "host-apple-ios") - echo "====================================" - echo "= =" - echo "= CHECK README.md IF BUILD FAILS =" - echo "= =" - echo "====================================" - pwd - pushd ../../../external/ios - ./install_missing_headers.sh - ./build_openssl.sh - ./build_boost.sh - ./build_sodium.sh - ./build_zmq.sh - ./build_unbound.sh - if [[ "$repo" == "wownero" ]]; - then - ./build_wownero_seed.sh - fi - popd - POLYSEED_DIR=../../../external/polyseed/build/${HOST_ABI} - rm -rf ${POLYSEED_DIR} - mkdir -p ${POLYSEED_DIR} - pushd ${POLYSEED_DIR} - CC="${IOS_CC}" CXX="${IOS_CXX}" cmake -DCMAKE_TOOLCHAIN_FILE=../../../ios-cmake/ios.toolchain.cmake -DPLATFORM=OS64 ../.. - make $NPROC - popd - if [[ "$repo" == "wownero" ]]; - then - WOWNEROSEED_DIR=../../../external/wownero-seed/build/${HOST_ABI} - rm -rf ${WOWNEROSEED_DIR} - mkdir -p ${WOWNEROSEED_DIR} - pushd ${WOWNEROSEED_DIR} - pushd ../.. - git reset --hard - patch -p1 < ../wownero-seed-0001-fix-duplicate-symbol-error.patch - popd - CC="${CC}" CXX="${CXX}" cmake -DCMAKE_TOOLCHAIN_FILE=../../../ios-cmake/ios.toolchain.cmake -DPLATFORM=OS64 ../.. - make $NPROC - popd - fi - IOS_LIBS_DIR="${PWD}/host-apple-ios" - rm -rf ${IOS_LIBS_DIR} - mkdir -p ${IOS_LIBS_DIR}/lib - export IOS_PREFIX="$(realpath "${PWD}/../../../external/ios/build/ios")" - verbose_copy "${IOS_PREFIX}/lib/libunbound.a" ${IOS_LIBS_DIR}/lib/libunbound.a - verbose_copy "${IOS_PREFIX}/lib/libboost_chrono.a" ${IOS_LIBS_DIR}/lib/libboost_chrono.a - verbose_copy "${IOS_PREFIX}/lib/libboost_locale.a" ${IOS_LIBS_DIR}/lib/libboost_locale.a - verbose_copy "${IOS_PREFIX}/lib/libboost_date_time.a" ${IOS_LIBS_DIR}/lib/libboost_date_time.a - verbose_copy "${IOS_PREFIX}/lib/libboost_filesystem.a" ${IOS_LIBS_DIR}/lib/libboost_filesystem.a - verbose_copy "${IOS_PREFIX}/lib/libboost_program_options.a" ${IOS_LIBS_DIR}/lib/libboost_program_options.a - verbose_copy "${IOS_PREFIX}/lib/libboost_regex.a" ${IOS_LIBS_DIR}/lib/libboost_regex.a - verbose_copy "${IOS_PREFIX}/lib/libboost_serialization.a" ${IOS_LIBS_DIR}/lib/libboost_serialization.a - verbose_copy "${IOS_PREFIX}/lib/libboost_system.a" ${IOS_LIBS_DIR}/lib/libboost_system.a - verbose_copy "${IOS_PREFIX}/lib/libboost_thread.a" ${IOS_LIBS_DIR}/lib/libboost_thread.a - verbose_copy "${IOS_PREFIX}/lib/libboost_wserialization.a" ${IOS_LIBS_DIR}/lib/libboost_wserialization.a - verbose_copy "${POLYSEED_DIR}/libpolyseed.a" ${IOS_LIBS_DIR}/lib/libpolyseed.a - verbose_copy "${IOS_PREFIX}/lib/libssl.a" ${IOS_LIBS_DIR}/lib/libssl.a - verbose_copy "${IOS_PREFIX}/lib/libcrypto.a" ${IOS_LIBS_DIR}/lib/libcrypto.a - verbose_copy "${IOS_PREFIX}/lib/libsodium.a" ${IOS_LIBS_DIR}/lib/libsodium.a - if [[ "$repo" == "wownero" ]]; - then - verbose_copy "${WOWNEROSEED_DIR}/libwownero-seed.a" ${IOS_LIBS_DIR}/lib/libwownero-seed.a - fi - # verbose_copy "${IOS_PREFIX}/lib/libevent.a" ${IOS_LIBS_DIR}/lib/libevent.a - ;; - *) - echo "Unable to build dependencies for '$HOST_ABI'." - exit 1 - ;; - esac +pushd contrib/depends + env -i PATH="$PATH" CC=gcc CXX=g++ make "$NPROC" HOST="$HOST_ABI" popd buildType=Debug -rm -rf $repo/build/${HOST_ABI} 2>/dev/null || true -mkdir -p $repo/build/${HOST_ABI} -pushd $repo/build/${HOST_ABI} - case "$HOST_ABI" in - "x86_64-linux-gnu") - env CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.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=$buildType -D ANDROID=false -D BUILD_TAG="linux-x64" -D CMAKE_SYSTEM_NAME="Linux" ../.. - ;; - "i686-linux-gnu") - env CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.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=$buildType -D ANDROID=false -D BUILD_TAG="linux-x86" -D CMAKE_SYSTEM_NAME="Linux" ../.. - ;; - "i686-meego-linux-gnu") - env CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.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=$buildType -D ANDROID=false -D BUILD_TAG="linux-x86" -D CMAKE_SYSTEM_NAME="Linux" ../.. - ;; - "aarch64-linux-gnu") - env CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.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=$buildType -D ANDROID=false -D BUILD_TAG="linux-armv8" -D CMAKE_SYSTEM_NAME="Linux" ../.. - ;; - "aarch64-meego-linux-gnu") - env CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.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=$buildType -D ANDROID=false -D BUILD_TAG="linux-armv8" -D CMAKE_SYSTEM_NAME="Linux" ../.. - ;; - "x86_64-linux-android") - env CC="${CC}" CXX="${CXX}" cmake -DMONERO_WALLET_CRYPTO_LIBRARY=amd64-64-24k -DHIDAPI_DUMMY=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_SYSTEM_VERSION=1 -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.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=$buildType -D ANDROID=true -D BUILD_TAG="android-x86_64" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_ARCH_ABI="x86_64" ../.. - ;; - "i686-linux-android") - env CC="${CC}" CXX="${CXX}" cmake -DHIDAPI_DUMMY=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_SYSTEM_VERSION=1 -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="x86" -D STATIC=ON -D BUILD_64="OFF" -D CMAKE_BUILD_TYPE=$buildType -D ANDROID=true -D BUILD_TAG="android-x86" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_ARCH_ABI="x86" ../.. - ;; - "aarch64-linux-android") - env CC="${CC}" CXX="${CXX}" cmake -DHIDAPI_DUMMY=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_SYSTEM_VERSION=1 -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.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=$buildType -D ANDROID=true -D BUILD_TAG="android-armv8" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_ARCH_ABI="arm64-v8a" ../.. - ;; - "armv7a-linux-androideabi") - env CC="${CC}" CXX="${CXX}" cmake -DHIDAPI_DUMMY=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_SYSTEM_VERSION=1 -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.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=$buildType -D ANDROID=true -D BUILD_TAG="android-armv7" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_ARCH_ABI="armeabi-v7a" ../.. - ;; - "x86_64-w64-mingw32") - env CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=$buildType -D BUILD_TAG="win-x64" ../.. - ;; - "i686-w64-mingw32") - env CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=$buildType -D BUILD_TAG="win-x32" ../.. - ;; - "x86_64-apple-darwin11") - env CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=$buildType -D BUILD_TAG="mac-x64" ../.. - ;; - "aarch64-apple-darwin11") - env CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_TOOLCHAIN_FILE=$PWD/../../contrib/depends/${HOST_ABI}/share/toolchain.cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D STATIC=ON -D ARCH="armv8-a" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=$buildType -D BUILD_TAG="mac-armv8" ../.. - ;; - "host-apple-darwin" | "x86_64-host-apple-darwin" | "aarch64-host-apple-darwin") - PREFIX="$(realpath "${PWD}/../../../external/macos/build/MACOS")" - env \ - CMAKE_INCLUDE_PATH="${PREFIX}/include" \ - CMAKE_LIBRARY_PATH="${PREFIX}/lib" \ - CC="${CC}" CXX="${CXX}" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=$buildType ../.. - ;; - "host-apple-ios") - PREFIX="$(realpath "${PWD}/../../../external/ios/build/ios")" - # echo $PREFIX - # exit 1 - env \ - CMAKE_INCLUDE_PATH="${PREFIX}/include" \ - CMAKE_LIBRARY_PATH="${PREFIX}/lib" \ - CC="${IOS_CC}" CXX="${IOS_CXX}" cmake -DHIDAPI_DUMMY=ON -D IOS=ON -D ARCH=arm64 -D CMAKE_BUILD_DYPE=$buildType -D STATIC=ON -D BUILD_GUI_DEPS=1 -D UNBOUND_INCLUDE_DIR="${PREFIX}/lib" ../.. - ;; - *) - echo "we don't know how to compile monero for '$HOST_ABI'" - exit 1 - ;; - esac - CC=gcc CXX=g++ make wallet_api $NPROC -popd - -# Special treatment for apple -if [[ "${HOST_ABI}" == "x86_64-apple-darwin11" || "${HOST_ABI}" == "aarch64-apple-darwin11" ]]; -then - ${HOST_ABI}-ranlib $PWD/$repo/contrib/depends/${HOST_ABI}/lib/libpolyseed.a - if [[ "$repo" == "wownero" ]]; - then - ${HOST_ABI}-ranlib $PWD/$repo/contrib/depends/${HOST_ABI}/lib/libwownero-seed.a - fi -fi - pushd ${repo}_libwallet2_api_c rm -rf build/${HOST_ABI} || true mkdir -p build/${HOST_ABI} -p pushd build/${HOST_ABI} - case $HOST_ABI in - "x86_64-linux-gnu" | "i686-linux-gnu" | "i686-meego-linux-gnu" | "aarch64-linux-gnu" | "aarch64-meego-linux-gnu" | "i686-w64-mingw32" | "x86_64-w64-mingw32" | "x86_64-apple-darwin11" | "aarch64-apple-darwin11" | "host-apple-darwin" | "x86_64-host-apple-darwin" | "aarch64-host-apple-darwin") - echo $CC - env CC="${CC}" CXX="${CXX}" cmake -DMONERO_FLAVOR=$repo -DCMAKE_BUILD_TYPE=Debug -DHOST_ABI=${HOST_ABI} ../.. - CC="${CC}" CXX="${CXX}" make $NPROC - ;; - "x86_64-linux-android" | "i686-linux-android" | "aarch64-linux-android" | "armv7a-linux-androideabi") - echo $CC - env CC="${CC}" CXX="${CXX}" cmake -DMONERO_FLAVOR=$repo -DCMAKE_BUILD_TYPE=Debug -DHOST_ABI=${HOST_ABI} ../.. - CC="${CC}" CXX="${CXX}" make $NPROC - ;; - "host-apple-ios") - export -n CC CXX - CC=clang CXX=clang++ cmake -DCMAKE_TOOLCHAIN_FILE=../../../external/ios-cmake/ios.toolchain.cmake -DPLATFORM=OS64 -DMONERO_FLAVOR=$repo -DCMAKE_BUILD_TYPE=Debug -DHOST_ABI=${HOST_ABI} ../.. - CC=clang CXX=clang++ make $NPROC - ;; - *) - echo "Unable to build ${repo}_libwallet2_api_c for ${HOST_ABI}" - exit 1 - ;; - esac + cmake -DCMAKE_TOOLCHAIN_FILE=$PWD/../../../contrib/depends/${HOST_ABI}/share/toolchain.cmake -DUSE_DEVICE_TREZOR=OFF -DMONERO_FLAVOR=$repo -DCMAKE_BUILD_TYPE=Debug -DHOST_ABI=${HOST_ABI} ../.. + make $NPROC popd popd @@ -409,11 +74,12 @@ pushd release/$repo APPENDIX="" if [[ "${HOST_ABI}" == "x86_64-w64-mingw32" || "${HOST_ABI}" == "i686-w64-mingw32" ]]; then + echo "TODO: check if it's still needed" APPENDIX="${APPENDIX}dll" - cp ../../$repo/build/${HOST_ABI}/external/polyseed/libpolyseed.${APPENDIX} ${HOST_ABI}_libpolyseed.${APPENDIX} - rm ${HOST_ABI}_libpolyseed.${APPENDIX}.xz || true - xz -e ${HOST_ABI}_libpolyseed.${APPENDIX} - elif [[ "${HOST_ABI}" == "x86_64-apple-darwin11" || "${HOST_ABI}" == "aarch64-apple-darwin11" || "${HOST_ABI}" == "host-apple-darwin" || "${HOST_ABI}" == "x86_64-host-apple-darwin" || "${HOST_ABI}" == "aarch64-host-apple-darwin" || "${HOST_ABI}" == "host-apple-ios" ]]; + # cp ../../$repo/build/${HOST_ABI}/external/polyseed/libpolyseed.${APPENDIX} ${HOST_ABI}_libpolyseed.${APPENDIX} + # rm ${HOST_ABI}_libpolyseed.${APPENDIX}.xz || true + # xz -e ${HOST_ABI}_libpolyseed.${APPENDIX} + elif [[ "${HOST_ABI}" == "x86_64-apple-darwin11" || "${HOST_ABI}" == "aarch64-apple-darwin11" || "${HOST_ABI}" == "host-apple-darwin" || "${HOST_ABI}" == "x86_64-host-apple-darwin" || "${HOST_ABI}" == "aarch64-apple-darwin" || "${HOST_ABI}" == "x86_64-apple-darwin" || "${HOST_ABI}" == "host-apple-ios" || "${HOST_ABI}" == "aarch64-apple-ios" ]]; then APPENDIX="${APPENDIX}dylib" else diff --git a/contrib/depends/.gitignore b/contrib/depends/.gitignore new file mode 100644 index 0000000..3cb4b9a --- /dev/null +++ b/contrib/depends/.gitignore @@ -0,0 +1,10 @@ +SDKs/ +work/ +built/ +sources/ +config.site +x86_64* +i686* +mips* +arm* +aarch64* diff --git a/contrib/depends/Makefile b/contrib/depends/Makefile new file mode 100644 index 0000000..7474dcf --- /dev/null +++ b/contrib/depends/Makefile @@ -0,0 +1,240 @@ +.NOTPARALLEL : + +SOURCES_PATH ?= $(BASEDIR)/sources +BASE_CACHE ?= $(BASEDIR)/built +FALLBACK_DOWNLOAD_PATH ?= https://static.mrcyjanek.net/download_mirror/ + +BUILD = $(shell ./config.guess) +HOST ?= $(BUILD) +PATCHES_PATH = $(BASEDIR)/patches +BASEDIR = $(CURDIR) +HASH_LENGTH:=11 +DOWNLOAD_CONNECT_TIMEOUT:=30 +DOWNLOAD_RETRIES:=5 +HOST_ID_SALT ?= salt +BUILD_ID_SALT ?= salt +# Detect the number of CPU cores +ifeq ($(shell uname), Darwin) + NUM_CORES := $(shell sysctl -n hw.ncpu) +else ifeq ($(shell uname), Linux) + NUM_CORES := $(shell nproc) +else + NUM_CORES := 1 # Default if the OS is not detected +endif + +host:=$(BUILD) +ifneq ($(HOST),) +host:=$(HOST) +host_toolchain:=$(HOST)- +endif + +ifneq ($(DEBUG),) +release_type=Debug +else +release_type=Release +endif + +ifneq ($(TESTS),) +build_tests=ON +release_type=Debug +else +build_tests=OFF +endif + +base_build_dir=$(BASEDIR)/work/build +base_staging_dir=$(BASEDIR)/work/staging +base_download_dir=$(BASEDIR)/work/download +canonical_host:=$(shell ./config.sub $(HOST)) +build:=$(shell ./config.sub $(BUILD)) + +build_arch =$(firstword $(subst -, ,$(build))) +build_vendor=$(word 2,$(subst -, ,$(build))) +full_build_os:=$(subst $(build_arch)-$(build_vendor)-,,$(build)) +build_os:=$(findstring linux,$(full_build_os)) +build_os+=$(findstring darwin,$(full_build_os)) +build_os:=$(strip $(build_os)) +ifeq ($(build_os),) +build_os=$(full_build_os) +endif + +host_arch=$(firstword $(subst -, ,$(canonical_host))) +host_vendor=$(word 2,$(subst -, ,$(canonical_host))) +full_host_os:=$(subst $(host_arch)-$(host_vendor)-,,$(canonical_host)) +host_os:=$(findstring android,$(full_host_os)) +ifeq ($(host_os),) +host_os:=$(findstring linux,$(full_host_os)) +endif +host_os+=$(findstring darwin,$(full_host_os)) +host_os+=$(findstring freebsd,$(full_host_os)) +host_os+=$(findstring mingw32,$(full_host_os)) +host_os:=$(strip $(host_os)) +ifeq ($(host_os),) +host_os=$(full_host_os) +endif + +$(host_arch)_$(host_os)_prefix=$(BASEDIR)/$(host) +$(host_arch)_$(host_os)_host=$(host) +host_prefix=$($(host_arch)_$(host_os)_prefix) +build_prefix=$(host_prefix)/native +ifeq ($(host_os),mingw32) +host_cmake=Windows +endif +ifeq ($(host_os),linux) +host_cmake=Linux +endif +ifeq ($(host_os),freebsd) +host_cmake=FreeBSD +endif +ifeq ($(host_os),darwin) +host_cmake=Darwin +endif +ifeq ($(host_os),ios) +host_cmake=iOS +endif +ifeq ($(host_os),android) +host_cmake=Android +endif + +AT_$(V):= +AT_:=@ +AT:=$(AT_$(V)) + +all: install + +include hosts/$(host_os).mk +include hosts/default.mk +include builders/$(build_os).mk +include builders/default.mk +include packages/packages.mk + +build_id_string:=$(BUILD_ID_SALT) +build_id_string+=$(shell $(build_CC) --version 2>/dev/null) +build_id_string+=$(shell $(build_AR) --version 2>/dev/null) +build_id_string+=$(shell $(build_CXX) --version 2>/dev/null) +build_id_string+=$(shell $(build_RANLIB) --version 2>/dev/null) +build_id_string+=$(shell $(build_STRIP) --version 2>/dev/null) + +$(host_arch)_$(host_os)_id_string:=$(HOST_ID_SALT) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_CC) --version 2>/dev/null) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_AR) --version 2>/dev/null) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) --version 2>/dev/null) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null) +$(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null) + +packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) +native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) + +all_packages = $(packages) $(native_packages) + +meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk + +$(host_arch)_$(host_os)_native_toolchain?=$($(host_os)_native_toolchain) + +include funcs.mk + +CONF_PKGS := cmake-conf mxe-conf + +build-only-$(1)_$(3): CMAKE_RUNRESULT_FILE = $(PREFIX)/share/cmake/modules/TryRunResults.cmake +build-only-$(1)_$(3): CMAKE_TOOLCHAIN_FILE = $(PREFIX)/$(3)/share/cmake/mxe-conf.cmake +build-only-$(1)_$(3): CMAKE_TOOLCHAIN_DIR = $(PREFIX)/$(3)/share/cmake/mxe-conf.d +build-only-$(1)_$(3): CMAKE_STATIC_BOOL = $(if $(findstring shared,$(3)),OFF,ON) +build-only-$(1)_$(3): CMAKE_SHARED_BOOL = $(if $(findstring shared,$(3)),ON,OFF) + + +toolchain_path=$($($(host_arch)_$(host_os)_native_toolchain)_prefixbin) +final_build_id_long+=$(shell $(build_SHA256SUM) config.site.in) +final_build_id_long+=$(shell $(build_SHA256SUM) toolchain.cmake.in) +final_build_id+=$(shell echo -n "$(final_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH)) +$(host_prefix)/.stamp_$(final_build_id): $(native_packages) $(packages) + $(AT)rm -rf $(@D) + $(AT)mkdir -p $(@D) + $(AT)echo copying packages: $^ + $(AT)echo to: $(@D) + $(AT)cd $(@D); $(foreach package,$^, tar xf $($(package)_cached); ) + $(AT)touch $@ + +$(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_build_id) + $(AT)@mkdir -p $(@D) + $(AT)sed -e 's|@HOST@|$(host)|' \ + -e 's|@CC@|$(toolchain_path)$(host_CC)|' \ + -e 's|@CXX@|$(toolchain_path)$(host_CXX)|' \ + -e 's|@AR@|$(toolchain_path)$(host_AR)|' \ + -e 's|@RANLIB@|$(toolchain_path)$(host_RANLIB)|' \ + -e 's|@NM@|$(toolchain_path)$(host_NM)|' \ + -e 's|@STRIP@|$(toolchain_path)$(host_STRIP)|' \ + -e 's|@build_os@|$(build_os)|' \ + -e 's|@host_os@|$(host_os)|' \ + -e 's|@CFLAGS@|$(strip $(host_CFLAGS) $(host_$(release_type)_CFLAGS))|' \ + -e 's|@CXXFLAGS@|$(strip $(host_CXXFLAGS) $(host_$(release_type)_CXXFLAGS))|' \ + -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ + -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ + -e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ + -e 's|@debug@|$(DEBUG)|' \ + $< > $@ + $(AT)touch $@ + +$(host_prefix)/share/toolchain.cmake : toolchain.cmake.in $(host_prefix)/.stamp_$(final_build_id) + $(AT)@mkdir -p $(@D) + $(AT)sed -e 's|@HOST@|$(host)|' \ + -e 's|@CC@|$(toolchain_path)$(host_CC)|' \ + -e 's|@CXX@|$(toolchain_path)$(host_CXX)|' \ + -e 's|@AR@|$(toolchain_path)$(host_AR)|' \ + -e 's|@RANLIB@|$(toolchain_path)$(host_RANLIB)|' \ + -e 's|@NM@|$(toolchain_path)$(host_NM)|' \ + -e 's|@STRIP@|$(toolchain_path)$(host_STRIP)|' \ + -e 's|@build_os@|$(build_os)|' \ + -e 's|@host_os@|$(host_os)|' \ + -e 's|@CFLAGS@|$(strip $(host_CFLAGS) $(host_$(release_type)_CFLAGS))|' \ + -e 's|@CXXFLAGS@|$(strip $(host_CXXFLAGS) $(host_$(release_type)_CXXFLAGS))|' \ + -e 's|@CPPFLAGS@|$(strip $(host_CPPFLAGS) $(host_$(release_type)_CPPFLAGS))|' \ + -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ + -e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ + -e 's|@debug@|$(DEBUG)|' \ + -e 's|@release_type@|$(release_type)|' \ + -e 's|@build_tests@|$(build_tests)|' \ + -e 's|@depends@|$(host_cmake)|' \ + -e 's|@prefix@|$($(host_arch)_$(host_os)_prefix)|'\ + -e 's|@arch@|$(host_arch)|'\ + $< > $@ + $(AT)touch $@ + +define check_or_remove_cached + mkdir -p $(BASE_CACHE)/$(host)/$(package) && cd $(BASE_CACHE)/$(host)/$(package); \ + $(build_SHA256SUM) -c $($(package)_cached_checksum) >/dev/null 2>/dev/null || \ + ( rm -f $($(package)_cached_checksum); \ + if test -f "$($(package)_cached)"; then echo "Checksum mismatch for $(package). Forcing rebuild.."; rm -f $($(package)_cached_checksum) $($(package)_cached); fi ) +endef + +define check_or_remove_sources + mkdir -p $($(package)_source_dir); cd $($(package)_source_dir); \ + test -f $($(package)_fetched) && ( $(build_SHA256SUM) -c $($(package)_fetched) >/dev/null 2>/dev/null || \ + ( echo "Checksum missing or mismatched for $(package) source. Forcing re-download."; \ + rm -f $($(package)_all_sources) $($(1)_fetched))) || true +endef + +check-packages: + @$(foreach package,$(all_packages),$(call check_or_remove_cached,$(package));) +check-sources: + @$(foreach package,$(all_packages),$(call check_or_remove_sources,$(package));) + +$(host_prefix)/share/config.site: check-packages +$(host_prefix)/share/toolchain.cmake: check-packages + +check-packages: check-sources + +install: $(host_prefix)/share/config.site check-packages +install: $(host_prefix)/share/toolchain.cmake check-packages + +download-one: check-sources $(all_sources) + +download-osx: + @$(MAKE) -s HOST=x86_64-apple-darwin11 download-one +download-linux: + @$(MAKE) -s HOST=x86_64-unknown-linux-gnu download-one +download-win: + @$(MAKE) -s HOST=x86_64-w64-mingw32 download-one +download: download-osx download-linux download-win + + $(foreach package,$(all_packages),$(eval $(call ext_add_stages,$(package)))) + +.PHONY: install cached download-one download-osx download-linux download-win download check-packages check-sources diff --git a/contrib/depends/README.md b/contrib/depends/README.md new file mode 100644 index 0000000..1aa5b27 --- /dev/null +++ b/contrib/depends/README.md @@ -0,0 +1,74 @@ +### Usage + +To build dependencies for the current arch+OS: + +```bash +make +``` + +To build for another arch/OS: + +```bash +make HOST=host-platform-triplet +``` + +For example: + +```bash +make HOST=x86_64-w64-mingw32 -j4 +``` + +A toolchain will be generated that's suitable for plugging into Monero's +cmake. In the above example, a dir named x86_64-w64-mingw32 will be +created. To use it for Monero: + +```bash +cmake -DCMAKE_TOOLCHAIN=`pwd`/contrib/depends/x86_64-w64-mingw32 +``` + +Common `host-platform-triplets` for cross compilation are: + +- `i686-w64-mingw32` for Win32 +- `x86_64-w64-mingw32` for Win64 +- `x86_64-apple-darwin11` for MacOSX x86_64 +- `arm-linux-gnueabihf` for Linux ARM 32 bit +- `aarch64-linux-gnu` for Linux ARM 64 bit +- `riscv64-linux-gnu` for Linux RISCV 64 bit + +No other options are needed, the paths are automatically configured. + +Dependency Options: +The following can be set when running make: make FOO=bar + +``` +SOURCES_PATH: downloaded sources will be placed here +BASE_CACHE: built packages will be placed here +FALLBACK_DOWNLOAD_PATH: If a source file can't be fetched, try here before giving up +DEBUG: disable some optimizations and enable more runtime checking +HOST_ID_SALT: Optional salt to use when generating host package ids +BUILD_ID_SALT: Optional salt to use when generating build package ids +``` + +Additional targets: + +``` +download: run 'make download' to fetch all sources without building them +download-osx: run 'make download-osx' to fetch all sources needed for osx builds +download-win: run 'make download-win' to fetch all sources needed for win builds +download-linux: run 'make download-linux' to fetch all sources needed for linux builds +``` + +#Mingw builds + +Building for 32/64bit mingw requires switching alternatives to a posix mode + +```bash +update-alternatives --set x86_64-w64-mingw32-g++ x86_64-w64-mingw32-g++-posix +update-alternatives --set x86_64-w64-mingw32-gcc x86_64-w64-mingw32-gcc-posix +``` + +### Other documentation + +- [description.md](description.md): General description of the depends system +- [packages.md](packages.md): Steps for adding packages + diff --git a/contrib/depends/builders/darwin.mk b/contrib/depends/builders/darwin.mk new file mode 100644 index 0000000..bbc0475 --- /dev/null +++ b/contrib/depends/builders/darwin.mk @@ -0,0 +1,23 @@ +build_darwin_CC: = $(shell xcrun -f clang) +build_darwin_CXX: = $(shell xcrun -f clang++) +build_darwin_AR: = $(shell xcrun -f ar) +build_darwin_RANLIB: = $(shell xcrun -f ranlib) +build_darwin_STRIP: = $(shell xcrun -f strip) +build_darwin_OTOOL: = $(shell xcrun -f otool) +build_darwin_NM: = $(shell xcrun -f nm) +build_darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) +build_darwin_SHA256SUM = shasum -a 256 +build_darwin_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o + +#darwin host on darwin builder. overrides darwin host preferences. + +darwin_CC=$(shell xcrun -f clang) -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(shell xcrun --sdk macosx --show-sdk-path) -I$(shell xcrun --sdk macosx --show-sdk-path)/usr/include -I$(host_prefix)/include +darwin_CXX:=$(shell xcrun -f clang++) -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ --sysroot $(shell xcrun --sdk macosx --show-sdk-path) -I$(shell xcrun --sdk macosx --show-sdk-path)/usr/include -I$(host_prefix)/include +darwin_AR:=$(shell xcrun -f ar) +darwin_RANLIB:=$(shell xcrun -f ranlib) +darwin_STRIP:=$(shell xcrun -f strip) +darwin_LIBTOOL:=$(shell xcrun -f libtool) +darwin_OTOOL:=$(shell xcrun -f otool) +darwin_NM:=$(shell xcrun -f nm) +darwin_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) +darwin_native_toolchain= diff --git a/contrib/depends/builders/default.mk b/contrib/depends/builders/default.mk new file mode 100644 index 0000000..59a887f --- /dev/null +++ b/contrib/depends/builders/default.mk @@ -0,0 +1,21 @@ +default_build_CC = gcc +default_build_CXX = g++ +default_build_AR = ar +default_build_RANLIB = ranlib +default_build_STRIP = strip +default_build_NM = nm +default_build_OTOOL = otool +default_build_INSTALL_NAME_TOOL = install_name_tool + + +define add_build_tool_func +build_$(build_os)_$1 ?= $$(default_build_$1) +build_$(build_arch)_$(build_os)_$1 ?= $$(build_$(build_os)_$1) +build_$1=$$(build_$(build_arch)_$(build_os)_$1) +endef +$(foreach var,CC CXX AR RANLIB NM STRIP SHA256SUM DOWNLOAD OTOOL INSTALL_NAME_TOOL,$(eval $(call add_build_tool_func,$(var)))) +define add_build_flags_func +build_$(build_arch)_$(build_os)_$1 += $(build_$(build_os)_$1) +build_$1=$$(build_$(build_arch)_$(build_os)_$1) +endef +$(foreach flags, CFLAGS CXXFLAGS ARFLAGS LDFLAGS, $(eval $(call add_build_flags_func,$(flags)))) diff --git a/contrib/depends/builders/linux.mk b/contrib/depends/builders/linux.mk new file mode 100644 index 0000000..b03f424 --- /dev/null +++ b/contrib/depends/builders/linux.mk @@ -0,0 +1,2 @@ +build_linux_SHA256SUM = sha256sum +build_linux_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -o diff --git a/contrib/depends/config.guess b/contrib/depends/config.guess new file mode 100755 index 0000000..48a6846 --- /dev/null +++ b/contrib/depends/config.guess @@ -0,0 +1,1815 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2024 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2024-07-27' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess +# +# Please send patches to . + + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system '$me' is run on. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2024 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try '$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +# Just in case it came from the environment. +GUESS= + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still +# use 'HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039,SC3028 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c17 c99 c89 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD=$driver + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if test -f /.attbin/uname ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case $UNAME_SYSTEM in +Linux|GNU|GNU/*) + LIBC=unknown + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #if defined(__ANDROID__) + LIBC=android + #else + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #elif defined(__GLIBC__) + LIBC=gnu + #elif defined(__LLVM_LIBC__) + LIBC=llvm + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif + #endif + #endif + EOF + cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$cc_set_libc" + + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu + fi + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + echo unknown)` + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=$UNAME_MACHINE_ARCH-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case $UNAME_MACHINE_ARCH in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case $UNAME_MACHINE_ARCH in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case $UNAME_VERSION in + Debian*) + release='-gnu' + ;; + *) + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + GUESS=$machine-${os}${release}${abi-} + ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE + ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE + ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE + ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE + ;; + *:MidnightBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE + ;; + *:ekkoBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE + ;; + *:SolidBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE + ;; + *:OS108:*:*) + GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE + ;; + macppc:MirBSD:*:*) + GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE + ;; + *:MirBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE + ;; + *:Sortix:*:*) + GUESS=$UNAME_MACHINE-unknown-sortix + ;; + *:Twizzler:*:*) + GUESS=$UNAME_MACHINE-unknown-twizzler + ;; + *:Redox:*:*) + GUESS=$UNAME_MACHINE-unknown-redox + ;; + mips:OSF1:*.*) + GUESS=mips-dec-osf1 + ;; + alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case $ALPHA_CPU_TYPE in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + GUESS=$UNAME_MACHINE-dec-osf$OSF_REL + ;; + Amiga*:UNIX_System_V:4.0:*) + GUESS=m68k-unknown-sysv4 + ;; + *:[Aa]miga[Oo][Ss]:*:*) + GUESS=$UNAME_MACHINE-unknown-amigaos + ;; + *:[Mm]orph[Oo][Ss]:*:*) + GUESS=$UNAME_MACHINE-unknown-morphos + ;; + *:OS/390:*:*) + GUESS=i370-ibm-openedition + ;; + *:z/VM:*:*) + GUESS=s390-ibm-zvmoe + ;; + *:OS400:*:*) + GUESS=powerpc-ibm-os400 + ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + GUESS=arm-acorn-riscix$UNAME_RELEASE + ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + GUESS=arm-unknown-riscos + ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + GUESS=hppa1.1-hitachi-hiuxmpp + ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + case `(/bin/universe) 2>/dev/null` in + att) GUESS=pyramid-pyramid-sysv3 ;; + *) GUESS=pyramid-pyramid-bsd ;; + esac + ;; + NILE*:*:*:dcosx) + GUESS=pyramid-pyramid-svr4 + ;; + DRS?6000:unix:4.0:6*) + GUESS=sparc-icl-nx6 + ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) GUESS=sparc-icl-nx7 ;; + esac + ;; + s390x:SunOS:*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL + ;; + sun4H:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-hal-solaris2$SUN_REL + ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris2$SUN_REL + ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + GUESS=i386-pc-auroraux$UNAME_RELEASE + ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$SUN_ARCH-pc-solaris2$SUN_REL + ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris3$SUN_REL + ;; + sun4*:SunOS:*:*) + case `/usr/bin/arch -k` in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like '4.1.3-JL'. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` + GUESS=sparc-sun-sunos$SUN_REL + ;; + sun3*:SunOS:*:*) + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case `/bin/arch` in + sun3) + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; + sun4) + GUESS=sparc-sun-sunos$UNAME_RELEASE + ;; + esac + ;; + aushp:SunOS:*:*) + GUESS=sparc-auspex-sunos$UNAME_RELEASE + ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + GUESS=m68k-milan-mint$UNAME_RELEASE + ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + GUESS=m68k-hades-mint$UNAME_RELEASE + ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + GUESS=m68k-unknown-mint$UNAME_RELEASE + ;; + m68k:machten:*:*) + GUESS=m68k-apple-machten$UNAME_RELEASE + ;; + powerpc:machten:*:*) + GUESS=powerpc-apple-machten$UNAME_RELEASE + ;; + RISC*:Mach:*:*) + GUESS=mips-dec-mach_bsd4.3 + ;; + RISC*:ULTRIX:*:*) + GUESS=mips-dec-ultrix$UNAME_RELEASE + ;; + VAX*:ULTRIX*:*:*) + GUESS=vax-dec-ultrix$UNAME_RELEASE + ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + GUESS=clipper-intergraph-clix$UNAME_RELEASE + ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && + { echo "$SYSTEM_NAME"; exit; } + GUESS=mips-mips-riscos$UNAME_RELEASE + ;; + Motorola:PowerMAX_OS:*:*) + GUESS=powerpc-motorola-powermax + ;; + Motorola:*:4.3:PL8-*) + GUESS=powerpc-harris-powermax + ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + GUESS=powerpc-harris-powermax + ;; + Night_Hawk:Power_UNIX:*:*) + GUESS=powerpc-harris-powerunix + ;; + m88k:CX/UX:7*:*) + GUESS=m88k-harris-cxux7 + ;; + m88k:*:4*:R4*) + GUESS=m88k-motorola-sysv4 + ;; + m88k:*:3*:R3*) + GUESS=m88k-motorola-sysv3 + ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 + then + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x + then + GUESS=m88k-dg-dgux$UNAME_RELEASE + else + GUESS=m88k-dg-dguxbcs$UNAME_RELEASE + fi + else + GUESS=i586-dg-dgux$UNAME_RELEASE + fi + ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + GUESS=m88k-dolphin-sysv3 + ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + GUESS=m88k-motorola-sysv3 + ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + GUESS=m88k-tektronix-sysv3 + ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + GUESS=m68k-tektronix-bsd + ;; + *:IRIX*:*:*) + IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` + GUESS=mips-sgi-irix$IRIX_REL + ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id + ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + GUESS=i386-ibm-aix + ;; + ia64:AIX:*:*) + if test -x /usr/bin/oslevel ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE + fi + GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV + ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + + int + main () + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` + then + GUESS=$SYSTEM_NAME + else + GUESS=rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + GUESS=rs6000-ibm-aix3.2.4 + else + GUESS=rs6000-ibm-aix3.2 + fi + ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE + fi + GUESS=$IBM_ARCH-ibm-aix$IBM_REV + ;; + *:AIX:*:*) + GUESS=rs6000-ibm-aix + ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + GUESS=romp-ibm-bsd4.4 + ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to + ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + GUESS=rs6000-bull-bosx + ;; + DPX/2?00:B.O.S.:*:*) + GUESS=m68k-bull-sysv3 + ;; + 9000/[34]??:4.3bsd:1.*:*) + GUESS=m68k-hp-bsd + ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + GUESS=m68k-hp-bsd4.4 + ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + case $UNAME_MACHINE in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if test -x /usr/bin/getconf; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case $sc_cpu_version in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case $sc_kernel_bits in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + + #define _HPUX_SOURCE + #include + #include + + int + main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if test "$HP_ARCH" = hppa2.0w + then + set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + GUESS=$HP_ARCH-hp-hpux$HPUX_REV + ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + GUESS=ia64-hp-hpux$HPUX_REV + ;; + 3050*:HI-UX:*:*) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + GUESS=unknown-hitachi-hiuxwe2 + ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + GUESS=hppa1.1-hp-bsd + ;; + 9000/8??:4.3bsd:*:*) + GUESS=hppa1.0-hp-bsd + ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + GUESS=hppa1.0-hp-mpeix + ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + GUESS=hppa1.1-hp-osf + ;; + hp8??:OSF1:*:*) + GUESS=hppa1.0-hp-osf + ;; + i*86:OSF1:*:*) + if test -x /usr/sbin/sysversion ; then + GUESS=$UNAME_MACHINE-unknown-osf1mk + else + GUESS=$UNAME_MACHINE-unknown-osf1 + fi + ;; + parisc*:Lites*:*:*) + GUESS=hppa1.1-hp-lites + ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + GUESS=c1-convex-bsd + ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + GUESS=c34-convex-bsd + ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + GUESS=c38-convex-bsd + ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + GUESS=c4-convex-bsd + ;; + CRAY*Y-MP:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=ymp-cray-unicos$CRAY_REL + ;; + CRAY*[A-Z]90:*:*:*) + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=t90-cray-unicos$CRAY_REL + ;; + CRAY*T3E:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=alphaev5-cray-unicosmk$CRAY_REL + ;; + CRAY*SV1:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=sv1-cray-unicos$CRAY_REL + ;; + *:UNICOS/mp:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=craynv-cray-unicosmp$CRAY_REL + ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE + ;; + sparc*:BSD/OS:*:*) + GUESS=sparc-unknown-bsdi$UNAME_RELEASE + ;; + *:BSD/OS:*:*) + GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE + ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi + else + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf + fi + ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + amd64) + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; + esac + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL + ;; + i*:CYGWIN*:*) + GUESS=$UNAME_MACHINE-pc-cygwin + ;; + *:MINGW64*:*) + GUESS=$UNAME_MACHINE-pc-mingw64 + ;; + *:MINGW*:*) + GUESS=$UNAME_MACHINE-pc-mingw32 + ;; + *:MSYS*:*) + GUESS=$UNAME_MACHINE-pc-msys + ;; + i*:PW*:*) + GUESS=$UNAME_MACHINE-pc-pw32 + ;; + *:SerenityOS:*:*) + GUESS=$UNAME_MACHINE-pc-serenity + ;; + *:Interix*:*) + case $UNAME_MACHINE in + x86) + GUESS=i586-pc-interix$UNAME_RELEASE + ;; + authenticamd | genuineintel | EM64T) + GUESS=x86_64-unknown-interix$UNAME_RELEASE + ;; + IA64) + GUESS=ia64-unknown-interix$UNAME_RELEASE + ;; + esac ;; + i*:UWIN*:*) + GUESS=$UNAME_MACHINE-pc-uwin + ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + GUESS=x86_64-pc-cygwin + ;; + prep*:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=powerpcle-unknown-solaris2$SUN_REL + ;; + *:GNU:*:*) + # the GNU system + GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` + GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL + ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC + ;; + x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-pc-managarm-mlibc" + ;; + *:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" + ;; + *:Minix:*:*) + GUESS=$UNAME_MACHINE-unknown-minix + ;; + aarch64:Linux:*:*) + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __ARM_EABI__ + #ifdef __ARM_PCS_VFP + ABI=eabihf + #else + ABI=eabi + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; + esac + fi + GUESS=$CPU-unknown-linux-$LIBCABI + ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arm*:Linux:*:*) + set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi + else + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf + fi + fi + ;; + avr32*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + cris:Linux:*:*) + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + crisv32:Linux:*:*) + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + e2k:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + frv:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + hexagon:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + i*86:Linux:*:*) + GUESS=$UNAME_MACHINE-pc-linux-$LIBC + ;; + ia64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + k1om:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:cos:*:*) + GUESS=$UNAME_MACHINE-unknown-cos + ;; + kvx:mbr:*:*) + GUESS=$UNAME_MACHINE-unknown-mbr + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + m32r*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + m68*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + mips:Linux:*:* | mips64:Linux:*:*) + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" + #undef CPU + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + MIPS_ENDIAN=el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + MIPS_ENDIAN= + #else + MIPS_ENDIAN= + #endif + #endif +EOF + cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` + eval "$cc_set_vars" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + openrisc*:Linux:*:*) + GUESS=or1k-unknown-linux-$LIBC + ;; + or32:Linux:*:* | or1k*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + padre:Linux:*:*) + GUESS=sparc-unknown-linux-$LIBC + ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + GUESS=hppa64-unknown-linux-$LIBC + ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; + PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; + *) GUESS=hppa-unknown-linux-$LIBC ;; + esac + ;; + ppc64:Linux:*:*) + GUESS=powerpc64-unknown-linux-$LIBC + ;; + ppc:Linux:*:*) + GUESS=powerpc-unknown-linux-$LIBC + ;; + ppc64le:Linux:*:*) + GUESS=powerpc64le-unknown-linux-$LIBC + ;; + ppcle:Linux:*:*) + GUESS=powerpcle-unknown-linux-$LIBC + ;; + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + s390:Linux:*:* | s390x:Linux:*:*) + GUESS=$UNAME_MACHINE-ibm-linux-$LIBC + ;; + sh64*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + sh*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + tile*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + vax:Linux:*:*) + GUESS=$UNAME_MACHINE-dec-linux-$LIBC + ;; + x86_64:Linux:*:*) + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac + fi + GUESS=$CPU-pc-linux-$LIBCABI + ;; + xtensa*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + GUESS=i386-sequent-sysv4 + ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION + ;; + i*86:OS/2:*:*) + # If we were able to find 'uname', then EMX Unix compatibility + # is probably installed. + GUESS=$UNAME_MACHINE-pc-os2-emx + ;; + i*86:XTS-300:*:STOP) + GUESS=$UNAME_MACHINE-unknown-stop + ;; + i*86:atheos:*:*) + GUESS=$UNAME_MACHINE-unknown-atheos + ;; + i*86:syllable:*:*) + GUESS=$UNAME_MACHINE-pc-syllable + ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + GUESS=i386-unknown-lynxos$UNAME_RELEASE + ;; + i*86:*DOS:*:*) + GUESS=$UNAME_MACHINE-pc-msdosdjgpp + ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL + else + GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL + fi + ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL + else + GUESS=$UNAME_MACHINE-pc-sysv32 + fi + ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + GUESS=i586-pc-msdosdjgpp + ;; + Intel:Mach:3*:*) + GUESS=i386-pc-mach3 + ;; + paragon:*:*:*) + GUESS=i860-intel-osf1 + ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 + fi + ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + GUESS=m68010-convergent-sysv + ;; + mc68k:UNIX:SYSTEM5:3.51m) + GUESS=m68k-convergent-sysv + ;; + M680?0:D-NIX:5.3:*) + GUESS=m68k-diab-dnix + ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + GUESS=m68k-unknown-lynxos$UNAME_RELEASE + ;; + mc68030:UNIX_System_V:4.*:*) + GUESS=m68k-atari-sysv4 + ;; + TSUNAMI:LynxOS:2.*:*) + GUESS=sparc-unknown-lynxos$UNAME_RELEASE + ;; + rs6000:LynxOS:2.*:*) + GUESS=rs6000-unknown-lynxos$UNAME_RELEASE + ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + GUESS=powerpc-unknown-lynxos$UNAME_RELEASE + ;; + SM[BE]S:UNIX_SV:*:*) + GUESS=mips-dde-sysv$UNAME_RELEASE + ;; + RM*:ReliantUNIX-*:*:*) + GUESS=mips-sni-sysv4 + ;; + RM*:SINIX-*:*:*) + GUESS=mips-sni-sysv4 + ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + GUESS=$UNAME_MACHINE-sni-sysv4 + else + GUESS=ns32k-sni-sysv + fi + ;; + PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort + # says + GUESS=i586-unisys-sysv4 + ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + GUESS=hppa1.1-stratus-sysv4 + ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + GUESS=i860-stratus-sysv4 + ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + GUESS=$UNAME_MACHINE-stratus-vos + ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + GUESS=hppa1.1-stratus-vos + ;; + mc68*:A/UX:*:*) + GUESS=m68k-apple-aux$UNAME_RELEASE + ;; + news*:NEWS-OS:6*:*) + GUESS=mips-sony-newsos6 + ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if test -d /usr/nec; then + GUESS=mips-nec-sysv$UNAME_RELEASE + else + GUESS=mips-unknown-sysv$UNAME_RELEASE + fi + ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + GUESS=powerpc-be-beos + ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + GUESS=powerpc-apple-beos + ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + GUESS=i586-pc-beos + ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + GUESS=i586-pc-haiku + ;; + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku + ;; + SX-4:SUPER-UX:*:*) + GUESS=sx4-nec-superux$UNAME_RELEASE + ;; + SX-5:SUPER-UX:*:*) + GUESS=sx5-nec-superux$UNAME_RELEASE + ;; + SX-6:SUPER-UX:*:*) + GUESS=sx6-nec-superux$UNAME_RELEASE + ;; + SX-7:SUPER-UX:*:*) + GUESS=sx7-nec-superux$UNAME_RELEASE + ;; + SX-8:SUPER-UX:*:*) + GUESS=sx8-nec-superux$UNAME_RELEASE + ;; + SX-8R:SUPER-UX:*:*) + GUESS=sx8r-nec-superux$UNAME_RELEASE + ;; + SX-ACE:SUPER-UX:*:*) + GUESS=sxace-nec-superux$UNAME_RELEASE + ;; + Power*:Rhapsody:*:*) + GUESS=powerpc-apple-rhapsody$UNAME_RELEASE + ;; + *:Rhapsody:*:*) + GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE + ;; + arm64:Darwin:*:*) + GUESS=aarch64-apple-darwin$UNAME_RELEASE + ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE + fi + GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE + ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE + ;; + *:QNX:*:4*) + GUESS=i386-pc-qnx + ;; + NEO-*:NONSTOP_KERNEL:*:*) + GUESS=neo-tandem-nsk$UNAME_RELEASE + ;; + NSE-*:NONSTOP_KERNEL:*:*) + GUESS=nse-tandem-nsk$UNAME_RELEASE + ;; + NSR-*:NONSTOP_KERNEL:*:*) + GUESS=nsr-tandem-nsk$UNAME_RELEASE + ;; + NSV-*:NONSTOP_KERNEL:*:*) + GUESS=nsv-tandem-nsk$UNAME_RELEASE + ;; + NSX-*:NONSTOP_KERNEL:*:*) + GUESS=nsx-tandem-nsk$UNAME_RELEASE + ;; + *:NonStop-UX:*:*) + GUESS=mips-compaq-nonstopux + ;; + BS2000:POSIX*:*:*) + GUESS=bs2000-siemens-sysv + ;; + DS/*:UNIX_System_V:*:*) + GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE + ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "${cputype-}" = 386; then + UNAME_MACHINE=i386 + elif test "x${cputype-}" != x; then + UNAME_MACHINE=$cputype + fi + GUESS=$UNAME_MACHINE-unknown-plan9 + ;; + *:TOPS-10:*:*) + GUESS=pdp10-unknown-tops10 + ;; + *:TENEX:*:*) + GUESS=pdp10-unknown-tenex + ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + GUESS=pdp10-dec-tops20 + ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + GUESS=pdp10-xkl-tops20 + ;; + *:TOPS-20:*:*) + GUESS=pdp10-unknown-tops20 + ;; + *:ITS:*:*) + GUESS=pdp10-unknown-its + ;; + SEI:*:*:SEIUX) + GUESS=mips-sei-seiux$UNAME_RELEASE + ;; + *:DragonFly:*:*) + DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL + ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case $UNAME_MACHINE in + A*) GUESS=alpha-dec-vms ;; + I*) GUESS=ia64-dec-vms ;; + V*) GUESS=vax-dec-vms ;; + esac ;; + *:XENIX:*:SysV) + GUESS=i386-pc-xenix + ;; + i*86:skyos:*:*) + SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` + GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL + ;; + i*86:rdos:*:*) + GUESS=$UNAME_MACHINE-pc-rdos + ;; + i*86:Fiwix:*:*) + GUESS=$UNAME_MACHINE-pc-fiwix + ;; + *:AROS:*:*) + GUESS=$UNAME_MACHINE-unknown-aros + ;; + x86_64:VMkernel:*:*) + GUESS=$UNAME_MACHINE-unknown-esx + ;; + amd64:Isilon\ OneFS:*:*) + GUESS=x86_64-unknown-onefs + ;; + *:Unleashed:*:*) + GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE + ;; + *:Ironclad:*:*) + GUESS=$UNAME_MACHINE-unknown-ironclad + ;; +esac + +# Do we have a guess based on uname results? +if test "x$GUESS" != x; then + echo "$GUESS" + exit +fi + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +int +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case $UNAME_MACHINE:$UNAME_SYSTEM in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 <&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" +EOF +fi + +exit 1 + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/contrib/depends/config.site.in b/contrib/depends/config.site.in new file mode 100755 index 0000000..11dab89 --- /dev/null +++ b/contrib/depends/config.site.in @@ -0,0 +1,66 @@ +depends_prefix="`dirname ${ac_site_file}`/.." + +cross_compiling=maybe +host_alias=@HOST@ +ac_tool_prefix=${host_alias}- + +if test -z $with_boost; then + with_boost=$depends_prefix +fi + +if test x@host_os@ = xdarwin; then + BREW=no + PORT=no +fi + +PATH=$depends_prefix/native/bin:$PATH +PKG_CONFIG="`which pkg-config` --static" + +# These two need to remain exported because pkg-config does not see them +# otherwise. That means they must be unexported at the end of configure.ac to +# avoid ruining the cache. Sigh. +export PKG_CONFIG_PATH=$depends_prefix/share/pkgconfig:$depends_prefix/lib/pkgconfig +if test -z "@allow_host_packages@"; then + export PKGCONFIG_LIBDIR= +fi + +CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS" +LDFLAGS="-L$depends_prefix/lib $LDFLAGS" + +CC="@CC@" +CXX="@CXX@" +OBJC="${CC}" +CCACHE=$depends_prefix/native/bin/ccache +PYTHONPATH=$depends_prefix/native/lib/python/dist-packages:$PYTHONPATH + +if test -n "@AR@"; then + AR=@AR@ + ac_cv_path_ac_pt_AR=${AR} +fi + +if test -n "@RANLIB@"; then + RANLIB=@RANLIB@ + ac_cv_path_ac_pt_RANLIB=${RANLIB} +fi + +if test -n "@NM@"; then + NM=@NM@ + ac_cv_path_ac_pt_NM=${NM} +fi + +if test -n "@debug@"; then + enable_reduce_exports=no +fi + +if test -n "@CFLAGS@"; then + CFLAGS="@CFLAGS@ $CFLAGS" +fi +if test -n "@CXXFLAGS@"; then + CXXFLAGS="@CXXFLAGS@ $CXXFLAGS" +fi +if test -n "@CPPFLAGS@"; then + CPPFLAGS="@CPPFLAGS@ $CPPFLAGS" +fi +if test -n "@LDFLAGS@"; then + LDFLAGS="@LDFLAGS@ $LDFLAGS" +fi diff --git a/contrib/depends/config.sub b/contrib/depends/config.sub new file mode 100755 index 0000000..4aaae46 --- /dev/null +++ b/contrib/depends/config.sub @@ -0,0 +1,2354 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2024 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale + +timestamp='2024-05-27' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2024 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try '$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo "$1" + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Split fields of configuration type +saved_IFS=$IFS +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 + ;; + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 + ;; + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + cloudabi*-eabi* \ + | kfreebsd*-gnu* \ + | knetbsd*-gnu* \ + | kopensolaris*-gnu* \ + | linux-* \ + | managarm-* \ + | netbsd*-eabi* \ + | netbsd*-gnu* \ + | nto-qnx* \ + | os2-emx* \ + | rtmk-nova* \ + | storm-chaos* \ + | uclinux-gnu* \ + | uclinux-uclibc* \ + | windows-* ) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac + ;; + *-*) + case $field1-$field2 in + # Shorthands that happen to contain a single dash + convex-c[12] | convex-c3[248]) + basic_machine=$field2-convex + basic_os= + ;; + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Do not treat sunos as a manufacturer + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + # Manufacturers + 3100* \ + | 32* \ + | 3300* \ + | 3600* \ + | 7300* \ + | acorn \ + | altos* \ + | apollo \ + | apple \ + | atari \ + | att* \ + | axis \ + | be \ + | bull \ + | cbm \ + | ccur \ + | cisco \ + | commodore \ + | convergent* \ + | convex* \ + | cray \ + | crds \ + | dec* \ + | delta* \ + | dg \ + | digital \ + | dolphin \ + | encore* \ + | gould \ + | harris \ + | highlevel \ + | hitachi* \ + | hp \ + | ibm* \ + | intergraph \ + | isi* \ + | knuth \ + | masscomp \ + | microblaze* \ + | mips* \ + | motorola* \ + | ncr* \ + | news \ + | next \ + | ns \ + | oki \ + | omron* \ + | pc533* \ + | rebel \ + | rom68k \ + | rombug \ + | semi \ + | sequent* \ + | siemens \ + | sgi* \ + | siemens \ + | sim \ + | sni \ + | sony* \ + | stratus \ + | sun \ + | sun[234]* \ + | tektronix \ + | tti* \ + | ultra \ + | unicom* \ + | wec \ + | winbond \ + | wrs) + basic_machine=$field1-$field2 + basic_os= + ;; + zephyr*) + basic_machine=$field1-unknown + basic_os=$field2 + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac + ;; + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac + ;; +esac + +# Decode 1-component or ad-hoc basic machines +case $basic_machine in + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond + ;; + op50n) + cpu=hppa1.1 + vendor=oki + ;; + op60c) + cpu=hppa1.1 + vendor=oki + ;; + ibm*) + cpu=i370 + vendor=ibm + ;; + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple + ;; + + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + cpu=m68000 + vendor=att + ;; + 3b*) + cpu=we32k + vendor=att + ;; + bluegene*) + cpu=powerpc + vendor=ibm + basic_os=cnk + ;; + decsystem10* | dec10*) + cpu=pdp10 + vendor=dec + basic_os=tops10 + ;; + decsystem20* | dec20*) + cpu=pdp10 + vendor=dec + basic_os=tops20 + ;; + delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300) + cpu=m68k + vendor=motorola + ;; + # This used to be dpx2*, but that gets the RS6000-based + # DPX/20 and the x86-based DPX/2-100 wrong. See + # https://oldskool.silicium.org/stations/bull_dpx20.htm + # https://www.feb-patrimoine.com/english/bull_dpx2.htm + # https://www.feb-patrimoine.com/english/unix_and_bull.htm + dpx2 | dpx2[23]00 | dpx2[23]xx) + cpu=m68k + vendor=bull + ;; + dpx2100 | dpx21xx) + cpu=i386 + vendor=bull + ;; + dpx20) + cpu=rs6000 + vendor=bull + ;; + encore | umax | mmax) + cpu=ns32k + vendor=encore + ;; + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} + ;; + fx2800) + cpu=i860 + vendor=alliant + ;; + genix) + cpu=ns32k + vendor=ns + ;; + h3050r* | hiux*) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + cpu=m68000 + vendor=hp + ;; + hp9k3[2-9][0-9]) + cpu=m68k + vendor=hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + i*86v32) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv32 + ;; + i*86v4*) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv4 + ;; + i*86v) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv + ;; + i*86sol2) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} + ;; + iris | iris4d) + cpu=mips + vendor=sgi + case $basic_os in + irix*) + ;; + *) + basic_os=irix4 + ;; + esac + ;; + miniframe) + cpu=m68000 + vendor=convergent + ;; + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint + ;; + news-3600 | risc-news) + cpu=mips + vendor=sony + basic_os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + ;; + np1) + cpu=np1 + vendor=gould + ;; + op50n-* | op60c-*) + cpu=hppa1.1 + vendor=oki + basic_os=proelf + ;; + pa-hitachi) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + pbd) + cpu=sparc + vendor=tti + ;; + pbb) + cpu=m68k + vendor=tti + ;; + pc532) + cpu=ns32k + vendor=pc532 + ;; + pn) + cpu=pn + vendor=gould + ;; + power) + cpu=power + vendor=ibm + ;; + ps2) + cpu=i386 + vendor=ibm + ;; + rm[46]00) + cpu=mips + vendor=siemens + ;; + rtpc | rtpc-*) + cpu=romp + vendor=ibm + ;; + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks + ;; + tower | tower-32) + cpu=m68k + vendor=ncr + ;; + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu + ;; + w65) + cpu=w65 + vendor=wdc + ;; + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf + ;; + none) + cpu=none + vendor=none + ;; + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine + ;; + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` + ;; + + *-*) + saved_IFS=$IFS + IFS="-" read cpu vendor <&2 + exit 1 + ;; + esac + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $vendor in + digital*) + vendor=dec + ;; + commodore*) + vendor=cbm + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if test x"$basic_os" != x +then + +# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just +# set os. +obj= +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` + ;; + os2-emx) + kernel=os2 + os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` + ;; + nto-qnx*) + kernel=nto + os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + saved_IFS=$IFS + IFS="-" read kernel os <&2 + fi + ;; + *) + echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 + exit 1 + ;; +esac + +case $obj in + aout* | coff* | elf* | pe*) + ;; + '') + # empty is fine + ;; + *) + echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 + exit 1 + ;; +esac + +# Here we handle the constraint that a (synthetic) cpu and os are +# valid only in combination with each other and nowhere else. +case $cpu-$os in + # The "javascript-unknown-ghcjs" triple is used by GHC; we + # accept it here in order to tolerate that, but reject any + # variations. + javascript-ghcjs) + ;; + javascript-* | *-ghcjs) + echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os-$obj in + linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ + | linux-mlibc*- | linux-musl*- | linux-newlib*- \ + | linux-relibc*- | linux-uclibc*- | linux-ohos*- ) + ;; + uclinux-uclibc*- | uclinux-gnu*- ) + ;; + managarm-mlibc*- | managarm-kernel*- ) + ;; + windows*-msvc*-) + ;; + -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \ + | -uclibc*- ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + -kernel*- ) + echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + *-kernel*- ) + echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 + exit 1 + ;; + *-msvc*- ) + echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 + exit 1 + ;; + kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-) + ;; + vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) + ;; + nto-qnx*-) + ;; + os2-emx-) + ;; + rtmk-nova-) + ;; + *-eabi*- | *-gnueabi*-) + ;; + none--*) + # None (no kernel, i.e. freestanding / bare metal), + # can be paired with an machine code file format + ;; + -*-) + # Blank kernel with real OS is always fine. + ;; + --*) + # Blank kernel and OS with real machine code file format is always fine. + ;; + *-*-*) + echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 + exit 1 + ;; +esac + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) + vendor=acorn + ;; + *-sunos* | *-solaris*) + vendor=sun + ;; + *-cnk* | *-aix*) + vendor=ibm + ;; + *-beos*) + vendor=be + ;; + *-hpux*) + vendor=hp + ;; + *-mpeix*) + vendor=hp + ;; + *-hiux*) + vendor=hitachi + ;; + *-unos*) + vendor=crds + ;; + *-dgux*) + vendor=dg + ;; + *-luna*) + vendor=omron + ;; + *-genix*) + vendor=ns + ;; + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) + vendor=ibm + ;; + s390-* | s390x-*) + vendor=ibm + ;; + *-ptx*) + vendor=sequent + ;; + *-tpf*) + vendor=ibm + ;; + *-vxsim* | *-vxworks* | *-windiss*) + vendor=wrs + ;; + *-aux*) + vendor=apple + ;; + *-hms*) + vendor=hitachi + ;; + *-mpw* | *-macos*) + vendor=apple + ;; + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) + vendor=atari + ;; + *-vos*) + vendor=stratus + ;; + esac + ;; +esac + +echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" +exit + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/contrib/depends/description.md b/contrib/depends/description.md new file mode 100644 index 0000000..74f9ef3 --- /dev/null +++ b/contrib/depends/description.md @@ -0,0 +1,53 @@ +This is a system of building and caching dependencies necessary for building Bitcoin. +There are several features that make it different from most similar systems: + +### It is designed to be builder and host agnostic + +In theory, binaries for any target OS/architecture can be created, from a +builder running any OS/architecture. In practice, build-side tools must be +specified when the defaults don't fit, and packages must be amended to work +on new hosts. For now, a build architecture of x86_64 is assumed, either on +Linux or OSX. + +### No reliance on timestamps + +File presence is used to determine what needs to be built. This makes the +results distributable and easily digestable by automated builders. + +### Each build only has its specified dependencies available at build-time. + +For each build, the sysroot is wiped and the (recursive) dependencies are +installed. This makes each build deterministic, since there will never be any +unknown files available to cause side-effects. + +### Each package is cached and only rebuilt as needed. + +Before building, a unique build-id is generated for each package. This id +consists of a hash of all files used to build the package (Makefiles, packages, +etc), and as well as a hash of the same data for each recursive dependency. If +any portion of a package's build recipe changes, it will be rebuilt as well as +any other package that depends on it. If any of the main makefiles (Makefile, +funcs.mk, etc) are changed, all packages will be rebuilt. After building, the +results are cached into a tarball that can be re-used and distributed. + +### Package build results are (relatively) deterministic. + +Each package is configured and patched so that it will yield the same +build-results with each consequent build, within a reasonable set of +constraints. Some things like timestamp insertion are unavoidable, and are +beyond the scope of this system. Additionally, the toolchain itself must be +capable of deterministic results. When revisions are properly bumped, a cached +build should represent an exact single payload. + +### Sources are fetched and verified automatically + +Each package must define its source location and checksum. The build will fail +if the fetched source does not match. Sources may be pre-seeded and/or cached +as desired. + +### Self-cleaning + +Build and staging dirs are wiped after use, and any previous version of a +cached result is removed following a successful build. Automated builders +should be able to build each revision and store the results with no further +intervention. diff --git a/contrib/depends/funcs.mk b/contrib/depends/funcs.mk new file mode 100644 index 0000000..70b592f --- /dev/null +++ b/contrib/depends/funcs.mk @@ -0,0 +1,292 @@ +define int_vars +#Set defaults for vars which may be overridden per-package +$(1)_cc=$($($(1)_type)_CC) +$(1)_cxx=$($($(1)_type)_CXX) +$(1)_objc=$($($(1)_type)_OBJC) +$(1)_objcxx=$($($(1)_type)_OBJCXX) +$(1)_ar=$($($(1)_type)_AR) +$(1)_ranlib=$($($(1)_type)_RANLIB) +$(1)_libtool=$($($(1)_type)_LIBTOOL) +$(1)_nm=$($($(1)_type)_NM) +$(1)_cflags=$($($(1)_type)_CFLAGS) $($($(1)_type)_$(release_type)_CFLAGS) +$(1)_cxxflags=$($($(1)_type)_CXXFLAGS) $($($(1)_type)_$(release_type)_CXXFLAGS) +$(1)_arflags=$($($(1)_type)_ARFLAGS) $($($(1)_type)_$(release_type)_ARFLAGS) +$(1)_ldflags=$($($(1)_type)_LDFLAGS) $($($(1)_type)_$(release_type)_LDFLAGS) -L$($($(1)_type)_prefix)/lib +$(1)_cppflags=$($($(1)_type)_CPPFLAGS) $($($(1)_type)_$(release_type)_CPPFLAGS) -I$($($(1)_type)_prefix)/include +$(1)_recipe_hash:= +endef + +define int_get_all_dependencies +$(sort $(foreach dep,$(2),$(2) $(call int_get_all_dependencies,$(1),$($(dep)_dependencies)))) +endef + +define fetch_file_inner + ( mkdir -p $$($(1)_download_dir) && echo Fetching $(3) from $(2) && \ + $(build_DOWNLOAD) "$$($(1)_download_dir)/$(4).temp" "$(2)/$(3)" && \ + echo "$(5) $$($(1)_download_dir)/$(4).temp" > $$($(1)_download_dir)/.$(4).hash && \ + $(build_SHA256SUM) -c $$($(1)_download_dir)/.$(4).hash && \ + mv $$($(1)_download_dir)/$(4).temp $$($(1)_source_dir)/$(4) && \ + rm -rf $$($(1)_download_dir) ) +endef + +define fetch_file + ( test -f $$($(1)_source_dir)/$(4) || \ + ( $(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(4),$(4),$(5)) || \ + $(call fetch_file_inner,$(1),$(2),$(3),$(4),$(5)))) +endef + +define int_get_build_recipe_hash +$(eval $(1)_all_file_checksums:=$(shell $(build_SHA256SUM) $(meta_depends) packages/$(1).mk $(addprefix $(PATCHES_PATH)/$(1)/,$($(1)_patches)) | cut -d" " -f1)) +$(eval $(1)_recipe_hash:=$(shell echo -n "$($(1)_all_file_checksums)" | $(build_SHA256SUM) | cut -d" " -f1)) +endef + +define int_get_build_id +$(eval $(1)_dependencies += $($(1)_$(host_arch)_$(host_os)_dependencies) $($(1)_$(host_os)_dependencies)) +$(eval $(1)_all_dependencies:=$(call int_get_all_dependencies,$(1),$($($(1)_type)_native_toolchain) $($(1)_dependencies))) +$(foreach dep,$($(1)_all_dependencies),$(eval $(1)_build_id_deps+=$(dep)-$($(dep)_version)-$($(dep)_recipe_hash))) +$(eval $(1)_build_id_long:=$(1)-$($(1)_version)-$($(1)_recipe_hash)-$(release_type) $($(1)_build_id_deps) $($($(1)_type)_id_string)) +$(eval $(1)_build_id:=$(shell echo -n "$($(1)_build_id_long)" | $(build_SHA256SUM) | cut -c-$(HASH_LENGTH))) +final_build_id_long+=$($(package)_build_id_long) + +#compute package-specific paths +$(1)_build_subdir?=. +$(1)_download_file?=$($(1)_file_name) +$(1)_source_dir:=$(SOURCES_PATH) +$(1)_source:=$$($(1)_source_dir)/$($(1)_file_name) +$(1)_staging_dir=$(base_staging_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id) +$(1)_staging_prefix_dir:=$$($(1)_staging_dir)$($($(1)_type)_prefix) +$(1)_extract_dir:=$(base_build_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id) +$(1)_download_dir:=$(base_download_dir)/$(1)-$($(1)_version) +$(1)_build_dir:=$$($(1)_extract_dir)/$$($(1)_build_subdir) +$(1)_cached_checksum:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_build_id).tar.gz.hash +$(1)_patch_dir:=$(base_build_dir)/$(host)/$(1)/$($(1)_version)-$($(1)_build_id)/.patches-$($(1)_build_id) +$(1)_prefixbin:=$($($(1)_type)_prefix)/bin/ +$(1)_cached:=$(BASE_CACHE)/$(host)/$(1)/$(1)-$($(1)_version)-$($(1)_build_id).tar.gz +$(1)_all_sources=$($(1)_file_name) $($(1)_extra_sources) + +#stamps +$(1)_fetched=$(SOURCES_PATH)/download-stamps/.stamp_fetched-$(1)-$($(1)_file_name).hash +$(1)_extracted=$$($(1)_extract_dir)/.stamp_extracted +$(1)_preprocessed=$$($(1)_extract_dir)/.stamp_preprocessed +$(1)_cleaned=$$($(1)_extract_dir)/.stamp_cleaned +$(1)_built=$$($(1)_build_dir)/.stamp_built +$(1)_configured=$$($(1)_build_dir)/.stamp_configured +$(1)_staged=$$($(1)_staging_dir)/.stamp_staged +$(1)_postprocessed=$$($(1)_staging_prefix_dir)/.stamp_postprocessed +$(1)_download_path_fixed=$(subst :,\:,$$($(1)_download_path)) + + +#default commands +$(1)_fetch_cmds ?= $(call fetch_file,$(1),$(subst \:,:,$$($(1)_download_path_fixed)),$$($(1)_download_file),$($(1)_file_name),$($(1)_sha256_hash)) +$(1)_extract_cmds ?= mkdir -p $$($(1)_extract_dir) && echo "$$($(1)_sha256_hash) $$($(1)_source)" > $$($(1)_extract_dir)/.$$($(1)_file_name).hash && $(build_SHA256SUM) -c $$($(1)_extract_dir)/.$$($(1)_file_name).hash && tar --strip-components=1 -xf $$($(1)_source) +$(1)_preprocess_cmds ?= +$(1)_build_cmds ?= +$(1)_config_cmds ?= +$(1)_stage_cmds ?= +$(1)_set_vars ?= + + +all_sources+=$$($(1)_fetched) +endef +#$(foreach dep_target,$($(1)_all_dependencies),$(eval $(1)_dependency_targets=$($(dep_target)_cached))) + + +define int_config_attach_build_config +$(eval $(call $(1)_set_vars,$(1))) +$(1)_cflags+=$($(1)_cflags_$(release_type)) +$(1)_cflags+=$($(1)_cflags_$(host_arch)) $($(1)_cflags_$(host_arch)_$(release_type)) +$(1)_cflags+=$($(1)_cflags_$(host_os)) $($(1)_cflags_$(host_os)_$(release_type)) +$(1)_cflags+=$($(1)_cflags_$(host_arch)_$(host_os)) $($(1)_cflags_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_cxxflags+=$($(1)_cxxflags_$(release_type)) +$(1)_cxxflags+=$($(1)_cxxflags_$(host_arch)) $($(1)_cxxflags_$(host_arch)_$(release_type)) +$(1)_cxxflags+=$($(1)_cxxflags_$(host_os)) $($(1)_cxxflags_$(host_os)_$(release_type)) +$(1)_cxxflags+=$($(1)_cxxflags_$(host_arch)_$(host_os)) $($(1)_cxxflags_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_arflags+=$($(1)_arflags_$(release_type)) +$(1)_arflags+=$($(1)_arflags_$(host_arch)) $($(1)_arflags_$(host_arch)_$(release_type)) +$(1)_arflags+=$($(1)_arflags_$(host_os)) $($(1)_arflags_$(host_os)_$(release_type)) +$(1)_arflags+=$($(1)_arflags_$(host_arch)_$(host_os)) $($(1)_arflags_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_cppflags+=$($(1)_cppflags_$(release_type)) +$(1)_cppflags+=$($(1)_cppflags_$(host_arch)) $($(1)_cppflags_$(host_arch)_$(release_type)) +$(1)_cppflags+=$($(1)_cppflags_$(host_os)) $($(1)_cppflags_$(host_os)_$(release_type)) +$(1)_cppflags+=$($(1)_cppflags_$(host_arch)_$(host_os)) $($(1)_cppflags_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_ldflags+=$($(1)_ldflags_$(release_type)) +$(1)_ldflags+=$($(1)_ldflags_$(host_arch)) $($(1)_ldflags_$(host_arch)_$(release_type)) +$(1)_ldflags+=$($(1)_ldflags_$(host_os)) $($(1)_ldflags_$(host_os)_$(release_type)) +$(1)_ldflags+=$($(1)_ldflags_$(host_arch)_$(host_os)) $($(1)_ldflags_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_build_opts+=$$($(1)_build_opts_$(release_type)) +$(1)_build_opts+=$$($(1)_build_opts_$(host_arch)) $$($(1)_build_opts_$(host_arch)_$(release_type)) +$(1)_build_opts+=$$($(1)_build_opts_$(host_os)) $$($(1)_build_opts_$(host_os)_$(release_type)) +$(1)_build_opts+=$$($(1)_build_opts_$(host_arch)_$(host_os)) $$($(1)_build_opts_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_config_opts+=$$($(1)_config_opts_$(release_type)) +$(1)_config_opts+=$$($(1)_config_opts_$(host_arch)) $$($(1)_config_opts_$(host_arch)_$(release_type)) +$(1)_config_opts+=$$($(1)_config_opts_$(host_os)) $$($(1)_config_opts_$(host_os)_$(release_type)) +$(1)_config_opts+=$$($(1)_config_opts_$(host_arch)_$(host_os)) $$($(1)_config_opts_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_config_env+=$$($(1)_config_env_$(release_type)) +$(1)_config_env+=$($(1)_config_env_$(host_arch)) $($(1)_config_env_$(host_arch)_$(release_type)) +$(1)_config_env+=$($(1)_config_env_$(host_os)) $($(1)_config_env_$(host_os)_$(release_type)) +$(1)_config_env+=$($(1)_config_env_$(host_arch)_$(host_os)) $($(1)_config_env_$(host_arch)_$(host_os)_$(release_type)) + +$(1)_build_env+=$$($(1)_build_env_$(release_type)) +$(1)_build_env+=$($(1)_build_env_$(host_arch)) $($(1)_build_env_$(host_arch)_$(release_type)) +$(1)_build_env+=$($(1)_build_env_$(host_os)) $($(1)_build_env_$(host_os)_$(release_type)) +$(1)_build_env+=$($(1)_build_env_$(host_arch)_$(host_os)) $($(1)_build_env_$(host_arch)_$(host_os)_$(release_type)) + +ifneq ($$($(1)_ar_$(host_arch)),) +$(1)_ar=$$($(1)_ar_$(host_arch)) +endif +ifneq ($$($(1)_ar_$(host_os)),) +$(1)_ar=$$($(1)_ar_$(host_os)) +endif +ifneq ($$($(1)_ar_$(host_arch)_$(host_os)),) +$(1)_ar=$$($(1)_ar_$(host_arch)_$(host_os)) +endif + +$(1)_config_env+=PKG_CONFIG_LIBDIR=$($($(1)_type)_prefix)/lib/pkgconfig +$(1)_config_env+=PKG_CONFIG_PATH=$($($(1)_type)_prefix)/share/pkgconfig +$(1)_config_env+=PATH="$(build_prefix)/bin:$(PATH)" +$(1)_build_env+=PATH="$(build_prefix)/bin:$(PATH)" +$(1)_stage_env+=PATH="$(build_prefix)/bin:$(PATH)" +$(1)_autoconf_args=--prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)" AR="$$($(1)_ar)" +$(1)_autoconf=./configure --host=$($($(1)_type)_host) --prefix=$($($(1)_type)_prefix) $$($(1)_config_opts) CC="$$($(1)_cc)" CXX="$$($(1)_cxx)" AR="$$($(1)_ar)" + +ifeq ($(filter $(1),libusb unbound),) +$(1)_autoconf_args += --disable-dependency-tracking +$(1)_autoconf += --disable-dependency-tracking +endif +ifneq ($($(1)_nm),) +$(1)_autoconf_args += NM="$$($(1)_nm)" +$(1)_autoconf += NM="$$($(1)_nm)" +endif +ifneq ($($(1)_ranlib),) +$(1)_autoconf_args += RANLIB="$$($(1)_ranlib)" +$(1)_autoconf += RANLIB="$$($(1)_ranlib)" +endif +ifneq ($($(1)_ar),) +$(1)_autoconf_args += AR="$$($(1)_ar)" +$(1)_autoconf += AR="$$($(1)_ar)" +endif +ifneq ($($(1)_arflags),) +$(1)_autoconf_args += ARFLAGS="$$($(1)_arflags)" +$(1)_autoconf += ARFLAGS="$$($(1)_arflags)" +endif +ifneq ($($(1)_cflags),) +$(1)_autoconf_args += CFLAGS="$$($(1)_cflags)" +$(1)_autoconf += CFLAGS="$$($(1)_cflags)" +endif +ifneq ($($(1)_cxxflags),) +$(1)_autoconf_args += CXXFLAGS="$$($(1)_cxxflags)" +$(1)_autoconf += CXXFLAGS="$$($(1)_cxxflags)" +endif +ifneq ($($(1)_cppflags),) +$(1)_autoconf_args += CPPFLAGS="$$($(1)_cppflags)" +$(1)_autoconf += CPPFLAGS="$$($(1)_cppflags)" +endif +ifneq ($($(1)_ldflags),) +$(1)_autoconf_args += LDFLAGS="$$($(1)_ldflags)" +$(1)_autoconf += LDFLAGS="$$($(1)_ldflags)" +endif +endef + +COMPRESS_CMD := $(shell if command -v pigz >/dev/null 2>&1; then echo "pigz"; else echo "gzip"; fi) + +define int_add_cmds +$($(1)_fetched): + $(AT)mkdir -p $$(@D) $(SOURCES_PATH) + $(AT)rm -f $$@ + $(AT)touch $$@ + $(AT)cd $$(@D); $(call $(1)_fetch_cmds,$(1)) + $(AT)cd $($(1)_source_dir); $(foreach source,$($(1)_all_sources),$(build_SHA256SUM) $(source) >> $$(@);) + $(AT)touch $$@ +$($(1)_extracted): | $($(1)_fetched) + $(AT)echo Extracting $(1)... + $(AT)mkdir -p $$(@D) + $(AT)cd $$(@D); $(call $(1)_extract_cmds,$(1)) + $(AT)touch $$@ +$($(1)_preprocessed): | $($(1)_dependencies) $($(1)_extracted) + $(AT)echo Preprocessing $(1)... + $(AT)mkdir -p $$(@D) $($(1)_patch_dir) + $(AT)$(foreach patch,$($(1)_patches),cd $(PATCHES_PATH)/$(1); cp $(patch) $($(1)_patch_dir) ;) + $(AT)cd $$(@D); $(call $(1)_preprocess_cmds, $(1)) + $(AT)touch $$@ +$($(1)_configured): | $($(1)_preprocessed) + $(AT)echo Configuring $(1)... + $(AT)rm -rf $(host_prefix); mkdir -p $(host_prefix)/lib; cd $(host_prefix); $(foreach package,$($(1)_all_dependencies), tar xf $($(package)_cached); ) + $(AT)mkdir -p $$(@D) + $(AT)+cd $$(@D); $($(1)_config_env) $(call $(1)_config_cmds, $(1)) + $(AT)touch $$@ +$($(1)_built): | $($(1)_configured) + $(AT)echo Building $(1)... + $(AT)mkdir -p $$(@D) + $(AT)+cd $$(@D); $($(1)_build_env) $(call $(1)_build_cmds, $(1)) + $(AT)touch $$@ +$($(1)_staged): | $($(1)_built) + $(AT)echo Staging $(1)... + $(AT)mkdir -p $($(1)_staging_dir)/$(host_prefix) + $(AT)cd $($(1)_build_dir); $($(1)_stage_env) $(call $(1)_stage_cmds, $(1)) + $(AT)rm -rf $($(1)_extract_dir) + $(AT)touch $$@ +$($(1)_postprocessed): | $($(1)_staged) + $(AT)echo Postprocessing $(1)... + $(AT)cd $($(1)_staging_prefix_dir); $(call $(1)_postprocess_cmds) + $(AT)touch $$@ +$($(1)_cached): | $($(1)_dependencies) $($(1)_postprocessed) + $(AT)echo Caching $(1)... + $(AT)cd $$($(1)_staging_dir)/$(host_prefix); find . | sort | tar --no-recursion --use-compress-program='$(COMPRESS_CMD)' -cf $$($(1)_staging_dir)/$$(@F) -T - + $(AT)mkdir -p $$(@D) + $(AT)rm -rf $$(@D) && mkdir -p $$(@D) + $(AT)mv $$($(1)_staging_dir)/$$(@F) $$(@) + $(AT)rm -rf $($(1)_staging_dir) +$($(1)_cached_checksum): $($(1)_cached) + $(AT)cd $$(@D); $(build_SHA256SUM) $$( $$(@) + +.PHONY: $(1) +$(1): | $($(1)_cached_checksum) +.SECONDARY: $($(1)_cached) $($(1)_postprocessed) $($(1)_staged) $($(1)_built) $($(1)_configured) $($(1)_preprocessed) $($(1)_extracted) $($(1)_fetched) + +endef + +stages = fetched extracted preprocessed configured built staged postprocessed cached cached_checksum + +define ext_add_stages +$(foreach stage,$(stages), + $(1)_$(stage): $($(1)_$(stage)) + .PHONY: $(1)_$(stage)) +endef + +# These functions create the build targets for each package. They must be +# broken down into small steps so that each part is done for all packages +# before moving on to the next step. Otherwise, a package's info +# (build-id for example) would only be available to another package if it +# happened to be computed already. + +#set the type for host/build packages. +$(foreach native_package,$(native_packages),$(eval $(native_package)_type=build)) +$(foreach package,$(packages),$(eval $(package)_type=$(host_arch)_$(host_os))) + +#set overridable defaults +$(foreach package,$(all_packages),$(eval $(call int_vars,$(package)))) + +#include package files +$(foreach package,$(all_packages),$(eval include packages/$(package).mk)) + +#compute a hash of all files that comprise this package's build recipe +$(foreach package,$(all_packages),$(eval $(call int_get_build_recipe_hash,$(package)))) + +#generate a unique id for this package, incorporating its dependencies as well +$(foreach package,$(all_packages),$(eval $(call int_get_build_id,$(package)))) + +#compute final vars after reading package vars +$(foreach package,$(all_packages),$(eval $(call int_config_attach_build_config,$(package)))) + +#create build targets +$(foreach package,$(all_packages),$(eval $(call int_add_cmds,$(package)))) + +#special exception: if a toolchain package exists, all non-native packages depend on it +$(foreach package,$(packages),$(eval $($(package)_unpacked): |$($($(host_arch)_$(host_os)_native_toolchain)_cached) )) diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk new file mode 100644 index 0000000..827103c --- /dev/null +++ b/contrib/depends/hosts/android.mk @@ -0,0 +1,32 @@ +ANDROID_API=21 +host_toolchain=nonexistent +ifeq ($(host_arch),armv7a) +host_toolchain=armv7a-linux-androideabi${ANDROID_API}- +endif +ifeq ($(host_arch),x86_64) +host_toolchain=x86_64-linux-android${ANDROID_API}- +endif +ifeq ($(host_arch),i686) +host_toolchain=i686-linux-android${ANDROID_API}- +endif +ifeq ($(host_arch),aarch64) +host_toolchain=aarch64-linux-android${ANDROID_API}- +endif + +android_CC=$(host_toolchain)clang +android_CXX=$(host_toolchain)clang++ +android_RANLIB=llvm-ranlib +android_AR=llvm-ar + +android_CFLAGS=-pipe +android_CXXFLAGS=$(android_CFLAGS) +android_ARFLAGS=crsD + +android_release_CFLAGS=-O2 +android_release_CXXFLAGS=$(android_release_CFLAGS) + +android_debug_CFLAGS=-g -O0 +android_debug_CXXFLAGS=$(android_debug_CFLAGS) + +android_native_toolchain=android_ndk + diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk new file mode 100644 index 0000000..aa5d8fc --- /dev/null +++ b/contrib/depends/hosts/darwin.mk @@ -0,0 +1,23 @@ +OSX_MIN_VERSION=10.14 +LD64_VERSION=609 +ifeq (aarch64, $(host_arch)) +CC_target=arm64-apple-$(host_os) +else +CC_target=$(host) +endif +darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -B$(host_prefix)/native/bin/$(host)- +darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)- + +darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib +darwin_AR=$(host_prefix)/native/bin/$(host)-ar +darwin_CFLAGS=-pipe +darwin_CXXFLAGS=$(darwin_CFLAGS) +darwin_ARFLAGS=cr + +darwin_release_CFLAGS=-O1 +darwin_release_CXXFLAGS=$(darwin_release_CFLAGS) + +darwin_debug_CFLAGS=-O1 +darwin_debug_CXXFLAGS=$(darwin_debug_CFLAGS) + +darwin_native_toolchain=native_cctools darwin_sdk diff --git a/contrib/depends/hosts/default.mk b/contrib/depends/hosts/default.mk new file mode 100644 index 0000000..2e7f9fa --- /dev/null +++ b/contrib/depends/hosts/default.mk @@ -0,0 +1,26 @@ +default_host_CC = $(host_toolchain)gcc +default_host_CXX = $(host_toolchain)g++ +default_host_AR = $(host_toolchain)ar +default_host_RANLIB = $(host_toolchain)ranlib +default_host_STRIP = $(host_toolchain)strip +default_host_LIBTOOL = $(host_toolchain)libtool +default_host_INSTALL_NAME_TOOL = $(host_toolchain)install_name_tool +default_host_OTOOL = $(host_toolchain)otool +default_host_NM = $(host_toolchain)nm + +define add_host_tool_func +$(host_os)_$1?=$$(default_host_$1) +$(host_arch)_$(host_os)_$1?=$$($(host_os)_$1) +$(host_arch)_$(host_os)_$(release_type)_$1?=$$($(host_os)_$1) +host_$1=$$($(host_arch)_$(host_os)_$1) +endef + +define add_host_flags_func +$(host_arch)_$(host_os)_$1 += $($(host_os)_$1) +$(host_arch)_$(host_os)_$(release_type)_$1 += $($(host_os)_$(release_type)_$1) +host_$1 = $$($(host_arch)_$(host_os)_$1) +host_$(release_type)_$1 = $$($(host_arch)_$(host_os)_$(release_type)_$1) +endef + +$(foreach tool,CC CXX AR RANLIB STRIP NM LIBTOOL OTOOL INSTALL_NAME_TOOL,$(eval $(call add_host_tool_func,$(tool)))) +$(foreach flags,CFLAGS CXXFLAGS ARFLAGS CPPFLAGS LDFLAGS, $(eval $(call add_host_flags_func,$(flags)))) diff --git a/contrib/depends/hosts/freebsd.mk b/contrib/depends/hosts/freebsd.mk new file mode 100644 index 0000000..2e3b593 --- /dev/null +++ b/contrib/depends/hosts/freebsd.mk @@ -0,0 +1,18 @@ +freebsd_CC=clang-8 +freebsd_CXX=clang++-8 +freebsd_AR=ar +freebsd_RANLIB=ranlib +freebsd_NM=nm + +freebsd_CFLAGS=-pipe +freebsd_CXXFLAGS=$(freebsd_CFLAGS) +freebsd_ARFLAGS=cr + +freebsd_release_CFLAGS=-O2 +freebsd_release_CXXFLAGS=$(freebsd_release_CFLAGS) + +freebsd_debug_CFLAGS=-g -O0 +freebsd_debug_CXXFLAGS=$(freebsd_debug_CFLAGS) + +freebsd_native_toolchain=freebsd_base + diff --git a/contrib/depends/hosts/ios.mk b/contrib/depends/hosts/ios.mk new file mode 100644 index 0000000..c1a7c33 --- /dev/null +++ b/contrib/depends/hosts/ios.mk @@ -0,0 +1,24 @@ +IOS_MIN_VERSION=12.0 +IOS_SDK=$(shell xcrun --sdk iphoneos --show-sdk-path) +IOS_SIMULATOR_SDK=$(shell xcrun --sdk iphonesimulator --show-sdk-path) + +ifeq (aarch64, $(host_arch)) +CC_target_ios=arm64-apple-ios +else +CC_target_ios=x86_64-apple-ios +endif + +ios_CC=$(shell xcrun -f clang) -target $(CC_target_ios) -mios-version-min=$(IOS_MIN_VERSION) --sysroot $(IOS_SDK) +ios_CXX=$(shell xcrun -f clang++) -target $(CC_target_ios) -mios-version-min=$(IOS_MIN_VERSION) --sysroot $(IOS_SDK) -stdlib=libc++ -std=c++14 +ios_AR:=$(shell xcrun -f ar) +ios_RANLIB:=$(shell xcrun -f ranlib) +ios_STRIP:=$(shell xcrun -f strip) +ios_LIBTOOL:=$(shell xcrun -f libtool) +ios_OTOOL:=$(shell xcrun -f otool) +ios_NM:=$(shell xcrun -f nm) +ios_INSTALL_NAME_TOOL:=$(shell xcrun -f install_name_tool) +ios_native_toolchain= + + +ios_CFLAGS=-pipe +ios_CXXFLAGS=$(ios_CFLAGS) \ No newline at end of file diff --git a/contrib/depends/hosts/linux.mk b/contrib/depends/hosts/linux.mk new file mode 100644 index 0000000..b79799f --- /dev/null +++ b/contrib/depends/hosts/linux.mk @@ -0,0 +1,32 @@ +linux_CFLAGS=-pipe +linux_CXXFLAGS=$(linux_CFLAGS) +linux_ARFLAGS=cr + +linux_release_CFLAGS=-O2 +linux_release_CXXFLAGS=$(linux_release_CFLAGS) + +linux_debug_CFLAGS=-O1 +linux_debug_CXXFLAGS=$(linux_debug_CFLAGS) + +linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC + +ifeq (86,$(findstring 86,$(build_arch))) +i686_linux_CC=i686-linux-gnu-gcc +i686_linux_CXX=i686-linux-gnu-g++ +i686_linux_AR=ar +i686_linux_RANLIB=ranlib +i686_linux_NM=nm +i686_linux_STRIP=strip + +x86_64_linux_CC=x86_64-linux-gnu-gcc +x86_64_linux_CXX=x86_64-linux-gnu-g++ +x86_64_linux_AR=ar +x86_64_linux_RANLIB=ranlib +x86_64_linux_NM=nm +x86_64_linux_STRIP=strip +else +i686_linux_CC=$(default_host_CC) -m32 +i686_linux_CXX=$(default_host_CXX) -m32 +x86_64_linux_CC=$(default_host_CC) -m64 +x86_64_linux_CXX=$(default_host_CXX) -m64 +endif diff --git a/contrib/depends/hosts/mingw32.mk b/contrib/depends/hosts/mingw32.mk new file mode 100644 index 0000000..ccc4c50 --- /dev/null +++ b/contrib/depends/hosts/mingw32.mk @@ -0,0 +1,11 @@ +mingw32_CFLAGS=-pipe +mingw32_CXXFLAGS=$(mingw32_CFLAGS) +mingw32_ARFLAGS=cr + +mingw32_release_CFLAGS=-O2 +mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) + +mingw32_debug_CFLAGS=-O1 +mingw32_debug_CXXFLAGS=$(mingw32_debug_CFLAGS) + +mingw32_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC diff --git a/contrib/depends/packages.md b/contrib/depends/packages.md new file mode 100644 index 0000000..ae5b473 --- /dev/null +++ b/contrib/depends/packages.md @@ -0,0 +1,165 @@ +Each recipe consists of 3 main parts: defining identifiers, setting build +variables, and defining build commands. + +The package "mylib" will be used here as an example + +General tips: +- mylib_foo is written as $(package)_foo in order to make recipes more similar. + +## Identifiers +Each package is required to define at least these variables: + +``` +$(package)_version: +Version of the upstream library or program. If there is no version, a +placeholder such as 1.0 can be used. + +$(package)_download_path: +Location of the upstream source, without the file-name. Usually http or +ftp. + +$(package)_file_name: +The upstream source filename available at the download path. + +$(package)_sha256_hash: +The sha256 hash of the upstream file +``` + +These variables are optional: + +``` +$(package)_build_subdir: +cd to this dir before running configure/build/stage commands. + +$(package)_download_file: +The file-name of the upstream source if it differs from how it should be +stored locally. This can be used to avoid storing file-names with strange +characters. + +$(package)_dependencies: +Names of any other packages that this one depends on. + +$(package)_patches: +Filenames of any patches needed to build the package + +$(package)_extra_sources: +Any extra files that will be fetched via $(package)_fetch_cmds. These are +specified so that they can be fetched and verified via 'make download'. +``` + + +## Build Variables: +After defining the main identifiers, build variables may be added or customized +before running the build commands. They should be added to a function called +$(package)_set_vars. For example: + +``` +define $(package)_set_vars +... +endef +``` + +Most variables can be prefixed with the host, architecture, or both, to make +the modifications specific to that case. For example: + +``` +Universal: $(package)_cc=gcc +Linux only: $(package)_linux_cc=gcc +x86_64 only: $(package)_x86_64_cc = gcc +x86_64 linux only: $(package)_x86_64_linux_cc = gcc +``` + +These variables may be set to override or append their default values. + +``` +$(package)_cc +$(package)_cxx +$(package)_objc +$(package)_objcxx +$(package)_ar +$(package)_ranlib +$(package)_libtool +$(package)_nm +$(package)_cflags +$(package)_cxxflags +$(package)_ldflags +$(package)_cppflags +$(package)_config_env +$(package)_build_env +$(package)_stage_env +$(package)_build_opts +$(package)_config_opts +``` + +The `*_env` variables are used to add environment variables to the respective +commands. + +Many variables respect a debug/release suffix as well, in order to use them for +only the appropriate build config. For example: + +``` +$(package)_cflags_release = -O3 +$(package)_cflags_i686_debug = -g +$(package)_config_opts_release = --disable-debug +``` + +These will be used in addition to the options that do not specify +debug/release. All builds are considered to be release unless DEBUG=1 is set by +the user. Other variables may be defined as needed. + +## Build commands: + +For each build, a unique build dir and staging dir are created. For example, +`work/build/mylib/1.0-1adac830f6e` and `work/staging/mylib/1.0-1adac830f6e`. + +The following build commands are available for each recipe: + +``` +$(package)_fetch_cmds: +Runs from: build dir +Fetch the source file. If undefined, it will be fetched and verified +against its hash. + +$(package)_extract_cmds: +Runs from: build dir +Verify the source file against its hash and extract it. If undefined, the +source is assumed to be a tarball. + +$(package)_preprocess_cmds: +Runs from: build dir/$(package)_build_subdir +Preprocess the source as necessary. If undefined, does nothing. + +$(package)_config_cmds: +Runs from: build dir/$(package)_build_subdir +Configure the source. If undefined, does nothing. + +$(package)_build_cmds: +Runs from: build dir/$(package)_build_subdir +Build the source. If undefined, does nothing. + +$(package)_stage_cmds: +Runs from: build dir/$(package)_build_subdir +Stage the build results. If undefined, does nothing. +``` + +The following variables are available for each recipe: + +``` +$(1)_staging_dir: package's destination sysroot path +$(1)_staging_prefix_dir: prefix path inside of the package's staging dir +$(1)_extract_dir: path to the package's extracted sources +$(1)_build_dir: path where configure/build/stage commands will be run +$(1)_patch_dir: path where the package's patches (if any) are found +``` + +Notes on build commands: + +For packages built with autotools, `$($(package)_autoconf)` can be used in the +configure step to (usually) correctly configure automatically. Any +`$($(package)_config_opts`) will be appended. + +Most autotools projects can be properly staged using: + +```bash +$(MAKE) DESTDIR=$($(package)_staging_dir) install +``` diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk new file mode 100644 index 0000000..2c2914e --- /dev/null +++ b/contrib/depends/packages/android_ndk.mk @@ -0,0 +1,36 @@ +package=android_ndk +$(package)_version=26d +$(package)_download_path=https://dl.google.com/android/repository/ +$(package)_file_name=android-ndk-r$($(package)_version)-linux.zip +$(package)_sha256_hash=eefeafe7ccf177de7cc57158da585e7af119bb7504a63604ad719e4b2a328b54 + +$(package)_version_apiversion=21 + +define $(package)_set_vars +$(package)_config_opts_armv7a=--arch arm +$(package)_config_opts_aarch64=--arch arm64 +$(package)_config_opts_x86_64=--arch x86_64 +$(package)_config_opts_i686=--arch x86 +endef + +define $(package)_extract_cmds + echo $($(package)_sha256_hash) $($(1)_source_dir)/$($(package)_file_name) | sha256sum -c &&\ + unzip -q $($(1)_source_dir)/$($(package)_file_name) +endef + +# arm-linux-androideabi-ar - openssl workaround + +define $(package)_stage_cmds + mkdir -p $(build_prefix) &&\ + echo $(build_prefix)/toolchain && \ + android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api $($(package)_version_apiversion) \ + --install-dir $(build_prefix)/toolchain --stl=libc++ $($(package)_config_opts) &&\ + mv $(build_prefix)/toolchain $($(package)_staging_dir)/$(host_prefix)/native && \ + cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ar &&\ + cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ar &&\ + cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ranlib &&\ + cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ranlib &&\ + cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ar &&\ + cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ranlib +endef + diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk new file mode 100644 index 0000000..efbfdec --- /dev/null +++ b/contrib/depends/packages/boost.mk @@ -0,0 +1,56 @@ +package=boost +$(package)_version=1_85_0 +$(package)_download_path=https://downloads.sourceforge.net/project/boost/boost/1.85.0/ +$(package)_file_name=$(package)_$($(package)_version).tar.bz2 +$(package)_sha256_hash=7009fe1faa1697476bdc7027703a2badb84e849b7b0baad5086b087b971f8617 +$(package)_dependencies=libiconv +$(package)_patches=fix_io_control_hpp.patch +$(package)_ios_COMPILER_VERSION=16 + +define $(package)_set_vars +$(package)_config_opts_release=variant=release +$(package)_config_opts_debug=variant=debug --build-dir=stage/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_android=threadapi=pthread runtime-link=static target-os=android +$(package)_config_opts_darwin=--toolset=darwin runtime-link=static +$(package)_config_opts_ios=--toolset=darwin-$($(package)_ios_COMPILER_VERSION)~iphone runtime-link=static +$(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)_archiver_darwin=$($(package)_libtool) +$(package)_archiver_ios=$($(package)_libtool) +$(package)_toolset_darwin=darwin +$(package)_toolset_ios=darwin +$(package)_toolset_android=gcc +$(package)_toolset_mingw32=gcc +$(package)_toolset2_$(host_os)= +$(package)_toolset2_ios=$($(package)_ios_COMPILER_VERSION)~iphone +$(package)_config_libraries=system,filesystem,thread,timer,date_time,chrono,regex,serialization,atomic,program_options,locale,log +$(package)_cxxflags_linux=-fPIC +$(package)_cxxflags_freebsd=-fPIC +$(package)_cxxflags_android=-fPIC +$(package)_cxxflags_darwin=-fPIC -std=c++11 +$(package)_ldflags_darwin=-L$(host_prefix)/lib -L$(shell xcrun --sdk macosx --show-sdk-path)/usr/lib +$(package)_ldflags_ios=-L$(host_prefix)/lib -L$(IOS_SDK)/usr/lib +endef + +define $(package)_preprocess_cmds + echo "using $(boost_toolset_$(host_os)) : $(boost_toolset2_$(host_os)) : $($(package)_cxx) : \"$($(package)_cxxflags) $($(package)_cppflags)\" \"$($(package)_ldflags)\" \"$($(package)_archiver_$(host_os))\" \"$($(package)_arflags)\" \"$(host_STRIP)\" \"$(host_RANLIB)\" \"$(host_WINDRES)\" iphone : ;" > user-config.jam +endef + +define $(package)_config_cmds + ./bootstrap.sh --with-libraries=$(boost_config_libraries) +endef + +define $(package)_build_cmds + ./b2 -d2 -j$(NUM_CORES) --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) $($(package)_config_opts_release) stage +endef + +define $(package)_stage_cmds + ./b2 -d0 -j$(NUM_CORES) --prefix=$($(package)_staging_prefix_dir) $($(package)_config_opts) $($(package)_config_opts_release) install +endef diff --git a/contrib/depends/packages/darwin_sdk.mk b/contrib/depends/packages/darwin_sdk.mk new file mode 100644 index 0000000..6812179 --- /dev/null +++ b/contrib/depends/packages/darwin_sdk.mk @@ -0,0 +1,15 @@ +package=darwin_sdk +$(package)_version=11.1 +$(package)_download_path=https://github.com/phracker/MacOSX-SDKs/releases/download/11.3/ +$(package)_file_name=MacOSX$($(package)_version).sdk.tar.xz +$(package)_sha256_hash=68797baaacb52f56f713400de306a58a7ca00b05c3dc6d58f0a8283bcac721f8 +$(package)_patches=fix_missing_definitions.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/fix_missing_definitions.patch +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_staging_dir)/$(host_prefix)/native/SDK &&\ + mv * $($(package)_staging_dir)/$(host_prefix)/native/SDK +endef diff --git a/contrib/depends/packages/eudev.mk b/contrib/depends/packages/eudev.mk new file mode 100644 index 0000000..e0f8d00 --- /dev/null +++ b/contrib/depends/packages/eudev.mk @@ -0,0 +1,30 @@ +package=eudev +$(package)_version=v3.2.6 +$(package)_download_path=https://github.com/gentoo/eudev/archive/ +$(package)_download_file=$($(package)_version).tar.gz +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=a96ecb8637667897b8bd4dee4c22c7c5f08b327be45186e912ce6bc768385852 + +define $(package)_set_vars + $(package)_config_opts=--disable-gudev --disable-introspection --disable-hwdb --disable-manpages --disable-shared +endef + +define $(package)_config_cmds + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) +endef + +define $(package)_build_cmd + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_preprocess_cmds + cd $($(package)_build_subdir); autoreconf -f -i +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef diff --git a/contrib/depends/packages/expat.mk b/contrib/depends/packages/expat.mk new file mode 100644 index 0000000..73c2e24 --- /dev/null +++ b/contrib/depends/packages/expat.mk @@ -0,0 +1,28 @@ +package=expat +$(package)_version=2.6.0 +$(package)_download_path=https://github.com/libexpat/libexpat/releases/download/R_$(subst .,_,$($(package)_version))/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=ff60e6a6b6ce570ae012dc7b73169c7fdf4b6bf08c12ed0ec6f55736b78d85ba + +define $(package)_set_vars +$(package)_config_opts=--disable-shared --without-docbook --without-tests --without-examples +$(package)_config_opts+=--enable-option-checking --without-xmlwf --with-pic +$(package)_config_opts+=--prefix=$(host_prefix) +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm -rf share lib/cmake lib/*.la +endef + diff --git a/contrib/depends/packages/freebsd_base.mk b/contrib/depends/packages/freebsd_base.mk new file mode 100644 index 0000000..ad9975f --- /dev/null +++ b/contrib/depends/packages/freebsd_base.mk @@ -0,0 +1,23 @@ +package=freebsd_base +$(package)_version=11.3 +$(package)_download_path=https://download.freebsd.org/ftp/releases/amd64/$($(package)_version)-RELEASE/ +$(package)_download_file=base.txz +$(package)_file_name=freebsd-base-$($(package)_version).txz +$(package)_sha256_hash=4599023ac136325b86f2fddeec64c1624daa83657e40b00b2ef944c81463a4ff + +define $(package)_extract_cmds + echo $($(package)_sha256_hash) $($(1)_source_dir)/$($(package)_file_name) | sha256sum -c &&\ + tar xf $($(1)_source_dir)/$($(package)_file_name) ./lib/ ./usr/lib/ ./usr/include/ +endef + +define $(package)_build_cmds + mkdir bin &&\ + echo "#!/bin/sh\n\nexec /usr/bin/clang-8 -target x86_64-unknown-freebsd$($(package)_version) --sysroot=$(host_prefix)/native $$$$""@" > bin/clang-8 &&\ + echo "#!/bin/sh\n\nexec /usr/bin/clang++-8 -target x86_64-unknown-freebsd$($(package)_version) --sysroot=$(host_prefix)/native $$$$""@" > bin/clang++-8 &&\ + chmod 755 bin/* +endef + +define $(package)_stage_cmds + mkdir $($(package)_staging_dir)/$(host_prefix)/native &&\ + mv bin lib usr $($(package)_staging_dir)/$(host_prefix)/native +endef diff --git a/contrib/depends/packages/graphviz.mk b/contrib/depends/packages/graphviz.mk new file mode 100644 index 0000000..e9077b6 --- /dev/null +++ b/contrib/depends/packages/graphviz.mk @@ -0,0 +1,30 @@ +package=graphviz +$(package)_version=2.40.1 +$(package)_download_path=www.graphviz.org/pub/graphviz/stable/SOURCES/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=ca5218fade0204d59947126c38439f432853543b0818d9d728c589dfe7f3a421 + +define $(package)_preprocess_cmds + ./autogen.sh +endef + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --enable-multibye --without-purify --without-curses + $(package)_config_opts_release=--disable-debug-mode + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds +endef diff --git a/contrib/depends/packages/gtest.mk b/contrib/depends/packages/gtest.mk new file mode 100644 index 0000000..1208d7c --- /dev/null +++ b/contrib/depends/packages/gtest.mk @@ -0,0 +1,39 @@ +package=gtest +$(package)_version=1.8.1 +$(package)_download_path=https://github.com/google/googletest/archive/ +$(package)_download_file=release-$($(package)_version).tar.gz +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c +$(package)_cxxflags=-std=c++11 +$(package)_cxxflags_linux=-fPIC + +define $(package)_config_cmds + cd googletest && \ + CC="$(host_prefix)/native/bin/$($(package)_cc)" \ + CXX="$(host_prefix)/native/bin/$($(package)_cxx)" \ + AR="$(host_prefix)/native/bin/$($(package)_ar)" \ + RANLIB="$(host_prefix)/native/bin/$($(package)_ranlib)" \ + LIBTOOL="$(host_prefix)/native/bin/$($(package)_libtool)" \ + CXXFLAGS="$($(package)_cxxflags)" \ + CCFLAGS="$($(package)_ccflags)" \ + CPPFLAGS="$($(package)_cppflags)" \ + CFLAGS="$($(package)_cflags) $($(package)_cppflags)" \ + LDLAGS="$($(package)_ldflags)" \ + cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix) \ + -DTOOLCHAIN_PREFIX=$(host_toolchain) \ + -DCMAKE_AR="$(host_prefix)/native/bin/$($(package)_ar)" \ + -DCMAKE_RANLIB="$(host_prefix)/native/bin/$($(package)_ranlib)" \ + -DCMAKE_CXX_FLAGS_DEBUG=ON +endef +# -DCMAKE_TOOLCHAIN_FILE=$(HOST)/share/toolchain.cmake + +define $(package)_build_cmds + cd googletest && CC="$(host_prefix)/native/bin/$($(package)_cc)" $(MAKE) +endef + +define $(package)_stage_cmds + mkdir $($(package)_staging_prefix_dir)/lib $($(package)_staging_prefix_dir)/include &&\ + cp googletest/libgtest.a $($(package)_staging_prefix_dir)/lib/ &&\ + cp googletest/libgtest_main.a $($(package)_staging_prefix_dir)/lib/ &&\ + cp -a googletest/include/* $($(package)_staging_prefix_dir)/include/ +endef diff --git a/contrib/depends/packages/hidapi.mk b/contrib/depends/packages/hidapi.mk new file mode 100644 index 0000000..2f04be1 --- /dev/null +++ b/contrib/depends/packages/hidapi.mk @@ -0,0 +1,38 @@ +package=hidapi +$(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=476a2c9a4dc7d1fc97dd223b84338dbea3809a84caea2dcd887d9778725490e3 +$(package)_linux_dependencies=libusb eudev +$(package)_patches=missing_win_include.patch + +define $(package)_set_vars +$(package)_config_opts=--enable-static --disable-shared +$(package)_config_opts+=--prefix=$(host_prefix) +$(package)_config_opts_linux+=libudev_LIBS="-L$(host_prefix)/lib -ludev" +$(package)_config_opts_linux+=libudev_CFLAGS=-I$(host_prefix)/include +$(package)_config_opts_linux+=libusb_LIBS="-L$(host_prefix)/lib -lusb-1.0" +$(package)_config_opts_linux+=libusb_CFLAGS=-I$(host_prefix)/include/libusb-1.0 +$(package)_config_opts_linux+=--with-pic +endef + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/missing_win_include.patch && ./bootstrap +endef + +define $(package)_config_cmds + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef + diff --git a/contrib/depends/packages/icu4c.mk b/contrib/depends/packages/icu4c.mk new file mode 100644 index 0000000..2de540b --- /dev/null +++ b/contrib/depends/packages/icu4c.mk @@ -0,0 +1,27 @@ +package=icu4c +$(package)_version=55.2 +$(package)_download_path=https://github.com/unicode-org/icu/releases/download/release-55-2/ +$(package)_file_name=$(package)-55_2-src.tgz +$(package)_sha256_hash=eda2aa9f9c787748a2e2d310590720ca8bcc6252adf6b4cfb03b65bef9d66759 +$(package)_patches=icu-001-dont-build-static-dynamic-twice.patch + +define $(package)_set_vars + $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -DU_USING_ICU_NAMESPACE=0 -DU_STATIC_IMPLEMENTATION -DU_COMBINED_IMPLEMENTATION -fPIC -DENABLE_STATIC=YES -DPGKDATA_MODE=static" +endef + +define $(package)_config_cmds + patch -p1 < $($(package)_patch_dir)/icu-001-dont-build-static-dynamic-twice.patch &&\ + mkdir builda &&\ + mkdir buildb &&\ + cd builda &&\ + sh ../source/runConfigureICU Linux &&\ + make &&\ + cd ../buildb &&\ + sh ../source/runConfigureICU MinGW --enable-static=yes --disable-shared --disable-layout --disable-layoutex --disable-tests --disable-samples --prefix=$(host_prefix) --with-cross-build=`pwd`/../builda &&\ + $(MAKE) -j$(NUM_CORES) $($(package)_build_opts) +endef + +define $(package)_stage_cmds + cd buildb &&\ + $(MAKE) -j$(NUM_CORES) $($(package)_build_opts) DESTDIR=$($(package)_staging_dir) install lib/* +endef diff --git a/contrib/depends/packages/libICE.mk b/contrib/depends/packages/libICE.mk new file mode 100644 index 0000000..9c1a201 --- /dev/null +++ b/contrib/depends/packages/libICE.mk @@ -0,0 +1,23 @@ +package=libICE +$(package)_version=1.0.9 +$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=8f7032f2c1c64352b5423f6b48a8ebdc339cc63064af34d66a6c9aa79759e202 +$(package)_dependencies=xtrans xproto + +define $(package)_set_vars + $(package)_config_opts=--disable-static --disable-docs --disable-specs --without-xsltproc + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libSM.mk b/contrib/depends/packages/libSM.mk new file mode 100644 index 0000000..fc010ad --- /dev/null +++ b/contrib/depends/packages/libSM.mk @@ -0,0 +1,23 @@ +package=libSM +$(package)_version=1.2.2 +$(package)_download_path=https://xorg.freedesktop.org/releases/individual/lib/ +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=0baca8c9f5d934450a70896c4ad38d06475521255ca63b717a6510fdb6e287bd +$(package)_dependencies=xtrans xproto libICE + +define $(package)_set_vars + $(package)_config_opts=--without-libuuid --without-xsltproc --disable-docs --disable-static + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/libiconv.mk b/contrib/depends/packages/libiconv.mk new file mode 100644 index 0000000..abd1b71 --- /dev/null +++ b/contrib/depends/packages/libiconv.mk @@ -0,0 +1,34 @@ +package=libiconv +$(package)_version=1.17 +$(package)_download_path=https://ftp.gnu.org/gnu/libiconv +$(package)_file_name=libiconv-$($(package)_version).tar.gz +$(package)_sha256_hash=8f74213b56238c85a50a5329f77e06198771e70dd9a739779f4c02f65d971313 + +define $(package)_set_vars + $(package)_config_opts=--disable-nls + $(package)_config_opts=--enable-static + $(package)_config_opts=--disable-shared + $(package)_config_opts_linux=--with-pic + $(package)_config_opts_freebsd=--with-pic + $(package)_ar_ios=$($(package)_ar) +endef + +define $(package)_preprocess_cmds + cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub build-aux/ +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef diff --git a/contrib/depends/packages/libusb.mk b/contrib/depends/packages/libusb.mk new file mode 100644 index 0000000..f8ef7bc --- /dev/null +++ b/contrib/depends/packages/libusb.mk @@ -0,0 +1,39 @@ +package=libusb +$(package)_version=1.0.26 +$(package)_download_path=https://github.com/libusb/libusb/releases/download/v$($(package)_version) +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=12ce7a61fc9854d1d2a1ffe095f7b5fac19ddba095c259e6067a46500381b5a5 + +define $(package)_preprocess_cmds + autoreconf -i +endef + +define $(package)_set_vars + $(package)_config_opts=--disable-shared + $(package)_config_opts_linux=--with-pic --disable-udev + $(package)_config_opts_mingw32=--disable-udev + $(package)_config_opts_darwin=--disable-udev +endef + +ifneq ($(host_os),darwin) + define $(package)_config_cmds + cp -f $(BASEDIR)/config.guess config.guess &&\ + cp -f $(BASEDIR)/config.sub config.sub &&\ + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) + endef +else + define $(package)_config_cmds + $($(package)_autoconf) AR_FLAGS=$($(package)_arflags) + endef +endif + +define $(package)_build_cmd + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds cp -f lib/libusb-1.0.a lib/libusb.a +endef diff --git a/contrib/depends/packages/native_biplist.mk b/contrib/depends/packages/native_biplist.mk new file mode 100644 index 0000000..3c6e890 --- /dev/null +++ b/contrib/depends/packages/native_biplist.mk @@ -0,0 +1,20 @@ +package=native_biplist +$(package)_version=0.9 +$(package)_download_path=https://pypi.python.org/packages/source/b/biplist +$(package)_file_name=biplist-$($(package)_version).tar.gz +$(package)_sha256_hash=b57cadfd26e4754efdf89e9e37de87885f9b5c847b2615688ca04adfaf6ca604 +$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages +$(package)_patches=sorted_list.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/sorted_list.patch +endef + +define $(package)_build_cmds + python setup.py build +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_install_libdir) && \ + python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) +endef diff --git a/contrib/depends/packages/native_ccache.mk b/contrib/depends/packages/native_ccache.mk new file mode 100644 index 0000000..6821454 --- /dev/null +++ b/contrib/depends/packages/native_ccache.mk @@ -0,0 +1,25 @@ +package=native_ccache +$(package)_version=4.10.2 +$(package)_download_path=https://samba.org/ftp/ccache +$(package)_file_name=ccache-$($(package)_version).tar.gz +$(package)_sha256_hash=108100960bb7e64573ea925af2ee7611701241abb36ce0aae3354528403a7d87 + +define $(package)_set_vars +$(package)_config_opts=-DCMAKE_INSTALL_PREFIX="$(host_prefix)/native" +endef + +define $(package)_config_cmds + cmake -S . -B build $($(package)_config_opts) +endef + +define $(package)_build_cmds + cmake --build build --parallel +endef + +define $(package)_stage_cmds + cd build && $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm -rf $($(package)_staging_dir)/lib $($(package)_staging_dir)/include +endef diff --git a/contrib/depends/packages/native_cctools.mk b/contrib/depends/packages/native_cctools.mk new file mode 100644 index 0000000..82079a6 --- /dev/null +++ b/contrib/depends/packages/native_cctools.mk @@ -0,0 +1,28 @@ +package=native_cctools +$(package)_version=04663295d0425abfac90a42440a7ec02d7155fea +$(package)_download_path=https://github.com/tpoechtrager/cctools-port/archive +$(package)_download_file=$($(package)_version).tar.gz +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=70a7189418c2086d20c299c5d59250cf5940782c778892ccc899c66516ed240e +$(package)_build_subdir=cctools +$(package)_dependencies=native_clang native_libtapi + +define $(package)_set_vars +$(package)_config_opts=--target=$(host) --disable-lto-support --with-libtapi=$(host_prefix) +$(package)_ldflags+=-Wl,-rpath=\\$$$$$$$$\$$$$$$$$ORIGIN/../lib +$(package)_cc=$(host_prefix)/native/bin/clang +$(package)_cxx=$(host_prefix)/native/bin/clang++ +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install && \ + cp $($(package)_extract_dir)/cctools/misc/install_name_tool $($(package)_staging_prefix_dir)/bin/ +endef diff --git a/contrib/depends/packages/native_cdrkit.mk b/contrib/depends/packages/native_cdrkit.mk new file mode 100644 index 0000000..ea1cc59 --- /dev/null +++ b/contrib/depends/packages/native_cdrkit.mk @@ -0,0 +1,26 @@ +package=native_cdrkit +$(package)_version=1.1.11 +$(package)_download_path=https://distro.ibiblio.org/fatdog/source/600/c +$(package)_file_name=cdrkit-$($(package)_version).tar.bz2 +$(package)_sha256_hash=b50d64c214a65b1a79afe3a964c691931a4233e2ba605d793eb85d0ac3652564 +$(package)_patches=cdrkit-deterministic.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/cdrkit-deterministic.patch +endef + +define $(package)_config_cmds + cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) genisoimage +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) -C genisoimage install +endef + +define $(package)_postprocess_cmds + rm bin/isovfy bin/isoinfo bin/isodump bin/isodebug bin/devdump +endef diff --git a/contrib/depends/packages/native_clang.mk b/contrib/depends/packages/native_clang.mk new file mode 100644 index 0000000..79523ae --- /dev/null +++ b/contrib/depends/packages/native_clang.mk @@ -0,0 +1,29 @@ +package=native_clang +$(package)_version=9.0.0 +$(package)_download_path=https://releases.llvm.org/$($(package)_version) +$(package)_download_file=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz +$(package)_file_name=clang+llvm-$($(package)_version)-x86_64-linux-gnu-ubuntu-18.04.tar.xz +$(package)_sha256_hash=a23b082b30c128c9831dbdd96edad26b43f56624d0ad0ea9edec506f5385038d + +define $(package)_extract_cmds + echo $($(package)_sha256_hash) $($(package)_source) | sha256sum -c &&\ + mkdir -p toolchain/bin toolchain/lib/clang/3.5/include && \ + tar --strip-components=1 -C toolchain -xf $($(package)_source) && \ + rm -f toolchain/lib/libc++abi.so* && \ + echo "#!/bin/sh" > toolchain/bin/$(host)-dsymutil && \ + echo "exit 0" >> toolchain/bin/$(host)-dsymutil && \ + chmod +x toolchain/bin/$(host)-dsymutil +endef + +define $(package)_stage_cmds + cd $($(package)_extract_dir)/toolchain && \ + mkdir -p $($(package)_staging_prefix_dir)/lib/clang/$($(package)_version)/include && \ + mkdir -p $($(package)_staging_prefix_dir)/bin $($(package)_staging_prefix_dir)/include && \ + cp bin/clang $($(package)_staging_prefix_dir)/bin/ &&\ + cp -P bin/clang++ $($(package)_staging_prefix_dir)/bin/ &&\ + cp lib/libLTO.so $($(package)_staging_prefix_dir)/lib/ && \ + cp -rf lib/clang/$($(package)_version)/include/* $($(package)_staging_prefix_dir)/lib/clang/$($(package)_version)/include/ && \ + cp bin/dsymutil $($(package)_staging_prefix_dir)/bin/$(host)-dsymutil && \ + if `test -d include/c++/`; then cp -rf include/c++/ $($(package)_staging_prefix_dir)/include/; fi && \ + if `test -d lib/c++/`; then cp -rf lib/c++/ $($(package)_staging_prefix_dir)/lib/; fi +endef diff --git a/contrib/depends/packages/native_cmake-unused.mk b/contrib/depends/packages/native_cmake-unused.mk new file mode 100644 index 0000000..2fb5874 --- /dev/null +++ b/contrib/depends/packages/native_cmake-unused.mk @@ -0,0 +1,23 @@ +package=native_cmake +$(package)_version=3.14.0 +$(package)_version_dot=v3.14 +$(package)_download_path=https://cmake.org/files/$($(package)_version_dot)/ +$(package)_file_name=cmake-$($(package)_version).tar.gz +$(package)_sha256_hash=aa76ba67b3c2af1946701f847073f4652af5cbd9f141f221c97af99127e75502 + +define $(package)_set_vars +$(package)_config_opts= +endef + +define $(package)_config_cmds + ./bootstrap &&\ + ./configure $($(package)_config_opts) +endef + +define $(package)_build_cmd + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/native_ds_store.mk b/contrib/depends/packages/native_ds_store.mk new file mode 100644 index 0000000..f0c6176 --- /dev/null +++ b/contrib/depends/packages/native_ds_store.mk @@ -0,0 +1,17 @@ +package=native_ds_store +$(package)_version=1.1.0 +$(package)_download_path=https://github.com/al45tair/ds_store/archive/ +$(package)_download_file=v$($(package)_version).tar.gz +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=a9f4c0755c6be7224ff7029e188dd262e830bb81e801424841db9eb0780ec8ed +$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages +$(package)_dependencies=native_biplist + +define $(package)_build_cmds + python setup.py build +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_install_libdir) && \ + python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) +endef diff --git a/contrib/depends/packages/native_libdmg-hfsplus.mk b/contrib/depends/packages/native_libdmg-hfsplus.mk new file mode 100644 index 0000000..839b065 --- /dev/null +++ b/contrib/depends/packages/native_libdmg-hfsplus.mk @@ -0,0 +1,22 @@ +package=native_libdmg-hfsplus +$(package)_version=0.1 +$(package)_download_path=https://github.com/theuni/libdmg-hfsplus/archive +$(package)_file_name=libdmg-hfsplus-v$($(package)_version).tar.gz +$(package)_sha256_hash=6569a02eb31c2827080d7d59001869ea14484c281efab0ae7f2b86af5c3120b3 +$(package)_build_subdir=build + +define $(package)_preprocess_cmds + mkdir build +endef + +define $(package)_config_cmds + cmake -DCMAKE_INSTALL_PREFIX:PATH=$(build_prefix)/bin .. +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) -C dmg -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) -C dmg install +endef diff --git a/contrib/depends/packages/native_libtapi.mk b/contrib/depends/packages/native_libtapi.mk new file mode 100644 index 0000000..ba25ba2 --- /dev/null +++ b/contrib/depends/packages/native_libtapi.mk @@ -0,0 +1,28 @@ +package=native_libtapi +$(package)_version=664b8414f89612f2dfd35a9b679c345aa5389026 +$(package)_download_path=https://github.com/tpoechtrager/apple-libtapi/archive +$(package)_download_file=$($(package)_version).tar.gz +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=62e419c12d1c9fad67cc1cd523132bc00db050998337c734c15bc8d73cc02b61 +$(package)_build_subdir=build +$(package)_dependencies=native_clang + +define $(package)_config_cmds + echo -n $(build_prefix) > INSTALLPREFIX; \ + CC=$(host_prefix)/native/bin/clang CXX=$(host_prefix)/native/bin/clang++ \ + cmake -DCMAKE_INSTALL_PREFIX=$(build_prefix) \ + -DLLVM_INCLUDE_TESTS=OFF \ + -DCMAKE_BUILD_TYPE=RELEASE \ + -DTAPI_REPOSITORY_STRING="1100.0.11" \ + -DTAPI_FULL_VERSION="11.0.0" \ + -DCMAKE_CXX_FLAGS="-I $($(package)_extract_dir)/src/llvm/projects/clang/include -I $($(package)_build_dir)/projects/clang/include" \ + $($(package)_extract_dir)/src/llvm +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) clangBasic && $(MAKE) -j$(NUM_CORES) libtapi +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install-libtapi install-tapi-headers +endef diff --git a/contrib/depends/packages/native_mac_alias.mk b/contrib/depends/packages/native_mac_alias.mk new file mode 100644 index 0000000..48bd90f --- /dev/null +++ b/contrib/depends/packages/native_mac_alias.mk @@ -0,0 +1,21 @@ +package=native_mac_alias +$(package)_version=1.1.0 +$(package)_download_path=https://github.com/al45tair/mac_alias/archive/ +$(package)_download_file=v$($(package)_version).tar.gz +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=b10cb44ecb64fc25283fae7a9cf365d2829377d84e37b9c21100aca8757509be +$(package)_install_libdir=$(build_prefix)/lib/python/dist-packages +$(package)_patches=python3.patch + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/python3.patch +endef + +define $(package)_build_cmds + python setup.py build +endef + +define $(package)_stage_cmds + mkdir -p $($(package)_install_libdir) && \ + python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) +endef diff --git a/contrib/depends/packages/native_protobuf.mk b/contrib/depends/packages/native_protobuf.mk new file mode 100644 index 0000000..2ef299c --- /dev/null +++ b/contrib/depends/packages/native_protobuf.mk @@ -0,0 +1,27 @@ +package=protobuf3 +$(package)_version=3.6.1 +$(package)_download_path=https://github.com/protocolbuffers/protobuf/releases/download/v$($(package)_version)/ +$(package)_file_name=protobuf-cpp-$($(package)_version).tar.gz +$(package)_sha256_hash=b3732e471a9bb7950f090fd0457ebd2536a9ba0891b7f3785919c654fe2a2529 +$(package)_cxxflags=-std=c++11 + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --prefix=$(build_prefix) + $(package)_config_opts_linux=--with-pic +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) -C src +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) -C src install +endef + +define $(package)_postprocess_cmds + rm lib/libprotoc.a || true +endef diff --git a/contrib/depends/packages/ncurses.mk b/contrib/depends/packages/ncurses.mk new file mode 100644 index 0000000..770748b --- /dev/null +++ b/contrib/depends/packages/ncurses.mk @@ -0,0 +1,63 @@ +package=ncurses +$(package)_version=6.5 +$(package)_download_path=https://ftp.gnu.org/gnu/ncurses +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=136d91bc269a9a5785e5f9e980bc76ab57428f604ce3e5a5a90cebc767971cc6 +$(package)_patches=fallback.c + +define $(package)_set_vars + $(package)_build_opts=CC="$($(package)_cc)" + $(package)_config_env=cf_cv_ar_flags="" + $(package)_config_opts=--prefix=$(host_prefix) + $(package)_config_opts+=--disable-shared + $(package)_config_opts+=--with-build-cc=gcc + $(package)_config_opts+=--without-debug + $(package)_config_opts+=--without-ada + $(package)_config_opts+=--without-cxx-binding + $(package)_config_opts+=--without-cxx + $(package)_config_opts+=--without-ticlib + $(package)_config_opts+=--without-tic + $(package)_config_opts+=--without-progs + $(package)_config_opts+=--without-tests + $(package)_config_opts+=--without-tack + $(package)_config_opts+=--without-manpages + $(package)_config_opts+=--with-termlib + $(package)_config_opts+=--disable-tic-depends + $(package)_config_opts+=--disable-big-strings + $(package)_config_opts+=--disable-ext-colors + $(package)_config_opts+=--enable-pc-files + $(package)_config_opts+=--host=$(HOST) + $(pacakge)_config_opts+=--without-shared + $(pacakge)_config_opts+=--without-pthread + $(pacakge)_config_opts+=--disable-rpath + $(pacakge)_config_opts+=--disable-colorfgbg + $(pacakge)_config_opts+=--disable-ext-mouse + $(pacakge)_config_opts+=--disable-symlinks + $(pacakge)_config_opts+=--enable-warnings + $(pacakge)_config_opts+=--enable-assertions + $(package)_config_opts+=--with-default-terminfo-dir=/etc/_terminfo_ + $(package)_config_opts+=--with-terminfo-dirs=/etc/_terminfo_ + $(pacakge)_config_opts+=--enable-database + $(pacakge)_config_opts+=--enable-sp-funcs + $(pacakge)_config_opts+=--disable-term-driver + $(pacakge)_config_opts+=--enable-interop + $(pacakge)_config_opts+=--enable-widec + $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" +endef + +define $(package)_preprocess_cmds + cp $($(package)_patch_dir)/fallback.c ncurses +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) $($(package)_build_opts) V=1 +endef + +define $(package)_stage_cmds + $(MAKE) install.libs DESTDIR=$($(package)_staging_dir) +endef + diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk new file mode 100644 index 0000000..dc49a7e --- /dev/null +++ b/contrib/depends/packages/openssl.mk @@ -0,0 +1,68 @@ +package=openssl +$(package)_version=3.0.13 +$(package)_download_path=https://www.openssl.org/source +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=88525753f79d3bec27d2fa7c66aa0b92b3aa9498dafd93d7cfa4b3780cdae313 + +define $(package)_set_vars +$(package)_config_env=AR="$($(package)_ar)" ARFLAGS=$($(package)_arflags) RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" +$(package)_config_env_android=ANDROID_NDK_ROOT="$(host_prefix)/native" PATH="$(host_prefix)/native/bin" CC=clang AR=ar RANLIB=ranlib +$(package)_build_env_android=ANDROID_NDK_ROOT="$(host_prefix)/native" +$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl --libdir=$(host_prefix)/lib +$(package)_config_opts+=no-capieng +$(package)_config_opts+=no-dso +$(package)_config_opts+=no-dtls1 +$(package)_config_opts+=no-ec_nistp_64_gcc_128 +$(package)_config_opts+=no-gost +$(package)_config_opts+=no-md2 +$(package)_config_opts+=no-rc5 +$(package)_config_opts+=no-rdrand +$(package)_config_opts+=no-rfc3779 +$(package)_config_opts+=no-sctp +$(package)_config_opts+=no-shared +$(package)_config_opts+=no-ssl-trace +$(package)_config_opts+=no-ssl3 +$(package)_config_opts+=no-tests +$(package)_config_opts+=no-unit-test +$(package)_config_opts+=no-weak-ssl-ciphers +$(package)_config_opts+=no-zlib +$(package)_config_opts+=no-zlib-dynamic +$(package)_config_opts+=$($(package)_cflags) $($(package)_cppflags) +$(package)_config_opts_linux=-fPIC -Wa,--noexecstack +$(package)_config_opts_freebsd=-fPIC -Wa,--noexecstack +$(package)_config_opts_x86_64_linux=linux-x86_64 +$(package)_config_opts_i686_linux=linux-generic32 +$(package)_config_opts_arm_linux=linux-generic32 +$(package)_config_opts_aarch64_linux=linux-generic64 +$(package)_config_opts_armv7a_android=--static android-arm +$(package)_config_opts_aarch64_android=--static android-arm64 +$(package)_config_opts_aarch64_darwin=darwin64-arm64-cc +$(package)_config_opts_riscv64_linux=linux-generic64 +$(package)_config_opts_mipsel_linux=linux-generic32 +$(package)_config_opts_mips_linux=linux-generic32 +$(package)_config_opts_powerpc_linux=linux-generic32 +$(package)_config_opts_x86_64_darwin=darwin64-x86_64-cc +$(package)_config_opts_x86_64_mingw32=mingw64 +$(package)_config_opts_i686_mingw32=mingw +$(package)_config_opts_x86_64_freebsd=BSD-x86_64 +endef + +define $(package)_preprocess_cmds + sed -i.old 's|crypto ssl apps util tools fuzz providers doc|crypto ssl util tools providers|' build.info +endef + +define $(package)_config_cmds + ./Configure $($(package)_config_opts) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) build_libs +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install_sw +endef + +define $(package)_postprocess_cmds + rm -rf share bin etc +endef diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk new file mode 100644 index 0000000..9059d9b --- /dev/null +++ b/contrib/depends/packages/packages.mk @@ -0,0 +1,39 @@ +packages:=boost openssl zeromq libiconv expat unbound polyseed + +# ccache is useless in gitian builds +ifneq ($(GITIAN),1) +native_packages := native_ccache +endif + +hardware_packages := hidapi protobuf libusb +hardware_native_packages := native_protobuf + +android_native_packages = android_ndk +android_packages = ncurses readline sodium + +darwin_native_packages = $(hardware_native_packages) +darwin_packages = ncurses readline sodium $(hardware_packages) +ios_packages = sodium protobuf native_protobuf + +# not really native... +freebsd_native_packages = freebsd_base +freebsd_packages = ncurses readline sodium + +linux_packages = eudev ncurses readline sodium $(hardware_packages) +linux_native_packages = $(hardware_native_packages) + +ifeq ($(build_tests),ON) +packages += gtest +endif + +ifneq ($(host_arch),riscv64) +linux_packages += unwind +endif + +mingw32_packages = icu4c sodium $(hardware_packages) +mingw32_native_packages = $(hardware_native_packages) + +ifneq ($(build_os),darwin) +darwin_native_packages += darwin_sdk native_clang native_cctools native_libtapi +endif + diff --git a/contrib/depends/packages/polyseed.mk b/contrib/depends/packages/polyseed.mk new file mode 100644 index 0000000..9818563 --- /dev/null +++ b/contrib/depends/packages/polyseed.mk @@ -0,0 +1,22 @@ +package=polyseed +$(package)_version=2.1.0-patch +$(package)_download_path=https://github.com/MrCyjaneK/$(package)/archive/refs/tags/ +$(package)_download_file=v$($(package)_version).tar.gz +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=7f5c583a1f48ee6d63174dd1f1485d00b02d76d6df0181bc42c54558502c8443 + +define $(package)_config_cmds + CC="$($(package)_cc)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" -DSTATIC=ON . +endef + +define $(package)_set_vars + $(package)_build_opts=CC="$($(package)_cc)" +endef + +define $(package)_build_cmds + CC="$($(package)_cc)" $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/protobuf.mk b/contrib/depends/packages/protobuf.mk new file mode 100644 index 0000000..5ee93ba --- /dev/null +++ b/contrib/depends/packages/protobuf.mk @@ -0,0 +1,37 @@ +package=protobuf +$(package)_version=$(native_$(package)_version) +$(package)_download_path=$(native_$(package)_download_path) +$(package)_file_name=$(native_$(package)_file_name) +$(package)_sha256_hash=$(native_$(package)_sha256_hash) +$(package)_dependencies=native_$(package) +$(package)_cxxflags=-std=c++11 +$(package)_patches=visibility.patch + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --with-protoc=$(build_prefix)/bin/protoc + $(package)_config_opts_linux=--with-pic + $(package)_ar=$($(package)_ar) +endef + +define $(package)_preprocess_cmds + patch -p0 < $($(package)_patch_dir)/visibility.patch +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) -C src libprotobuf.la +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) -C src install-libLTLIBRARIES install-nobase_includeHEADERS &&\ + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA +endef + +define $(package)_postprocess_cmds + rm lib/libprotoc.a &&\ + rm lib/*.la +endef + diff --git a/contrib/depends/packages/readline.mk b/contrib/depends/packages/readline.mk new file mode 100644 index 0000000..4eb04f1 --- /dev/null +++ b/contrib/depends/packages/readline.mk @@ -0,0 +1,29 @@ +package=readline +$(package)_version=8.0 +$(package)_download_path=https://ftp.gnu.org/gnu/readline +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461 +$(package)_dependencies=ncurses + +define $(package)_set_vars + $(package)_build_opts=CC="$($(package)_cc)" + $(package)_config_opts+=--prefix=$(host_prefix) + $(package)_config_opts+=--exec-prefix=$(host_prefix) + $(package)_config_opts+=--host=$(HOST) + $(package)_config_opts+=--disable-shared --with-curses + $(package)_config_opts_release=--disable-debug-mode + $(package)_build_opts=CFLAGS="$($(package)_cflags) $($(package)_cppflags) -fPIC" +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) $($(package)_build_opts) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) install DESTDIR=$($(package)_staging_dir) prefix=$(host_prefix) exec-prefix=$(host_prefix) +endef + diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk new file mode 100644 index 0000000..8e85a4e --- /dev/null +++ b/contrib/depends/packages/sodium.mk @@ -0,0 +1,34 @@ +package=sodium +$(package)_version=1.0.18 +$(package)_download_path=https://download.libsodium.org/libsodium/releases/ +$(package)_file_name=libsodium-$($(package)_version).tar.gz +$(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e46636c1 +$(package)_patches=disable-glibc-getrandom-getentropy.patch fix-whitespace.patch + +define $(package)_set_vars +$(package)_config_env_android=ANDROID_NDK_ROOT="$(host_prefix)/native" PATH="$(host_prefix)/native/bin" CC=clang AR=ar RANLIB=ranlib +$(package)_config_opts=--enable-static --disable-shared --with-pic +$(package)_config_opts+=--prefix=$(host_prefix) +endef + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/disable-glibc-getrandom-getentropy.patch &&\ + patch -p1 < $($(package)_patch_dir)/fix-whitespace.patch +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef + diff --git a/contrib/depends/packages/unbound.mk b/contrib/depends/packages/unbound.mk new file mode 100644 index 0000000..d5e5c0d --- /dev/null +++ b/contrib/depends/packages/unbound.mk @@ -0,0 +1,36 @@ +package=unbound +$(package)_version=1.19.1 +$(package)_download_path=https://www.nlnetlabs.nl/downloads/$(package)/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=bc1d576f3dd846a0739adc41ffaa702404c6767d2b6082deb9f2f97cbb24a3a9 +$(package)_dependencies=openssl expat +$(package)_patches=disable-glibc-reallocarray.patch + + +define $(package)_set_vars + $(package)_config_opts=--disable-shared --enable-static --without-pyunbound --prefix=$(host_prefix) + $(package)_config_opts+=--with-libexpat=$(host_prefix) --with-ssl=$(host_prefix) --with-libevent=no + $(package)_config_opts+=--without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only + $(package)_config_opts_linux=--with-pic + $(package)_config_opts_w64=--enable-static-exe --sysconfdir=/etc --prefix=$(host_prefix) --target=$(host_prefix) + $(package)_config_opts_x86_64_darwin=ac_cv_func_SHA384_Init=yes + $(package)_build_opts_mingw32=LDFLAGS="$($(package)_ldflags) -lpthread" + $(package)_cflags_mingw32+="-D_WIN32_WINNT=0x600" +endef + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/disable-glibc-reallocarray.patch &&\ + autoconf +endef + +define $(package)_config_cmds + $($(package)_autoconf) ac_cv_func_getentropy=no +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) $($(package)_build_opts) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/unwind.mk b/contrib/depends/packages/unwind.mk new file mode 100644 index 0000000..c6bec1f --- /dev/null +++ b/contrib/depends/packages/unwind.mk @@ -0,0 +1,29 @@ +package=unwind +$(package)_version=1.5.0 +$(package)_download_path=https://download.savannah.nongnu.org/releases/libunwind +$(package)_file_name=lib$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=90337653d92d4a13de590781371c604f9031cdb50520366aa1e3a91e1efb1017 +$(package)_patches=fix_obj_order.patch + +define $(package)_preprocess_cmds + patch -p0 < $($(package)_patch_dir)/fix_obj_order.patch +endef + +define $(package)_config_cmds + cp -f $(BASEDIR)/config.guess config/config.guess &&\ + cp -f $(BASEDIR)/config.sub config/config.sub &&\ + $($(package)_autoconf) --disable-shared --enable-static --disable-tests --disable-documentation AR_FLAGS=$($(package)_arflags) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef + +define $(package)_postprocess_cmds + rm lib/*.la +endef + diff --git a/contrib/depends/packages/xproto.mk b/contrib/depends/packages/xproto.mk new file mode 100644 index 0000000..34acd4d --- /dev/null +++ b/contrib/depends/packages/xproto.mk @@ -0,0 +1,21 @@ +package=xproto +$(package)_version=7.0.26 +$(package)_download_path=https://xorg.freedesktop.org/releases/individual/proto +$(package)_file_name=$(package)-$($(package)_version).tar.bz2 +$(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f + +define $(package)_set_vars +$(package)_config_opts=--disable-shared +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install +endef diff --git a/contrib/depends/packages/zeromq.mk b/contrib/depends/packages/zeromq.mk new file mode 100644 index 0000000..3af263c --- /dev/null +++ b/contrib/depends/packages/zeromq.mk @@ -0,0 +1,40 @@ +package=zeromq +$(package)_version=4.3.5 +$(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/ +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=6653ef5910f17954861fe72332e68b03ca6e4d9c7160eb3a8de5a5a913bfab43 +$(package)_patches=fix_declaration.patch + +define $(package)_set_vars + $(package)_config_opts=--without-documentation --disable-shared --without-libsodium --disable-curve + $(package)_config_opts_linux=--with-pic + $(package)_config_opts_freebsd=--with-pic + $(package)_config_opts_ios=--host=$(host_arch)-apple-darwin + $(package)_cxxflags=-std=c++11 + $(package)_cxxflags_darwin=-std=c++11 + $(package)_cxxflags_ios=-std=c++11 +endef + +define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/fix_declaration.patch +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) -j$(NUM_CORES) src/libzmq.la +endef + +define $(package)_stage_cmds + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA VERBOSE=1 &&\ + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES VERBOSE=1 &&\ + $(MAKE) -j$(NUM_CORES) DESTDIR=$($(package)_staging_dir) install-includeHEADERS VERBOSE=1 +endef + +define $(package)_postprocess_cmds + rm -rf bin share &&\ + rm lib/*.la || true +endef + diff --git a/contrib/depends/patches/boost/fix_arm_arch.patch b/contrib/depends/patches/boost/fix_arm_arch.patch new file mode 100644 index 0000000..3cf6b6f --- /dev/null +++ b/contrib/depends/patches/boost/fix_arm_arch.patch @@ -0,0 +1,11 @@ +--- boost_1_64_0/tools/build/src/tools/darwin.jam.O 2017-04-17 03:22:26.000000000 +0100 ++++ boost_1_64_0/tools/build/src/tools/darwin.jam 2022-05-04 17:26:29.984464447 +0000 +@@ -505,7 +505,7 @@ + if $(instruction-set) { + options = -arch$(_)$(instruction-set) ; + } else { +- options = -arch arm ; ++# options = -arch arm ; + } + } + } diff --git a/contrib/depends/patches/boost/fix_aroptions.patch b/contrib/depends/patches/boost/fix_aroptions.patch new file mode 100644 index 0000000..5b2ec10 --- /dev/null +++ b/contrib/depends/patches/boost/fix_aroptions.patch @@ -0,0 +1,28 @@ +--- boost_1_64_0/tools/build/src/tools/gcc.jam.O 2017-04-17 03:22:26.000000000 +0100 ++++ boost_1_64_0/tools/build/src/tools/gcc.jam 2019-11-15 15:46:16.957937137 +0000 +@@ -243,6 +243,8 @@ + { + ECHO notice: using gcc archiver :: $(condition) :: $(archiver[1]) ; + } ++ local arflags = [ feature.get-values : $(options) ] ; ++ toolset.flags gcc.archive .ARFLAGS $(condition) : $(arflags) ; + + # - Ranlib. + local ranlib = [ common.get-invocation-command gcc +@@ -970,6 +972,7 @@ + # logic in intel-linux, but that is hardly worth the trouble as on Linux, 'ar' + # is always available. + .AR = ar ; ++.ARFLAGS = rc ; + .RANLIB = ranlib ; + + toolset.flags gcc.archive AROPTIONS ; +@@ -1011,7 +1014,7 @@ + # + actions piecemeal archive + { +- "$(.AR)" $(AROPTIONS) rc "$(<)" "$(>)" ++ "$(.AR)" $(AROPTIONS) $(.ARFLAGS) "$(<)" "$(>)" + "$(.RANLIB)" "$(<)" + } + diff --git a/contrib/depends/patches/boost/fix_io_control_hpp.patch b/contrib/depends/patches/boost/fix_io_control_hpp.patch new file mode 100644 index 0000000..4d022f2 --- /dev/null +++ b/contrib/depends/patches/boost/fix_io_control_hpp.patch @@ -0,0 +1,11 @@ +--- boost_1_84_0/boost/asio/detail/io_control.hpp.O 2017-04-17 03:22:26.000000000 +0100 ++++ boost_1_84_0/boost/asio/detail/io_control.hpp 2019-11-15 15:46:16.957937137 +0000 +@@ -11,6 +11,9 @@ + # pragma once + #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + ++#ifdef APPLE ++#include ++#endif + #include + #include diff --git a/contrib/depends/patches/cmake/cmake-1-fixes.patch b/contrib/depends/patches/cmake/cmake-1-fixes.patch new file mode 100644 index 0000000..062c067 --- /dev/null +++ b/contrib/depends/patches/cmake/cmake-1-fixes.patch @@ -0,0 +1,67 @@ +This file is part of MXE. See LICENSE.md for licensing information. + +Contains ad hoc patches for cross building. + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tony Theodore +Date: Fri, 12 Aug 2016 02:01:20 +1000 +Subject: [PATCH 1/3] fix windres invocation options + +windres doesn't recognise various gcc flags like -mms-bitfields, +-fopenmp, -mthreads etc. (basically not `-D` or `-I`) + +diff --git a/Modules/Platform/Windows-windres.cmake b/Modules/Platform/Windows-windres.cmake +index 1111111..2222222 100644 +--- a/Modules/Platform/Windows-windres.cmake ++++ b/Modules/Platform/Windows-windres.cmake +@@ -1 +1 @@ +-set(CMAKE_RC_COMPILE_OBJECT " -O coff ") ++set(CMAKE_RC_COMPILE_OBJECT " -O coff ") + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tony Theodore +Date: Tue, 25 Jul 2017 20:34:56 +1000 +Subject: [PATCH 2/3] add option to disable -isystem + +taken from (not accepted): +https://gitlab.kitware.com/cmake/cmake/merge_requests/895 + +see also: +https://gitlab.kitware.com/cmake/cmake/issues/16291 +https://gitlab.kitware.com/cmake/cmake/issues/16919 + +diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake +index 1111111..2222222 100644 +--- a/Modules/Compiler/GNU.cmake ++++ b/Modules/Compiler/GNU.cmake +@@ -42,7 +42,7 @@ macro(__compiler_gnu lang) + string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG") + set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE " -E > ") + set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE " -S -o ") +- if(NOT APPLE OR NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4) # work around #4462 ++ if(NOT APPLE OR NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4 AND (NOT MXE_DISABLE_INCLUDE_SYSTEM_FLAG)) # work around #4462 + set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ") + endif() + endmacro() + +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Tony Theodore +Date: Tue, 15 Aug 2017 15:25:06 +1000 +Subject: [PATCH 3/3] add CPACK_NSIS_EXECUTABLE variable + + +diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx +index 1111111..2222222 100644 +--- a/Source/CPack/cmCPackNSISGenerator.cxx ++++ b/Source/CPack/cmCPackNSISGenerator.cxx +@@ -384,7 +384,9 @@ int cmCPackNSISGenerator::InitializeInternal() + } + #endif + +- nsisPath = cmSystemTools::FindProgram("makensis", path, false); ++ this->SetOptionIfNotSet("CPACK_NSIS_EXECUTABLE", "makensis"); ++ nsisPath = cmSystemTools::FindProgram( ++ this->GetOption("CPACK_NSIS_EXECUTABLE"), path, false); + + if (nsisPath.empty()) { + cmCPackLogger( diff --git a/contrib/depends/patches/darwin_sdk/fix_missing_definitions.patch b/contrib/depends/patches/darwin_sdk/fix_missing_definitions.patch new file mode 100644 index 0000000..6a346ca --- /dev/null +++ b/contrib/depends/patches/darwin_sdk/fix_missing_definitions.patch @@ -0,0 +1,18 @@ +--- a/usr/include/sys/ucred.h 2024-11-15 15:03:31.037756112 +0000 ++++ b/usr/include/sys/ucred.h 2024-11-15 15:03:35.337755945 +0000 +@@ -70,6 +70,15 @@ + #ifndef _SYS_UCRED_H_ + #define _SYS_UCRED_H_ + ++#ifndef u_int ++typedef unsigned int u_int; ++#endif ++#ifndef u_char ++typedef unsigned char u_char; ++#endif ++#ifndef u_short ++typedef unsigned short u_short; ++#endif + #include + #include + #include \ No newline at end of file diff --git a/contrib/depends/patches/hidapi/missing_win_include.patch b/contrib/depends/patches/hidapi/missing_win_include.patch new file mode 100644 index 0000000..5bbe82d --- /dev/null +++ b/contrib/depends/patches/hidapi/missing_win_include.patch @@ -0,0 +1,21 @@ +From a77b066311da42ed7654e39c0356a3b951b2e296 Mon Sep 17 00:00:00 2001 +From: selsta +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 + #include + #include + #endif diff --git a/contrib/depends/patches/icu4c/icu-001-dont-build-static-dynamic-twice.patch b/contrib/depends/patches/icu4c/icu-001-dont-build-static-dynamic-twice.patch new file mode 100644 index 0000000..bbd4e99 --- /dev/null +++ b/contrib/depends/patches/icu4c/icu-001-dont-build-static-dynamic-twice.patch @@ -0,0 +1,37 @@ +Don't build object files twice + +When passed --enable-static and --enable-shared, icu will generate +both a shared and a static version of its libraries. + +However, in order to do so, it builds each and every object file +twice: once with -fPIC (for the shared library), and once without +-fPIC (for the static library). While admittedly building -fPIC for a +static library generates a slightly suboptimal code, this is what all +the autotools-based project are doing. They build each object file +once, and they use it for both the static and shared libraries. + +icu builds the object files for the shared library as .o files, and +the object files for static library as .ao files. By simply changing +the suffix of object files used for static libraries to ".o", we tell +icu to use the ones built for the shared library (i.e, with -fPIC), +and avoid the double build of icu. + +On a fast build server, this brings the target icu build from +3m41.302s down to 1m43.926s (approximate numbers: some other builds +are running on the system at the same time). + +Signed-off-by: Thomas Petazzoni + +Index: b/source/config/mh-linux +=================================================================== +--- a/source/config/mh-linux ++++ b/source/config/mh-linux +@@ -38,7 +38,7 @@ + ## Shared object suffix + SO = so + ## Non-shared intermediate object suffix +-STATIC_O = ao ++STATIC_O = o + + ## Compilation rules + %.$(STATIC_O): $(srcdir)/%.c diff --git a/contrib/depends/patches/libiconv/fix-whitespace.patch b/contrib/depends/patches/libiconv/fix-whitespace.patch new file mode 100644 index 0000000..531364b --- /dev/null +++ b/contrib/depends/patches/libiconv/fix-whitespace.patch @@ -0,0 +1,13 @@ +diff --git a/preload/configure b/preload/configure +index aab5c77..e20b8f0 100755 +--- a/preload/configure ++++ b/preload/configure +@@ -588,7 +588,7 @@ MAKEFLAGS= + PACKAGE_NAME='libiconv' + PACKAGE_TARNAME='libiconv' + PACKAGE_VERSION='0' +-PACKAGE_STRING='libiconv 0' ++PACKAGE_STRING='libiconv0' + PACKAGE_BUGREPORT='' + PACKAGE_URL='' + diff --git a/contrib/depends/patches/native_biplist/sorted_list.patch b/contrib/depends/patches/native_biplist/sorted_list.patch new file mode 100644 index 0000000..89abdb1 --- /dev/null +++ b/contrib/depends/patches/native_biplist/sorted_list.patch @@ -0,0 +1,29 @@ +--- a/biplist/__init__.py 2014-10-26 19:03:11.000000000 +0000 ++++ b/biplist/__init__.py 2016-07-19 19:30:17.663521999 +0000 +@@ -541,7 +541,7 @@ + return HashableWrapper(n) + elif isinstance(root, dict): + n = {} +- for key, value in iteritems(root): ++ for key, value in sorted(iteritems(root)): + n[self.wrapRoot(key)] = self.wrapRoot(value) + return HashableWrapper(n) + elif isinstance(root, list): +@@ -616,7 +616,7 @@ + elif isinstance(obj, dict): + size = proc_size(len(obj)) + self.incrementByteCount('dictBytes', incr=1+size) +- for key, value in iteritems(obj): ++ for key, value in sorted(iteritems(obj)): + check_key(key) + self.computeOffsets(key, asReference=True) + self.computeOffsets(value, asReference=True) +@@ -714,7 +714,7 @@ + keys = [] + values = [] + objectsToWrite = [] +- for key, value in iteritems(obj): ++ for key, value in sorted(iteritems(obj)): + keys.append(key) + values.append(value) + for key in keys: diff --git a/contrib/depends/patches/native_cdrkit/cdrkit-deterministic.patch b/contrib/depends/patches/native_cdrkit/cdrkit-deterministic.patch new file mode 100644 index 0000000..8ab0993 --- /dev/null +++ b/contrib/depends/patches/native_cdrkit/cdrkit-deterministic.patch @@ -0,0 +1,86 @@ +--- cdrkit-1.1.11.old/genisoimage/tree.c 2008-10-21 19:57:47.000000000 -0400 ++++ cdrkit-1.1.11/genisoimage/tree.c 2013-12-06 00:23:18.489622668 -0500 +@@ -1139,8 +1139,9 @@ + scan_directory_tree(struct directory *this_dir, char *path, + struct directory_entry *de) + { +- DIR *current_dir; ++ int current_file; + char whole_path[PATH_MAX]; ++ struct dirent **d_list; + struct dirent *d_entry; + struct directory *parent; + int dflag; +@@ -1164,7 +1165,8 @@ + this_dir->dir_flags |= DIR_WAS_SCANNED; + + errno = 0; /* Paranoia */ +- current_dir = opendir(path); ++ //current_dir = opendir(path); ++ current_file = scandir(path, &d_list, NULL, alphasort); + d_entry = NULL; + + /* +@@ -1173,12 +1175,12 @@ + */ + old_path = path; + +- if (current_dir) { ++ if (current_file >= 0) { + errno = 0; +- d_entry = readdir(current_dir); ++ d_entry = d_list[0]; + } + +- if (!current_dir || !d_entry) { ++ if (current_file < 0 || !d_entry) { + int ret = 1; + + #ifdef USE_LIBSCHILY +@@ -1191,8 +1193,8 @@ + de->isorec.flags[0] &= ~ISO_DIRECTORY; + ret = 0; + } +- if (current_dir) +- closedir(current_dir); ++ if(d_list) ++ free(d_list); + return (ret); + } + #ifdef ABORT_DEEP_ISO_ONLY +@@ -1208,7 +1210,7 @@ + errmsgno(EX_BAD, "use Rock Ridge extensions via -R or -r,\n"); + errmsgno(EX_BAD, "or allow deep ISO9660 directory nesting via -D.\n"); + } +- closedir(current_dir); ++ free(d_list); + return (1); + } + #endif +@@ -1250,13 +1252,13 @@ + * The first time through, skip this, since we already asked + * for the first entry when we opened the directory. + */ +- if (dflag) +- d_entry = readdir(current_dir); ++ if (dflag && current_file >= 0) ++ d_entry = d_list[current_file]; + dflag++; + +- if (!d_entry) ++ if (current_file < 0) + break; +- ++ current_file--; + /* OK, got a valid entry */ + + /* If we do not want all files, then pitch the backups. */ +@@ -1348,7 +1350,7 @@ + insert_file_entry(this_dir, whole_path, d_entry->d_name); + #endif /* APPLE_HYB */ + } +- closedir(current_dir); ++ free(d_list); + + #ifdef APPLE_HYB + /* diff --git a/contrib/depends/patches/native_mac_alias/python3.patch b/contrib/depends/patches/native_mac_alias/python3.patch new file mode 100644 index 0000000..1a32340 --- /dev/null +++ b/contrib/depends/patches/native_mac_alias/python3.patch @@ -0,0 +1,72 @@ +diff -dur a/mac_alias/alias.py b/mac_alias/alias.py +--- a/mac_alias/alias.py 2015-10-19 12:12:48.000000000 +0200 ++++ b/mac_alias/alias.py 2016-04-03 12:13:12.037159417 +0200 +@@ -243,10 +243,10 @@ + alias = Alias() + alias.appinfo = appinfo + +- alias.volume = VolumeInfo (volname.replace('/',':'), ++ alias.volume = VolumeInfo (volname.decode().replace('/',':'), + voldate, fstype, disktype, + volattrs, volfsid) +- alias.target = TargetInfo (kind, filename.replace('/',':'), ++ alias.target = TargetInfo (kind, filename.decode().replace('/',':'), + folder_cnid, cnid, + crdate, creator_code, type_code) + alias.target.levels_from = levels_from +@@ -261,9 +261,9 @@ + b.read(1) + + if tag == TAG_CARBON_FOLDER_NAME: +- alias.target.folder_name = value.replace('/',':') ++ alias.target.folder_name = value.decode().replace('/',':') + elif tag == TAG_CNID_PATH: +- alias.target.cnid_path = struct.unpack(b'>%uI' % (length // 4), ++ alias.target.cnid_path = struct.unpack('>%uI' % (length // 4), + value) + elif tag == TAG_CARBON_PATH: + alias.target.carbon_path = value +@@ -298,9 +298,9 @@ + alias.target.creation_date \ + = mac_epoch + datetime.timedelta(seconds=seconds) + elif tag == TAG_POSIX_PATH: +- alias.target.posix_path = value ++ alias.target.posix_path = value.decode() + elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT: +- alias.volume.posix_path = value ++ alias.volume.posix_path = value.decode() + elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE: + alias.volume.disk_image_alias = Alias.from_bytes(value) + elif tag == TAG_USER_HOME_LENGTH_PREFIX: +@@ -422,13 +422,13 @@ + # (so doing so is ridiculous, and nothing could rely on it). + b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s', + self.target.kind, +- carbon_volname, voldate, ++ carbon_volname, int(voldate), + self.volume.fs_type, + self.volume.disk_type, + self.target.folder_cnid, + carbon_filename, + self.target.cnid, +- crdate, ++ int(crdate), + self.target.creator_code, + self.target.type_code, + self.target.levels_from, +@@ -449,12 +449,12 @@ + + b.write(struct.pack(b'>hhQhhQ', + TAG_HIGH_RES_VOLUME_CREATION_DATE, +- 8, long(voldate * 65536), ++ 8, int(voldate * 65536), + TAG_HIGH_RES_CREATION_DATE, +- 8, long(crdate * 65536))) ++ 8, int(crdate * 65536))) + + if self.target.cnid_path: +- cnid_path = struct.pack(b'>%uI' % len(self.target.cnid_path), ++ cnid_path = struct.pack('>%uI' % len(self.target.cnid_path), + *self.target.cnid_path) + b.write(struct.pack(b'>hh', TAG_CNID_PATH, + len(cnid_path))) diff --git a/contrib/depends/patches/ncurses/fallback.c b/contrib/depends/patches/ncurses/fallback.c new file mode 100644 index 0000000..fab108c --- /dev/null +++ b/contrib/depends/patches/ncurses/fallback.c @@ -0,0 +1,6621 @@ +/* This file was generated by tinfo/MKfallback.sh */ + +/* + * DO NOT EDIT THIS FILE BY HAND! + */ + +#include + +#include + +/* fallback entries for: linux rxvt vt100 xterm xterm-256color screen screen.linux screen.rxvt screen.xterm-new screen.xterm-256color */ +/* linux */ + +static char linux_alias_data[] = "linux|linux console"; + +static char linux_s_bel [] = "\007"; +static char linux_s_cr [] = "\015"; +static char linux_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char linux_s_tbc [] = "\033[3g"; +static char linux_s_clear [] = "\033[H\033[J"; +static char linux_s_el [] = "\033[K"; +static char linux_s_ed [] = "\033[J"; +static char linux_s_hpa [] = "\033[%i%p1%dG"; +static char linux_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char linux_s_cud1 [] = "\012"; +static char linux_s_home [] = "\033[H"; +static char linux_s_civis [] = "\033[?25l\033[?1c"; +static char linux_s_cub1 [] = "\010"; +static char linux_s_cnorm [] = "\033[?25h\033[?0c"; +static char linux_s_cuf1 [] = "\033[C"; +static char linux_s_cuu1 [] = "\033[A"; +static char linux_s_cvvis [] = "\033[?25h\033[?8c"; +static char linux_s_dch1 [] = "\033[P"; +static char linux_s_dl1 [] = "\033[M"; +static char linux_s_smacs [] = "\016"; +static char linux_s_blink [] = "\033[5m"; +static char linux_s_bold [] = "\033[1m"; +static char linux_s_dim [] = "\033[2m"; +static char linux_s_smir [] = "\033[4h"; +static char linux_s_rev [] = "\033[7m"; +static char linux_s_smso [] = "\033[7m"; +static char linux_s_smul [] = "\033[4m"; +static char linux_s_ech [] = "\033[%p1%dX"; +static char linux_s_rmacs [] = "\017"; +static char linux_s_sgr0 [] = "\033[m\017"; +static char linux_s_rmir [] = "\033[4l"; +static char linux_s_rmso [] = "\033[27m"; +static char linux_s_rmul [] = "\033[24m"; +static char linux_s_flash [] = "\033[?5h$<200/>\033[?5l"; +static char linux_s_ich1 [] = "\033[@"; +static char linux_s_il1 [] = "\033[L"; +static char linux_s_kbs [] = "\177"; +static char linux_s_kdch1 [] = "\033[3~"; +static char linux_s_kcud1 [] = "\033[B"; +static char linux_s_kf1 [] = "\033[[A"; +static char linux_s_kf10 [] = "\033[21~"; +static char linux_s_kf2 [] = "\033[[B"; +static char linux_s_kf3 [] = "\033[[C"; +static char linux_s_kf4 [] = "\033[[D"; +static char linux_s_kf5 [] = "\033[[E"; +static char linux_s_kf6 [] = "\033[17~"; +static char linux_s_kf7 [] = "\033[18~"; +static char linux_s_kf8 [] = "\033[19~"; +static char linux_s_kf9 [] = "\033[20~"; +static char linux_s_khome [] = "\033[1~"; +static char linux_s_kich1 [] = "\033[2~"; +static char linux_s_kcub1 [] = "\033[D"; +static char linux_s_knp [] = "\033[6~"; +static char linux_s_kpp [] = "\033[5~"; +static char linux_s_kcuf1 [] = "\033[C"; +static char linux_s_kcuu1 [] = "\033[A"; +static char linux_s_nel [] = "\015\012"; +static char linux_s_dch [] = "\033[%p1%dP"; +static char linux_s_dl [] = "\033[%p1%dM"; +static char linux_s_cud [] = "\033[%p1%dB"; +static char linux_s_ich [] = "\033[%p1%d@"; +static char linux_s_il [] = "\033[%p1%dL"; +static char linux_s_cub [] = "\033[%p1%dD"; +static char linux_s_cuf [] = "\033[%p1%dC"; +static char linux_s_cuu [] = "\033[%p1%dA"; +static char linux_s_rs1 [] = "\033c\033]R"; +static char linux_s_rc [] = "\0338"; +static char linux_s_vpa [] = "\033[%i%p1%dd"; +static char linux_s_sc [] = "\0337"; +static char linux_s_ind [] = "\012"; +static char linux_s_ri [] = "\033M"; +static char linux_s_sgr [] = "\033[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;%?%p6%t;1%;m%?%p9%t\016%e\017%;"; +static char linux_s_hts [] = "\033H"; +static char linux_s_ht [] = "\011"; +static char linux_s_kb2 [] = "\033[G"; +static char linux_s_acsc [] = "++,,--..00__``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}c~~"; +static char linux_s_kcbt [] = "\033[Z"; +static char linux_s_smam [] = "\033[?7h"; +static char linux_s_rmam [] = "\033[?7l"; +static char linux_s_enacs [] = "\033)0"; +static char linux_s_kend [] = "\033[4~"; +static char linux_s_kspd [] = "\032"; +static char linux_s_kf11 [] = "\033[23~"; +static char linux_s_kf12 [] = "\033[24~"; +static char linux_s_kf13 [] = "\033[25~"; +static char linux_s_kf14 [] = "\033[26~"; +static char linux_s_kf15 [] = "\033[28~"; +static char linux_s_kf16 [] = "\033[29~"; +static char linux_s_kf17 [] = "\033[31~"; +static char linux_s_kf18 [] = "\033[32~"; +static char linux_s_kf19 [] = "\033[33~"; +static char linux_s_kf20 [] = "\033[34~"; +static char linux_s_el1 [] = "\033[1K"; +static char linux_s_u6 [] = "\033[%i%d;%dR"; +static char linux_s_u7 [] = "\033[6n"; +static char linux_s_u8 [] = "\033[?6c"; +static char linux_s_u9 [] = "\033[c"; +static char linux_s_op [] = "\033[39;49m"; +static char linux_s_oc [] = "\033]R"; +static char linux_s_initc [] = "\033]P%p1%x%p2%{255}%*%{1000}%/%02x%p3%{255}%*%{1000}%/%02x%p4%{255}%*%{1000}%/%02x"; +static char linux_s_kmous [] = "\033[M"; +static char linux_s_setaf [] = "\033[3%p1%dm"; +static char linux_s_setab [] = "\033[4%p1%dm"; +static char linux_s_smpch [] = "\033[11m"; +static char linux_s_rmpch [] = "\033[10m"; + +static char linux_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ TRUE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ FALSE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ TRUE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ TRUE, + /* 28: bce */ TRUE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ FALSE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 linux_number_data[] = { + /* 0: cols */ ABSENT_NUMERIC, + /* 1: it */ 8, + /* 2: lines */ ABSENT_NUMERIC, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ 18, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * linux_string_data[] = { + /* 0: cbt */ ABSENT_STRING, + /* 1: bel */ linux_s_bel, + /* 2: cr */ linux_s_cr, + /* 3: csr */ linux_s_csr, + /* 4: tbc */ linux_s_tbc, + /* 5: clear */ linux_s_clear, + /* 6: el */ linux_s_el, + /* 7: ed */ linux_s_ed, + /* 8: hpa */ linux_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ linux_s_cup, + /* 11: cud1 */ linux_s_cud1, + /* 12: home */ linux_s_home, + /* 13: civis */ linux_s_civis, + /* 14: cub1 */ linux_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ linux_s_cnorm, + /* 17: cuf1 */ linux_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ linux_s_cuu1, + /* 20: cvvis */ linux_s_cvvis, + /* 21: dch1 */ linux_s_dch1, + /* 22: dl1 */ linux_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ linux_s_smacs, + /* 26: blink */ linux_s_blink, + /* 27: bold */ linux_s_bold, + /* 28: smcup */ ABSENT_STRING, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ linux_s_dim, + /* 31: smir */ linux_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ linux_s_rev, + /* 35: smso */ linux_s_smso, + /* 36: smul */ linux_s_smul, + /* 37: ech */ linux_s_ech, + /* 38: rmacs */ linux_s_rmacs, + /* 39: sgr0 */ linux_s_sgr0, + /* 40: rmcup */ ABSENT_STRING, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ linux_s_rmir, + /* 43: rmso */ linux_s_rmso, + /* 44: rmul */ linux_s_rmul, + /* 45: flash */ linux_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ ABSENT_STRING, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ linux_s_ich1, + /* 53: il1 */ linux_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ linux_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ linux_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ linux_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ linux_s_kf1, + /* 67: kf10 */ linux_s_kf10, + /* 68: kf2 */ linux_s_kf2, + /* 69: kf3 */ linux_s_kf3, + /* 70: kf4 */ linux_s_kf4, + /* 71: kf5 */ linux_s_kf5, + /* 72: kf6 */ linux_s_kf6, + /* 73: kf7 */ linux_s_kf7, + /* 74: kf8 */ linux_s_kf8, + /* 75: kf9 */ linux_s_kf9, + /* 76: khome */ linux_s_khome, + /* 77: kich1 */ linux_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ linux_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ linux_s_knp, + /* 82: kpp */ linux_s_kpp, + /* 83: kcuf1 */ linux_s_kcuf1, + /* 84: kind */ ABSENT_STRING, + /* 85: kri */ ABSENT_STRING, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ linux_s_kcuu1, + /* 88: rmkx */ ABSENT_STRING, + /* 89: smkx */ ABSENT_STRING, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ linux_s_nel, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ linux_s_dch, + /* 106: dl */ linux_s_dl, + /* 107: cud */ linux_s_cud, + /* 108: ich */ linux_s_ich, + /* 109: indn */ ABSENT_STRING, + /* 110: il */ linux_s_il, + /* 111: cub */ linux_s_cub, + /* 112: cuf */ linux_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ linux_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ linux_s_rs1, + /* 123: rs2 */ ABSENT_STRING, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ linux_s_rc, + /* 127: vpa */ linux_s_vpa, + /* 128: sc */ linux_s_sc, + /* 129: ind */ linux_s_ind, + /* 130: ri */ linux_s_ri, + /* 131: sgr */ linux_s_sgr, + /* 132: hts */ linux_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ linux_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ linux_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ linux_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ linux_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ linux_s_smam, + /* 152: rmam */ linux_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ linux_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ linux_s_kend, + /* 165: kent */ ABSENT_STRING, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ linux_s_kspd, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ ABSENT_STRING, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ ABSENT_STRING, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ ABSENT_STRING, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ ABSENT_STRING, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ ABSENT_STRING, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ linux_s_kf11, + /* 217: kf12 */ linux_s_kf12, + /* 218: kf13 */ linux_s_kf13, + /* 219: kf14 */ linux_s_kf14, + /* 220: kf15 */ linux_s_kf15, + /* 221: kf16 */ linux_s_kf16, + /* 222: kf17 */ linux_s_kf17, + /* 223: kf18 */ linux_s_kf18, + /* 224: kf19 */ linux_s_kf19, + /* 225: kf20 */ linux_s_kf20, + /* 226: kf21 */ ABSENT_STRING, + /* 227: kf22 */ ABSENT_STRING, + /* 228: kf23 */ ABSENT_STRING, + /* 229: kf24 */ ABSENT_STRING, + /* 230: kf25 */ ABSENT_STRING, + /* 231: kf26 */ ABSENT_STRING, + /* 232: kf27 */ ABSENT_STRING, + /* 233: kf28 */ ABSENT_STRING, + /* 234: kf29 */ ABSENT_STRING, + /* 235: kf30 */ ABSENT_STRING, + /* 236: kf31 */ ABSENT_STRING, + /* 237: kf32 */ ABSENT_STRING, + /* 238: kf33 */ ABSENT_STRING, + /* 239: kf34 */ ABSENT_STRING, + /* 240: kf35 */ ABSENT_STRING, + /* 241: kf36 */ ABSENT_STRING, + /* 242: kf37 */ ABSENT_STRING, + /* 243: kf38 */ ABSENT_STRING, + /* 244: kf39 */ ABSENT_STRING, + /* 245: kf40 */ ABSENT_STRING, + /* 246: kf41 */ ABSENT_STRING, + /* 247: kf42 */ ABSENT_STRING, + /* 248: kf43 */ ABSENT_STRING, + /* 249: kf44 */ ABSENT_STRING, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ linux_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ linux_s_u6, + /* 294: u7 */ linux_s_u7, + /* 295: u8 */ linux_s_u8, + /* 296: u9 */ linux_s_u9, + /* 297: op */ linux_s_op, + /* 298: oc */ linux_s_oc, + /* 299: initc */ linux_s_initc, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ linux_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ linux_s_setaf, + /* 360: setab */ linux_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ linux_s_smpch, + /* 380: rmpch */ linux_s_rmpch, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* rxvt */ + +static char rxvt_alias_data[] = "rxvt|rxvt terminal emulator (X Window System)"; + +static char rxvt_s_bel [] = "\007"; +static char rxvt_s_cr [] = "\015"; +static char rxvt_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char rxvt_s_tbc [] = "\033[3g"; +static char rxvt_s_clear [] = "\033[H\033[2J"; +static char rxvt_s_el [] = "\033[K"; +static char rxvt_s_ed [] = "\033[J"; +static char rxvt_s_hpa [] = "\033[%i%p1%dG"; +static char rxvt_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char rxvt_s_cud1 [] = "\012"; +static char rxvt_s_home [] = "\033[H"; +static char rxvt_s_civis [] = "\033[?25l"; +static char rxvt_s_cub1 [] = "\010"; +static char rxvt_s_cnorm [] = "\033[?25h"; +static char rxvt_s_cuf1 [] = "\033[C"; +static char rxvt_s_cuu1 [] = "\033[A"; +static char rxvt_s_dl1 [] = "\033[M"; +static char rxvt_s_smacs [] = "\016"; +static char rxvt_s_blink [] = "\033[5m"; +static char rxvt_s_bold [] = "\033[1m"; +static char rxvt_s_smcup [] = "\0337\033[?47h"; +static char rxvt_s_smir [] = "\033[4h"; +static char rxvt_s_rev [] = "\033[7m"; +static char rxvt_s_smso [] = "\033[7m"; +static char rxvt_s_smul [] = "\033[4m"; +static char rxvt_s_rmacs [] = "\017"; +static char rxvt_s_sgr0 [] = "\033[m\017"; +static char rxvt_s_rmcup [] = "\033[2J\033[?47l\0338"; +static char rxvt_s_rmir [] = "\033[4l"; +static char rxvt_s_rmso [] = "\033[27m"; +static char rxvt_s_rmul [] = "\033[24m"; +static char rxvt_s_flash [] = "\033[?5h$<100/>\033[?5l"; +static char rxvt_s_is1 [] = "\033[?47l\033=\033[?1l"; +static char rxvt_s_is2 [] = "\033[r\033[m\033[2J\033[H\033[?7h\033[?1;3;4;6l\033[4l"; +static char rxvt_s_ich1 [] = "\033[@"; +static char rxvt_s_il1 [] = "\033[L"; +static char rxvt_s_kbs [] = "\010"; +static char rxvt_s_kdch1 [] = "\033[3~"; +static char rxvt_s_kcud1 [] = "\033[B"; +static char rxvt_s_kel [] = "\033[8^"; +static char rxvt_s_kf0 [] = "\033[21~"; +static char rxvt_s_kf1 [] = "\033[11~"; +static char rxvt_s_kf10 [] = "\033[21~"; +static char rxvt_s_kf2 [] = "\033[12~"; +static char rxvt_s_kf3 [] = "\033[13~"; +static char rxvt_s_kf4 [] = "\033[14~"; +static char rxvt_s_kf5 [] = "\033[15~"; +static char rxvt_s_kf6 [] = "\033[17~"; +static char rxvt_s_kf7 [] = "\033[18~"; +static char rxvt_s_kf8 [] = "\033[19~"; +static char rxvt_s_kf9 [] = "\033[20~"; +static char rxvt_s_khome [] = "\033[7~"; +static char rxvt_s_kich1 [] = "\033[2~"; +static char rxvt_s_kcub1 [] = "\033[D"; +static char rxvt_s_knp [] = "\033[6~"; +static char rxvt_s_kpp [] = "\033[5~"; +static char rxvt_s_kcuf1 [] = "\033[C"; +static char rxvt_s_kind [] = "\033[a"; +static char rxvt_s_kri [] = "\033[b"; +static char rxvt_s_kcuu1 [] = "\033[A"; +static char rxvt_s_rmkx [] = "\033>"; +static char rxvt_s_smkx [] = "\033="; +static char rxvt_s_dl [] = "\033[%p1%dM"; +static char rxvt_s_cud [] = "\033[%p1%dB"; +static char rxvt_s_ich [] = "\033[%p1%d@"; +static char rxvt_s_il [] = "\033[%p1%dL"; +static char rxvt_s_cub [] = "\033[%p1%dD"; +static char rxvt_s_cuf [] = "\033[%p1%dC"; +static char rxvt_s_cuu [] = "\033[%p1%dA"; +static char rxvt_s_rs1 [] = "\033>\033[1;3;4;5;6l\033[?7h\033[m\033[r\033[2J\033[H"; +static char rxvt_s_rs2 [] = "\033[r\033[m\033[2J\033[H\033[?7h\033[?1;3;4;6l\033[4l\033>\033[?1000l\033[?25h"; +static char rxvt_s_rc [] = "\0338"; +static char rxvt_s_vpa [] = "\033[%i%p1%dd"; +static char rxvt_s_sc [] = "\0337"; +static char rxvt_s_ind [] = "\012"; +static char rxvt_s_ri [] = "\033M"; +static char rxvt_s_sgr [] = "\033[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;"; +static char rxvt_s_hts [] = "\033H"; +static char rxvt_s_ht [] = "\011"; +static char rxvt_s_ka1 [] = "\033Ow"; +static char rxvt_s_ka3 [] = "\033Oy"; +static char rxvt_s_kb2 [] = "\033Ou"; +static char rxvt_s_kc1 [] = "\033Oq"; +static char rxvt_s_kc3 [] = "\033Os"; +static char rxvt_s_acsc [] = "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char rxvt_s_kcbt [] = "\033[Z"; +static char rxvt_s_enacs [] = "\033(B\033)0"; +static char rxvt_s_kend [] = "\033[8~"; +static char rxvt_s_kent [] = "\033OM"; +static char rxvt_s_kfnd [] = "\033[1~"; +static char rxvt_s_kDC [] = "\033[3$"; +static char rxvt_s_kslt [] = "\033[4~"; +static char rxvt_s_kEND [] = "\033[8$"; +static char rxvt_s_kHOM [] = "\033[7$"; +static char rxvt_s_kIC [] = "\033[2$"; +static char rxvt_s_kLFT [] = "\033[d"; +static char rxvt_s_kNXT [] = "\033[6$"; +static char rxvt_s_kPRV [] = "\033[5$"; +static char rxvt_s_kRIT [] = "\033[c"; +static char rxvt_s_kf11 [] = "\033[23~"; +static char rxvt_s_kf12 [] = "\033[24~"; +static char rxvt_s_kf13 [] = "\033[25~"; +static char rxvt_s_kf14 [] = "\033[26~"; +static char rxvt_s_kf15 [] = "\033[28~"; +static char rxvt_s_kf16 [] = "\033[29~"; +static char rxvt_s_kf17 [] = "\033[31~"; +static char rxvt_s_kf18 [] = "\033[32~"; +static char rxvt_s_kf19 [] = "\033[33~"; +static char rxvt_s_kf20 [] = "\033[34~"; +static char rxvt_s_kf21 [] = "\033[23$"; +static char rxvt_s_kf22 [] = "\033[24$"; +static char rxvt_s_kf23 [] = "\033[11^"; +static char rxvt_s_kf24 [] = "\033[12^"; +static char rxvt_s_kf25 [] = "\033[13^"; +static char rxvt_s_kf26 [] = "\033[14^"; +static char rxvt_s_kf27 [] = "\033[15^"; +static char rxvt_s_kf28 [] = "\033[17^"; +static char rxvt_s_kf29 [] = "\033[18^"; +static char rxvt_s_kf30 [] = "\033[19^"; +static char rxvt_s_kf31 [] = "\033[20^"; +static char rxvt_s_kf32 [] = "\033[21^"; +static char rxvt_s_kf33 [] = "\033[23^"; +static char rxvt_s_kf34 [] = "\033[24^"; +static char rxvt_s_kf35 [] = "\033[25^"; +static char rxvt_s_kf36 [] = "\033[26^"; +static char rxvt_s_kf37 [] = "\033[28^"; +static char rxvt_s_kf38 [] = "\033[29^"; +static char rxvt_s_kf39 [] = "\033[31^"; +static char rxvt_s_kf40 [] = "\033[32^"; +static char rxvt_s_kf41 [] = "\033[33^"; +static char rxvt_s_kf42 [] = "\033[34^"; +static char rxvt_s_kf43 [] = "\033[23@"; +static char rxvt_s_kf44 [] = "\033[24@"; +static char rxvt_s_el1 [] = "\033[1K"; +static char rxvt_s_u6 [] = "\033[%i%d;%dR"; +static char rxvt_s_u7 [] = "\033[6n"; +static char rxvt_s_u8 [] = "\033[?1;2c"; +static char rxvt_s_u9 [] = "\033[c"; +static char rxvt_s_op [] = "\033[39;49m"; +static char rxvt_s_kmous [] = "\033[M"; +static char rxvt_s_setaf [] = "\033[3%p1%dm"; +static char rxvt_s_setab [] = "\033[4%p1%dm"; +static char rxvt_s_s0ds [] = "\033(B"; +static char rxvt_s_s1ds [] = "\033(0"; + +static char rxvt_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ TRUE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ FALSE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ TRUE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ TRUE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 rxvt_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ CANCELLED_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * rxvt_string_data[] = { + /* 0: cbt */ ABSENT_STRING, + /* 1: bel */ rxvt_s_bel, + /* 2: cr */ rxvt_s_cr, + /* 3: csr */ rxvt_s_csr, + /* 4: tbc */ rxvt_s_tbc, + /* 5: clear */ rxvt_s_clear, + /* 6: el */ rxvt_s_el, + /* 7: ed */ rxvt_s_ed, + /* 8: hpa */ rxvt_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ rxvt_s_cup, + /* 11: cud1 */ rxvt_s_cud1, + /* 12: home */ rxvt_s_home, + /* 13: civis */ rxvt_s_civis, + /* 14: cub1 */ rxvt_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ rxvt_s_cnorm, + /* 17: cuf1 */ rxvt_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ rxvt_s_cuu1, + /* 20: cvvis */ ABSENT_STRING, + /* 21: dch1 */ ABSENT_STRING, + /* 22: dl1 */ rxvt_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ rxvt_s_smacs, + /* 26: blink */ rxvt_s_blink, + /* 27: bold */ rxvt_s_bold, + /* 28: smcup */ rxvt_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ ABSENT_STRING, + /* 31: smir */ rxvt_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ rxvt_s_rev, + /* 35: smso */ rxvt_s_smso, + /* 36: smul */ rxvt_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ rxvt_s_rmacs, + /* 39: sgr0 */ rxvt_s_sgr0, + /* 40: rmcup */ rxvt_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ rxvt_s_rmir, + /* 43: rmso */ rxvt_s_rmso, + /* 44: rmul */ rxvt_s_rmul, + /* 45: flash */ rxvt_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ rxvt_s_is1, + /* 49: is2 */ rxvt_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ rxvt_s_ich1, + /* 53: il1 */ rxvt_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ rxvt_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ rxvt_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ rxvt_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ rxvt_s_kel, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ rxvt_s_kf0, + /* 66: kf1 */ rxvt_s_kf1, + /* 67: kf10 */ rxvt_s_kf10, + /* 68: kf2 */ rxvt_s_kf2, + /* 69: kf3 */ rxvt_s_kf3, + /* 70: kf4 */ rxvt_s_kf4, + /* 71: kf5 */ rxvt_s_kf5, + /* 72: kf6 */ rxvt_s_kf6, + /* 73: kf7 */ rxvt_s_kf7, + /* 74: kf8 */ rxvt_s_kf8, + /* 75: kf9 */ rxvt_s_kf9, + /* 76: khome */ rxvt_s_khome, + /* 77: kich1 */ rxvt_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ rxvt_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ rxvt_s_knp, + /* 82: kpp */ rxvt_s_kpp, + /* 83: kcuf1 */ rxvt_s_kcuf1, + /* 84: kind */ rxvt_s_kind, + /* 85: kri */ rxvt_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ rxvt_s_kcuu1, + /* 88: rmkx */ rxvt_s_rmkx, + /* 89: smkx */ rxvt_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ ABSENT_STRING, + /* 106: dl */ rxvt_s_dl, + /* 107: cud */ rxvt_s_cud, + /* 108: ich */ rxvt_s_ich, + /* 109: indn */ ABSENT_STRING, + /* 110: il */ rxvt_s_il, + /* 111: cub */ rxvt_s_cub, + /* 112: cuf */ rxvt_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ rxvt_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ rxvt_s_rs1, + /* 123: rs2 */ rxvt_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ rxvt_s_rc, + /* 127: vpa */ rxvt_s_vpa, + /* 128: sc */ rxvt_s_sc, + /* 129: ind */ rxvt_s_ind, + /* 130: ri */ rxvt_s_ri, + /* 131: sgr */ rxvt_s_sgr, + /* 132: hts */ rxvt_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ rxvt_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ rxvt_s_ka1, + /* 140: ka3 */ rxvt_s_ka3, + /* 141: kb2 */ rxvt_s_kb2, + /* 142: kc1 */ rxvt_s_kc1, + /* 143: kc3 */ rxvt_s_kc3, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ rxvt_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ rxvt_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ ABSENT_STRING, + /* 152: rmam */ ABSENT_STRING, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ rxvt_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ rxvt_s_kend, + /* 165: kent */ rxvt_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ rxvt_s_kfnd, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ rxvt_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ rxvt_s_kslt, + /* 194: kEND */ rxvt_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ rxvt_s_kHOM, + /* 200: kIC */ rxvt_s_kIC, + /* 201: kLFT */ rxvt_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ rxvt_s_kNXT, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ rxvt_s_kPRV, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ rxvt_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ rxvt_s_kf11, + /* 217: kf12 */ rxvt_s_kf12, + /* 218: kf13 */ rxvt_s_kf13, + /* 219: kf14 */ rxvt_s_kf14, + /* 220: kf15 */ rxvt_s_kf15, + /* 221: kf16 */ rxvt_s_kf16, + /* 222: kf17 */ rxvt_s_kf17, + /* 223: kf18 */ rxvt_s_kf18, + /* 224: kf19 */ rxvt_s_kf19, + /* 225: kf20 */ rxvt_s_kf20, + /* 226: kf21 */ rxvt_s_kf21, + /* 227: kf22 */ rxvt_s_kf22, + /* 228: kf23 */ rxvt_s_kf23, + /* 229: kf24 */ rxvt_s_kf24, + /* 230: kf25 */ rxvt_s_kf25, + /* 231: kf26 */ rxvt_s_kf26, + /* 232: kf27 */ rxvt_s_kf27, + /* 233: kf28 */ rxvt_s_kf28, + /* 234: kf29 */ rxvt_s_kf29, + /* 235: kf30 */ rxvt_s_kf30, + /* 236: kf31 */ rxvt_s_kf31, + /* 237: kf32 */ rxvt_s_kf32, + /* 238: kf33 */ rxvt_s_kf33, + /* 239: kf34 */ rxvt_s_kf34, + /* 240: kf35 */ rxvt_s_kf35, + /* 241: kf36 */ rxvt_s_kf36, + /* 242: kf37 */ rxvt_s_kf37, + /* 243: kf38 */ rxvt_s_kf38, + /* 244: kf39 */ rxvt_s_kf39, + /* 245: kf40 */ rxvt_s_kf40, + /* 246: kf41 */ rxvt_s_kf41, + /* 247: kf42 */ rxvt_s_kf42, + /* 248: kf43 */ rxvt_s_kf43, + /* 249: kf44 */ rxvt_s_kf44, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ rxvt_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ rxvt_s_u6, + /* 294: u7 */ rxvt_s_u7, + /* 295: u8 */ rxvt_s_u8, + /* 296: u9 */ rxvt_s_u9, + /* 297: op */ rxvt_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ rxvt_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ rxvt_s_setaf, + /* 360: setab */ rxvt_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ rxvt_s_s0ds, + /* 365: s1ds */ rxvt_s_s1ds, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* vt100 */ + +static char vt100_alias_data[] = "vt100|vt100-am|dec vt100 (w/advanced video)"; + +static char vt100_s_bel [] = "\007"; +static char vt100_s_cr [] = "\015"; +static char vt100_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char vt100_s_tbc [] = "\033[3g"; +static char vt100_s_clear [] = "\033[H\033[J$<50>"; +static char vt100_s_el [] = "\033[K$<3>"; +static char vt100_s_ed [] = "\033[J$<50>"; +static char vt100_s_cup [] = "\033[%i%p1%d;%p2%dH$<5>"; +static char vt100_s_cud1 [] = "\012"; +static char vt100_s_home [] = "\033[H"; +static char vt100_s_cub1 [] = "\010"; +static char vt100_s_cuf1 [] = "\033[C$<2>"; +static char vt100_s_cuu1 [] = "\033[A$<2>"; +static char vt100_s_smacs [] = "\016"; +static char vt100_s_blink [] = "\033[5m$<2>"; +static char vt100_s_bold [] = "\033[1m$<2>"; +static char vt100_s_rev [] = "\033[7m$<2>"; +static char vt100_s_smso [] = "\033[7m$<2>"; +static char vt100_s_smul [] = "\033[4m$<2>"; +static char vt100_s_rmacs [] = "\017"; +static char vt100_s_sgr0 [] = "\033[m\017$<2>"; +static char vt100_s_rmso [] = "\033[m$<2>"; +static char vt100_s_rmul [] = "\033[m$<2>"; +static char vt100_s_kbs [] = "\010"; +static char vt100_s_kcud1 [] = "\033OB"; +static char vt100_s_kf0 [] = "\033Oy"; +static char vt100_s_kf1 [] = "\033OP"; +static char vt100_s_kf10 [] = "\033Ox"; +static char vt100_s_kf2 [] = "\033OQ"; +static char vt100_s_kf3 [] = "\033OR"; +static char vt100_s_kf4 [] = "\033OS"; +static char vt100_s_kf5 [] = "\033Ot"; +static char vt100_s_kf6 [] = "\033Ou"; +static char vt100_s_kf7 [] = "\033Ov"; +static char vt100_s_kf8 [] = "\033Ol"; +static char vt100_s_kf9 [] = "\033Ow"; +static char vt100_s_kcub1 [] = "\033OD"; +static char vt100_s_kcuf1 [] = "\033OC"; +static char vt100_s_kcuu1 [] = "\033OA"; +static char vt100_s_rmkx [] = "\033[?1l\033>"; +static char vt100_s_smkx [] = "\033[?1h\033="; +static char vt100_s_lf1 [] = "pf1"; +static char vt100_s_lf2 [] = "pf2"; +static char vt100_s_lf3 [] = "pf3"; +static char vt100_s_lf4 [] = "pf4"; +static char vt100_s_cud [] = "\033[%p1%dB"; +static char vt100_s_cub [] = "\033[%p1%dD"; +static char vt100_s_cuf [] = "\033[%p1%dC"; +static char vt100_s_cuu [] = "\033[%p1%dA"; +static char vt100_s_mc0 [] = "\033[0i"; +static char vt100_s_mc4 [] = "\033[4i"; +static char vt100_s_mc5 [] = "\033[5i"; +static char vt100_s_rs2 [] = "\033<\033>\033[?3;4;5l\033[?7;8h\033[r"; +static char vt100_s_rc [] = "\0338"; +static char vt100_s_sc [] = "\0337"; +static char vt100_s_ind [] = "\012"; +static char vt100_s_ri [] = "\033M$<5>"; +static char vt100_s_sgr [] = "\033[0%?%p1%p6%|%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;$<2>"; +static char vt100_s_hts [] = "\033H"; +static char vt100_s_ht [] = "\011"; +static char vt100_s_ka1 [] = "\033Oq"; +static char vt100_s_ka3 [] = "\033Os"; +static char vt100_s_kb2 [] = "\033Or"; +static char vt100_s_kc1 [] = "\033Op"; +static char vt100_s_kc3 [] = "\033On"; +static char vt100_s_acsc [] = "``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char vt100_s_smam [] = "\033[?7h"; +static char vt100_s_rmam [] = "\033[?7l"; +static char vt100_s_enacs [] = "\033(B\033)0"; +static char vt100_s_kent [] = "\033OM"; +static char vt100_s_el1 [] = "\033[1K$<3>"; + +static char vt100_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ FALSE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ FALSE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ TRUE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 vt100_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ 3, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ ABSENT_NUMERIC, + /* 14: pairs */ ABSENT_NUMERIC, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * vt100_string_data[] = { + /* 0: cbt */ ABSENT_STRING, + /* 1: bel */ vt100_s_bel, + /* 2: cr */ vt100_s_cr, + /* 3: csr */ vt100_s_csr, + /* 4: tbc */ vt100_s_tbc, + /* 5: clear */ vt100_s_clear, + /* 6: el */ vt100_s_el, + /* 7: ed */ vt100_s_ed, + /* 8: hpa */ ABSENT_STRING, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ vt100_s_cup, + /* 11: cud1 */ vt100_s_cud1, + /* 12: home */ vt100_s_home, + /* 13: civis */ ABSENT_STRING, + /* 14: cub1 */ vt100_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ ABSENT_STRING, + /* 17: cuf1 */ vt100_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ vt100_s_cuu1, + /* 20: cvvis */ ABSENT_STRING, + /* 21: dch1 */ ABSENT_STRING, + /* 22: dl1 */ ABSENT_STRING, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ vt100_s_smacs, + /* 26: blink */ vt100_s_blink, + /* 27: bold */ vt100_s_bold, + /* 28: smcup */ ABSENT_STRING, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ ABSENT_STRING, + /* 31: smir */ ABSENT_STRING, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ vt100_s_rev, + /* 35: smso */ vt100_s_smso, + /* 36: smul */ vt100_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ vt100_s_rmacs, + /* 39: sgr0 */ vt100_s_sgr0, + /* 40: rmcup */ ABSENT_STRING, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ ABSENT_STRING, + /* 43: rmso */ vt100_s_rmso, + /* 44: rmul */ vt100_s_rmul, + /* 45: flash */ ABSENT_STRING, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ ABSENT_STRING, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ ABSENT_STRING, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ vt100_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ ABSENT_STRING, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ vt100_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ vt100_s_kf0, + /* 66: kf1 */ vt100_s_kf1, + /* 67: kf10 */ vt100_s_kf10, + /* 68: kf2 */ vt100_s_kf2, + /* 69: kf3 */ vt100_s_kf3, + /* 70: kf4 */ vt100_s_kf4, + /* 71: kf5 */ vt100_s_kf5, + /* 72: kf6 */ vt100_s_kf6, + /* 73: kf7 */ vt100_s_kf7, + /* 74: kf8 */ vt100_s_kf8, + /* 75: kf9 */ vt100_s_kf9, + /* 76: khome */ ABSENT_STRING, + /* 77: kich1 */ ABSENT_STRING, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ vt100_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ ABSENT_STRING, + /* 82: kpp */ ABSENT_STRING, + /* 83: kcuf1 */ vt100_s_kcuf1, + /* 84: kind */ ABSENT_STRING, + /* 85: kri */ ABSENT_STRING, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ vt100_s_kcuu1, + /* 88: rmkx */ vt100_s_rmkx, + /* 89: smkx */ vt100_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ vt100_s_lf1, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ vt100_s_lf2, + /* 94: lf3 */ vt100_s_lf3, + /* 95: lf4 */ vt100_s_lf4, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ ABSENT_STRING, + /* 106: dl */ ABSENT_STRING, + /* 107: cud */ vt100_s_cud, + /* 108: ich */ ABSENT_STRING, + /* 109: indn */ ABSENT_STRING, + /* 110: il */ ABSENT_STRING, + /* 111: cub */ vt100_s_cub, + /* 112: cuf */ vt100_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ vt100_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ vt100_s_mc0, + /* 119: mc4 */ vt100_s_mc4, + /* 120: mc5 */ vt100_s_mc5, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ ABSENT_STRING, + /* 123: rs2 */ vt100_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ vt100_s_rc, + /* 127: vpa */ ABSENT_STRING, + /* 128: sc */ vt100_s_sc, + /* 129: ind */ vt100_s_ind, + /* 130: ri */ vt100_s_ri, + /* 131: sgr */ vt100_s_sgr, + /* 132: hts */ vt100_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ vt100_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ vt100_s_ka1, + /* 140: ka3 */ vt100_s_ka3, + /* 141: kb2 */ vt100_s_kb2, + /* 142: kc1 */ vt100_s_kc1, + /* 143: kc3 */ vt100_s_kc3, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ vt100_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ ABSENT_STRING, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ vt100_s_smam, + /* 152: rmam */ vt100_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ vt100_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ ABSENT_STRING, + /* 165: kent */ vt100_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ ABSENT_STRING, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ ABSENT_STRING, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ ABSENT_STRING, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ ABSENT_STRING, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ ABSENT_STRING, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ ABSENT_STRING, + /* 217: kf12 */ ABSENT_STRING, + /* 218: kf13 */ ABSENT_STRING, + /* 219: kf14 */ ABSENT_STRING, + /* 220: kf15 */ ABSENT_STRING, + /* 221: kf16 */ ABSENT_STRING, + /* 222: kf17 */ ABSENT_STRING, + /* 223: kf18 */ ABSENT_STRING, + /* 224: kf19 */ ABSENT_STRING, + /* 225: kf20 */ ABSENT_STRING, + /* 226: kf21 */ ABSENT_STRING, + /* 227: kf22 */ ABSENT_STRING, + /* 228: kf23 */ ABSENT_STRING, + /* 229: kf24 */ ABSENT_STRING, + /* 230: kf25 */ ABSENT_STRING, + /* 231: kf26 */ ABSENT_STRING, + /* 232: kf27 */ ABSENT_STRING, + /* 233: kf28 */ ABSENT_STRING, + /* 234: kf29 */ ABSENT_STRING, + /* 235: kf30 */ ABSENT_STRING, + /* 236: kf31 */ ABSENT_STRING, + /* 237: kf32 */ ABSENT_STRING, + /* 238: kf33 */ ABSENT_STRING, + /* 239: kf34 */ ABSENT_STRING, + /* 240: kf35 */ ABSENT_STRING, + /* 241: kf36 */ ABSENT_STRING, + /* 242: kf37 */ ABSENT_STRING, + /* 243: kf38 */ ABSENT_STRING, + /* 244: kf39 */ ABSENT_STRING, + /* 245: kf40 */ ABSENT_STRING, + /* 246: kf41 */ ABSENT_STRING, + /* 247: kf42 */ ABSENT_STRING, + /* 248: kf43 */ ABSENT_STRING, + /* 249: kf44 */ ABSENT_STRING, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ vt100_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ ABSENT_STRING, + /* 294: u7 */ ABSENT_STRING, + /* 295: u8 */ ABSENT_STRING, + /* 296: u9 */ ABSENT_STRING, + /* 297: op */ ABSENT_STRING, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ ABSENT_STRING, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ ABSENT_STRING, + /* 360: setab */ ABSENT_STRING, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* xterm */ + +static char xterm_alias_data[] = "xterm|xterm terminal emulator (X Window System)"; + +static char xterm_s_cbt [] = "\033[Z"; +static char xterm_s_bel [] = "\007"; +static char xterm_s_cr [] = "\015"; +static char xterm_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char xterm_s_tbc [] = "\033[3g"; +static char xterm_s_clear [] = "\033[H\033[2J"; +static char xterm_s_el [] = "\033[K"; +static char xterm_s_ed [] = "\033[J"; +static char xterm_s_hpa [] = "\033[%i%p1%dG"; +static char xterm_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char xterm_s_cud1 [] = "\012"; +static char xterm_s_home [] = "\033[H"; +static char xterm_s_civis [] = "\033[?25l"; +static char xterm_s_cub1 [] = "\010"; +static char xterm_s_cnorm [] = "\033[?12l\033[?25h"; +static char xterm_s_cuf1 [] = "\033[C"; +static char xterm_s_cuu1 [] = "\033[A"; +static char xterm_s_cvvis [] = "\033[?12;25h"; +static char xterm_s_dch1 [] = "\033[P"; +static char xterm_s_dl1 [] = "\033[M"; +static char xterm_s_smacs [] = "\033(0"; +static char xterm_s_blink [] = "\033[5m"; +static char xterm_s_bold [] = "\033[1m"; +static char xterm_s_smcup [] = "\033[?1049h\033[22;0;0t"; +static char xterm_s_dim [] = "\033[2m"; +static char xterm_s_smir [] = "\033[4h"; +static char xterm_s_invis [] = "\033[8m"; +static char xterm_s_rev [] = "\033[7m"; +static char xterm_s_smso [] = "\033[7m"; +static char xterm_s_smul [] = "\033[4m"; +static char xterm_s_ech [] = "\033[%p1%dX"; +static char xterm_s_rmacs [] = "\033(B"; +static char xterm_s_sgr0 [] = "\033(B\033[m"; +static char xterm_s_rmcup [] = "\033[?1049l\033[23;0;0t"; +static char xterm_s_rmir [] = "\033[4l"; +static char xterm_s_rmso [] = "\033[27m"; +static char xterm_s_rmul [] = "\033[24m"; +static char xterm_s_flash [] = "\033[?5h$<100/>\033[?5l"; +static char xterm_s_is2 [] = "\033[!p\033[?3;4l\033[4l\033>"; +static char xterm_s_il1 [] = "\033[L"; +static char xterm_s_kbs [] = "\010"; +static char xterm_s_kdch1 [] = "\033[3~"; +static char xterm_s_kcud1 [] = "\033OB"; +static char xterm_s_kf1 [] = "\033OP"; +static char xterm_s_kf10 [] = "\033[21~"; +static char xterm_s_kf2 [] = "\033OQ"; +static char xterm_s_kf3 [] = "\033OR"; +static char xterm_s_kf4 [] = "\033OS"; +static char xterm_s_kf5 [] = "\033[15~"; +static char xterm_s_kf6 [] = "\033[17~"; +static char xterm_s_kf7 [] = "\033[18~"; +static char xterm_s_kf8 [] = "\033[19~"; +static char xterm_s_kf9 [] = "\033[20~"; +static char xterm_s_khome [] = "\033OH"; +static char xterm_s_kich1 [] = "\033[2~"; +static char xterm_s_kcub1 [] = "\033OD"; +static char xterm_s_knp [] = "\033[6~"; +static char xterm_s_kpp [] = "\033[5~"; +static char xterm_s_kcuf1 [] = "\033OC"; +static char xterm_s_kind [] = "\033[1;2B"; +static char xterm_s_kri [] = "\033[1;2A"; +static char xterm_s_kcuu1 [] = "\033OA"; +static char xterm_s_rmkx [] = "\033[?1l\033>"; +static char xterm_s_smkx [] = "\033[?1h\033="; +static char xterm_s_rmm [] = "\033[?1034l"; +static char xterm_s_smm [] = "\033[?1034h"; +static char xterm_s_dch [] = "\033[%p1%dP"; +static char xterm_s_dl [] = "\033[%p1%dM"; +static char xterm_s_cud [] = "\033[%p1%dB"; +static char xterm_s_ich [] = "\033[%p1%d@"; +static char xterm_s_indn [] = "\033[%p1%dS"; +static char xterm_s_il [] = "\033[%p1%dL"; +static char xterm_s_cub [] = "\033[%p1%dD"; +static char xterm_s_cuf [] = "\033[%p1%dC"; +static char xterm_s_rin [] = "\033[%p1%dT"; +static char xterm_s_cuu [] = "\033[%p1%dA"; +static char xterm_s_mc0 [] = "\033[i"; +static char xterm_s_mc4 [] = "\033[4i"; +static char xterm_s_mc5 [] = "\033[5i"; +static char xterm_s_rep [] = "%p1%c\033[%p2%{1}%-%db"; +static char xterm_s_rs1 [] = "\033c"; +static char xterm_s_rs2 [] = "\033[!p\033[?3;4l\033[4l\033>"; +static char xterm_s_rc [] = "\0338"; +static char xterm_s_vpa [] = "\033[%i%p1%dd"; +static char xterm_s_sc [] = "\0337"; +static char xterm_s_ind [] = "\012"; +static char xterm_s_ri [] = "\033M"; +static char xterm_s_sgr [] = "%?%p9%t\033(0%e\033(B%;\033[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m"; +static char xterm_s_hts [] = "\033H"; +static char xterm_s_ht [] = "\011"; +static char xterm_s_kb2 [] = "\033OE"; +static char xterm_s_acsc [] = "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char xterm_s_kcbt [] = "\033[Z"; +static char xterm_s_smam [] = "\033[?7h"; +static char xterm_s_rmam [] = "\033[?7l"; +static char xterm_s_kend [] = "\033OF"; +static char xterm_s_kent [] = "\033OM"; +static char xterm_s_kDC [] = "\033[3;2~"; +static char xterm_s_kEND [] = "\033[1;2F"; +static char xterm_s_kHOM [] = "\033[1;2H"; +static char xterm_s_kIC [] = "\033[2;2~"; +static char xterm_s_kLFT [] = "\033[1;2D"; +static char xterm_s_kNXT [] = "\033[6;2~"; +static char xterm_s_kPRV [] = "\033[5;2~"; +static char xterm_s_kRIT [] = "\033[1;2C"; +static char xterm_s_kf11 [] = "\033[23~"; +static char xterm_s_kf12 [] = "\033[24~"; +static char xterm_s_kf13 [] = "\033[1;2P"; +static char xterm_s_kf14 [] = "\033[1;2Q"; +static char xterm_s_kf15 [] = "\033[1;2R"; +static char xterm_s_kf16 [] = "\033[1;2S"; +static char xterm_s_kf17 [] = "\033[15;2~"; +static char xterm_s_kf18 [] = "\033[17;2~"; +static char xterm_s_kf19 [] = "\033[18;2~"; +static char xterm_s_kf20 [] = "\033[19;2~"; +static char xterm_s_kf21 [] = "\033[20;2~"; +static char xterm_s_kf22 [] = "\033[21;2~"; +static char xterm_s_kf23 [] = "\033[23;2~"; +static char xterm_s_kf24 [] = "\033[24;2~"; +static char xterm_s_kf25 [] = "\033[1;5P"; +static char xterm_s_kf26 [] = "\033[1;5Q"; +static char xterm_s_kf27 [] = "\033[1;5R"; +static char xterm_s_kf28 [] = "\033[1;5S"; +static char xterm_s_kf29 [] = "\033[15;5~"; +static char xterm_s_kf30 [] = "\033[17;5~"; +static char xterm_s_kf31 [] = "\033[18;5~"; +static char xterm_s_kf32 [] = "\033[19;5~"; +static char xterm_s_kf33 [] = "\033[20;5~"; +static char xterm_s_kf34 [] = "\033[21;5~"; +static char xterm_s_kf35 [] = "\033[23;5~"; +static char xterm_s_kf36 [] = "\033[24;5~"; +static char xterm_s_kf37 [] = "\033[1;6P"; +static char xterm_s_kf38 [] = "\033[1;6Q"; +static char xterm_s_kf39 [] = "\033[1;6R"; +static char xterm_s_kf40 [] = "\033[1;6S"; +static char xterm_s_kf41 [] = "\033[15;6~"; +static char xterm_s_kf42 [] = "\033[17;6~"; +static char xterm_s_kf43 [] = "\033[18;6~"; +static char xterm_s_kf44 [] = "\033[19;6~"; +static char xterm_s_kf45 [] = "\033[20;6~"; +static char xterm_s_kf46 [] = "\033[21;6~"; +static char xterm_s_kf47 [] = "\033[23;6~"; +static char xterm_s_kf48 [] = "\033[24;6~"; +static char xterm_s_kf49 [] = "\033[1;3P"; +static char xterm_s_kf50 [] = "\033[1;3Q"; +static char xterm_s_kf51 [] = "\033[1;3R"; +static char xterm_s_kf52 [] = "\033[1;3S"; +static char xterm_s_kf53 [] = "\033[15;3~"; +static char xterm_s_kf54 [] = "\033[17;3~"; +static char xterm_s_kf55 [] = "\033[18;3~"; +static char xterm_s_kf56 [] = "\033[19;3~"; +static char xterm_s_kf57 [] = "\033[20;3~"; +static char xterm_s_kf58 [] = "\033[21;3~"; +static char xterm_s_kf59 [] = "\033[23;3~"; +static char xterm_s_kf60 [] = "\033[24;3~"; +static char xterm_s_kf61 [] = "\033[1;4P"; +static char xterm_s_kf62 [] = "\033[1;4Q"; +static char xterm_s_kf63 [] = "\033[1;4R"; +static char xterm_s_el1 [] = "\033[1K"; +static char xterm_s_u6 [] = "\033[%i%d;%dR"; +static char xterm_s_u7 [] = "\033[6n"; +static char xterm_s_u8 [] = "\033[?%[;0123456789]c"; +static char xterm_s_u9 [] = "\033[c"; +static char xterm_s_op [] = "\033[39;49m"; +static char xterm_s_setf [] = "\033[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m"; +static char xterm_s_setb [] = "\033[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m"; +static char xterm_s_sitm [] = "\033[3m"; +static char xterm_s_ritm [] = "\033[23m"; +static char xterm_s_kmous [] = "\033[<"; +static char xterm_s_setaf [] = "\033[3%p1%dm"; +static char xterm_s_setab [] = "\033[4%p1%dm"; +static char xterm_s_meml [] = "\033l"; +static char xterm_s_memu [] = "\033m"; + +static char xterm_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ TRUE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ TRUE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 xterm_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * xterm_string_data[] = { + /* 0: cbt */ xterm_s_cbt, + /* 1: bel */ xterm_s_bel, + /* 2: cr */ xterm_s_cr, + /* 3: csr */ xterm_s_csr, + /* 4: tbc */ xterm_s_tbc, + /* 5: clear */ xterm_s_clear, + /* 6: el */ xterm_s_el, + /* 7: ed */ xterm_s_ed, + /* 8: hpa */ xterm_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ xterm_s_cup, + /* 11: cud1 */ xterm_s_cud1, + /* 12: home */ xterm_s_home, + /* 13: civis */ xterm_s_civis, + /* 14: cub1 */ xterm_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ xterm_s_cnorm, + /* 17: cuf1 */ xterm_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ xterm_s_cuu1, + /* 20: cvvis */ xterm_s_cvvis, + /* 21: dch1 */ xterm_s_dch1, + /* 22: dl1 */ xterm_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ xterm_s_smacs, + /* 26: blink */ xterm_s_blink, + /* 27: bold */ xterm_s_bold, + /* 28: smcup */ xterm_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ xterm_s_dim, + /* 31: smir */ xterm_s_smir, + /* 32: invis */ xterm_s_invis, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ xterm_s_rev, + /* 35: smso */ xterm_s_smso, + /* 36: smul */ xterm_s_smul, + /* 37: ech */ xterm_s_ech, + /* 38: rmacs */ xterm_s_rmacs, + /* 39: sgr0 */ xterm_s_sgr0, + /* 40: rmcup */ xterm_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ xterm_s_rmir, + /* 43: rmso */ xterm_s_rmso, + /* 44: rmul */ xterm_s_rmul, + /* 45: flash */ xterm_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ xterm_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ xterm_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ xterm_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ xterm_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ xterm_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ xterm_s_kf1, + /* 67: kf10 */ xterm_s_kf10, + /* 68: kf2 */ xterm_s_kf2, + /* 69: kf3 */ xterm_s_kf3, + /* 70: kf4 */ xterm_s_kf4, + /* 71: kf5 */ xterm_s_kf5, + /* 72: kf6 */ xterm_s_kf6, + /* 73: kf7 */ xterm_s_kf7, + /* 74: kf8 */ xterm_s_kf8, + /* 75: kf9 */ xterm_s_kf9, + /* 76: khome */ xterm_s_khome, + /* 77: kich1 */ xterm_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ xterm_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ xterm_s_knp, + /* 82: kpp */ xterm_s_kpp, + /* 83: kcuf1 */ xterm_s_kcuf1, + /* 84: kind */ xterm_s_kind, + /* 85: kri */ xterm_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ xterm_s_kcuu1, + /* 88: rmkx */ xterm_s_rmkx, + /* 89: smkx */ xterm_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ xterm_s_rmm, + /* 102: smm */ xterm_s_smm, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ xterm_s_dch, + /* 106: dl */ xterm_s_dl, + /* 107: cud */ xterm_s_cud, + /* 108: ich */ xterm_s_ich, + /* 109: indn */ xterm_s_indn, + /* 110: il */ xterm_s_il, + /* 111: cub */ xterm_s_cub, + /* 112: cuf */ xterm_s_cuf, + /* 113: rin */ xterm_s_rin, + /* 114: cuu */ xterm_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ xterm_s_mc0, + /* 119: mc4 */ xterm_s_mc4, + /* 120: mc5 */ xterm_s_mc5, + /* 121: rep */ xterm_s_rep, + /* 122: rs1 */ xterm_s_rs1, + /* 123: rs2 */ xterm_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ xterm_s_rc, + /* 127: vpa */ xterm_s_vpa, + /* 128: sc */ xterm_s_sc, + /* 129: ind */ xterm_s_ind, + /* 130: ri */ xterm_s_ri, + /* 131: sgr */ xterm_s_sgr, + /* 132: hts */ xterm_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ xterm_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ xterm_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ xterm_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ xterm_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ xterm_s_smam, + /* 152: rmam */ xterm_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ ABSENT_STRING, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ xterm_s_kend, + /* 165: kent */ xterm_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ xterm_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ xterm_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ xterm_s_kHOM, + /* 200: kIC */ xterm_s_kIC, + /* 201: kLFT */ xterm_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ xterm_s_kNXT, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ xterm_s_kPRV, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ xterm_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ xterm_s_kf11, + /* 217: kf12 */ xterm_s_kf12, + /* 218: kf13 */ xterm_s_kf13, + /* 219: kf14 */ xterm_s_kf14, + /* 220: kf15 */ xterm_s_kf15, + /* 221: kf16 */ xterm_s_kf16, + /* 222: kf17 */ xterm_s_kf17, + /* 223: kf18 */ xterm_s_kf18, + /* 224: kf19 */ xterm_s_kf19, + /* 225: kf20 */ xterm_s_kf20, + /* 226: kf21 */ xterm_s_kf21, + /* 227: kf22 */ xterm_s_kf22, + /* 228: kf23 */ xterm_s_kf23, + /* 229: kf24 */ xterm_s_kf24, + /* 230: kf25 */ xterm_s_kf25, + /* 231: kf26 */ xterm_s_kf26, + /* 232: kf27 */ xterm_s_kf27, + /* 233: kf28 */ xterm_s_kf28, + /* 234: kf29 */ xterm_s_kf29, + /* 235: kf30 */ xterm_s_kf30, + /* 236: kf31 */ xterm_s_kf31, + /* 237: kf32 */ xterm_s_kf32, + /* 238: kf33 */ xterm_s_kf33, + /* 239: kf34 */ xterm_s_kf34, + /* 240: kf35 */ xterm_s_kf35, + /* 241: kf36 */ xterm_s_kf36, + /* 242: kf37 */ xterm_s_kf37, + /* 243: kf38 */ xterm_s_kf38, + /* 244: kf39 */ xterm_s_kf39, + /* 245: kf40 */ xterm_s_kf40, + /* 246: kf41 */ xterm_s_kf41, + /* 247: kf42 */ xterm_s_kf42, + /* 248: kf43 */ xterm_s_kf43, + /* 249: kf44 */ xterm_s_kf44, + /* 250: kf45 */ xterm_s_kf45, + /* 251: kf46 */ xterm_s_kf46, + /* 252: kf47 */ xterm_s_kf47, + /* 253: kf48 */ xterm_s_kf48, + /* 254: kf49 */ xterm_s_kf49, + /* 255: kf50 */ xterm_s_kf50, + /* 256: kf51 */ xterm_s_kf51, + /* 257: kf52 */ xterm_s_kf52, + /* 258: kf53 */ xterm_s_kf53, + /* 259: kf54 */ xterm_s_kf54, + /* 260: kf55 */ xterm_s_kf55, + /* 261: kf56 */ xterm_s_kf56, + /* 262: kf57 */ xterm_s_kf57, + /* 263: kf58 */ xterm_s_kf58, + /* 264: kf59 */ xterm_s_kf59, + /* 265: kf60 */ xterm_s_kf60, + /* 266: kf61 */ xterm_s_kf61, + /* 267: kf62 */ xterm_s_kf62, + /* 268: kf63 */ xterm_s_kf63, + /* 269: el1 */ xterm_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ xterm_s_u6, + /* 294: u7 */ xterm_s_u7, + /* 295: u8 */ xterm_s_u8, + /* 296: u9 */ xterm_s_u9, + /* 297: op */ xterm_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ xterm_s_setf, + /* 303: setb */ xterm_s_setb, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ xterm_s_sitm, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ xterm_s_ritm, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ xterm_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ xterm_s_setaf, + /* 360: setab */ xterm_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ xterm_s_meml, + /* 412: memu */ xterm_s_memu, + /* 413: box1 */ ABSENT_STRING, +}; +/* xterm-256color */ + +static char xterm_256color_alias_data[] = "xterm-256color|xterm with 256 colors"; + +static char xterm_256color_s_cbt[] = "\033[Z"; +static char xterm_256color_s_bel[] = "\007"; +static char xterm_256color_s_cr [] = "\015"; +static char xterm_256color_s_csr[] = "\033[%i%p1%d;%p2%dr"; +static char xterm_256color_s_tbc[] = "\033[3g"; +static char xterm_256color_s_clear[] = "\033[H\033[2J"; +static char xterm_256color_s_el [] = "\033[K"; +static char xterm_256color_s_ed [] = "\033[J"; +static char xterm_256color_s_hpa[] = "\033[%i%p1%dG"; +static char xterm_256color_s_cup[] = "\033[%i%p1%d;%p2%dH"; +static char xterm_256color_s_cud1[] = "\012"; +static char xterm_256color_s_home[] = "\033[H"; +static char xterm_256color_s_civis[] = "\033[?25l"; +static char xterm_256color_s_cub1[] = "\010"; +static char xterm_256color_s_cnorm[] = "\033[?12l\033[?25h"; +static char xterm_256color_s_cuf1[] = "\033[C"; +static char xterm_256color_s_cuu1[] = "\033[A"; +static char xterm_256color_s_cvvis[] = "\033[?12;25h"; +static char xterm_256color_s_dch1[] = "\033[P"; +static char xterm_256color_s_dl1[] = "\033[M"; +static char xterm_256color_s_smacs[] = "\033(0"; +static char xterm_256color_s_blink[] = "\033[5m"; +static char xterm_256color_s_bold[] = "\033[1m"; +static char xterm_256color_s_smcup[] = "\033[?1049h\033[22;0;0t"; +static char xterm_256color_s_dim[] = "\033[2m"; +static char xterm_256color_s_smir[] = "\033[4h"; +static char xterm_256color_s_invis[] = "\033[8m"; +static char xterm_256color_s_rev[] = "\033[7m"; +static char xterm_256color_s_smso[] = "\033[7m"; +static char xterm_256color_s_smul[] = "\033[4m"; +static char xterm_256color_s_ech[] = "\033[%p1%dX"; +static char xterm_256color_s_rmacs[] = "\033(B"; +static char xterm_256color_s_sgr0[] = "\033(B\033[m"; +static char xterm_256color_s_rmcup[] = "\033[?1049l\033[23;0;0t"; +static char xterm_256color_s_rmir[] = "\033[4l"; +static char xterm_256color_s_rmso[] = "\033[27m"; +static char xterm_256color_s_rmul[] = "\033[24m"; +static char xterm_256color_s_flash[] = "\033[?5h$<100/>\033[?5l"; +static char xterm_256color_s_is2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char xterm_256color_s_il1[] = "\033[L"; +static char xterm_256color_s_kbs[] = "\010"; +static char xterm_256color_s_kdch1[] = "\033[3~"; +static char xterm_256color_s_kcud1[] = "\033OB"; +static char xterm_256color_s_kf1[] = "\033OP"; +static char xterm_256color_s_kf10[] = "\033[21~"; +static char xterm_256color_s_kf2[] = "\033OQ"; +static char xterm_256color_s_kf3[] = "\033OR"; +static char xterm_256color_s_kf4[] = "\033OS"; +static char xterm_256color_s_kf5[] = "\033[15~"; +static char xterm_256color_s_kf6[] = "\033[17~"; +static char xterm_256color_s_kf7[] = "\033[18~"; +static char xterm_256color_s_kf8[] = "\033[19~"; +static char xterm_256color_s_kf9[] = "\033[20~"; +static char xterm_256color_s_khome[] = "\033OH"; +static char xterm_256color_s_kich1[] = "\033[2~"; +static char xterm_256color_s_kcub1[] = "\033OD"; +static char xterm_256color_s_knp[] = "\033[6~"; +static char xterm_256color_s_kpp[] = "\033[5~"; +static char xterm_256color_s_kcuf1[] = "\033OC"; +static char xterm_256color_s_kind[] = "\033[1;2B"; +static char xterm_256color_s_kri[] = "\033[1;2A"; +static char xterm_256color_s_kcuu1[] = "\033OA"; +static char xterm_256color_s_rmkx[] = "\033[?1l\033>"; +static char xterm_256color_s_smkx[] = "\033[?1h\033="; +static char xterm_256color_s_rmm[] = "\033[?1034l"; +static char xterm_256color_s_smm[] = "\033[?1034h"; +static char xterm_256color_s_dch[] = "\033[%p1%dP"; +static char xterm_256color_s_dl [] = "\033[%p1%dM"; +static char xterm_256color_s_cud[] = "\033[%p1%dB"; +static char xterm_256color_s_ich[] = "\033[%p1%d@"; +static char xterm_256color_s_indn[] = "\033[%p1%dS"; +static char xterm_256color_s_il [] = "\033[%p1%dL"; +static char xterm_256color_s_cub[] = "\033[%p1%dD"; +static char xterm_256color_s_cuf[] = "\033[%p1%dC"; +static char xterm_256color_s_rin[] = "\033[%p1%dT"; +static char xterm_256color_s_cuu[] = "\033[%p1%dA"; +static char xterm_256color_s_mc0[] = "\033[i"; +static char xterm_256color_s_mc4[] = "\033[4i"; +static char xterm_256color_s_mc5[] = "\033[5i"; +static char xterm_256color_s_rep[] = "%p1%c\033[%p2%{1}%-%db"; +static char xterm_256color_s_rs1[] = "\033c\033]104\007"; +static char xterm_256color_s_rs2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char xterm_256color_s_rc [] = "\0338"; +static char xterm_256color_s_vpa[] = "\033[%i%p1%dd"; +static char xterm_256color_s_sc [] = "\0337"; +static char xterm_256color_s_ind[] = "\012"; +static char xterm_256color_s_ri [] = "\033M"; +static char xterm_256color_s_sgr[] = "%?%p9%t\033(0%e\033(B%;\033[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m"; +static char xterm_256color_s_hts[] = "\033H"; +static char xterm_256color_s_ht [] = "\011"; +static char xterm_256color_s_kb2[] = "\033OE"; +static char xterm_256color_s_acsc[] = "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char xterm_256color_s_kcbt[] = "\033[Z"; +static char xterm_256color_s_smam[] = "\033[?7h"; +static char xterm_256color_s_rmam[] = "\033[?7l"; +static char xterm_256color_s_kend[] = "\033OF"; +static char xterm_256color_s_kent[] = "\033OM"; +static char xterm_256color_s_kDC[] = "\033[3;2~"; +static char xterm_256color_s_kEND[] = "\033[1;2F"; +static char xterm_256color_s_kHOM[] = "\033[1;2H"; +static char xterm_256color_s_kIC[] = "\033[2;2~"; +static char xterm_256color_s_kLFT[] = "\033[1;2D"; +static char xterm_256color_s_kNXT[] = "\033[6;2~"; +static char xterm_256color_s_kPRV[] = "\033[5;2~"; +static char xterm_256color_s_kRIT[] = "\033[1;2C"; +static char xterm_256color_s_kf11[] = "\033[23~"; +static char xterm_256color_s_kf12[] = "\033[24~"; +static char xterm_256color_s_kf13[] = "\033[1;2P"; +static char xterm_256color_s_kf14[] = "\033[1;2Q"; +static char xterm_256color_s_kf15[] = "\033[1;2R"; +static char xterm_256color_s_kf16[] = "\033[1;2S"; +static char xterm_256color_s_kf17[] = "\033[15;2~"; +static char xterm_256color_s_kf18[] = "\033[17;2~"; +static char xterm_256color_s_kf19[] = "\033[18;2~"; +static char xterm_256color_s_kf20[] = "\033[19;2~"; +static char xterm_256color_s_kf21[] = "\033[20;2~"; +static char xterm_256color_s_kf22[] = "\033[21;2~"; +static char xterm_256color_s_kf23[] = "\033[23;2~"; +static char xterm_256color_s_kf24[] = "\033[24;2~"; +static char xterm_256color_s_kf25[] = "\033[1;5P"; +static char xterm_256color_s_kf26[] = "\033[1;5Q"; +static char xterm_256color_s_kf27[] = "\033[1;5R"; +static char xterm_256color_s_kf28[] = "\033[1;5S"; +static char xterm_256color_s_kf29[] = "\033[15;5~"; +static char xterm_256color_s_kf30[] = "\033[17;5~"; +static char xterm_256color_s_kf31[] = "\033[18;5~"; +static char xterm_256color_s_kf32[] = "\033[19;5~"; +static char xterm_256color_s_kf33[] = "\033[20;5~"; +static char xterm_256color_s_kf34[] = "\033[21;5~"; +static char xterm_256color_s_kf35[] = "\033[23;5~"; +static char xterm_256color_s_kf36[] = "\033[24;5~"; +static char xterm_256color_s_kf37[] = "\033[1;6P"; +static char xterm_256color_s_kf38[] = "\033[1;6Q"; +static char xterm_256color_s_kf39[] = "\033[1;6R"; +static char xterm_256color_s_kf40[] = "\033[1;6S"; +static char xterm_256color_s_kf41[] = "\033[15;6~"; +static char xterm_256color_s_kf42[] = "\033[17;6~"; +static char xterm_256color_s_kf43[] = "\033[18;6~"; +static char xterm_256color_s_kf44[] = "\033[19;6~"; +static char xterm_256color_s_kf45[] = "\033[20;6~"; +static char xterm_256color_s_kf46[] = "\033[21;6~"; +static char xterm_256color_s_kf47[] = "\033[23;6~"; +static char xterm_256color_s_kf48[] = "\033[24;6~"; +static char xterm_256color_s_kf49[] = "\033[1;3P"; +static char xterm_256color_s_kf50[] = "\033[1;3Q"; +static char xterm_256color_s_kf51[] = "\033[1;3R"; +static char xterm_256color_s_kf52[] = "\033[1;3S"; +static char xterm_256color_s_kf53[] = "\033[15;3~"; +static char xterm_256color_s_kf54[] = "\033[17;3~"; +static char xterm_256color_s_kf55[] = "\033[18;3~"; +static char xterm_256color_s_kf56[] = "\033[19;3~"; +static char xterm_256color_s_kf57[] = "\033[20;3~"; +static char xterm_256color_s_kf58[] = "\033[21;3~"; +static char xterm_256color_s_kf59[] = "\033[23;3~"; +static char xterm_256color_s_kf60[] = "\033[24;3~"; +static char xterm_256color_s_kf61[] = "\033[1;4P"; +static char xterm_256color_s_kf62[] = "\033[1;4Q"; +static char xterm_256color_s_kf63[] = "\033[1;4R"; +static char xterm_256color_s_el1[] = "\033[1K"; +static char xterm_256color_s_u6 [] = "\033[%i%d;%dR"; +static char xterm_256color_s_u7 [] = "\033[6n"; +static char xterm_256color_s_u8 [] = "\033[?%[;0123456789]c"; +static char xterm_256color_s_u9 [] = "\033[c"; +static char xterm_256color_s_op [] = "\033[39;49m"; +static char xterm_256color_s_oc [] = "\033]104\007"; +static char xterm_256color_s_initc[] = "\033]4;%p1%d;rgb:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\033\134"; +static char xterm_256color_s_sitm[] = "\033[3m"; +static char xterm_256color_s_ritm[] = "\033[23m"; +static char xterm_256color_s_kmous[] = "\033[<"; +static char xterm_256color_s_setaf[] = "\033[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"; +static char xterm_256color_s_setab[] = "\033[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"; +static char xterm_256color_s_meml[] = "\033l"; +static char xterm_256color_s_memu[] = "\033m"; + +static char xterm_256color_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ TRUE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ TRUE, + /* 28: bce */ TRUE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 xterm_256color_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 256, + /* 14: pairs */ 32767, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * xterm_256color_string_data[] = { + /* 0: cbt */ xterm_256color_s_cbt, + /* 1: bel */ xterm_256color_s_bel, + /* 2: cr */ xterm_256color_s_cr, + /* 3: csr */ xterm_256color_s_csr, + /* 4: tbc */ xterm_256color_s_tbc, + /* 5: clear */ xterm_256color_s_clear, + /* 6: el */ xterm_256color_s_el, + /* 7: ed */ xterm_256color_s_ed, + /* 8: hpa */ xterm_256color_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ xterm_256color_s_cup, + /* 11: cud1 */ xterm_256color_s_cud1, + /* 12: home */ xterm_256color_s_home, + /* 13: civis */ xterm_256color_s_civis, + /* 14: cub1 */ xterm_256color_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ xterm_256color_s_cnorm, + /* 17: cuf1 */ xterm_256color_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ xterm_256color_s_cuu1, + /* 20: cvvis */ xterm_256color_s_cvvis, + /* 21: dch1 */ xterm_256color_s_dch1, + /* 22: dl1 */ xterm_256color_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ xterm_256color_s_smacs, + /* 26: blink */ xterm_256color_s_blink, + /* 27: bold */ xterm_256color_s_bold, + /* 28: smcup */ xterm_256color_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ xterm_256color_s_dim, + /* 31: smir */ xterm_256color_s_smir, + /* 32: invis */ xterm_256color_s_invis, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ xterm_256color_s_rev, + /* 35: smso */ xterm_256color_s_smso, + /* 36: smul */ xterm_256color_s_smul, + /* 37: ech */ xterm_256color_s_ech, + /* 38: rmacs */ xterm_256color_s_rmacs, + /* 39: sgr0 */ xterm_256color_s_sgr0, + /* 40: rmcup */ xterm_256color_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ xterm_256color_s_rmir, + /* 43: rmso */ xterm_256color_s_rmso, + /* 44: rmul */ xterm_256color_s_rmul, + /* 45: flash */ xterm_256color_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ xterm_256color_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ xterm_256color_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ xterm_256color_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ xterm_256color_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ xterm_256color_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ xterm_256color_s_kf1, + /* 67: kf10 */ xterm_256color_s_kf10, + /* 68: kf2 */ xterm_256color_s_kf2, + /* 69: kf3 */ xterm_256color_s_kf3, + /* 70: kf4 */ xterm_256color_s_kf4, + /* 71: kf5 */ xterm_256color_s_kf5, + /* 72: kf6 */ xterm_256color_s_kf6, + /* 73: kf7 */ xterm_256color_s_kf7, + /* 74: kf8 */ xterm_256color_s_kf8, + /* 75: kf9 */ xterm_256color_s_kf9, + /* 76: khome */ xterm_256color_s_khome, + /* 77: kich1 */ xterm_256color_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ xterm_256color_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ xterm_256color_s_knp, + /* 82: kpp */ xterm_256color_s_kpp, + /* 83: kcuf1 */ xterm_256color_s_kcuf1, + /* 84: kind */ xterm_256color_s_kind, + /* 85: kri */ xterm_256color_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ xterm_256color_s_kcuu1, + /* 88: rmkx */ xterm_256color_s_rmkx, + /* 89: smkx */ xterm_256color_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ xterm_256color_s_rmm, + /* 102: smm */ xterm_256color_s_smm, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ xterm_256color_s_dch, + /* 106: dl */ xterm_256color_s_dl, + /* 107: cud */ xterm_256color_s_cud, + /* 108: ich */ xterm_256color_s_ich, + /* 109: indn */ xterm_256color_s_indn, + /* 110: il */ xterm_256color_s_il, + /* 111: cub */ xterm_256color_s_cub, + /* 112: cuf */ xterm_256color_s_cuf, + /* 113: rin */ xterm_256color_s_rin, + /* 114: cuu */ xterm_256color_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ xterm_256color_s_mc0, + /* 119: mc4 */ xterm_256color_s_mc4, + /* 120: mc5 */ xterm_256color_s_mc5, + /* 121: rep */ xterm_256color_s_rep, + /* 122: rs1 */ xterm_256color_s_rs1, + /* 123: rs2 */ xterm_256color_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ xterm_256color_s_rc, + /* 127: vpa */ xterm_256color_s_vpa, + /* 128: sc */ xterm_256color_s_sc, + /* 129: ind */ xterm_256color_s_ind, + /* 130: ri */ xterm_256color_s_ri, + /* 131: sgr */ xterm_256color_s_sgr, + /* 132: hts */ xterm_256color_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ xterm_256color_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ xterm_256color_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ xterm_256color_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ xterm_256color_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ xterm_256color_s_smam, + /* 152: rmam */ xterm_256color_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ ABSENT_STRING, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ xterm_256color_s_kend, + /* 165: kent */ xterm_256color_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ xterm_256color_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ xterm_256color_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ xterm_256color_s_kHOM, + /* 200: kIC */ xterm_256color_s_kIC, + /* 201: kLFT */ xterm_256color_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ xterm_256color_s_kNXT, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ xterm_256color_s_kPRV, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ xterm_256color_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ xterm_256color_s_kf11, + /* 217: kf12 */ xterm_256color_s_kf12, + /* 218: kf13 */ xterm_256color_s_kf13, + /* 219: kf14 */ xterm_256color_s_kf14, + /* 220: kf15 */ xterm_256color_s_kf15, + /* 221: kf16 */ xterm_256color_s_kf16, + /* 222: kf17 */ xterm_256color_s_kf17, + /* 223: kf18 */ xterm_256color_s_kf18, + /* 224: kf19 */ xterm_256color_s_kf19, + /* 225: kf20 */ xterm_256color_s_kf20, + /* 226: kf21 */ xterm_256color_s_kf21, + /* 227: kf22 */ xterm_256color_s_kf22, + /* 228: kf23 */ xterm_256color_s_kf23, + /* 229: kf24 */ xterm_256color_s_kf24, + /* 230: kf25 */ xterm_256color_s_kf25, + /* 231: kf26 */ xterm_256color_s_kf26, + /* 232: kf27 */ xterm_256color_s_kf27, + /* 233: kf28 */ xterm_256color_s_kf28, + /* 234: kf29 */ xterm_256color_s_kf29, + /* 235: kf30 */ xterm_256color_s_kf30, + /* 236: kf31 */ xterm_256color_s_kf31, + /* 237: kf32 */ xterm_256color_s_kf32, + /* 238: kf33 */ xterm_256color_s_kf33, + /* 239: kf34 */ xterm_256color_s_kf34, + /* 240: kf35 */ xterm_256color_s_kf35, + /* 241: kf36 */ xterm_256color_s_kf36, + /* 242: kf37 */ xterm_256color_s_kf37, + /* 243: kf38 */ xterm_256color_s_kf38, + /* 244: kf39 */ xterm_256color_s_kf39, + /* 245: kf40 */ xterm_256color_s_kf40, + /* 246: kf41 */ xterm_256color_s_kf41, + /* 247: kf42 */ xterm_256color_s_kf42, + /* 248: kf43 */ xterm_256color_s_kf43, + /* 249: kf44 */ xterm_256color_s_kf44, + /* 250: kf45 */ xterm_256color_s_kf45, + /* 251: kf46 */ xterm_256color_s_kf46, + /* 252: kf47 */ xterm_256color_s_kf47, + /* 253: kf48 */ xterm_256color_s_kf48, + /* 254: kf49 */ xterm_256color_s_kf49, + /* 255: kf50 */ xterm_256color_s_kf50, + /* 256: kf51 */ xterm_256color_s_kf51, + /* 257: kf52 */ xterm_256color_s_kf52, + /* 258: kf53 */ xterm_256color_s_kf53, + /* 259: kf54 */ xterm_256color_s_kf54, + /* 260: kf55 */ xterm_256color_s_kf55, + /* 261: kf56 */ xterm_256color_s_kf56, + /* 262: kf57 */ xterm_256color_s_kf57, + /* 263: kf58 */ xterm_256color_s_kf58, + /* 264: kf59 */ xterm_256color_s_kf59, + /* 265: kf60 */ xterm_256color_s_kf60, + /* 266: kf61 */ xterm_256color_s_kf61, + /* 267: kf62 */ xterm_256color_s_kf62, + /* 268: kf63 */ xterm_256color_s_kf63, + /* 269: el1 */ xterm_256color_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ xterm_256color_s_u6, + /* 294: u7 */ xterm_256color_s_u7, + /* 295: u8 */ xterm_256color_s_u8, + /* 296: u9 */ xterm_256color_s_u9, + /* 297: op */ xterm_256color_s_op, + /* 298: oc */ xterm_256color_s_oc, + /* 299: initc */ xterm_256color_s_initc, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ xterm_256color_s_sitm, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ xterm_256color_s_ritm, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ xterm_256color_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ xterm_256color_s_setaf, + /* 360: setab */ xterm_256color_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ xterm_256color_s_meml, + /* 412: memu */ xterm_256color_s_memu, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen */ + +static char screen_alias_data[] = "screen|VT 100/ANSI X3.64 virtual terminal"; + +static char screen_s_cbt [] = "\033[Z"; +static char screen_s_bel [] = "\007"; +static char screen_s_cr [] = "\015"; +static char screen_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char screen_s_tbc [] = "\033[3g"; +static char screen_s_clear [] = "\033[H\033[J"; +static char screen_s_el [] = "\033[K"; +static char screen_s_ed [] = "\033[J"; +static char screen_s_hpa [] = "\033[%i%p1%dG"; +static char screen_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char screen_s_cud1 [] = "\012"; +static char screen_s_home [] = "\033[H"; +static char screen_s_civis [] = "\033[?25l"; +static char screen_s_cub1 [] = "\010"; +static char screen_s_cnorm [] = "\033[34h\033[?25h"; +static char screen_s_cuf1 [] = "\033[C"; +static char screen_s_cuu1 [] = "\033M"; +static char screen_s_cvvis [] = "\033[34l"; +static char screen_s_dch1 [] = "\033[P"; +static char screen_s_dl1 [] = "\033[M"; +static char screen_s_smacs [] = "\016"; +static char screen_s_blink [] = "\033[5m"; +static char screen_s_bold [] = "\033[1m"; +static char screen_s_smcup [] = "\033[?1049h"; +static char screen_s_dim [] = "\033[2m"; +static char screen_s_smir [] = "\033[4h"; +static char screen_s_rev [] = "\033[7m"; +static char screen_s_smso [] = "\033[3m"; +static char screen_s_smul [] = "\033[4m"; +static char screen_s_rmacs [] = "\017"; +static char screen_s_sgr0 [] = "\033[m\017"; +static char screen_s_rmcup [] = "\033[?1049l"; +static char screen_s_rmir [] = "\033[4l"; +static char screen_s_rmso [] = "\033[23m"; +static char screen_s_rmul [] = "\033[24m"; +static char screen_s_flash [] = "\033g"; +static char screen_s_is2 [] = "\033)0"; +static char screen_s_il1 [] = "\033[L"; +static char screen_s_kbs [] = "\010"; +static char screen_s_kdch1 [] = "\033[3~"; +static char screen_s_kcud1 [] = "\033OB"; +static char screen_s_kf1 [] = "\033OP"; +static char screen_s_kf10 [] = "\033[21~"; +static char screen_s_kf2 [] = "\033OQ"; +static char screen_s_kf3 [] = "\033OR"; +static char screen_s_kf4 [] = "\033OS"; +static char screen_s_kf5 [] = "\033[15~"; +static char screen_s_kf6 [] = "\033[17~"; +static char screen_s_kf7 [] = "\033[18~"; +static char screen_s_kf8 [] = "\033[19~"; +static char screen_s_kf9 [] = "\033[20~"; +static char screen_s_khome [] = "\033[1~"; +static char screen_s_kich1 [] = "\033[2~"; +static char screen_s_kcub1 [] = "\033OD"; +static char screen_s_knp [] = "\033[6~"; +static char screen_s_kpp [] = "\033[5~"; +static char screen_s_kcuf1 [] = "\033OC"; +static char screen_s_kcuu1 [] = "\033OA"; +static char screen_s_rmkx [] = "\033[?1l\033>"; +static char screen_s_smkx [] = "\033[?1h\033="; +static char screen_s_nel [] = "\033E"; +static char screen_s_dch [] = "\033[%p1%dP"; +static char screen_s_dl [] = "\033[%p1%dM"; +static char screen_s_cud [] = "\033[%p1%dB"; +static char screen_s_ich [] = "\033[%p1%d@"; +static char screen_s_indn [] = "\033[%p1%dS"; +static char screen_s_il [] = "\033[%p1%dL"; +static char screen_s_cub [] = "\033[%p1%dD"; +static char screen_s_cuf [] = "\033[%p1%dC"; +static char screen_s_cuu [] = "\033[%p1%dA"; +static char screen_s_rs2 [] = "\033c\033[?1000l\033[?25h"; +static char screen_s_rc [] = "\0338"; +static char screen_s_vpa [] = "\033[%i%p1%dd"; +static char screen_s_sc [] = "\0337"; +static char screen_s_ind [] = "\012"; +static char screen_s_ri [] = "\033M"; +static char screen_s_sgr [] = "\033[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t\016%e\017%;"; +static char screen_s_hts [] = "\033H"; +static char screen_s_ht [] = "\011"; +static char screen_s_acsc [] = "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_s_kcbt [] = "\033[Z"; +static char screen_s_enacs [] = "\033(B\033)0"; +static char screen_s_kend [] = "\033[4~"; +static char screen_s_kf11 [] = "\033[23~"; +static char screen_s_kf12 [] = "\033[24~"; +static char screen_s_el1 [] = "\033[1K"; +static char screen_s_op [] = "\033[39;49m"; +static char screen_s_kmous [] = "\033[M"; +static char screen_s_setaf [] = "\033[3%p1%dm"; +static char screen_s_setab [] = "\033[4%p1%dm"; + +static char screen_bool_data[] = { + /* 0: bw */ FALSE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ TRUE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ CANCELLED_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_string_data[] = { + /* 0: cbt */ screen_s_cbt, + /* 1: bel */ screen_s_bel, + /* 2: cr */ screen_s_cr, + /* 3: csr */ screen_s_csr, + /* 4: tbc */ screen_s_tbc, + /* 5: clear */ screen_s_clear, + /* 6: el */ screen_s_el, + /* 7: ed */ screen_s_ed, + /* 8: hpa */ screen_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_s_cup, + /* 11: cud1 */ screen_s_cud1, + /* 12: home */ screen_s_home, + /* 13: civis */ screen_s_civis, + /* 14: cub1 */ screen_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_s_cnorm, + /* 17: cuf1 */ screen_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_s_cuu1, + /* 20: cvvis */ screen_s_cvvis, + /* 21: dch1 */ screen_s_dch1, + /* 22: dl1 */ screen_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_s_smacs, + /* 26: blink */ screen_s_blink, + /* 27: bold */ screen_s_bold, + /* 28: smcup */ screen_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_s_dim, + /* 31: smir */ screen_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_s_rev, + /* 35: smso */ screen_s_smso, + /* 36: smul */ screen_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ screen_s_rmacs, + /* 39: sgr0 */ screen_s_sgr0, + /* 40: rmcup */ screen_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_s_rmir, + /* 43: rmso */ screen_s_rmso, + /* 44: rmul */ screen_s_rmul, + /* 45: flash */ screen_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_s_kf1, + /* 67: kf10 */ screen_s_kf10, + /* 68: kf2 */ screen_s_kf2, + /* 69: kf3 */ screen_s_kf3, + /* 70: kf4 */ screen_s_kf4, + /* 71: kf5 */ screen_s_kf5, + /* 72: kf6 */ screen_s_kf6, + /* 73: kf7 */ screen_s_kf7, + /* 74: kf8 */ screen_s_kf8, + /* 75: kf9 */ screen_s_kf9, + /* 76: khome */ screen_s_khome, + /* 77: kich1 */ screen_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_s_knp, + /* 82: kpp */ screen_s_kpp, + /* 83: kcuf1 */ screen_s_kcuf1, + /* 84: kind */ ABSENT_STRING, + /* 85: kri */ ABSENT_STRING, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_s_kcuu1, + /* 88: rmkx */ screen_s_rmkx, + /* 89: smkx */ screen_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ screen_s_nel, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_s_dch, + /* 106: dl */ screen_s_dl, + /* 107: cud */ screen_s_cud, + /* 108: ich */ screen_s_ich, + /* 109: indn */ screen_s_indn, + /* 110: il */ screen_s_il, + /* 111: cub */ screen_s_cub, + /* 112: cuf */ screen_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ screen_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ ABSENT_STRING, + /* 123: rs2 */ screen_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_s_rc, + /* 127: vpa */ screen_s_vpa, + /* 128: sc */ screen_s_sc, + /* 129: ind */ screen_s_ind, + /* 130: ri */ screen_s_ri, + /* 131: sgr */ screen_s_sgr, + /* 132: hts */ screen_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ ABSENT_STRING, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ screen_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ ABSENT_STRING, + /* 152: rmam */ ABSENT_STRING, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ screen_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_s_kend, + /* 165: kent */ ABSENT_STRING, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ ABSENT_STRING, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ ABSENT_STRING, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ ABSENT_STRING, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ ABSENT_STRING, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ ABSENT_STRING, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_s_kf11, + /* 217: kf12 */ screen_s_kf12, + /* 218: kf13 */ ABSENT_STRING, + /* 219: kf14 */ ABSENT_STRING, + /* 220: kf15 */ ABSENT_STRING, + /* 221: kf16 */ ABSENT_STRING, + /* 222: kf17 */ ABSENT_STRING, + /* 223: kf18 */ ABSENT_STRING, + /* 224: kf19 */ ABSENT_STRING, + /* 225: kf20 */ ABSENT_STRING, + /* 226: kf21 */ ABSENT_STRING, + /* 227: kf22 */ ABSENT_STRING, + /* 228: kf23 */ ABSENT_STRING, + /* 229: kf24 */ ABSENT_STRING, + /* 230: kf25 */ ABSENT_STRING, + /* 231: kf26 */ ABSENT_STRING, + /* 232: kf27 */ ABSENT_STRING, + /* 233: kf28 */ ABSENT_STRING, + /* 234: kf29 */ ABSENT_STRING, + /* 235: kf30 */ ABSENT_STRING, + /* 236: kf31 */ ABSENT_STRING, + /* 237: kf32 */ ABSENT_STRING, + /* 238: kf33 */ ABSENT_STRING, + /* 239: kf34 */ ABSENT_STRING, + /* 240: kf35 */ ABSENT_STRING, + /* 241: kf36 */ ABSENT_STRING, + /* 242: kf37 */ ABSENT_STRING, + /* 243: kf38 */ ABSENT_STRING, + /* 244: kf39 */ ABSENT_STRING, + /* 245: kf40 */ ABSENT_STRING, + /* 246: kf41 */ ABSENT_STRING, + /* 247: kf42 */ ABSENT_STRING, + /* 248: kf43 */ ABSENT_STRING, + /* 249: kf44 */ ABSENT_STRING, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ screen_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ ABSENT_STRING, + /* 294: u7 */ ABSENT_STRING, + /* 295: u8 */ ABSENT_STRING, + /* 296: u9 */ ABSENT_STRING, + /* 297: op */ screen_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_s_setaf, + /* 360: setab */ screen_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen.linux */ + +static char screen_linux_alias_data[] = "screen.linux|screen in linux console"; + +static char screen_linux_s_cbt [] = "\033[Z"; +static char screen_linux_s_bel [] = "\007"; +static char screen_linux_s_cr [] = "\015"; +static char screen_linux_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char screen_linux_s_tbc [] = "\033[3g"; +static char screen_linux_s_clear[] = "\033[H\033[J"; +static char screen_linux_s_el [] = "\033[K"; +static char screen_linux_s_ed [] = "\033[J"; +static char screen_linux_s_hpa [] = "\033[%i%p1%dG"; +static char screen_linux_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char screen_linux_s_cud1 [] = "\012"; +static char screen_linux_s_home [] = "\033[H"; +static char screen_linux_s_civis[] = "\033[?25l"; +static char screen_linux_s_cub1 [] = "\010"; +static char screen_linux_s_cnorm[] = "\033[34h\033[?25h"; +static char screen_linux_s_cuf1 [] = "\033[C"; +static char screen_linux_s_cuu1 [] = "\033M"; +static char screen_linux_s_cvvis[] = "\033[34l"; +static char screen_linux_s_dch1 [] = "\033[P"; +static char screen_linux_s_dl1 [] = "\033[M"; +static char screen_linux_s_smacs[] = "\016"; +static char screen_linux_s_blink[] = "\033[5m"; +static char screen_linux_s_bold [] = "\033[1m"; +static char screen_linux_s_smcup[] = "\033[?1049h"; +static char screen_linux_s_dim [] = "\033[2m"; +static char screen_linux_s_smir [] = "\033[4h"; +static char screen_linux_s_rev [] = "\033[7m"; +static char screen_linux_s_smso [] = "\033[3m"; +static char screen_linux_s_smul [] = "\033[4m"; +static char screen_linux_s_rmacs[] = "\017"; +static char screen_linux_s_sgr0 [] = "\033[m\017"; +static char screen_linux_s_rmcup[] = "\033[?1049l"; +static char screen_linux_s_rmir [] = "\033[4l"; +static char screen_linux_s_rmso [] = "\033[23m"; +static char screen_linux_s_rmul [] = "\033[24m"; +static char screen_linux_s_flash[] = "\033g"; +static char screen_linux_s_is2 [] = "\033)0"; +static char screen_linux_s_il1 [] = "\033[L"; +static char screen_linux_s_kbs [] = "\177"; +static char screen_linux_s_kdch1[] = "\033[3~"; +static char screen_linux_s_kcud1[] = "\033OB"; +static char screen_linux_s_kf1 [] = "\033OP"; +static char screen_linux_s_kf10 [] = "\033[21~"; +static char screen_linux_s_kf2 [] = "\033OQ"; +static char screen_linux_s_kf3 [] = "\033OR"; +static char screen_linux_s_kf4 [] = "\033OS"; +static char screen_linux_s_kf5 [] = "\033[15~"; +static char screen_linux_s_kf6 [] = "\033[17~"; +static char screen_linux_s_kf7 [] = "\033[18~"; +static char screen_linux_s_kf8 [] = "\033[19~"; +static char screen_linux_s_kf9 [] = "\033[20~"; +static char screen_linux_s_khome[] = "\033[1~"; +static char screen_linux_s_kich1[] = "\033[2~"; +static char screen_linux_s_kcub1[] = "\033OD"; +static char screen_linux_s_knp [] = "\033[6~"; +static char screen_linux_s_kpp [] = "\033[5~"; +static char screen_linux_s_kcuf1[] = "\033OC"; +static char screen_linux_s_kcuu1[] = "\033OA"; +static char screen_linux_s_rmkx [] = "\033[?1l\033>"; +static char screen_linux_s_smkx [] = "\033[?1h\033="; +static char screen_linux_s_nel [] = "\033E"; +static char screen_linux_s_dch [] = "\033[%p1%dP"; +static char screen_linux_s_dl [] = "\033[%p1%dM"; +static char screen_linux_s_cud [] = "\033[%p1%dB"; +static char screen_linux_s_ich [] = "\033[%p1%d@"; +static char screen_linux_s_indn [] = "\033[%p1%dS"; +static char screen_linux_s_il [] = "\033[%p1%dL"; +static char screen_linux_s_cub [] = "\033[%p1%dD"; +static char screen_linux_s_cuf [] = "\033[%p1%dC"; +static char screen_linux_s_cuu [] = "\033[%p1%dA"; +static char screen_linux_s_rs2 [] = "\033c\033[?1000l\033[?25h"; +static char screen_linux_s_rc [] = "\0338"; +static char screen_linux_s_vpa [] = "\033[%i%p1%dd"; +static char screen_linux_s_sc [] = "\0337"; +static char screen_linux_s_ind [] = "\012"; +static char screen_linux_s_ri [] = "\033M"; +static char screen_linux_s_sgr [] = "\033[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t\016%e\017%;"; +static char screen_linux_s_hts [] = "\033H"; +static char screen_linux_s_ht [] = "\011"; +static char screen_linux_s_acsc [] = "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_linux_s_enacs[] = "\033(B\033)0"; +static char screen_linux_s_kend [] = "\033[4~"; +static char screen_linux_s_kf11 [] = "\033[23~"; +static char screen_linux_s_kf12 [] = "\033[24~"; +static char screen_linux_s_el1 [] = "\033[1K"; +static char screen_linux_s_op [] = "\033[39;49m"; +static char screen_linux_s_kmous[] = "\033[M"; +static char screen_linux_s_setaf[] = "\033[3%p1%dm"; +static char screen_linux_s_setab[] = "\033[4%p1%dm"; + +static char screen_linux_bool_data[] = { + /* 0: bw */ TRUE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ TRUE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_linux_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_linux_string_data[] = { + /* 0: cbt */ screen_linux_s_cbt, + /* 1: bel */ screen_linux_s_bel, + /* 2: cr */ screen_linux_s_cr, + /* 3: csr */ screen_linux_s_csr, + /* 4: tbc */ screen_linux_s_tbc, + /* 5: clear */ screen_linux_s_clear, + /* 6: el */ screen_linux_s_el, + /* 7: ed */ screen_linux_s_ed, + /* 8: hpa */ screen_linux_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_linux_s_cup, + /* 11: cud1 */ screen_linux_s_cud1, + /* 12: home */ screen_linux_s_home, + /* 13: civis */ screen_linux_s_civis, + /* 14: cub1 */ screen_linux_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_linux_s_cnorm, + /* 17: cuf1 */ screen_linux_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_linux_s_cuu1, + /* 20: cvvis */ screen_linux_s_cvvis, + /* 21: dch1 */ screen_linux_s_dch1, + /* 22: dl1 */ screen_linux_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_linux_s_smacs, + /* 26: blink */ screen_linux_s_blink, + /* 27: bold */ screen_linux_s_bold, + /* 28: smcup */ screen_linux_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_linux_s_dim, + /* 31: smir */ screen_linux_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_linux_s_rev, + /* 35: smso */ screen_linux_s_smso, + /* 36: smul */ screen_linux_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ screen_linux_s_rmacs, + /* 39: sgr0 */ screen_linux_s_sgr0, + /* 40: rmcup */ screen_linux_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_linux_s_rmir, + /* 43: rmso */ screen_linux_s_rmso, + /* 44: rmul */ screen_linux_s_rmul, + /* 45: flash */ screen_linux_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_linux_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_linux_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_linux_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_linux_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_linux_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_linux_s_kf1, + /* 67: kf10 */ screen_linux_s_kf10, + /* 68: kf2 */ screen_linux_s_kf2, + /* 69: kf3 */ screen_linux_s_kf3, + /* 70: kf4 */ screen_linux_s_kf4, + /* 71: kf5 */ screen_linux_s_kf5, + /* 72: kf6 */ screen_linux_s_kf6, + /* 73: kf7 */ screen_linux_s_kf7, + /* 74: kf8 */ screen_linux_s_kf8, + /* 75: kf9 */ screen_linux_s_kf9, + /* 76: khome */ screen_linux_s_khome, + /* 77: kich1 */ screen_linux_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_linux_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_linux_s_knp, + /* 82: kpp */ screen_linux_s_kpp, + /* 83: kcuf1 */ screen_linux_s_kcuf1, + /* 84: kind */ ABSENT_STRING, + /* 85: kri */ ABSENT_STRING, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_linux_s_kcuu1, + /* 88: rmkx */ screen_linux_s_rmkx, + /* 89: smkx */ screen_linux_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ screen_linux_s_nel, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_linux_s_dch, + /* 106: dl */ screen_linux_s_dl, + /* 107: cud */ screen_linux_s_cud, + /* 108: ich */ screen_linux_s_ich, + /* 109: indn */ screen_linux_s_indn, + /* 110: il */ screen_linux_s_il, + /* 111: cub */ screen_linux_s_cub, + /* 112: cuf */ screen_linux_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ screen_linux_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ ABSENT_STRING, + /* 123: rs2 */ screen_linux_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_linux_s_rc, + /* 127: vpa */ screen_linux_s_vpa, + /* 128: sc */ screen_linux_s_sc, + /* 129: ind */ screen_linux_s_ind, + /* 130: ri */ screen_linux_s_ri, + /* 131: sgr */ screen_linux_s_sgr, + /* 132: hts */ screen_linux_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_linux_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ ABSENT_STRING, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_linux_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ CANCELLED_STRING, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ ABSENT_STRING, + /* 152: rmam */ ABSENT_STRING, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ screen_linux_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_linux_s_kend, + /* 165: kent */ ABSENT_STRING, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ ABSENT_STRING, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ ABSENT_STRING, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ ABSENT_STRING, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ ABSENT_STRING, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ ABSENT_STRING, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_linux_s_kf11, + /* 217: kf12 */ screen_linux_s_kf12, + /* 218: kf13 */ ABSENT_STRING, + /* 219: kf14 */ ABSENT_STRING, + /* 220: kf15 */ ABSENT_STRING, + /* 221: kf16 */ ABSENT_STRING, + /* 222: kf17 */ ABSENT_STRING, + /* 223: kf18 */ ABSENT_STRING, + /* 224: kf19 */ ABSENT_STRING, + /* 225: kf20 */ ABSENT_STRING, + /* 226: kf21 */ ABSENT_STRING, + /* 227: kf22 */ ABSENT_STRING, + /* 228: kf23 */ ABSENT_STRING, + /* 229: kf24 */ ABSENT_STRING, + /* 230: kf25 */ ABSENT_STRING, + /* 231: kf26 */ ABSENT_STRING, + /* 232: kf27 */ ABSENT_STRING, + /* 233: kf28 */ ABSENT_STRING, + /* 234: kf29 */ ABSENT_STRING, + /* 235: kf30 */ ABSENT_STRING, + /* 236: kf31 */ ABSENT_STRING, + /* 237: kf32 */ ABSENT_STRING, + /* 238: kf33 */ ABSENT_STRING, + /* 239: kf34 */ ABSENT_STRING, + /* 240: kf35 */ ABSENT_STRING, + /* 241: kf36 */ ABSENT_STRING, + /* 242: kf37 */ ABSENT_STRING, + /* 243: kf38 */ ABSENT_STRING, + /* 244: kf39 */ ABSENT_STRING, + /* 245: kf40 */ ABSENT_STRING, + /* 246: kf41 */ ABSENT_STRING, + /* 247: kf42 */ ABSENT_STRING, + /* 248: kf43 */ ABSENT_STRING, + /* 249: kf44 */ ABSENT_STRING, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ screen_linux_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ ABSENT_STRING, + /* 294: u7 */ ABSENT_STRING, + /* 295: u8 */ ABSENT_STRING, + /* 296: u9 */ ABSENT_STRING, + /* 297: op */ screen_linux_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_linux_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_linux_s_setaf, + /* 360: setab */ screen_linux_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen.rxvt */ + +static char screen_rxvt_alias_data[] = "screen.rxvt|screen in rxvt"; + +static char screen_rxvt_s_cbt [] = "\033[Z"; +static char screen_rxvt_s_bel [] = "\007"; +static char screen_rxvt_s_cr [] = "\015"; +static char screen_rxvt_s_csr [] = "\033[%i%p1%d;%p2%dr"; +static char screen_rxvt_s_tbc [] = "\033[3g"; +static char screen_rxvt_s_clear [] = "\033[H\033[J"; +static char screen_rxvt_s_el [] = "\033[K"; +static char screen_rxvt_s_ed [] = "\033[J"; +static char screen_rxvt_s_hpa [] = "\033[%i%p1%dG"; +static char screen_rxvt_s_cup [] = "\033[%i%p1%d;%p2%dH"; +static char screen_rxvt_s_cud1 [] = "\012"; +static char screen_rxvt_s_home [] = "\033[H"; +static char screen_rxvt_s_civis [] = "\033[?25l"; +static char screen_rxvt_s_cub1 [] = "\010"; +static char screen_rxvt_s_cnorm [] = "\033[34h\033[?25h"; +static char screen_rxvt_s_cuf1 [] = "\033[C"; +static char screen_rxvt_s_cuu1 [] = "\033M"; +static char screen_rxvt_s_dch1 [] = "\033[P"; +static char screen_rxvt_s_dl1 [] = "\033[M"; +static char screen_rxvt_s_smacs [] = "\016"; +static char screen_rxvt_s_blink [] = "\033[5m"; +static char screen_rxvt_s_bold [] = "\033[1m"; +static char screen_rxvt_s_smcup [] = "\033[?1049h"; +static char screen_rxvt_s_dim [] = "\033[2m"; +static char screen_rxvt_s_smir [] = "\033[4h"; +static char screen_rxvt_s_rev [] = "\033[7m"; +static char screen_rxvt_s_smso [] = "\033[3m"; +static char screen_rxvt_s_smul [] = "\033[4m"; +static char screen_rxvt_s_rmacs [] = "\017"; +static char screen_rxvt_s_sgr0 [] = "\033[m\017"; +static char screen_rxvt_s_rmcup [] = "\033[?1049l"; +static char screen_rxvt_s_rmir [] = "\033[4l"; +static char screen_rxvt_s_rmso [] = "\033[23m"; +static char screen_rxvt_s_rmul [] = "\033[24m"; +static char screen_rxvt_s_is2 [] = "\033)0"; +static char screen_rxvt_s_il1 [] = "\033[L"; +static char screen_rxvt_s_kbs [] = "\010"; +static char screen_rxvt_s_kdch1 [] = "\033[3~"; +static char screen_rxvt_s_kcud1 [] = "\033OB"; +static char screen_rxvt_s_kel [] = "\033[8^"; +static char screen_rxvt_s_kf1 [] = "\033OP"; +static char screen_rxvt_s_kf10 [] = "\033[21~"; +static char screen_rxvt_s_kf2 [] = "\033OQ"; +static char screen_rxvt_s_kf3 [] = "\033OR"; +static char screen_rxvt_s_kf4 [] = "\033OS"; +static char screen_rxvt_s_kf5 [] = "\033[15~"; +static char screen_rxvt_s_kf6 [] = "\033[17~"; +static char screen_rxvt_s_kf7 [] = "\033[18~"; +static char screen_rxvt_s_kf8 [] = "\033[19~"; +static char screen_rxvt_s_kf9 [] = "\033[20~"; +static char screen_rxvt_s_khome [] = "\033[1~"; +static char screen_rxvt_s_kich1 [] = "\033[2~"; +static char screen_rxvt_s_kcub1 [] = "\033OD"; +static char screen_rxvt_s_knp [] = "\033[6~"; +static char screen_rxvt_s_kpp [] = "\033[5~"; +static char screen_rxvt_s_kcuf1 [] = "\033OC"; +static char screen_rxvt_s_kind [] = "\033[a"; +static char screen_rxvt_s_kri [] = "\033[b"; +static char screen_rxvt_s_kcuu1 [] = "\033OA"; +static char screen_rxvt_s_rmkx [] = "\033[?1l\033>"; +static char screen_rxvt_s_smkx [] = "\033[?1h\033="; +static char screen_rxvt_s_nel [] = "\033E"; +static char screen_rxvt_s_dch [] = "\033[%p1%dP"; +static char screen_rxvt_s_dl [] = "\033[%p1%dM"; +static char screen_rxvt_s_cud [] = "\033[%p1%dB"; +static char screen_rxvt_s_ich [] = "\033[%p1%d@"; +static char screen_rxvt_s_indn [] = "\033[%p1%dS"; +static char screen_rxvt_s_il [] = "\033[%p1%dL"; +static char screen_rxvt_s_cub [] = "\033[%p1%dD"; +static char screen_rxvt_s_cuf [] = "\033[%p1%dC"; +static char screen_rxvt_s_cuu [] = "\033[%p1%dA"; +static char screen_rxvt_s_rs2 [] = "\033c\033[?1000l\033[?25h"; +static char screen_rxvt_s_rc [] = "\0338"; +static char screen_rxvt_s_vpa [] = "\033[%i%p1%dd"; +static char screen_rxvt_s_sc [] = "\0337"; +static char screen_rxvt_s_ind [] = "\012"; +static char screen_rxvt_s_ri [] = "\033M"; +static char screen_rxvt_s_sgr [] = "\033[0%?%p6%t;1%;%?%p1%t;3%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;m%?%p9%t\016%e\017%;"; +static char screen_rxvt_s_hts [] = "\033H"; +static char screen_rxvt_s_ht [] = "\011"; +static char screen_rxvt_s_ka1 [] = "\033Ow"; +static char screen_rxvt_s_ka3 [] = "\033Oy"; +static char screen_rxvt_s_kb2 [] = "\033Ou"; +static char screen_rxvt_s_kc1 [] = "\033Oq"; +static char screen_rxvt_s_kc3 [] = "\033Os"; +static char screen_rxvt_s_acsc [] = "++,,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_rxvt_s_kcbt [] = "\033[Z"; +static char screen_rxvt_s_enacs [] = "\033(B\033)0"; +static char screen_rxvt_s_kend [] = "\033[4~"; +static char screen_rxvt_s_kent [] = "\033OM"; +static char screen_rxvt_s_kDC [] = "\033[3$"; +static char screen_rxvt_s_kEND [] = "\033[8$"; +static char screen_rxvt_s_kHOM [] = "\033[7$"; +static char screen_rxvt_s_kIC [] = "\033[2$"; +static char screen_rxvt_s_kLFT [] = "\033[d"; +static char screen_rxvt_s_kNXT [] = "\033[6$"; +static char screen_rxvt_s_kPRV [] = "\033[5$"; +static char screen_rxvt_s_kRIT [] = "\033[c"; +static char screen_rxvt_s_kf11 [] = "\033[23~"; +static char screen_rxvt_s_kf12 [] = "\033[24~"; +static char screen_rxvt_s_kf13 [] = "\033[25~"; +static char screen_rxvt_s_kf14 [] = "\033[26~"; +static char screen_rxvt_s_kf15 [] = "\033[28~"; +static char screen_rxvt_s_kf16 [] = "\033[29~"; +static char screen_rxvt_s_kf17 [] = "\033[31~"; +static char screen_rxvt_s_kf18 [] = "\033[32~"; +static char screen_rxvt_s_kf19 [] = "\033[33~"; +static char screen_rxvt_s_kf20 [] = "\033[34~"; +static char screen_rxvt_s_kf21 [] = "\033[23$"; +static char screen_rxvt_s_kf22 [] = "\033[24$"; +static char screen_rxvt_s_kf23 [] = "\033[11^"; +static char screen_rxvt_s_kf24 [] = "\033[12^"; +static char screen_rxvt_s_kf25 [] = "\033[13^"; +static char screen_rxvt_s_kf26 [] = "\033[14^"; +static char screen_rxvt_s_kf27 [] = "\033[15^"; +static char screen_rxvt_s_kf28 [] = "\033[17^"; +static char screen_rxvt_s_kf29 [] = "\033[18^"; +static char screen_rxvt_s_kf30 [] = "\033[19^"; +static char screen_rxvt_s_kf31 [] = "\033[20^"; +static char screen_rxvt_s_kf32 [] = "\033[21^"; +static char screen_rxvt_s_kf33 [] = "\033[23^"; +static char screen_rxvt_s_kf34 [] = "\033[24^"; +static char screen_rxvt_s_kf35 [] = "\033[25^"; +static char screen_rxvt_s_kf36 [] = "\033[26^"; +static char screen_rxvt_s_kf37 [] = "\033[28^"; +static char screen_rxvt_s_kf38 [] = "\033[29^"; +static char screen_rxvt_s_kf39 [] = "\033[31^"; +static char screen_rxvt_s_kf40 [] = "\033[32^"; +static char screen_rxvt_s_kf41 [] = "\033[33^"; +static char screen_rxvt_s_kf42 [] = "\033[34^"; +static char screen_rxvt_s_kf43 [] = "\033[23@"; +static char screen_rxvt_s_kf44 [] = "\033[24@"; +static char screen_rxvt_s_el1 [] = "\033[1K"; +static char screen_rxvt_s_u6 [] = "\033[%i%d;%dR"; +static char screen_rxvt_s_u7 [] = "\033[6n"; +static char screen_rxvt_s_u8 [] = "\033[?1;2c"; +static char screen_rxvt_s_u9 [] = "\033[c"; +static char screen_rxvt_s_op [] = "\033[39;49m"; +static char screen_rxvt_s_kmous [] = "\033[M"; +static char screen_rxvt_s_setaf [] = "\033[3%p1%dm"; +static char screen_rxvt_s_setab [] = "\033[4%p1%dm"; + +static char screen_rxvt_bool_data[] = { + /* 0: bw */ TRUE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ FALSE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ FALSE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ TRUE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_rxvt_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_rxvt_string_data[] = { + /* 0: cbt */ screen_rxvt_s_cbt, + /* 1: bel */ screen_rxvt_s_bel, + /* 2: cr */ screen_rxvt_s_cr, + /* 3: csr */ screen_rxvt_s_csr, + /* 4: tbc */ screen_rxvt_s_tbc, + /* 5: clear */ screen_rxvt_s_clear, + /* 6: el */ screen_rxvt_s_el, + /* 7: ed */ screen_rxvt_s_ed, + /* 8: hpa */ screen_rxvt_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_rxvt_s_cup, + /* 11: cud1 */ screen_rxvt_s_cud1, + /* 12: home */ screen_rxvt_s_home, + /* 13: civis */ screen_rxvt_s_civis, + /* 14: cub1 */ screen_rxvt_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_rxvt_s_cnorm, + /* 17: cuf1 */ screen_rxvt_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_rxvt_s_cuu1, + /* 20: cvvis */ CANCELLED_STRING, + /* 21: dch1 */ screen_rxvt_s_dch1, + /* 22: dl1 */ screen_rxvt_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_rxvt_s_smacs, + /* 26: blink */ screen_rxvt_s_blink, + /* 27: bold */ screen_rxvt_s_bold, + /* 28: smcup */ screen_rxvt_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_rxvt_s_dim, + /* 31: smir */ screen_rxvt_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_rxvt_s_rev, + /* 35: smso */ screen_rxvt_s_smso, + /* 36: smul */ screen_rxvt_s_smul, + /* 37: ech */ ABSENT_STRING, + /* 38: rmacs */ screen_rxvt_s_rmacs, + /* 39: sgr0 */ screen_rxvt_s_sgr0, + /* 40: rmcup */ screen_rxvt_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_rxvt_s_rmir, + /* 43: rmso */ screen_rxvt_s_rmso, + /* 44: rmul */ screen_rxvt_s_rmul, + /* 45: flash */ CANCELLED_STRING, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_rxvt_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_rxvt_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_rxvt_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_rxvt_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_rxvt_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ screen_rxvt_s_kel, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_rxvt_s_kf1, + /* 67: kf10 */ screen_rxvt_s_kf10, + /* 68: kf2 */ screen_rxvt_s_kf2, + /* 69: kf3 */ screen_rxvt_s_kf3, + /* 70: kf4 */ screen_rxvt_s_kf4, + /* 71: kf5 */ screen_rxvt_s_kf5, + /* 72: kf6 */ screen_rxvt_s_kf6, + /* 73: kf7 */ screen_rxvt_s_kf7, + /* 74: kf8 */ screen_rxvt_s_kf8, + /* 75: kf9 */ screen_rxvt_s_kf9, + /* 76: khome */ screen_rxvt_s_khome, + /* 77: kich1 */ screen_rxvt_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_rxvt_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_rxvt_s_knp, + /* 82: kpp */ screen_rxvt_s_kpp, + /* 83: kcuf1 */ screen_rxvt_s_kcuf1, + /* 84: kind */ screen_rxvt_s_kind, + /* 85: kri */ screen_rxvt_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_rxvt_s_kcuu1, + /* 88: rmkx */ screen_rxvt_s_rmkx, + /* 89: smkx */ screen_rxvt_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ ABSENT_STRING, + /* 102: smm */ ABSENT_STRING, + /* 103: nel */ screen_rxvt_s_nel, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_rxvt_s_dch, + /* 106: dl */ screen_rxvt_s_dl, + /* 107: cud */ screen_rxvt_s_cud, + /* 108: ich */ screen_rxvt_s_ich, + /* 109: indn */ screen_rxvt_s_indn, + /* 110: il */ screen_rxvt_s_il, + /* 111: cub */ screen_rxvt_s_cub, + /* 112: cuf */ screen_rxvt_s_cuf, + /* 113: rin */ ABSENT_STRING, + /* 114: cuu */ screen_rxvt_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ ABSENT_STRING, + /* 119: mc4 */ ABSENT_STRING, + /* 120: mc5 */ ABSENT_STRING, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ ABSENT_STRING, + /* 123: rs2 */ screen_rxvt_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_rxvt_s_rc, + /* 127: vpa */ screen_rxvt_s_vpa, + /* 128: sc */ screen_rxvt_s_sc, + /* 129: ind */ screen_rxvt_s_ind, + /* 130: ri */ screen_rxvt_s_ri, + /* 131: sgr */ screen_rxvt_s_sgr, + /* 132: hts */ screen_rxvt_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_rxvt_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ screen_rxvt_s_ka1, + /* 140: ka3 */ screen_rxvt_s_ka3, + /* 141: kb2 */ screen_rxvt_s_kb2, + /* 142: kc1 */ screen_rxvt_s_kc1, + /* 143: kc3 */ screen_rxvt_s_kc3, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_rxvt_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ screen_rxvt_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ ABSENT_STRING, + /* 152: rmam */ ABSENT_STRING, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ screen_rxvt_s_enacs, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_rxvt_s_kend, + /* 165: kent */ screen_rxvt_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ screen_rxvt_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ screen_rxvt_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ screen_rxvt_s_kHOM, + /* 200: kIC */ screen_rxvt_s_kIC, + /* 201: kLFT */ screen_rxvt_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ screen_rxvt_s_kNXT, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ screen_rxvt_s_kPRV, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ screen_rxvt_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_rxvt_s_kf11, + /* 217: kf12 */ screen_rxvt_s_kf12, + /* 218: kf13 */ screen_rxvt_s_kf13, + /* 219: kf14 */ screen_rxvt_s_kf14, + /* 220: kf15 */ screen_rxvt_s_kf15, + /* 221: kf16 */ screen_rxvt_s_kf16, + /* 222: kf17 */ screen_rxvt_s_kf17, + /* 223: kf18 */ screen_rxvt_s_kf18, + /* 224: kf19 */ screen_rxvt_s_kf19, + /* 225: kf20 */ screen_rxvt_s_kf20, + /* 226: kf21 */ screen_rxvt_s_kf21, + /* 227: kf22 */ screen_rxvt_s_kf22, + /* 228: kf23 */ screen_rxvt_s_kf23, + /* 229: kf24 */ screen_rxvt_s_kf24, + /* 230: kf25 */ screen_rxvt_s_kf25, + /* 231: kf26 */ screen_rxvt_s_kf26, + /* 232: kf27 */ screen_rxvt_s_kf27, + /* 233: kf28 */ screen_rxvt_s_kf28, + /* 234: kf29 */ screen_rxvt_s_kf29, + /* 235: kf30 */ screen_rxvt_s_kf30, + /* 236: kf31 */ screen_rxvt_s_kf31, + /* 237: kf32 */ screen_rxvt_s_kf32, + /* 238: kf33 */ screen_rxvt_s_kf33, + /* 239: kf34 */ screen_rxvt_s_kf34, + /* 240: kf35 */ screen_rxvt_s_kf35, + /* 241: kf36 */ screen_rxvt_s_kf36, + /* 242: kf37 */ screen_rxvt_s_kf37, + /* 243: kf38 */ screen_rxvt_s_kf38, + /* 244: kf39 */ screen_rxvt_s_kf39, + /* 245: kf40 */ screen_rxvt_s_kf40, + /* 246: kf41 */ screen_rxvt_s_kf41, + /* 247: kf42 */ screen_rxvt_s_kf42, + /* 248: kf43 */ screen_rxvt_s_kf43, + /* 249: kf44 */ screen_rxvt_s_kf44, + /* 250: kf45 */ ABSENT_STRING, + /* 251: kf46 */ ABSENT_STRING, + /* 252: kf47 */ ABSENT_STRING, + /* 253: kf48 */ ABSENT_STRING, + /* 254: kf49 */ ABSENT_STRING, + /* 255: kf50 */ ABSENT_STRING, + /* 256: kf51 */ ABSENT_STRING, + /* 257: kf52 */ ABSENT_STRING, + /* 258: kf53 */ ABSENT_STRING, + /* 259: kf54 */ ABSENT_STRING, + /* 260: kf55 */ ABSENT_STRING, + /* 261: kf56 */ ABSENT_STRING, + /* 262: kf57 */ ABSENT_STRING, + /* 263: kf58 */ ABSENT_STRING, + /* 264: kf59 */ ABSENT_STRING, + /* 265: kf60 */ ABSENT_STRING, + /* 266: kf61 */ ABSENT_STRING, + /* 267: kf62 */ ABSENT_STRING, + /* 268: kf63 */ ABSENT_STRING, + /* 269: el1 */ screen_rxvt_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ screen_rxvt_s_u6, + /* 294: u7 */ screen_rxvt_s_u7, + /* 295: u8 */ screen_rxvt_s_u8, + /* 296: u9 */ screen_rxvt_s_u9, + /* 297: op */ screen_rxvt_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_rxvt_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_rxvt_s_setaf, + /* 360: setab */ screen_rxvt_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen.xterm-new */ + +static char screen_xterm_xfree86_alias_data[] = "screen.xterm-xfree86|screen.xterm-new|screen customized for modern xterm"; + +static char screen_xterm_xfree86_s_cbt[] = "\033[Z"; +static char screen_xterm_xfree86_s_bel[] = "\007"; +static char screen_xterm_xfree86_s_cr[] = "\015"; +static char screen_xterm_xfree86_s_csr[] = "\033[%i%p1%d;%p2%dr"; +static char screen_xterm_xfree86_s_tbc[] = "\033[3g"; +static char screen_xterm_xfree86_s_clear[] = "\033[H\033[2J"; +static char screen_xterm_xfree86_s_el[] = "\033[K"; +static char screen_xterm_xfree86_s_ed[] = "\033[J"; +static char screen_xterm_xfree86_s_hpa[] = "\033[%i%p1%dG"; +static char screen_xterm_xfree86_s_cup[] = "\033[%i%p1%d;%p2%dH"; +static char screen_xterm_xfree86_s_cud1[] = "\012"; +static char screen_xterm_xfree86_s_home[] = "\033[H"; +static char screen_xterm_xfree86_s_civis[] = "\033[?25l"; +static char screen_xterm_xfree86_s_cub1[] = "\010"; +static char screen_xterm_xfree86_s_cnorm[] = "\033[?12l\033[?25h"; +static char screen_xterm_xfree86_s_cuf1[] = "\033[C"; +static char screen_xterm_xfree86_s_cuu1[] = "\033[A"; +static char screen_xterm_xfree86_s_cvvis[] = "\033[?12;25h"; +static char screen_xterm_xfree86_s_dch1[] = "\033[P"; +static char screen_xterm_xfree86_s_dl1[] = "\033[M"; +static char screen_xterm_xfree86_s_smacs[] = "\033(0"; +static char screen_xterm_xfree86_s_blink[] = "\033[5m"; +static char screen_xterm_xfree86_s_bold[] = "\033[1m"; +static char screen_xterm_xfree86_s_smcup[] = "\033[?1049h\033[22;0;0t"; +static char screen_xterm_xfree86_s_dim[] = "\033[2m"; +static char screen_xterm_xfree86_s_smir[] = "\033[4h"; +static char screen_xterm_xfree86_s_rev[] = "\033[7m"; +static char screen_xterm_xfree86_s_smso[] = "\033[7m"; +static char screen_xterm_xfree86_s_smul[] = "\033[4m"; +static char screen_xterm_xfree86_s_ech[] = "\033[%p1%dX"; +static char screen_xterm_xfree86_s_rmacs[] = "\033(B"; +static char screen_xterm_xfree86_s_sgr0[] = "\033(B\033[m"; +static char screen_xterm_xfree86_s_rmcup[] = "\033[?1049l\033[23;0;0t"; +static char screen_xterm_xfree86_s_rmir[] = "\033[4l"; +static char screen_xterm_xfree86_s_rmso[] = "\033[27m"; +static char screen_xterm_xfree86_s_rmul[] = "\033[24m"; +static char screen_xterm_xfree86_s_flash[] = "\033[?5h$<100/>\033[?5l"; +static char screen_xterm_xfree86_s_is2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char screen_xterm_xfree86_s_il1[] = "\033[L"; +static char screen_xterm_xfree86_s_kbs[] = "\010"; +static char screen_xterm_xfree86_s_kdch1[] = "\033[3~"; +static char screen_xterm_xfree86_s_kcud1[] = "\033OB"; +static char screen_xterm_xfree86_s_kf1[] = "\033OP"; +static char screen_xterm_xfree86_s_kf10[] = "\033[21~"; +static char screen_xterm_xfree86_s_kf2[] = "\033OQ"; +static char screen_xterm_xfree86_s_kf3[] = "\033OR"; +static char screen_xterm_xfree86_s_kf4[] = "\033OS"; +static char screen_xterm_xfree86_s_kf5[] = "\033[15~"; +static char screen_xterm_xfree86_s_kf6[] = "\033[17~"; +static char screen_xterm_xfree86_s_kf7[] = "\033[18~"; +static char screen_xterm_xfree86_s_kf8[] = "\033[19~"; +static char screen_xterm_xfree86_s_kf9[] = "\033[20~"; +static char screen_xterm_xfree86_s_khome[] = "\033[1~"; +static char screen_xterm_xfree86_s_kich1[] = "\033[2~"; +static char screen_xterm_xfree86_s_kcub1[] = "\033OD"; +static char screen_xterm_xfree86_s_knp[] = "\033[6~"; +static char screen_xterm_xfree86_s_kpp[] = "\033[5~"; +static char screen_xterm_xfree86_s_kcuf1[] = "\033OC"; +static char screen_xterm_xfree86_s_kind[] = "\033[1;2B"; +static char screen_xterm_xfree86_s_kri[] = "\033[1;2A"; +static char screen_xterm_xfree86_s_kcuu1[] = "\033OA"; +static char screen_xterm_xfree86_s_rmkx[] = "\033[?1l\033>"; +static char screen_xterm_xfree86_s_smkx[] = "\033[?1h\033="; +static char screen_xterm_xfree86_s_rmm[] = "\033[?1034l"; +static char screen_xterm_xfree86_s_smm[] = "\033[?1034h"; +static char screen_xterm_xfree86_s_dch[] = "\033[%p1%dP"; +static char screen_xterm_xfree86_s_dl[] = "\033[%p1%dM"; +static char screen_xterm_xfree86_s_cud[] = "\033[%p1%dB"; +static char screen_xterm_xfree86_s_ich[] = "\033[%p1%d@"; +static char screen_xterm_xfree86_s_indn[] = "\033[%p1%dS"; +static char screen_xterm_xfree86_s_il[] = "\033[%p1%dL"; +static char screen_xterm_xfree86_s_cub[] = "\033[%p1%dD"; +static char screen_xterm_xfree86_s_cuf[] = "\033[%p1%dC"; +static char screen_xterm_xfree86_s_rin[] = "\033[%p1%dT"; +static char screen_xterm_xfree86_s_cuu[] = "\033[%p1%dA"; +static char screen_xterm_xfree86_s_mc0[] = "\033[i"; +static char screen_xterm_xfree86_s_mc4[] = "\033[4i"; +static char screen_xterm_xfree86_s_mc5[] = "\033[5i"; +static char screen_xterm_xfree86_s_rs1[] = "\033c"; +static char screen_xterm_xfree86_s_rs2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char screen_xterm_xfree86_s_rc[] = "\0338"; +static char screen_xterm_xfree86_s_vpa[] = "\033[%i%p1%dd"; +static char screen_xterm_xfree86_s_sc[] = "\0337"; +static char screen_xterm_xfree86_s_ind[] = "\012"; +static char screen_xterm_xfree86_s_ri[] = "\033M"; +static char screen_xterm_xfree86_s_sgr[] = "%?%p9%t\033(0%e\033(B%;\033[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p5%t;2%;m"; +static char screen_xterm_xfree86_s_hts[] = "\033H"; +static char screen_xterm_xfree86_s_ht[] = "\011"; +static char screen_xterm_xfree86_s_kb2[] = "\033OE"; +static char screen_xterm_xfree86_s_acsc[] = "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_xterm_xfree86_s_kcbt[] = "\033[Z"; +static char screen_xterm_xfree86_s_smam[] = "\033[?7h"; +static char screen_xterm_xfree86_s_rmam[] = "\033[?7l"; +static char screen_xterm_xfree86_s_kend[] = "\033[4~"; +static char screen_xterm_xfree86_s_kent[] = "\033OM"; +static char screen_xterm_xfree86_s_kDC[] = "\033[3;2~"; +static char screen_xterm_xfree86_s_kEND[] = "\033[1;2F"; +static char screen_xterm_xfree86_s_kHOM[] = "\033[1;2H"; +static char screen_xterm_xfree86_s_kLFT[] = "\033[1;2D"; +static char screen_xterm_xfree86_s_kRIT[] = "\033[1;2C"; +static char screen_xterm_xfree86_s_kf11[] = "\033[23~"; +static char screen_xterm_xfree86_s_kf12[] = "\033[24~"; +static char screen_xterm_xfree86_s_kf13[] = "\033[1;2P"; +static char screen_xterm_xfree86_s_kf14[] = "\033[1;2Q"; +static char screen_xterm_xfree86_s_kf15[] = "\033[1;2R"; +static char screen_xterm_xfree86_s_kf16[] = "\033[1;2S"; +static char screen_xterm_xfree86_s_kf17[] = "\033[15;2~"; +static char screen_xterm_xfree86_s_kf18[] = "\033[17;2~"; +static char screen_xterm_xfree86_s_kf19[] = "\033[18;2~"; +static char screen_xterm_xfree86_s_kf20[] = "\033[19;2~"; +static char screen_xterm_xfree86_s_kf21[] = "\033[20;2~"; +static char screen_xterm_xfree86_s_kf22[] = "\033[21;2~"; +static char screen_xterm_xfree86_s_kf23[] = "\033[23;2~"; +static char screen_xterm_xfree86_s_kf24[] = "\033[24;2~"; +static char screen_xterm_xfree86_s_kf25[] = "\033[1;5P"; +static char screen_xterm_xfree86_s_kf26[] = "\033[1;5Q"; +static char screen_xterm_xfree86_s_kf27[] = "\033[1;5R"; +static char screen_xterm_xfree86_s_kf28[] = "\033[1;5S"; +static char screen_xterm_xfree86_s_kf29[] = "\033[15;5~"; +static char screen_xterm_xfree86_s_kf30[] = "\033[17;5~"; +static char screen_xterm_xfree86_s_kf31[] = "\033[18;5~"; +static char screen_xterm_xfree86_s_kf32[] = "\033[19;5~"; +static char screen_xterm_xfree86_s_kf33[] = "\033[20;5~"; +static char screen_xterm_xfree86_s_kf34[] = "\033[21;5~"; +static char screen_xterm_xfree86_s_kf35[] = "\033[23;5~"; +static char screen_xterm_xfree86_s_kf36[] = "\033[24;5~"; +static char screen_xterm_xfree86_s_kf37[] = "\033[1;6P"; +static char screen_xterm_xfree86_s_kf38[] = "\033[1;6Q"; +static char screen_xterm_xfree86_s_kf39[] = "\033[1;6R"; +static char screen_xterm_xfree86_s_kf40[] = "\033[1;6S"; +static char screen_xterm_xfree86_s_kf41[] = "\033[15;6~"; +static char screen_xterm_xfree86_s_kf42[] = "\033[17;6~"; +static char screen_xterm_xfree86_s_kf43[] = "\033[18;6~"; +static char screen_xterm_xfree86_s_kf44[] = "\033[19;6~"; +static char screen_xterm_xfree86_s_kf45[] = "\033[20;6~"; +static char screen_xterm_xfree86_s_kf46[] = "\033[21;6~"; +static char screen_xterm_xfree86_s_kf47[] = "\033[23;6~"; +static char screen_xterm_xfree86_s_kf48[] = "\033[24;6~"; +static char screen_xterm_xfree86_s_kf49[] = "\033[1;3P"; +static char screen_xterm_xfree86_s_kf50[] = "\033[1;3Q"; +static char screen_xterm_xfree86_s_kf51[] = "\033[1;3R"; +static char screen_xterm_xfree86_s_kf52[] = "\033[1;3S"; +static char screen_xterm_xfree86_s_kf53[] = "\033[15;3~"; +static char screen_xterm_xfree86_s_kf54[] = "\033[17;3~"; +static char screen_xterm_xfree86_s_kf55[] = "\033[18;3~"; +static char screen_xterm_xfree86_s_kf56[] = "\033[19;3~"; +static char screen_xterm_xfree86_s_kf57[] = "\033[20;3~"; +static char screen_xterm_xfree86_s_kf58[] = "\033[21;3~"; +static char screen_xterm_xfree86_s_kf59[] = "\033[23;3~"; +static char screen_xterm_xfree86_s_kf60[] = "\033[24;3~"; +static char screen_xterm_xfree86_s_kf61[] = "\033[1;4P"; +static char screen_xterm_xfree86_s_kf62[] = "\033[1;4Q"; +static char screen_xterm_xfree86_s_kf63[] = "\033[1;4R"; +static char screen_xterm_xfree86_s_el1[] = "\033[1K"; +static char screen_xterm_xfree86_s_u6[] = "\033[%i%d;%dR"; +static char screen_xterm_xfree86_s_u7[] = "\033[6n"; +static char screen_xterm_xfree86_s_u8[] = "\033[?%[;0123456789]c"; +static char screen_xterm_xfree86_s_u9[] = "\033[c"; +static char screen_xterm_xfree86_s_op[] = "\033[39;49m"; +static char screen_xterm_xfree86_s_setf[] = "\033[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m"; +static char screen_xterm_xfree86_s_setb[] = "\033[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m"; +static char screen_xterm_xfree86_s_kmous[] = "\033[M"; +static char screen_xterm_xfree86_s_setaf[] = "\033[3%p1%dm"; +static char screen_xterm_xfree86_s_setab[] = "\033[4%p1%dm"; + +static char screen_xterm_xfree86_bool_data[] = { + /* 0: bw */ TRUE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ TRUE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_xterm_xfree86_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 8, + /* 14: pairs */ 64, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_xterm_xfree86_string_data[] = { + /* 0: cbt */ screen_xterm_xfree86_s_cbt, + /* 1: bel */ screen_xterm_xfree86_s_bel, + /* 2: cr */ screen_xterm_xfree86_s_cr, + /* 3: csr */ screen_xterm_xfree86_s_csr, + /* 4: tbc */ screen_xterm_xfree86_s_tbc, + /* 5: clear */ screen_xterm_xfree86_s_clear, + /* 6: el */ screen_xterm_xfree86_s_el, + /* 7: ed */ screen_xterm_xfree86_s_ed, + /* 8: hpa */ screen_xterm_xfree86_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_xterm_xfree86_s_cup, + /* 11: cud1 */ screen_xterm_xfree86_s_cud1, + /* 12: home */ screen_xterm_xfree86_s_home, + /* 13: civis */ screen_xterm_xfree86_s_civis, + /* 14: cub1 */ screen_xterm_xfree86_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_xterm_xfree86_s_cnorm, + /* 17: cuf1 */ screen_xterm_xfree86_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_xterm_xfree86_s_cuu1, + /* 20: cvvis */ screen_xterm_xfree86_s_cvvis, + /* 21: dch1 */ screen_xterm_xfree86_s_dch1, + /* 22: dl1 */ screen_xterm_xfree86_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_xterm_xfree86_s_smacs, + /* 26: blink */ screen_xterm_xfree86_s_blink, + /* 27: bold */ screen_xterm_xfree86_s_bold, + /* 28: smcup */ screen_xterm_xfree86_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_xterm_xfree86_s_dim, + /* 31: smir */ screen_xterm_xfree86_s_smir, + /* 32: invis */ CANCELLED_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_xterm_xfree86_s_rev, + /* 35: smso */ screen_xterm_xfree86_s_smso, + /* 36: smul */ screen_xterm_xfree86_s_smul, + /* 37: ech */ screen_xterm_xfree86_s_ech, + /* 38: rmacs */ screen_xterm_xfree86_s_rmacs, + /* 39: sgr0 */ screen_xterm_xfree86_s_sgr0, + /* 40: rmcup */ screen_xterm_xfree86_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_xterm_xfree86_s_rmir, + /* 43: rmso */ screen_xterm_xfree86_s_rmso, + /* 44: rmul */ screen_xterm_xfree86_s_rmul, + /* 45: flash */ screen_xterm_xfree86_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_xterm_xfree86_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_xterm_xfree86_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_xterm_xfree86_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_xterm_xfree86_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_xterm_xfree86_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_xterm_xfree86_s_kf1, + /* 67: kf10 */ screen_xterm_xfree86_s_kf10, + /* 68: kf2 */ screen_xterm_xfree86_s_kf2, + /* 69: kf3 */ screen_xterm_xfree86_s_kf3, + /* 70: kf4 */ screen_xterm_xfree86_s_kf4, + /* 71: kf5 */ screen_xterm_xfree86_s_kf5, + /* 72: kf6 */ screen_xterm_xfree86_s_kf6, + /* 73: kf7 */ screen_xterm_xfree86_s_kf7, + /* 74: kf8 */ screen_xterm_xfree86_s_kf8, + /* 75: kf9 */ screen_xterm_xfree86_s_kf9, + /* 76: khome */ screen_xterm_xfree86_s_khome, + /* 77: kich1 */ screen_xterm_xfree86_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_xterm_xfree86_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_xterm_xfree86_s_knp, + /* 82: kpp */ screen_xterm_xfree86_s_kpp, + /* 83: kcuf1 */ screen_xterm_xfree86_s_kcuf1, + /* 84: kind */ screen_xterm_xfree86_s_kind, + /* 85: kri */ screen_xterm_xfree86_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_xterm_xfree86_s_kcuu1, + /* 88: rmkx */ screen_xterm_xfree86_s_rmkx, + /* 89: smkx */ screen_xterm_xfree86_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ screen_xterm_xfree86_s_rmm, + /* 102: smm */ screen_xterm_xfree86_s_smm, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_xterm_xfree86_s_dch, + /* 106: dl */ screen_xterm_xfree86_s_dl, + /* 107: cud */ screen_xterm_xfree86_s_cud, + /* 108: ich */ screen_xterm_xfree86_s_ich, + /* 109: indn */ screen_xterm_xfree86_s_indn, + /* 110: il */ screen_xterm_xfree86_s_il, + /* 111: cub */ screen_xterm_xfree86_s_cub, + /* 112: cuf */ screen_xterm_xfree86_s_cuf, + /* 113: rin */ screen_xterm_xfree86_s_rin, + /* 114: cuu */ screen_xterm_xfree86_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ screen_xterm_xfree86_s_mc0, + /* 119: mc4 */ screen_xterm_xfree86_s_mc4, + /* 120: mc5 */ screen_xterm_xfree86_s_mc5, + /* 121: rep */ CANCELLED_STRING, + /* 122: rs1 */ screen_xterm_xfree86_s_rs1, + /* 123: rs2 */ screen_xterm_xfree86_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_xterm_xfree86_s_rc, + /* 127: vpa */ screen_xterm_xfree86_s_vpa, + /* 128: sc */ screen_xterm_xfree86_s_sc, + /* 129: ind */ screen_xterm_xfree86_s_ind, + /* 130: ri */ screen_xterm_xfree86_s_ri, + /* 131: sgr */ screen_xterm_xfree86_s_sgr, + /* 132: hts */ screen_xterm_xfree86_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_xterm_xfree86_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ screen_xterm_xfree86_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_xterm_xfree86_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ screen_xterm_xfree86_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ screen_xterm_xfree86_s_smam, + /* 152: rmam */ screen_xterm_xfree86_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ ABSENT_STRING, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_xterm_xfree86_s_kend, + /* 165: kent */ screen_xterm_xfree86_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ screen_xterm_xfree86_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ screen_xterm_xfree86_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ screen_xterm_xfree86_s_kHOM, + /* 200: kIC */ CANCELLED_STRING, + /* 201: kLFT */ screen_xterm_xfree86_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ CANCELLED_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ CANCELLED_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ screen_xterm_xfree86_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_xterm_xfree86_s_kf11, + /* 217: kf12 */ screen_xterm_xfree86_s_kf12, + /* 218: kf13 */ screen_xterm_xfree86_s_kf13, + /* 219: kf14 */ screen_xterm_xfree86_s_kf14, + /* 220: kf15 */ screen_xterm_xfree86_s_kf15, + /* 221: kf16 */ screen_xterm_xfree86_s_kf16, + /* 222: kf17 */ screen_xterm_xfree86_s_kf17, + /* 223: kf18 */ screen_xterm_xfree86_s_kf18, + /* 224: kf19 */ screen_xterm_xfree86_s_kf19, + /* 225: kf20 */ screen_xterm_xfree86_s_kf20, + /* 226: kf21 */ screen_xterm_xfree86_s_kf21, + /* 227: kf22 */ screen_xterm_xfree86_s_kf22, + /* 228: kf23 */ screen_xterm_xfree86_s_kf23, + /* 229: kf24 */ screen_xterm_xfree86_s_kf24, + /* 230: kf25 */ screen_xterm_xfree86_s_kf25, + /* 231: kf26 */ screen_xterm_xfree86_s_kf26, + /* 232: kf27 */ screen_xterm_xfree86_s_kf27, + /* 233: kf28 */ screen_xterm_xfree86_s_kf28, + /* 234: kf29 */ screen_xterm_xfree86_s_kf29, + /* 235: kf30 */ screen_xterm_xfree86_s_kf30, + /* 236: kf31 */ screen_xterm_xfree86_s_kf31, + /* 237: kf32 */ screen_xterm_xfree86_s_kf32, + /* 238: kf33 */ screen_xterm_xfree86_s_kf33, + /* 239: kf34 */ screen_xterm_xfree86_s_kf34, + /* 240: kf35 */ screen_xterm_xfree86_s_kf35, + /* 241: kf36 */ screen_xterm_xfree86_s_kf36, + /* 242: kf37 */ screen_xterm_xfree86_s_kf37, + /* 243: kf38 */ screen_xterm_xfree86_s_kf38, + /* 244: kf39 */ screen_xterm_xfree86_s_kf39, + /* 245: kf40 */ screen_xterm_xfree86_s_kf40, + /* 246: kf41 */ screen_xterm_xfree86_s_kf41, + /* 247: kf42 */ screen_xterm_xfree86_s_kf42, + /* 248: kf43 */ screen_xterm_xfree86_s_kf43, + /* 249: kf44 */ screen_xterm_xfree86_s_kf44, + /* 250: kf45 */ screen_xterm_xfree86_s_kf45, + /* 251: kf46 */ screen_xterm_xfree86_s_kf46, + /* 252: kf47 */ screen_xterm_xfree86_s_kf47, + /* 253: kf48 */ screen_xterm_xfree86_s_kf48, + /* 254: kf49 */ screen_xterm_xfree86_s_kf49, + /* 255: kf50 */ screen_xterm_xfree86_s_kf50, + /* 256: kf51 */ screen_xterm_xfree86_s_kf51, + /* 257: kf52 */ screen_xterm_xfree86_s_kf52, + /* 258: kf53 */ screen_xterm_xfree86_s_kf53, + /* 259: kf54 */ screen_xterm_xfree86_s_kf54, + /* 260: kf55 */ screen_xterm_xfree86_s_kf55, + /* 261: kf56 */ screen_xterm_xfree86_s_kf56, + /* 262: kf57 */ screen_xterm_xfree86_s_kf57, + /* 263: kf58 */ screen_xterm_xfree86_s_kf58, + /* 264: kf59 */ screen_xterm_xfree86_s_kf59, + /* 265: kf60 */ screen_xterm_xfree86_s_kf60, + /* 266: kf61 */ screen_xterm_xfree86_s_kf61, + /* 267: kf62 */ screen_xterm_xfree86_s_kf62, + /* 268: kf63 */ screen_xterm_xfree86_s_kf63, + /* 269: el1 */ screen_xterm_xfree86_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ screen_xterm_xfree86_s_u6, + /* 294: u7 */ screen_xterm_xfree86_s_u7, + /* 295: u8 */ screen_xterm_xfree86_s_u8, + /* 296: u9 */ screen_xterm_xfree86_s_u9, + /* 297: op */ screen_xterm_xfree86_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ screen_xterm_xfree86_s_setf, + /* 303: setb */ screen_xterm_xfree86_s_setb, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_xterm_xfree86_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_xterm_xfree86_s_setaf, + /* 360: setab */ screen_xterm_xfree86_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ CANCELLED_STRING, + /* 412: memu */ CANCELLED_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +/* screen.xterm-256color */ + +static char screen_xterm_256color_alias_data[] = "screen.xterm-256color|GNU Screen with xterm using 256 colors"; + +static char screen_xterm_256color_s_cbt[] = "\033[Z"; +static char screen_xterm_256color_s_bel[] = "\007"; +static char screen_xterm_256color_s_cr[] = "\015"; +static char screen_xterm_256color_s_csr[] = "\033[%i%p1%d;%p2%dr"; +static char screen_xterm_256color_s_tbc[] = "\033[3g"; +static char screen_xterm_256color_s_clear[] = "\033[H\033[2J"; +static char screen_xterm_256color_s_el[] = "\033[K"; +static char screen_xterm_256color_s_ed[] = "\033[J"; +static char screen_xterm_256color_s_hpa[] = "\033[%i%p1%dG"; +static char screen_xterm_256color_s_cup[] = "\033[%i%p1%d;%p2%dH"; +static char screen_xterm_256color_s_cud1[] = "\012"; +static char screen_xterm_256color_s_home[] = "\033[H"; +static char screen_xterm_256color_s_civis[] = "\033[?25l"; +static char screen_xterm_256color_s_cub1[] = "\010"; +static char screen_xterm_256color_s_cnorm[] = "\033[?12l\033[?25h"; +static char screen_xterm_256color_s_cuf1[] = "\033[C"; +static char screen_xterm_256color_s_cuu1[] = "\033[A"; +static char screen_xterm_256color_s_cvvis[] = "\033[?12;25h"; +static char screen_xterm_256color_s_dch1[] = "\033[P"; +static char screen_xterm_256color_s_dl1[] = "\033[M"; +static char screen_xterm_256color_s_smacs[] = "\033(0"; +static char screen_xterm_256color_s_blink[] = "\033[5m"; +static char screen_xterm_256color_s_bold[] = "\033[1m"; +static char screen_xterm_256color_s_smcup[] = "\033[?1049h\033[22;0;0t"; +static char screen_xterm_256color_s_dim[] = "\033[2m"; +static char screen_xterm_256color_s_smir[] = "\033[4h"; +static char screen_xterm_256color_s_rev[] = "\033[7m"; +static char screen_xterm_256color_s_smso[] = "\033[7m"; +static char screen_xterm_256color_s_smul[] = "\033[4m"; +static char screen_xterm_256color_s_ech[] = "\033[%p1%dX"; +static char screen_xterm_256color_s_rmacs[] = "\033(B"; +static char screen_xterm_256color_s_sgr0[] = "\033(B\033[m"; +static char screen_xterm_256color_s_rmcup[] = "\033[?1049l\033[23;0;0t"; +static char screen_xterm_256color_s_rmir[] = "\033[4l"; +static char screen_xterm_256color_s_rmso[] = "\033[27m"; +static char screen_xterm_256color_s_rmul[] = "\033[24m"; +static char screen_xterm_256color_s_flash[] = "\033[?5h$<100/>\033[?5l"; +static char screen_xterm_256color_s_is2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char screen_xterm_256color_s_il1[] = "\033[L"; +static char screen_xterm_256color_s_kbs[] = "\010"; +static char screen_xterm_256color_s_kdch1[] = "\033[3~"; +static char screen_xterm_256color_s_kcud1[] = "\033OB"; +static char screen_xterm_256color_s_kf1[] = "\033OP"; +static char screen_xterm_256color_s_kf10[] = "\033[21~"; +static char screen_xterm_256color_s_kf2[] = "\033OQ"; +static char screen_xterm_256color_s_kf3[] = "\033OR"; +static char screen_xterm_256color_s_kf4[] = "\033OS"; +static char screen_xterm_256color_s_kf5[] = "\033[15~"; +static char screen_xterm_256color_s_kf6[] = "\033[17~"; +static char screen_xterm_256color_s_kf7[] = "\033[18~"; +static char screen_xterm_256color_s_kf8[] = "\033[19~"; +static char screen_xterm_256color_s_kf9[] = "\033[20~"; +static char screen_xterm_256color_s_khome[] = "\033[1~"; +static char screen_xterm_256color_s_kich1[] = "\033[2~"; +static char screen_xterm_256color_s_kcub1[] = "\033OD"; +static char screen_xterm_256color_s_knp[] = "\033[6~"; +static char screen_xterm_256color_s_kpp[] = "\033[5~"; +static char screen_xterm_256color_s_kcuf1[] = "\033OC"; +static char screen_xterm_256color_s_kind[] = "\033[1;2B"; +static char screen_xterm_256color_s_kri[] = "\033[1;2A"; +static char screen_xterm_256color_s_kcuu1[] = "\033OA"; +static char screen_xterm_256color_s_rmkx[] = "\033[?1l\033>"; +static char screen_xterm_256color_s_smkx[] = "\033[?1h\033="; +static char screen_xterm_256color_s_rmm[] = "\033[?1034l"; +static char screen_xterm_256color_s_smm[] = "\033[?1034h"; +static char screen_xterm_256color_s_dch[] = "\033[%p1%dP"; +static char screen_xterm_256color_s_dl[] = "\033[%p1%dM"; +static char screen_xterm_256color_s_cud[] = "\033[%p1%dB"; +static char screen_xterm_256color_s_ich[] = "\033[%p1%d@"; +static char screen_xterm_256color_s_indn[] = "\033[%p1%dS"; +static char screen_xterm_256color_s_il[] = "\033[%p1%dL"; +static char screen_xterm_256color_s_cub[] = "\033[%p1%dD"; +static char screen_xterm_256color_s_cuf[] = "\033[%p1%dC"; +static char screen_xterm_256color_s_rin[] = "\033[%p1%dT"; +static char screen_xterm_256color_s_cuu[] = "\033[%p1%dA"; +static char screen_xterm_256color_s_mc0[] = "\033[i"; +static char screen_xterm_256color_s_mc4[] = "\033[4i"; +static char screen_xterm_256color_s_mc5[] = "\033[5i"; +static char screen_xterm_256color_s_rs1[] = "\033c"; +static char screen_xterm_256color_s_rs2[] = "\033[!p\033[?3;4l\033[4l\033>"; +static char screen_xterm_256color_s_rc[] = "\0338"; +static char screen_xterm_256color_s_vpa[] = "\033[%i%p1%dd"; +static char screen_xterm_256color_s_sc[] = "\0337"; +static char screen_xterm_256color_s_ind[] = "\012"; +static char screen_xterm_256color_s_ri[] = "\033M"; +static char screen_xterm_256color_s_sgr[] = "%?%p9%t\033(0%e\033(B%;\033[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p5%t;2%;m"; +static char screen_xterm_256color_s_hts[] = "\033H"; +static char screen_xterm_256color_s_ht[] = "\011"; +static char screen_xterm_256color_s_kb2[] = "\033OE"; +static char screen_xterm_256color_s_acsc[] = "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~"; +static char screen_xterm_256color_s_kcbt[] = "\033[Z"; +static char screen_xterm_256color_s_smam[] = "\033[?7h"; +static char screen_xterm_256color_s_rmam[] = "\033[?7l"; +static char screen_xterm_256color_s_kend[] = "\033[4~"; +static char screen_xterm_256color_s_kent[] = "\033OM"; +static char screen_xterm_256color_s_kDC[] = "\033[3;2~"; +static char screen_xterm_256color_s_kEND[] = "\033[1;2F"; +static char screen_xterm_256color_s_kHOM[] = "\033[1;2H"; +static char screen_xterm_256color_s_kLFT[] = "\033[1;2D"; +static char screen_xterm_256color_s_kRIT[] = "\033[1;2C"; +static char screen_xterm_256color_s_kf11[] = "\033[23~"; +static char screen_xterm_256color_s_kf12[] = "\033[24~"; +static char screen_xterm_256color_s_kf13[] = "\033[1;2P"; +static char screen_xterm_256color_s_kf14[] = "\033[1;2Q"; +static char screen_xterm_256color_s_kf15[] = "\033[1;2R"; +static char screen_xterm_256color_s_kf16[] = "\033[1;2S"; +static char screen_xterm_256color_s_kf17[] = "\033[15;2~"; +static char screen_xterm_256color_s_kf18[] = "\033[17;2~"; +static char screen_xterm_256color_s_kf19[] = "\033[18;2~"; +static char screen_xterm_256color_s_kf20[] = "\033[19;2~"; +static char screen_xterm_256color_s_kf21[] = "\033[20;2~"; +static char screen_xterm_256color_s_kf22[] = "\033[21;2~"; +static char screen_xterm_256color_s_kf23[] = "\033[23;2~"; +static char screen_xterm_256color_s_kf24[] = "\033[24;2~"; +static char screen_xterm_256color_s_kf25[] = "\033[1;5P"; +static char screen_xterm_256color_s_kf26[] = "\033[1;5Q"; +static char screen_xterm_256color_s_kf27[] = "\033[1;5R"; +static char screen_xterm_256color_s_kf28[] = "\033[1;5S"; +static char screen_xterm_256color_s_kf29[] = "\033[15;5~"; +static char screen_xterm_256color_s_kf30[] = "\033[17;5~"; +static char screen_xterm_256color_s_kf31[] = "\033[18;5~"; +static char screen_xterm_256color_s_kf32[] = "\033[19;5~"; +static char screen_xterm_256color_s_kf33[] = "\033[20;5~"; +static char screen_xterm_256color_s_kf34[] = "\033[21;5~"; +static char screen_xterm_256color_s_kf35[] = "\033[23;5~"; +static char screen_xterm_256color_s_kf36[] = "\033[24;5~"; +static char screen_xterm_256color_s_kf37[] = "\033[1;6P"; +static char screen_xterm_256color_s_kf38[] = "\033[1;6Q"; +static char screen_xterm_256color_s_kf39[] = "\033[1;6R"; +static char screen_xterm_256color_s_kf40[] = "\033[1;6S"; +static char screen_xterm_256color_s_kf41[] = "\033[15;6~"; +static char screen_xterm_256color_s_kf42[] = "\033[17;6~"; +static char screen_xterm_256color_s_kf43[] = "\033[18;6~"; +static char screen_xterm_256color_s_kf44[] = "\033[19;6~"; +static char screen_xterm_256color_s_kf45[] = "\033[20;6~"; +static char screen_xterm_256color_s_kf46[] = "\033[21;6~"; +static char screen_xterm_256color_s_kf47[] = "\033[23;6~"; +static char screen_xterm_256color_s_kf48[] = "\033[24;6~"; +static char screen_xterm_256color_s_kf49[] = "\033[1;3P"; +static char screen_xterm_256color_s_kf50[] = "\033[1;3Q"; +static char screen_xterm_256color_s_kf51[] = "\033[1;3R"; +static char screen_xterm_256color_s_kf52[] = "\033[1;3S"; +static char screen_xterm_256color_s_kf53[] = "\033[15;3~"; +static char screen_xterm_256color_s_kf54[] = "\033[17;3~"; +static char screen_xterm_256color_s_kf55[] = "\033[18;3~"; +static char screen_xterm_256color_s_kf56[] = "\033[19;3~"; +static char screen_xterm_256color_s_kf57[] = "\033[20;3~"; +static char screen_xterm_256color_s_kf58[] = "\033[21;3~"; +static char screen_xterm_256color_s_kf59[] = "\033[23;3~"; +static char screen_xterm_256color_s_kf60[] = "\033[24;3~"; +static char screen_xterm_256color_s_kf61[] = "\033[1;4P"; +static char screen_xterm_256color_s_kf62[] = "\033[1;4Q"; +static char screen_xterm_256color_s_kf63[] = "\033[1;4R"; +static char screen_xterm_256color_s_el1[] = "\033[1K"; +static char screen_xterm_256color_s_u6[] = "\033[%i%d;%dR"; +static char screen_xterm_256color_s_u7[] = "\033[6n"; +static char screen_xterm_256color_s_u8[] = "\033[?%[;0123456789]c"; +static char screen_xterm_256color_s_u9[] = "\033[c"; +static char screen_xterm_256color_s_op[] = "\033[39;49m"; +static char screen_xterm_256color_s_kmous[] = "\033[M"; +static char screen_xterm_256color_s_setaf[] = "\033[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"; +static char screen_xterm_256color_s_setab[] = "\033[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"; + +static char screen_xterm_256color_bool_data[] = { + /* 0: bw */ TRUE, + /* 1: am */ TRUE, + /* 2: xsb */ FALSE, + /* 3: xhp */ FALSE, + /* 4: xenl */ TRUE, + /* 5: eo */ FALSE, + /* 6: gn */ FALSE, + /* 7: hc */ FALSE, + /* 8: km */ TRUE, + /* 9: hs */ FALSE, + /* 10: in */ FALSE, + /* 11: da */ FALSE, + /* 12: db */ FALSE, + /* 13: mir */ TRUE, + /* 14: msgr */ TRUE, + /* 15: os */ FALSE, + /* 16: eslok */ FALSE, + /* 17: xt */ FALSE, + /* 18: hz */ FALSE, + /* 19: ul */ FALSE, + /* 20: xon */ FALSE, + /* 21: nxon */ FALSE, + /* 22: mc5i */ TRUE, + /* 23: chts */ FALSE, + /* 24: nrrmc */ FALSE, + /* 25: npc */ TRUE, + /* 26: ndscr */ FALSE, + /* 27: ccc */ FALSE, + /* 28: bce */ FALSE, + /* 29: hls */ FALSE, + /* 30: xhpa */ FALSE, + /* 31: crxm */ FALSE, + /* 32: daisy */ FALSE, + /* 33: xvpa */ FALSE, + /* 34: sam */ FALSE, + /* 35: cpix */ FALSE, + /* 36: lpix */ FALSE, + /* 37: OTbs */ TRUE, + /* 38: OTns */ FALSE, + /* 39: OTnc */ FALSE, + /* 40: OTMT */ FALSE, + /* 41: OTNL */ FALSE, + /* 42: OTpt */ FALSE, + /* 43: OTxr */ FALSE, +}; +static NCURSES_INT2 screen_xterm_256color_number_data[] = { + /* 0: cols */ 80, + /* 1: it */ 8, + /* 2: lines */ 24, + /* 3: lm */ ABSENT_NUMERIC, + /* 4: xmc */ ABSENT_NUMERIC, + /* 5: pb */ ABSENT_NUMERIC, + /* 6: vt */ ABSENT_NUMERIC, + /* 7: wsl */ ABSENT_NUMERIC, + /* 8: nlab */ ABSENT_NUMERIC, + /* 9: lh */ ABSENT_NUMERIC, + /* 10: lw */ ABSENT_NUMERIC, + /* 11: ma */ ABSENT_NUMERIC, + /* 12: wnum */ ABSENT_NUMERIC, + /* 13: colors */ 256, + /* 14: pairs */ 32767, + /* 15: ncv */ ABSENT_NUMERIC, + /* 16: bufsz */ ABSENT_NUMERIC, + /* 17: spinv */ ABSENT_NUMERIC, + /* 18: spinh */ ABSENT_NUMERIC, + /* 19: maddr */ ABSENT_NUMERIC, + /* 20: mjump */ ABSENT_NUMERIC, + /* 21: mcs */ ABSENT_NUMERIC, + /* 22: mls */ ABSENT_NUMERIC, + /* 23: npins */ ABSENT_NUMERIC, + /* 24: orc */ ABSENT_NUMERIC, + /* 25: orl */ ABSENT_NUMERIC, + /* 26: orhi */ ABSENT_NUMERIC, + /* 27: orvi */ ABSENT_NUMERIC, + /* 28: cps */ ABSENT_NUMERIC, + /* 29: widcs */ ABSENT_NUMERIC, + /* 30: btns */ ABSENT_NUMERIC, + /* 31: bitwin */ ABSENT_NUMERIC, + /* 32: bitype */ ABSENT_NUMERIC, + /* 33: OTug */ ABSENT_NUMERIC, + /* 34: OTdC */ ABSENT_NUMERIC, + /* 35: OTdN */ ABSENT_NUMERIC, + /* 36: OTdB */ ABSENT_NUMERIC, + /* 37: OTdT */ ABSENT_NUMERIC, + /* 38: OTkn */ ABSENT_NUMERIC, +}; +static char * screen_xterm_256color_string_data[] = { + /* 0: cbt */ screen_xterm_256color_s_cbt, + /* 1: bel */ screen_xterm_256color_s_bel, + /* 2: cr */ screen_xterm_256color_s_cr, + /* 3: csr */ screen_xterm_256color_s_csr, + /* 4: tbc */ screen_xterm_256color_s_tbc, + /* 5: clear */ screen_xterm_256color_s_clear, + /* 6: el */ screen_xterm_256color_s_el, + /* 7: ed */ screen_xterm_256color_s_ed, + /* 8: hpa */ screen_xterm_256color_s_hpa, + /* 9: cmdch */ ABSENT_STRING, + /* 10: cup */ screen_xterm_256color_s_cup, + /* 11: cud1 */ screen_xterm_256color_s_cud1, + /* 12: home */ screen_xterm_256color_s_home, + /* 13: civis */ screen_xterm_256color_s_civis, + /* 14: cub1 */ screen_xterm_256color_s_cub1, + /* 15: mrcup */ ABSENT_STRING, + /* 16: cnorm */ screen_xterm_256color_s_cnorm, + /* 17: cuf1 */ screen_xterm_256color_s_cuf1, + /* 18: ll */ ABSENT_STRING, + /* 19: cuu1 */ screen_xterm_256color_s_cuu1, + /* 20: cvvis */ screen_xterm_256color_s_cvvis, + /* 21: dch1 */ screen_xterm_256color_s_dch1, + /* 22: dl1 */ screen_xterm_256color_s_dl1, + /* 23: dsl */ ABSENT_STRING, + /* 24: hd */ ABSENT_STRING, + /* 25: smacs */ screen_xterm_256color_s_smacs, + /* 26: blink */ screen_xterm_256color_s_blink, + /* 27: bold */ screen_xterm_256color_s_bold, + /* 28: smcup */ screen_xterm_256color_s_smcup, + /* 29: smdc */ ABSENT_STRING, + /* 30: dim */ screen_xterm_256color_s_dim, + /* 31: smir */ screen_xterm_256color_s_smir, + /* 32: invis */ ABSENT_STRING, + /* 33: prot */ ABSENT_STRING, + /* 34: rev */ screen_xterm_256color_s_rev, + /* 35: smso */ screen_xterm_256color_s_smso, + /* 36: smul */ screen_xterm_256color_s_smul, + /* 37: ech */ screen_xterm_256color_s_ech, + /* 38: rmacs */ screen_xterm_256color_s_rmacs, + /* 39: sgr0 */ screen_xterm_256color_s_sgr0, + /* 40: rmcup */ screen_xterm_256color_s_rmcup, + /* 41: rmdc */ ABSENT_STRING, + /* 42: rmir */ screen_xterm_256color_s_rmir, + /* 43: rmso */ screen_xterm_256color_s_rmso, + /* 44: rmul */ screen_xterm_256color_s_rmul, + /* 45: flash */ screen_xterm_256color_s_flash, + /* 46: ff */ ABSENT_STRING, + /* 47: fsl */ ABSENT_STRING, + /* 48: is1 */ ABSENT_STRING, + /* 49: is2 */ screen_xterm_256color_s_is2, + /* 50: is3 */ ABSENT_STRING, + /* 51: if */ ABSENT_STRING, + /* 52: ich1 */ ABSENT_STRING, + /* 53: il1 */ screen_xterm_256color_s_il1, + /* 54: ip */ ABSENT_STRING, + /* 55: kbs */ screen_xterm_256color_s_kbs, + /* 56: ktbc */ ABSENT_STRING, + /* 57: kclr */ ABSENT_STRING, + /* 58: kctab */ ABSENT_STRING, + /* 59: kdch1 */ screen_xterm_256color_s_kdch1, + /* 60: kdl1 */ ABSENT_STRING, + /* 61: kcud1 */ screen_xterm_256color_s_kcud1, + /* 62: krmir */ ABSENT_STRING, + /* 63: kel */ ABSENT_STRING, + /* 64: ked */ ABSENT_STRING, + /* 65: kf0 */ ABSENT_STRING, + /* 66: kf1 */ screen_xterm_256color_s_kf1, + /* 67: kf10 */ screen_xterm_256color_s_kf10, + /* 68: kf2 */ screen_xterm_256color_s_kf2, + /* 69: kf3 */ screen_xterm_256color_s_kf3, + /* 70: kf4 */ screen_xterm_256color_s_kf4, + /* 71: kf5 */ screen_xterm_256color_s_kf5, + /* 72: kf6 */ screen_xterm_256color_s_kf6, + /* 73: kf7 */ screen_xterm_256color_s_kf7, + /* 74: kf8 */ screen_xterm_256color_s_kf8, + /* 75: kf9 */ screen_xterm_256color_s_kf9, + /* 76: khome */ screen_xterm_256color_s_khome, + /* 77: kich1 */ screen_xterm_256color_s_kich1, + /* 78: kil1 */ ABSENT_STRING, + /* 79: kcub1 */ screen_xterm_256color_s_kcub1, + /* 80: kll */ ABSENT_STRING, + /* 81: knp */ screen_xterm_256color_s_knp, + /* 82: kpp */ screen_xterm_256color_s_kpp, + /* 83: kcuf1 */ screen_xterm_256color_s_kcuf1, + /* 84: kind */ screen_xterm_256color_s_kind, + /* 85: kri */ screen_xterm_256color_s_kri, + /* 86: khts */ ABSENT_STRING, + /* 87: kcuu1 */ screen_xterm_256color_s_kcuu1, + /* 88: rmkx */ screen_xterm_256color_s_rmkx, + /* 89: smkx */ screen_xterm_256color_s_smkx, + /* 90: lf0 */ ABSENT_STRING, + /* 91: lf1 */ ABSENT_STRING, + /* 92: lf10 */ ABSENT_STRING, + /* 93: lf2 */ ABSENT_STRING, + /* 94: lf3 */ ABSENT_STRING, + /* 95: lf4 */ ABSENT_STRING, + /* 96: lf5 */ ABSENT_STRING, + /* 97: lf6 */ ABSENT_STRING, + /* 98: lf7 */ ABSENT_STRING, + /* 99: lf8 */ ABSENT_STRING, + /* 100: lf9 */ ABSENT_STRING, + /* 101: rmm */ screen_xterm_256color_s_rmm, + /* 102: smm */ screen_xterm_256color_s_smm, + /* 103: nel */ ABSENT_STRING, + /* 104: pad */ ABSENT_STRING, + /* 105: dch */ screen_xterm_256color_s_dch, + /* 106: dl */ screen_xterm_256color_s_dl, + /* 107: cud */ screen_xterm_256color_s_cud, + /* 108: ich */ screen_xterm_256color_s_ich, + /* 109: indn */ screen_xterm_256color_s_indn, + /* 110: il */ screen_xterm_256color_s_il, + /* 111: cub */ screen_xterm_256color_s_cub, + /* 112: cuf */ screen_xterm_256color_s_cuf, + /* 113: rin */ screen_xterm_256color_s_rin, + /* 114: cuu */ screen_xterm_256color_s_cuu, + /* 115: pfkey */ ABSENT_STRING, + /* 116: pfloc */ ABSENT_STRING, + /* 117: pfx */ ABSENT_STRING, + /* 118: mc0 */ screen_xterm_256color_s_mc0, + /* 119: mc4 */ screen_xterm_256color_s_mc4, + /* 120: mc5 */ screen_xterm_256color_s_mc5, + /* 121: rep */ ABSENT_STRING, + /* 122: rs1 */ screen_xterm_256color_s_rs1, + /* 123: rs2 */ screen_xterm_256color_s_rs2, + /* 124: rs3 */ ABSENT_STRING, + /* 125: rf */ ABSENT_STRING, + /* 126: rc */ screen_xterm_256color_s_rc, + /* 127: vpa */ screen_xterm_256color_s_vpa, + /* 128: sc */ screen_xterm_256color_s_sc, + /* 129: ind */ screen_xterm_256color_s_ind, + /* 130: ri */ screen_xterm_256color_s_ri, + /* 131: sgr */ screen_xterm_256color_s_sgr, + /* 132: hts */ screen_xterm_256color_s_hts, + /* 133: wind */ ABSENT_STRING, + /* 134: ht */ screen_xterm_256color_s_ht, + /* 135: tsl */ ABSENT_STRING, + /* 136: uc */ ABSENT_STRING, + /* 137: hu */ ABSENT_STRING, + /* 138: iprog */ ABSENT_STRING, + /* 139: ka1 */ ABSENT_STRING, + /* 140: ka3 */ ABSENT_STRING, + /* 141: kb2 */ screen_xterm_256color_s_kb2, + /* 142: kc1 */ ABSENT_STRING, + /* 143: kc3 */ ABSENT_STRING, + /* 144: mc5p */ ABSENT_STRING, + /* 145: rmp */ ABSENT_STRING, + /* 146: acsc */ screen_xterm_256color_s_acsc, + /* 147: pln */ ABSENT_STRING, + /* 148: kcbt */ screen_xterm_256color_s_kcbt, + /* 149: smxon */ ABSENT_STRING, + /* 150: rmxon */ ABSENT_STRING, + /* 151: smam */ screen_xterm_256color_s_smam, + /* 152: rmam */ screen_xterm_256color_s_rmam, + /* 153: xonc */ ABSENT_STRING, + /* 154: xoffc */ ABSENT_STRING, + /* 155: enacs */ ABSENT_STRING, + /* 156: smln */ ABSENT_STRING, + /* 157: rmln */ ABSENT_STRING, + /* 158: kbeg */ ABSENT_STRING, + /* 159: kcan */ ABSENT_STRING, + /* 160: kclo */ ABSENT_STRING, + /* 161: kcmd */ ABSENT_STRING, + /* 162: kcpy */ ABSENT_STRING, + /* 163: kcrt */ ABSENT_STRING, + /* 164: kend */ screen_xterm_256color_s_kend, + /* 165: kent */ screen_xterm_256color_s_kent, + /* 166: kext */ ABSENT_STRING, + /* 167: kfnd */ ABSENT_STRING, + /* 168: khlp */ ABSENT_STRING, + /* 169: kmrk */ ABSENT_STRING, + /* 170: kmsg */ ABSENT_STRING, + /* 171: kmov */ ABSENT_STRING, + /* 172: knxt */ ABSENT_STRING, + /* 173: kopn */ ABSENT_STRING, + /* 174: kopt */ ABSENT_STRING, + /* 175: kprv */ ABSENT_STRING, + /* 176: kprt */ ABSENT_STRING, + /* 177: krdo */ ABSENT_STRING, + /* 178: kref */ ABSENT_STRING, + /* 179: krfr */ ABSENT_STRING, + /* 180: krpl */ ABSENT_STRING, + /* 181: krst */ ABSENT_STRING, + /* 182: kres */ ABSENT_STRING, + /* 183: ksav */ ABSENT_STRING, + /* 184: kspd */ ABSENT_STRING, + /* 185: kund */ ABSENT_STRING, + /* 186: kBEG */ ABSENT_STRING, + /* 187: kCAN */ ABSENT_STRING, + /* 188: kCMD */ ABSENT_STRING, + /* 189: kCPY */ ABSENT_STRING, + /* 190: kCRT */ ABSENT_STRING, + /* 191: kDC */ screen_xterm_256color_s_kDC, + /* 192: kDL */ ABSENT_STRING, + /* 193: kslt */ ABSENT_STRING, + /* 194: kEND */ screen_xterm_256color_s_kEND, + /* 195: kEOL */ ABSENT_STRING, + /* 196: kEXT */ ABSENT_STRING, + /* 197: kFND */ ABSENT_STRING, + /* 198: kHLP */ ABSENT_STRING, + /* 199: kHOM */ screen_xterm_256color_s_kHOM, + /* 200: kIC */ ABSENT_STRING, + /* 201: kLFT */ screen_xterm_256color_s_kLFT, + /* 202: kMSG */ ABSENT_STRING, + /* 203: kMOV */ ABSENT_STRING, + /* 204: kNXT */ ABSENT_STRING, + /* 205: kOPT */ ABSENT_STRING, + /* 206: kPRV */ ABSENT_STRING, + /* 207: kPRT */ ABSENT_STRING, + /* 208: kRDO */ ABSENT_STRING, + /* 209: kRPL */ ABSENT_STRING, + /* 210: kRIT */ screen_xterm_256color_s_kRIT, + /* 211: kRES */ ABSENT_STRING, + /* 212: kSAV */ ABSENT_STRING, + /* 213: kSPD */ ABSENT_STRING, + /* 214: kUND */ ABSENT_STRING, + /* 215: rfi */ ABSENT_STRING, + /* 216: kf11 */ screen_xterm_256color_s_kf11, + /* 217: kf12 */ screen_xterm_256color_s_kf12, + /* 218: kf13 */ screen_xterm_256color_s_kf13, + /* 219: kf14 */ screen_xterm_256color_s_kf14, + /* 220: kf15 */ screen_xterm_256color_s_kf15, + /* 221: kf16 */ screen_xterm_256color_s_kf16, + /* 222: kf17 */ screen_xterm_256color_s_kf17, + /* 223: kf18 */ screen_xterm_256color_s_kf18, + /* 224: kf19 */ screen_xterm_256color_s_kf19, + /* 225: kf20 */ screen_xterm_256color_s_kf20, + /* 226: kf21 */ screen_xterm_256color_s_kf21, + /* 227: kf22 */ screen_xterm_256color_s_kf22, + /* 228: kf23 */ screen_xterm_256color_s_kf23, + /* 229: kf24 */ screen_xterm_256color_s_kf24, + /* 230: kf25 */ screen_xterm_256color_s_kf25, + /* 231: kf26 */ screen_xterm_256color_s_kf26, + /* 232: kf27 */ screen_xterm_256color_s_kf27, + /* 233: kf28 */ screen_xterm_256color_s_kf28, + /* 234: kf29 */ screen_xterm_256color_s_kf29, + /* 235: kf30 */ screen_xterm_256color_s_kf30, + /* 236: kf31 */ screen_xterm_256color_s_kf31, + /* 237: kf32 */ screen_xterm_256color_s_kf32, + /* 238: kf33 */ screen_xterm_256color_s_kf33, + /* 239: kf34 */ screen_xterm_256color_s_kf34, + /* 240: kf35 */ screen_xterm_256color_s_kf35, + /* 241: kf36 */ screen_xterm_256color_s_kf36, + /* 242: kf37 */ screen_xterm_256color_s_kf37, + /* 243: kf38 */ screen_xterm_256color_s_kf38, + /* 244: kf39 */ screen_xterm_256color_s_kf39, + /* 245: kf40 */ screen_xterm_256color_s_kf40, + /* 246: kf41 */ screen_xterm_256color_s_kf41, + /* 247: kf42 */ screen_xterm_256color_s_kf42, + /* 248: kf43 */ screen_xterm_256color_s_kf43, + /* 249: kf44 */ screen_xterm_256color_s_kf44, + /* 250: kf45 */ screen_xterm_256color_s_kf45, + /* 251: kf46 */ screen_xterm_256color_s_kf46, + /* 252: kf47 */ screen_xterm_256color_s_kf47, + /* 253: kf48 */ screen_xterm_256color_s_kf48, + /* 254: kf49 */ screen_xterm_256color_s_kf49, + /* 255: kf50 */ screen_xterm_256color_s_kf50, + /* 256: kf51 */ screen_xterm_256color_s_kf51, + /* 257: kf52 */ screen_xterm_256color_s_kf52, + /* 258: kf53 */ screen_xterm_256color_s_kf53, + /* 259: kf54 */ screen_xterm_256color_s_kf54, + /* 260: kf55 */ screen_xterm_256color_s_kf55, + /* 261: kf56 */ screen_xterm_256color_s_kf56, + /* 262: kf57 */ screen_xterm_256color_s_kf57, + /* 263: kf58 */ screen_xterm_256color_s_kf58, + /* 264: kf59 */ screen_xterm_256color_s_kf59, + /* 265: kf60 */ screen_xterm_256color_s_kf60, + /* 266: kf61 */ screen_xterm_256color_s_kf61, + /* 267: kf62 */ screen_xterm_256color_s_kf62, + /* 268: kf63 */ screen_xterm_256color_s_kf63, + /* 269: el1 */ screen_xterm_256color_s_el1, + /* 270: mgc */ ABSENT_STRING, + /* 271: smgl */ ABSENT_STRING, + /* 272: smgr */ ABSENT_STRING, + /* 273: fln */ ABSENT_STRING, + /* 274: sclk */ ABSENT_STRING, + /* 275: dclk */ ABSENT_STRING, + /* 276: rmclk */ ABSENT_STRING, + /* 277: cwin */ ABSENT_STRING, + /* 278: wingo */ ABSENT_STRING, + /* 279: hup */ ABSENT_STRING, + /* 280: dial */ ABSENT_STRING, + /* 281: qdial */ ABSENT_STRING, + /* 282: tone */ ABSENT_STRING, + /* 283: pulse */ ABSENT_STRING, + /* 284: hook */ ABSENT_STRING, + /* 285: pause */ ABSENT_STRING, + /* 286: wait */ ABSENT_STRING, + /* 287: u0 */ ABSENT_STRING, + /* 288: u1 */ ABSENT_STRING, + /* 289: u2 */ ABSENT_STRING, + /* 290: u3 */ ABSENT_STRING, + /* 291: u4 */ ABSENT_STRING, + /* 292: u5 */ ABSENT_STRING, + /* 293: u6 */ screen_xterm_256color_s_u6, + /* 294: u7 */ screen_xterm_256color_s_u7, + /* 295: u8 */ screen_xterm_256color_s_u8, + /* 296: u9 */ screen_xterm_256color_s_u9, + /* 297: op */ screen_xterm_256color_s_op, + /* 298: oc */ ABSENT_STRING, + /* 299: initc */ ABSENT_STRING, + /* 300: initp */ ABSENT_STRING, + /* 301: scp */ ABSENT_STRING, + /* 302: setf */ ABSENT_STRING, + /* 303: setb */ ABSENT_STRING, + /* 304: cpi */ ABSENT_STRING, + /* 305: lpi */ ABSENT_STRING, + /* 306: chr */ ABSENT_STRING, + /* 307: cvr */ ABSENT_STRING, + /* 308: defc */ ABSENT_STRING, + /* 309: swidm */ ABSENT_STRING, + /* 310: sdrfq */ ABSENT_STRING, + /* 311: sitm */ ABSENT_STRING, + /* 312: slm */ ABSENT_STRING, + /* 313: smicm */ ABSENT_STRING, + /* 314: snlq */ ABSENT_STRING, + /* 315: snrmq */ ABSENT_STRING, + /* 316: sshm */ ABSENT_STRING, + /* 317: ssubm */ ABSENT_STRING, + /* 318: ssupm */ ABSENT_STRING, + /* 319: sum */ ABSENT_STRING, + /* 320: rwidm */ ABSENT_STRING, + /* 321: ritm */ ABSENT_STRING, + /* 322: rlm */ ABSENT_STRING, + /* 323: rmicm */ ABSENT_STRING, + /* 324: rshm */ ABSENT_STRING, + /* 325: rsubm */ ABSENT_STRING, + /* 326: rsupm */ ABSENT_STRING, + /* 327: rum */ ABSENT_STRING, + /* 328: mhpa */ ABSENT_STRING, + /* 329: mcud1 */ ABSENT_STRING, + /* 330: mcub1 */ ABSENT_STRING, + /* 331: mcuf1 */ ABSENT_STRING, + /* 332: mvpa */ ABSENT_STRING, + /* 333: mcuu1 */ ABSENT_STRING, + /* 334: porder */ ABSENT_STRING, + /* 335: mcud */ ABSENT_STRING, + /* 336: mcub */ ABSENT_STRING, + /* 337: mcuf */ ABSENT_STRING, + /* 338: mcuu */ ABSENT_STRING, + /* 339: scs */ ABSENT_STRING, + /* 340: smgb */ ABSENT_STRING, + /* 341: smgbp */ ABSENT_STRING, + /* 342: smglp */ ABSENT_STRING, + /* 343: smgrp */ ABSENT_STRING, + /* 344: smgt */ ABSENT_STRING, + /* 345: smgtp */ ABSENT_STRING, + /* 346: sbim */ ABSENT_STRING, + /* 347: scsd */ ABSENT_STRING, + /* 348: rbim */ ABSENT_STRING, + /* 349: rcsd */ ABSENT_STRING, + /* 350: subcs */ ABSENT_STRING, + /* 351: supcs */ ABSENT_STRING, + /* 352: docr */ ABSENT_STRING, + /* 353: zerom */ ABSENT_STRING, + /* 354: csnm */ ABSENT_STRING, + /* 355: kmous */ screen_xterm_256color_s_kmous, + /* 356: minfo */ ABSENT_STRING, + /* 357: reqmp */ ABSENT_STRING, + /* 358: getm */ ABSENT_STRING, + /* 359: setaf */ screen_xterm_256color_s_setaf, + /* 360: setab */ screen_xterm_256color_s_setab, + /* 361: pfxl */ ABSENT_STRING, + /* 362: devt */ ABSENT_STRING, + /* 363: csin */ ABSENT_STRING, + /* 364: s0ds */ ABSENT_STRING, + /* 365: s1ds */ ABSENT_STRING, + /* 366: s2ds */ ABSENT_STRING, + /* 367: s3ds */ ABSENT_STRING, + /* 368: smglr */ ABSENT_STRING, + /* 369: smgtb */ ABSENT_STRING, + /* 370: birep */ ABSENT_STRING, + /* 371: binel */ ABSENT_STRING, + /* 372: bicr */ ABSENT_STRING, + /* 373: colornm */ ABSENT_STRING, + /* 374: defbi */ ABSENT_STRING, + /* 375: endbi */ ABSENT_STRING, + /* 376: setcolor */ ABSENT_STRING, + /* 377: slines */ ABSENT_STRING, + /* 378: dispc */ ABSENT_STRING, + /* 379: smpch */ ABSENT_STRING, + /* 380: rmpch */ ABSENT_STRING, + /* 381: smsc */ ABSENT_STRING, + /* 382: rmsc */ ABSENT_STRING, + /* 383: pctrm */ ABSENT_STRING, + /* 384: scesc */ ABSENT_STRING, + /* 385: scesa */ ABSENT_STRING, + /* 386: ehhlm */ ABSENT_STRING, + /* 387: elhlm */ ABSENT_STRING, + /* 388: elohlm */ ABSENT_STRING, + /* 389: erhlm */ ABSENT_STRING, + /* 390: ethlm */ ABSENT_STRING, + /* 391: evhlm */ ABSENT_STRING, + /* 392: sgr1 */ ABSENT_STRING, + /* 393: slength */ ABSENT_STRING, + /* 394: OTi2 */ ABSENT_STRING, + /* 395: OTrs */ ABSENT_STRING, + /* 396: OTnl */ ABSENT_STRING, + /* 397: OTbc */ ABSENT_STRING, + /* 398: OTko */ ABSENT_STRING, + /* 399: OTma */ ABSENT_STRING, + /* 400: OTG2 */ ABSENT_STRING, + /* 401: OTG3 */ ABSENT_STRING, + /* 402: OTG1 */ ABSENT_STRING, + /* 403: OTG4 */ ABSENT_STRING, + /* 404: OTGR */ ABSENT_STRING, + /* 405: OTGL */ ABSENT_STRING, + /* 406: OTGU */ ABSENT_STRING, + /* 407: OTGD */ ABSENT_STRING, + /* 408: OTGH */ ABSENT_STRING, + /* 409: OTGV */ ABSENT_STRING, + /* 410: OTGC */ ABSENT_STRING, + /* 411: meml */ ABSENT_STRING, + /* 412: memu */ ABSENT_STRING, + /* 413: box1 */ ABSENT_STRING, +}; +static const TERMTYPE2 fallbacks[10] = +{ + /* linux */ + { + linux_alias_data, + (char *)0, /* pointer to string table */ + linux_bool_data, + linux_number_data, + linux_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* rxvt */ + { + rxvt_alias_data, + (char *)0, /* pointer to string table */ + rxvt_bool_data, + rxvt_number_data, + rxvt_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* vt100 */ + { + vt100_alias_data, + (char *)0, /* pointer to string table */ + vt100_bool_data, + vt100_number_data, + vt100_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* xterm */ + { + xterm_alias_data, + (char *)0, /* pointer to string table */ + xterm_bool_data, + xterm_number_data, + xterm_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* xterm-256color */ + { + xterm_256color_alias_data, + (char *)0, /* pointer to string table */ + xterm_256color_bool_data, + xterm_256color_number_data, + xterm_256color_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen */ + { + screen_alias_data, + (char *)0, /* pointer to string table */ + screen_bool_data, + screen_number_data, + screen_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen.linux */ + { + screen_linux_alias_data, + (char *)0, /* pointer to string table */ + screen_linux_bool_data, + screen_linux_number_data, + screen_linux_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen.rxvt */ + { + screen_rxvt_alias_data, + (char *)0, /* pointer to string table */ + screen_rxvt_bool_data, + screen_rxvt_number_data, + screen_rxvt_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen.xterm-new */ + { + screen_xterm_xfree86_alias_data, + (char *)0, /* pointer to string table */ + screen_xterm_xfree86_bool_data, + screen_xterm_xfree86_number_data, + screen_xterm_xfree86_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +, /* screen.xterm-256color */ + { + screen_xterm_256color_alias_data, + (char *)0, /* pointer to string table */ + screen_xterm_256color_bool_data, + screen_xterm_256color_number_data, + screen_xterm_256color_string_data, +#if NCURSES_XNAMES + (char *)0, /* pointer to extended string table */ + (char **)0, /* ...corresponding names */ + 44, /* count total Booleans */ + 39, /* count total Numbers */ + 414, /* count total Strings */ + 0, /* count extensions to Booleans */ + 0, /* count extensions to Numbers */ + 0, /* count extensions to Strings */ +#endif /* NCURSES_XNAMES */ + } +}; + +NCURSES_EXPORT(const TERMTYPE2 *) +_nc_fallback2 (const char *name GCC_UNUSED) +{ + const TERMTYPE2 *tp; + + for (tp = fallbacks; + tp < fallbacks + sizeof(fallbacks)/sizeof(TERMTYPE2); + tp++) { + if (_nc_name_match(tp->term_names, name, "|")) { + return(tp); + } + } + return((const TERMTYPE2 *)0); +} + +#if NCURSES_EXT_NUMBERS +#undef _nc_fallback + +/* + * This entrypoint is used by tack. + */ +NCURSES_EXPORT(const TERMTYPE *) +_nc_fallback (const char *name) +{ + const TERMTYPE2 *tp = _nc_fallback2(name); + const TERMTYPE *result = 0; + if (tp != 0) { + static TERMTYPE temp; + _nc_export_termtype2(&temp, tp); + result = &temp; + } + return result; +} +#endif diff --git a/contrib/depends/patches/protobuf/visibility.patch b/contrib/depends/patches/protobuf/visibility.patch new file mode 100644 index 0000000..e66d596 --- /dev/null +++ b/contrib/depends/patches/protobuf/visibility.patch @@ -0,0 +1,159 @@ +--- src/google/protobuf/descriptor.cc.O 2018-07-30 22:16:10.000000000 +0000 ++++ src/google/protobuf/descriptor.cc 2022-05-06 13:38:14.827309092 +0000 +@@ -32,6 +32,9 @@ + // Based on original Protocol Buffers design by + // Sanjay Ghemawat, Jeff Dean, and others. + ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility push(hidden) ++#endif + #include + #include + #include +@@ -7274,3 +7277,6 @@ + + } // namespace protobuf + } // namespace google ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility pop ++#endif +--- src/google/protobuf/extension_set.cc.O 2018-07-23 20:56:42.000000000 +0000 ++++ src/google/protobuf/extension_set.cc 2022-05-06 14:48:55.369877050 +0000 +@@ -32,6 +32,9 @@ + // Based on original Protocol Buffers design by + // Sanjay Ghemawat, Jeff Dean, and others. + ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility push(hidden) ++#endif + #include + #include + #include +@@ -1914,3 +1917,6 @@ + } // namespace internal + } // namespace protobuf + } // namespace google ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility pop ++#endif +--- src/google/protobuf/extension_set_heavy.cc.O 2018-07-30 22:16:10.000000000 +0000 ++++ src/google/protobuf/extension_set_heavy.cc 2022-05-06 14:14:27.847320946 +0000 +@@ -35,6 +35,10 @@ + // Contains methods defined in extension_set.h which cannot be part of the + // lite library because they use descriptors or reflection. + ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility push(hidden) ++#endif ++ + #include + #include + #include +@@ -814,3 +818,6 @@ + } // namespace internal + } // namespace protobuf + } // namespace google ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility pop ++#endif +--- src/google/protobuf/generated_message_reflection.cc.O 2018-07-23 20:56:42.000000000 +0000 ++++ src/google/protobuf/generated_message_reflection.cc 2022-05-06 13:38:49.655540772 +0000 +@@ -32,6 +32,9 @@ + // Based on original Protocol Buffers design by + // Sanjay Ghemawat, Jeff Dean, and others. + ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility push(hidden) ++#endif + #include + #include + +@@ -2420,3 +2423,6 @@ + } // namespace internal + } // namespace protobuf + } // namespace google ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility pop ++#endif +--- src/google/protobuf/map_field.cc.O 2018-07-23 20:56:42.000000000 +0000 ++++ src/google/protobuf/map_field.cc 2022-05-06 13:34:44.913905697 +0000 +@@ -28,6 +28,10 @@ + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility push(hidden) ++#endif ++ + #include + #include + +@@ -462,3 +466,6 @@ + } // namespace internal + } // namespace protobuf + } // namespace google ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility pop ++#endif +--- src/google/protobuf/text_format.cc.O 2018-07-30 22:16:11.000000000 +0000 ++++ src/google/protobuf/text_format.cc 2022-05-06 13:34:58.881999517 +0000 +@@ -32,6 +32,10 @@ + // Based on original Protocol Buffers design by + // Sanjay Ghemawat, Jeff Dean, and others. + ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility push(hidden) ++#endif ++ + #include + #include + #include +@@ -2258,3 +2262,6 @@ + + } // namespace protobuf + } // namespace google ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility pop ++#endif +--- src/google/protobuf/wire_format.cc.O 2018-07-23 20:56:42.000000000 +0000 ++++ src/google/protobuf/wire_format.cc 2022-05-06 13:06:23.294219228 +0000 +@@ -32,6 +32,10 @@ + // Based on original Protocol Buffers design by + // Sanjay Ghemawat, Jeff Dean, and others. + ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility push(hidden) ++#endif ++ + #include + #include + #include +@@ -1445,3 +1449,7 @@ + } // namespace internal + } // namespace protobuf + } // namespace google ++ ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility pop ++#endif +--- src/google/protobuf/stubs/status.cc.O 2018-07-23 20:56:42.000000000 +0000 ++++ src/google/protobuf/stubs/status.cc 2022-05-06 15:18:53.393208814 +0000 +@@ -27,6 +27,11 @@ + // 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. ++ ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility push(hidden) ++#endif ++ + #include + + #include +@@ -132,3 +137,6 @@ + } // namespace util + } // namespace protobuf + } // namespace google ++#if defined(__APPLE__) && defined(__arm64__) ++#pragma GCC visibility pop ++#endif diff --git a/contrib/depends/patches/sodium/disable-glibc-getrandom-getentropy.patch b/contrib/depends/patches/sodium/disable-glibc-getrandom-getentropy.patch new file mode 100644 index 0000000..2f07c10 --- /dev/null +++ b/contrib/depends/patches/sodium/disable-glibc-getrandom-getentropy.patch @@ -0,0 +1,25 @@ +diff --git a/configure.ac b/configure.ac +index 9e2de27c..0fa85c2d 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -807,6 +807,10 @@ AS_IF([test "x$EMSCRIPTEN" = "x"],[ + # include + #endif + ]], [[ ++#ifdef __linux__ ++# error getrandom() is currently disabled on Linux to support glibc < 2.25 ++#endif ++ + unsigned char buf; + (void) getrandom((void *) &buf, 1U, 0U); + ]])], +@@ -825,6 +829,9 @@ unsigned char buf; + # include + #endif + ]], [[ ++#ifdef __linux__ ++# error getentropy() is currently disabled on Linux to support glibc < 2.25 ++#endif + #ifdef __APPLE__ + # error getentropy() is currently disabled on Apple operating systems + #endif diff --git a/contrib/depends/patches/sodium/fix-whitespace.patch b/contrib/depends/patches/sodium/fix-whitespace.patch new file mode 100644 index 0000000..c3d3af0 --- /dev/null +++ b/contrib/depends/patches/sodium/fix-whitespace.patch @@ -0,0 +1,13 @@ +diff --git a/configure b/configure +index b29f769..ca008ae 100755 +--- a/configure ++++ b/configure +@@ -591,7 +591,7 @@ MAKEFLAGS= + PACKAGE_NAME='libsodium' + PACKAGE_TARNAME='libsodium' + PACKAGE_VERSION='1.0.18' +-PACKAGE_STRING='libsodium 1.0.18' ++PACKAGE_STRING='libsodium' + PACKAGE_BUGREPORT='https://github.com/jedisct1/libsodium/issues' + PACKAGE_URL='https://github.com/jedisct1/libsodium' + diff --git a/contrib/depends/patches/unbound/disable-glibc-reallocarray.patch b/contrib/depends/patches/unbound/disable-glibc-reallocarray.patch new file mode 100644 index 0000000..d66a821 --- /dev/null +++ b/contrib/depends/patches/unbound/disable-glibc-reallocarray.patch @@ -0,0 +1,14 @@ +diff --git a/configure.ac b/configure.ac +index 5c7da197..e2b25288 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1702,6 +1702,9 @@ AC_LINK_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT + #ifndef _OPENBSD_SOURCE + #define _OPENBSD_SOURCE 1 + #endif ++#ifdef __linux__ ++# error reallocarray() is currently disabled on Linux to support glibc < 2.26 ++#endif + #include + int main(void) { + void* p = reallocarray(NULL, 10, 100); diff --git a/contrib/depends/patches/unwind/fix_obj_order.patch b/contrib/depends/patches/unwind/fix_obj_order.patch new file mode 100644 index 0000000..e764f0f --- /dev/null +++ b/contrib/depends/patches/unwind/fix_obj_order.patch @@ -0,0 +1,11 @@ +--- config/ltmain.sh.0 2020-11-10 17:25:26.000000000 +0100 ++++ config/ltmain.sh 2021-09-11 19:39:36.000000000 +0200 +@@ -10768,6 +10768,8 @@ + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result ++ oldobjs=`for obj in $oldobjs; do echo $obj; done | sort` ++ oldobjs=" `echo $oldobjs`" + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" diff --git a/contrib/depends/patches/zeromq/fix_declaration.patch b/contrib/depends/patches/zeromq/fix_declaration.patch new file mode 100644 index 0000000..23fa0a2 --- /dev/null +++ b/contrib/depends/patches/zeromq/fix_declaration.patch @@ -0,0 +1,11 @@ +--- a/src/proxy.cpp 2024-11-19 19:46:31 ++++ b/src/proxy.cpp 2024-11-19 19:47:37 +@@ -499,7 +499,7 @@ + zmq_pollitem_t itemsout[] = {{frontend_, 0, ZMQ_POLLOUT, 0}, + {backend_, 0, ZMQ_POLLOUT, 0}}; + +- stats_proxy stats = {0}; ++ stats_proxy stats = {{{0, 0}, {0, 0}}, {{0, 0}, {0, 0}}}; + + // Proxy can be in these three states + proxy_state_t state = active; diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in new file mode 100644 index 0000000..2e0ba52 --- /dev/null +++ b/contrib/depends/toolchain.cmake.in @@ -0,0 +1,202 @@ +cmake_minimum_required(VERSION 3.13) # Ensure CMake version supports CMP0077 +cmake_policy(SET CMP0077 NEW) # Ensure 'option' honors normal variables + +# Set the system name to one of Android, Darwin, iOS, FreeBSD, Linux, or Windows +SET(CMAKE_SYSTEM_NAME @depends@) +SET(CMAKE_SYSTEM_PROCESSOR @arch@) +SET(CMAKE_BUILD_TYPE @release_type@) + +OPTION(STATIC "Link libraries statically" ON) +OPTION(TREZOR_DEBUG "Main trezor debugging switch" OFF) +OPTION(BUILD_TESTS "Build tests." OFF) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +SET(STATIC ON) +SET(UNBOUND_STATIC ON) +SET(ARCH "default") + +SET(BUILD_TESTS @build_tests@) +SET(TREZOR_DEBUG @build_tests@) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH @prefix@ /usr) + +SET(ENV{PKG_CONFIG_PATH} @prefix@/lib/pkgconfig) + +SET(Readline_ROOT_DIR @prefix@) +SET(Readline_INCLUDE_DIR @prefix@/include) +SET(Readline_LIBRARY @prefix@/lib/libreadline.a) +SET(Terminfo_LIBRARY @prefix@/lib/libtinfo.a) + +SET(UNBOUND_INCLUDE_DIR @prefix@/include) +SET(UNBOUND_LIBRARIES @prefix@/lib/libunbound.a) + +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") +SET(LIBUNWIND_INCLUDE_DIR @prefix@/include) +SET(LIBUNWIND_LIBRARIES @prefix@/lib/libunwind.a) +SET(LIBUNWIND_LIBRARY_DIRS @prefix@/lib) +if(NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") +SET(LIBUSB-1.0_LIBRARY @prefix@/lib/libusb-1.0.a) +SET(LIBUDEV_LIBRARY @prefix@/lib/libudev.a) + +SET(Protobuf_FOUND 1) +SET(Protobuf_PROTOC_EXECUTABLE @prefix@/native/bin/protoc CACHE FILEPATH "Path to the native protoc") +SET(Protobuf_INCLUDE_DIR @prefix@/include CACHE PATH "Protobuf include dir") +SET(Protobuf_INCLUDE_DIRS @prefix@/include CACHE PATH "Protobuf include dir") +SET(Protobuf_LIBRARY @prefix@/lib/libprotobuf.a CACHE FILEPATH "Protobuf library") +endif() + +endif() + +SET(ZMQ_INCLUDE_PATH @prefix@/include) +SET(ZMQ_LIB @prefix@/lib/libzmq.a) + +SET(Boost_IGNORE_SYSTEM_PATH ON) +SET(BOOST_ROOT @prefix@) +SET(BOOST_INCLUDEDIR @prefix@/include) +SET(BOOST_LIBRARYDIR @prefix@/lib) +SET(Boost_IGNORE_SYSTEM_PATHS_DEFAULT OFF) +SET(Boost_NO_SYSTEM_PATHS ON) +SET(Boost_USE_STATIC_LIBS ON) +SET(Boost_USE_STATIC_RUNTIME ON) + +SET(OPENSSL_ROOT_DIR @prefix@) +SET(ARCHITECTURE @arch@) + +# for libraries and headers in the target directories +set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Find programs on host +set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # Find libs in target +set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Find includes in target + +# specify the cross compiler to be used. Darwin uses clang provided by the SDK. +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + if(ARCHITECTURE STREQUAL "aarch64") + SET(CLANG_TARGET "arm64-apple-darwin11") + SET(CONF_TRIPLE "aarch64-apple-darwin11") + SET(BUILD_TAG "mac-armv8") + SET(CMAKE_OSX_ARCHITECTURES "arm64") + set(ARM ON) + set(ARM_ID "armv8-a") + else() + SET(CLANG_TARGET "x86_64-apple-darwin11") + SET(CONF_TRIPLE "x86_64-apple-darwin11") + SET(BUILD_TAG "mac-x64") + SET(CMAKE_OSX_ARCHITECTURES "x86_64") + endif() + SET(_CMAKE_TOOLCHAIN_PREFIX @prefix@/native/bin/${CONF_TRIPLE}-) + SET(CMAKE_C_COMPILER @CC@) + SET(CMAKE_C_COMPILER_TARGET ${CLANG_TARGET}) + SET(CMAKE_C_FLAGS_INIT -B${_CMAKE_TOOLCHAIN_PREFIX}) + SET(CMAKE_CXX_COMPILER @CXX@) + SET(CMAKE_CXX_COMPILER_TARGET ${CLANG_TARGET}) + SET(CMAKE_CXX_FLAGS_INIT -B${_CMAKE_TOOLCHAIN_PREFIX}) + SET(CMAKE_ASM_COMPILER_TARGET ${CLANG_TARGET}) + SET(CMAKE_ASM-ATT_COMPILER_TARGET ${CLANG_TARGET}) + SET(APPLE True) + SET(BUILD_64 ON) + SET(BREW OFF) + SET(PORT OFF) + SET(CMAKE_OSX_SYSROOT "@prefix@/native/SDK/") + SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") + SET(CMAKE_CXX_STANDARD 14) + SET(LLVM_ENABLE_PIC OFF) + SET(LLVM_ENABLE_PIE OFF) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") + add_definitions(-DUSE_DEVICE_TREZOR=OFF) + add_definitions(-DHIDAPI_DUMMY=ON) + SET(ANDROID TRUE) + if(ARCHITECTURE STREQUAL "armv7a") + SET(CMAKE_ANDROID_ARCH_ABI "armeabi-v7a") + SET(CMAKE_SYSTEM_PROCESSOR "armv7-a") + SET(CMAKE_ANDROID_ARM_MODE ON) + SET(CMAKE_C_COMPILER_TARGET armv7a-linux-androideabi21) + SET(CMAKE_CXX_COMPILER_TARGET armv7a-linux-androideabi21) + SET(_CMAKE_TOOLCHAIN_PREFIX armv7a-linux-androideabi21-) + elseif(ARCHITECTURE STREQUAL "aarch64") + SET(CMAKE_ANDROID_ARCH_ABI "arm64-v8a") + SET(CMAKE_SYSTEM_PROCESSOR "aarch64") + elseif(ARCHITECTURE STREQUAL "x86_64") + SET(MONERO_WALLET_CRYPTO_LIBRARY amd64-64-24k) + SET(CMAKE_ANDROID_ARCH_ABI x86_64) + SET(CMAKE_SYSTEM_PROCESSOR "x86_64") + else() + message(SEND_ERROR Unsupported android architecture) + endif() + # SET(CMAKE_ANDROID_STANDALONE_TOOLCHAIN @prefix@/native) + SET(_ANDROID_STANDALONE_TOOLCHAIN_API 21) + SET(__ANDROID_API__ 21) + SET(CMAKE_SYSTEM_VERSION 1) + SET(CMAKE_C_COMPILER @CC@) + SET(CMAKE_CXX_COMPILER @CXX@) +elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") + set(USE_DEVICE_TREZOR OFF) + add_definitions(-DHIDAPI_DUMMY=ON) + SET(CMAKE_C_COMPILER @CC@) + SET(CMAKE_CXX_COMPILER @CXX@) +else() + SET(CMAKE_C_COMPILER @CC@) + SET(CMAKE_CXX_COMPILER @CXX@) +endif() + +if(ARCHITECTURE STREQUAL "arm") + set(ARCH "armv7-a") + set(ARM ON) + set(ARM_ID "armv7-a") + set(BUILD_64 OFF) + set(CMAKE_BUILD_TYPE release) + if(ANDROID) + set(BUILD_TAG "android-armv7") + else() + set(BUILD_TAG "linux-armv7") + endif() + set(ARM7) +elseif(ARCHITECTURE STREQUAL "aarch64") + set(ARCH "armv8-a") + set(ARM ON) + set(ARM_ID "armv8-a") + if(ANDROID) + set(BUILD_TAG "android-armv8") + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(BUILD_TAG "linux-armv8") + endif() + set(BUILD_64 ON) +endif() + +if(ARCHITECTURE STREQUAL "riscv64") + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(BUILD_TAG "linux-riscv64") + endif() + set(ARCH_ID "riscv64") + set(ARCH "rv64gc") +endif() + +if(ARCHITECTURE STREQUAL "i686") + SET(ARCH_ID "i386") + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(BUILD_TAG "linux-x86") + SET(LINUX_32 ON) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(BUILD_TAG "win-x32") + endif() +endif() + +if(ARCHITECTURE STREQUAL "x86_64") + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(BUILD_TAG "linux-x64") + elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set(BUILD_TAG "freebsd-x64") + elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(BUILD_TAG "win-x64") + endif() + SET(ARCH_ID "x86_64") +endif() + +link_directories(@prefix@/lib) +include_directories(@prefix@/include) +include_directories(@prefix@/include/wownero_seed) + +add_definitions(-DPOLYSEED_STATIC=ON) + +#Create a new global cmake flag that indicates building with depends +set (DEPENDS true) \ No newline at end of file diff --git a/external/Apple-Boost-BuildScript b/external/Apple-Boost-BuildScript deleted file mode 160000 index 513dd14..0000000 --- a/external/Apple-Boost-BuildScript +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 513dd145f6e67a694160b858957978e375c8d694 diff --git a/external/OpenSSL-for-iPhone b/external/OpenSSL-for-iPhone deleted file mode 160000 index d3ec433..0000000 --- a/external/OpenSSL-for-iPhone +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d3ec433a47708594d18ef6e248b66e3d64f00f0f diff --git a/external/alpine/libexecinfo/10-execinfo.patch b/external/alpine/libexecinfo/10-execinfo.patch deleted file mode 100644 index 44db530..0000000 --- a/external/alpine/libexecinfo/10-execinfo.patch +++ /dev/null @@ -1,64 +0,0 @@ ---- a/execinfo.c.orig -+++ b/execinfo.c -@@ -69,7 +69,8 @@ - char ** - backtrace_symbols(void *const *buffer, int size) - { -- int i, clen, alen, offset; -+ size_t clen, alen; -+ int i, offset; - char **rval; - char *cp; - Dl_info info; -@@ -78,7 +79,6 @@ - rval = malloc(clen); - if (rval == NULL) - return NULL; -- (char **)cp = &(rval[size]); - for (i = 0; i < size; i++) { - if (dladdr(buffer[i], &info) != 0) { - if (info.dli_sname == NULL) -@@ -92,14 +92,14 @@ - 2 + /* " <" */ - strlen(info.dli_sname) + /* "function" */ - 1 + /* "+" */ -- D10(offset) + /* "offset */ -+ 10 + /* "offset */ - 5 + /* "> at " */ - strlen(info.dli_fname) + /* "filename" */ - 1; /* "\0" */ - rval = realloc_safe(rval, clen + alen); - if (rval == NULL) - return NULL; -- snprintf(cp, alen, "%p <%s+%d> at %s", -+ snprintf((char *) rval + clen, alen, "%p <%s+%d> at %s", - buffer[i], info.dli_sname, offset, info.dli_fname); - } else { - alen = 2 + /* "0x" */ -@@ -108,12 +108,15 @@ - rval = realloc_safe(rval, clen + alen); - if (rval == NULL) - return NULL; -- snprintf(cp, alen, "%p", buffer[i]); -+ snprintf((char *) rval + clen, alen, "%p", buffer[i]); - } -- rval[i] = cp; -- cp += alen; -+ rval[i] = (char *) clen; -+ clen += alen; - } - -+ for (i = 0; i < size; i++) -+ rval[i] += (long) rval; -+ - return rval; - } - -@@ -155,6 +158,6 @@ - return; - snprintf(buf, len, "%p\n", buffer[i]); - } -- write(fd, buf, len - 1); -+ write(fd, buf, strlen(buf)); - } - } diff --git a/external/alpine/libexecinfo/20-define-gnu-source.patch b/external/alpine/libexecinfo/20-define-gnu-source.patch deleted file mode 100644 index 7255ecd..0000000 --- a/external/alpine/libexecinfo/20-define-gnu-source.patch +++ /dev/null @@ -1,24 +0,0 @@ ---- a/execinfo.c.orig -+++ b/execinfo.c -@@ -26,6 +26,7 @@ - * $Id: execinfo.c,v 1.3 2004/07/19 05:21:09 sobomax Exp $ - */ - -+#define _GNU_SOURCE - #include - #include - #include ---- a/stacktraverse.c.orig -+++ b/stacktraverse.c -@@ -1,3 +1,4 @@ -+#define _GNU_SOURCE - #include - - #include "stacktraverse.h" ---- a/test.c.orig -+++ b/test.c -@@ -1,3 +1,4 @@ -+#define _GNU_SOURCE - #include - #include - diff --git a/external/alpine/libexecinfo/30-linux-makefile.patch b/external/alpine/libexecinfo/30-linux-makefile.patch deleted file mode 100644 index 6bc8a89..0000000 --- a/external/alpine/libexecinfo/30-linux-makefile.patch +++ /dev/null @@ -1,44 +0,0 @@ ---- a/Makefile.orig -+++ b/Makefile -@@ -23,24 +23,25 @@ - # SUCH DAMAGE. - # - # $Id: Makefile,v 1.3 2004/07/19 05:19:55 sobomax Exp $ -+# -+# Linux Makefile by Matt Smith , 2011/01/04 - --LIB= execinfo -+CC=cc -+AR=ar -+EXECINFO_CFLAGS=$(CFLAGS) -O2 -pipe -fno-strict-aliasing -std=gnu99 -fstack-protector -c -+EXECINFO_LDFLAGS=$(LDFLAGS) - --SRCS= stacktraverse.c stacktraverse.h execinfo.c execinfo.h -+all: static dynamic - --INCS= execinfo.h -+static: -+ $(CC) $(EXECINFO_CFLAGS) $(EXECINFO_LDFLAGS) stacktraverse.c -+ $(CC) $(EXECINFO_CFLAGS) $(EXECINFO_LDFLAGS) execinfo.c -+ $(AR) rcs libexecinfo.a stacktraverse.o execinfo.o - --SHLIB_MAJOR= 1 --SHLIB_MINOR= 0 -+dynamic: -+ $(CC) -fpic -DPIC $(EXECINFO_CFLAGS) $(EXECINFO_LDFLAGS) stacktraverse.c -o stacktraverse.So -+ $(CC) -fpic -DPIC $(EXECINFO_CFLAGS) $(EXECINFO_LDFLAGS) execinfo.c -o execinfo.So -+ $(CC) -shared -Wl,-soname,libexecinfo.so.1 -o libexecinfo.so.1 stacktraverse.So execinfo.So - --NOPROFILE= yes -- --DPADD= ${LIBM} --LDADD= -lm -- --#WARNS?= 4 -- --#stacktraverse.c: gen.py --# ./gen.py > stacktraverse.c -- --.include -+clean: -+ rm -rf *.o *.So *.a *.so diff --git a/external/alpine/libexecinfo/APKBUILD b/external/alpine/libexecinfo/APKBUILD deleted file mode 100644 index 0e91c2c..0000000 --- a/external/alpine/libexecinfo/APKBUILD +++ /dev/null @@ -1,50 +0,0 @@ -# Contributor: Philipp Andronov -# Maintainer: Matt Smith -pkgname=libexecinfo -pkgver=1.1 -pkgrel=1 -pkgdesc="A quick-n-dirty BSD licensed clone of the GNU libc backtrace facility." -options="!check" # No testsuite -url="https://www.freshports.org/devel/libexecinfo" -arch="all" -license="BSD-2-Clause" -subpackages="$pkgname-static ${pkgname}-dev" -source="http://distcache.freebsd.org/local-distfiles/itetcu/$pkgname-$pkgver.tar.bz2 - 10-execinfo.patch - 20-define-gnu-source.patch - 30-linux-makefile.patch - " - -build() { - cd "$builddir" - export CFLAGS="${CFLAGS} -fno-omit-frame-pointer" - make -} - -package() { - cd "$builddir" - - install -D -m755 "$builddir"/execinfo.h \ - "$pkgdir"/usr/include/execinfo.h - install -D -m755 "$builddir"/stacktraverse.h \ - "$pkgdir"/usr/include/stacktraverse.h - install -D -m755 "$builddir"/libexecinfo.a \ - "$pkgdir"/usr/lib/libexecinfo.a - install -D -m755 "$builddir"/libexecinfo.so.1 \ - "$pkgdir"/usr/lib/libexecinfo.so.1 - ln -s /usr/lib/libexecinfo.so.1 \ - "$pkgdir"/usr/lib/libexecinfo.so -} - -static() { - depends="" - pkgdesc="$pkgdesc (static library)" - - mkdir -p "$subpkgdir"/usr/lib - mv "$pkgdir"/usr/lib/*.a "$subpkgdir"/usr/lib -} - -sha512sums="51fea7910ef6873061a25c22434ce4da724e9d8e37616a069ad0a58c0463755be4c6c7da88cd747484c2f3373909d7be4678b32a4bd91b6d9e0f74526094e92c libexecinfo-1.1.tar.bz2 -cd35c9046d88b39b05bc36faffb1e71ae3b2140632da7da37f374fff671d4ad812eebd0581011ff9e3b25d3cb4bc962cf35957074f713817b3b617511425af1a 10-execinfo.patch -c961b2f86cba291e8c69a507e3880354ad7369fd04c8948d54c4db0578fe30cca8f4250742cb63d1ab6e5875988f04c4729256197030369404f0e925f299a66c 20-define-gnu-source.patch -13d3df88a6dabd78ee2cf50092511f5a10f0e5ff3d81dbacb182fcf85ceb0c13a5f0252397b4eb0ac57f8d8bd3fc3af6c05865d6398cbc1517f347210c5750da 30-linux-makefile.patch" diff --git a/external/ios-cmake b/external/ios-cmake deleted file mode 160000 index ad96a37..0000000 --- a/external/ios-cmake +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ad96a372b168930c2a1ff9455e1a9ccb13021617 diff --git a/external/ios/build_boost.sh b/external/ios/build_boost.sh deleted file mode 100755 index 805fbd0..0000000 --- a/external/ios/build_boost.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -set -e - -. ./config.sh - -MIN_IOS_VERSION=10.0 -BOOST_DIR_PATH="${EXTERNAL_IOS_SOURCE_DIR}/Apple-Boost-BuildScript" -BOOST_VERSION=1.84.0 -BOOST_LIBS="random regex graph random chrono thread filesystem system date_time locale serialization program_options" - -echo "============================ Boost ============================" - -# Check if the directory already exists. -if [ -d "$BOOST_DIR_PATH" ]; then - echo "Boost directory already exists." -else - echo "Cloning Boost from $BOOST_URL" - mkdir -p "$BOOST_DIR_PATH" || true - rm -rf "$BOOST_DIR_PATH" || true - cp -r "${MONEROC_DIR}/external/Apple-Boost-BuildScript" "$BOOST_DIR_PATH" -fi -cd "$BOOST_DIR_PATH" - -./boost.sh -ios \ - --min-ios-version ${MIN_IOS_VERSION} \ - --boost-libs "${BOOST_LIBS}" \ - --boost-version ${BOOST_VERSION} \ - --no-framework - -cp -r "${BOOST_DIR_PATH}/build/boost/${BOOST_VERSION}"/ios/release/prefix/include/boost "$EXTERNAL_IOS_INCLUDE_DIR/boost" -cp -r "${BOOST_DIR_PATH}/build/boost/${BOOST_VERSION}"/ios/release/prefix/lib/* "$EXTERNAL_IOS_LIB_DIR/" \ No newline at end of file diff --git a/external/ios/build_monero_all.sh b/external/ios/build_monero_all.sh deleted file mode 100755 index 4957e69..0000000 --- a/external/ios/build_monero_all.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -e - -rm -rf build - -. ./config.sh - -rm -rf "$EXTERNAL_IOS_LIB_DIR" -rm -rf "$EXTERNAL_IOS_INCLUDE_DIR" - -./install_missing_headers.sh -./build_openssl.sh -./build_boost.sh -./build_sodium.sh -./build_zmq.sh -./build_unbound.sh - diff --git a/external/ios/build_openssl.sh b/external/ios/build_openssl.sh deleted file mode 100755 index 4bf6aa2..0000000 --- a/external/ios/build_openssl.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -set -e - -. ./config.sh - -OPEN_SSL_DIR_PATH="${EXTERNAL_IOS_SOURCE_DIR}/OpenSSL" - -echo "============================ OpenSSL ============================" - -echo "Cloning Open SSL from - $OPEN_SSL_URL" - -# Check if the directory already exists. -if [ -d "$OPEN_SSL_DIR_PATH" ]; then - echo "OpenSSL directory already exists." -else - mkdir -p "$OPEN_SSL_DIR_PATH" || true - rm -rf "$OPEN_SSL_DIR_PATH" - cp -r "${MONEROC_DIR}/external/OpenSSL-for-iPhone" "$OPEN_SSL_DIR_PATH" -fi -cd "$OPEN_SSL_DIR_PATH" - -./build-libssl.sh --version=1.1.1q --targets="ios-cross-arm64" --deprecated - - -cp -r "${OPEN_SSL_DIR_PATH}"/include/* "$EXTERNAL_IOS_INCLUDE_DIR/" -cp "${OPEN_SSL_DIR_PATH}"/lib/libcrypto-iOS.a "${EXTERNAL_IOS_LIB_DIR}"/libcrypto.a -cp "${OPEN_SSL_DIR_PATH}"/lib/libssl-iOS.a "${EXTERNAL_IOS_LIB_DIR}"/libssl.a \ No newline at end of file diff --git a/external/ios/build_sodium.sh b/external/ios/build_sodium.sh deleted file mode 100755 index d8f8777..0000000 --- a/external/ios/build_sodium.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -set -e - -. ./config.sh - -SODIUM_PATH="${EXTERNAL_IOS_SOURCE_DIR}/libsodium" -SODIUM_URL="https://github.com/jedisct1/libsodium.git" - -echo "============================ SODIUM ============================" - -echo "Cloning SODIUM from - $SODIUM_URL" - -# Check if the directory already exists. -if [ -d "$SODIUM_PATH" ]; then - echo "Sodium directory already exists." -else - echo "Cloning Sodium from $SODIUM_URL" - mkdir -p "$SODIUM_PATH" || true - rm -rf "$SODIUM_PATH" - cp -r "${MONEROC_DIR}/external/libsodium" "$SODIUM_PATH" -fi - -cd "$SODIUM_PATH" -../../../../libsodium_apple-ios.sh - -cp -r "${SODIUM_PATH}"/libsodium-apple/ios/include/* "$EXTERNAL_IOS_INCLUDE_DIR/" -cp -r "${SODIUM_PATH}"/libsodium-apple/ios/lib/* "$EXTERNAL_IOS_LIB_DIR/" \ No newline at end of file diff --git a/external/ios/build_unbound.sh b/external/ios/build_unbound.sh deleted file mode 100755 index 14efef8..0000000 --- a/external/ios/build_unbound.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -set -e - -. ./config.sh - -UNBOUND_VERSION=release-1.16.2 -UNBOUND_HASH="cbed768b8ff9bfcf11089a5f1699b7e5707f1ea5" -UNBOUND_URL="https://www.nlnetlabs.nl/downloads/unbound/unbound-${UNBOUND_VERSION}.tar.gz" -UNBOUND_GIT_URL="https://github.com/NLnetLabs/unbound.git" -UNBOUND_DIR_PATH="${EXTERNAL_IOS_SOURCE_DIR}/unbound-1.16.2" - -echo "============================ Unbound ============================" -rm -rf ${UNBOUND_DIR_PATH} - -# Check if the directory already exists. -if [ -d "$UNBOUND_DIR_PATH" ]; then - echo "Unbound directory already exists." -else - echo "Cloning Unbound from $Unbound_URL" - mkdir -p ${UNBOUND_DIR_PATH} || true - rm -rf ${UNBOUND_DIR_PATH} - cp -r "${MONEROC_DIR}/external/unbound" ${UNBOUND_DIR_PATH} -fi - -cd $UNBOUND_DIR_PATH - -export IOS_SDK=iPhone -export IOS_CPU=arm64 -export IOS_PREFIX=$EXTERNAL_IOS_DIR -export AUTOTOOLS_HOST=aarch64-apple-ios -export AUTOTOOLS_BUILD="$(./config.guess)" -source ./contrib/ios/setenv_ios.sh -./contrib/ios/install_tools.sh -./contrib/ios/install_expat.sh -./configure --build="$AUTOTOOLS_BUILD" --host="$AUTOTOOLS_HOST" --prefix="$IOS_PREFIX" --with-ssl="$IOS_PREFIX" --with-libexpat="$IOS_PREFIX" -make -j$(sysctl -n hw.logicalcpu) -make install \ No newline at end of file diff --git a/external/ios/build_wownero_seed.sh b/external/ios/build_wownero_seed.sh deleted file mode 100755 index 7672db5..0000000 --- a/external/ios/build_wownero_seed.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/sh - -set -x -e - -. ./config.sh - -SEED_VERSION=0.3.0 -SEED_SRC_DIR="${EXTERNAL_IOS_SOURCE_DIR}/seed" - -rm -rf "$SEED_SRC_DIR" > /dev/null - -echo "[*] cloning $SEED_URL" -mkdir -p ${SEED_SRC_DIR} || true -rm -rf ${SEED_SRC_DIR} -cp -r "${MONEROC_DIR}/external/wownero-seed" ${SEED_SRC_DIR} -cd $SEED_SRC_DIR - -patch -p1 < ../../../../../wownero-seed-0001-fix-duplicate-symbol-error.patch - -BUILD_TYPE=release -PREFIX=${EXTERNAL_IOS_DIR} -DEST_LIB_DIR=${EXTERNAL_IOS_LIB_DIR}/wownero-seed -DEST_INCLUDE_DIR=${EXTERNAL_IOS_INCLUDE_DIR}/wownero-seed - -ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -if [ -z $INSTALL_PREFIX ]; then - INSTALL_PREFIX=${ROOT_DIR}/wownero-seed -fi - -for arch in "arm64" #"armv7" "arm64" -do - -echo "Building wownero-seed IOS ${arch}" -export CMAKE_INCLUDE_PATH="${PREFIX}/include" -export CMAKE_LIBRARY_PATH="${PREFIX}/lib" - -case $arch in - "armv7" ) - DEST_LIB=../../lib-armv7;; - "arm64" ) - DEST_LIB=../../lib-armv8-a;; -esac - -cmake -Bbuild -DCMAKE_INSTALL_PREFIX="${PREFIX}" -DCMAKE_SYSTEM_NAME="iOS" -DCMAKE_OSX_ARCHITECTURES="${arch}" . -make -Cbuild -j$(nproc) -make -Cbuild install - -done \ No newline at end of file diff --git a/external/ios/build_zmq.sh b/external/ios/build_zmq.sh deleted file mode 100755 index 63306b2..0000000 --- a/external/ios/build_zmq.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -set -e - -. ./config.sh - -ZMQ_PATH="${EXTERNAL_IOS_SOURCE_DIR}/libzmq" - -echo "============================ ZMQ ============================" - -echo "Cloning ZMQ from - $ZMQ_URL" - -# Check if the directory already exists. -if [ -d "$ZMQ_PATH" ]; then - echo "ZeroMQ directory already exists." -else - echo "Cloning ZeroMQ from $ZeroMQ_URL" - mkdir -p $ZMQ_PATH || true - rm -rf $ZMQ_PATH - cp -r "${MONEROC_DIR}/external/libzmq" $ZMQ_PATH -fi - -cd $ZMQ_PATH - -mkdir -p cmake-build -cd cmake-build -cmake .. -make -j$(sysctl -n hw.logicalcpu) - - -cp ${ZMQ_PATH}/include/* $EXTERNAL_IOS_INCLUDE_DIR -cp ${ZMQ_PATH}/cmake-build/lib/libzmq.a $EXTERNAL_IOS_LIB_DIR \ No newline at end of file diff --git a/external/ios/config.sh b/external/ios/config.sh deleted file mode 100755 index e65c5a3..0000000 --- a/external/ios/config.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -export IOS_SCRIPTS_DIR="$(pwd)" -export MONEROC_DIR="$(pwd)/../.." -export CW_ROOT="${IOS_SCRIPTS_DIR}" -export EXTERNAL_DIR="${CW_ROOT}/build" -export EXTERNAL_IOS_DIR="${EXTERNAL_DIR}/ios" -export EXTERNAL_IOS_SOURCE_DIR="${EXTERNAL_IOS_DIR}/sources" -export EXTERNAL_IOS_LIB_DIR="${EXTERNAL_IOS_DIR}/lib" -export EXTERNAL_IOS_INCLUDE_DIR="${EXTERNAL_IOS_DIR}/include" - -mkdir -p "$EXTERNAL_IOS_LIB_DIR" -mkdir -p "$EXTERNAL_IOS_INCLUDE_DIR" \ No newline at end of file diff --git a/external/ios/install_missing_headers.sh b/external/ios/install_missing_headers.sh deleted file mode 100755 index 5ae794a..0000000 --- a/external/ios/install_missing_headers.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -set -e - -. ./config.sh - -echo "Installing missing headers" - -# vmmeter -mkdir -p ${EXTERNAL_IOS_INCLUDE_DIR}/sys - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/sys/vmmeter.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/vmmeter.h ${EXTERNAL_IOS_INCLUDE_DIR}/sys/vmmeter.h -fi - -# netinet -mkdir -p ${EXTERNAL_IOS_INCLUDE_DIR}/netinet -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/netinet/ip_var.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/netinet/ip_var.h ${EXTERNAL_IOS_INCLUDE_DIR}/netinet/ip_var.h -fi - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/netinet/udp_var.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/netinet/udp_var.h ${EXTERNAL_IOS_INCLUDE_DIR}/netinet/udp_var.h -fi - -# IOKit -mkdir -p ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOTypes.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers/IOTypes.h ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOTypes.h -fi - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOMapTypes.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers/IOMapTypes.h ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOMapTypes.h -fi - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOKitLib.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers/IOKitLib.h ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOKitLib.h -fi - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOReturn.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers/IOReturn.h ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOReturn.h -fi - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/OSMessageNotification.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers/OSMessageNotification.h ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/OSMessageNotification.h -fi - -# IOKit/ps -mkdir -p ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/ps - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/ps/IOPSKeys.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers/ps/IOPSKeys.h ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/ps/IOPSKeys.h -fi - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/ps/IOPowerSources.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers/ps/IOPowerSources.h ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/ps/IOPowerSources.h -fi - - -# libkern -mkdir -p ${EXTERNAL_IOS_INCLUDE_DIR}/libkern - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/libkern/OSTypes.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/libkern/OSTypes.h ${EXTERNAL_IOS_INCLUDE_DIR}/libkern/OSTypes.h -fi - -if [ ! -f ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOKitKeys.h ]; then - cp /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Versions/A/Headers/IOKitKeys.h ${EXTERNAL_IOS_INCLUDE_DIR}/IOKit/IOKitKeys.h -fi diff --git a/external/ios/libsodium_apple-ios.sh b/external/ios/libsodium_apple-ios.sh deleted file mode 100755 index 963ba32..0000000 --- a/external/ios/libsodium_apple-ios.sh +++ /dev/null @@ -1,147 +0,0 @@ -#! /bin/sh - -export PREFIX="$(pwd)/libsodium-apple" -export MACOS_ARM64_PREFIX="${PREFIX}/tmp/macos-arm64" -export MACOS_X86_64_PREFIX="${PREFIX}/tmp/macos-x86_64" -export IOS32_PREFIX="${PREFIX}/tmp/ios32" -export IOS32s_PREFIX="${PREFIX}/tmp/ios32s" -export IOS64_PREFIX="${PREFIX}/tmp/ios64" -export IOS_SIMULATOR_ARM64_PREFIX="${PREFIX}/tmp/ios-simulator-arm64" -export IOS_SIMULATOR_I386_PREFIX="${PREFIX}/tmp/ios-simulator-i386" -export IOS_SIMULATOR_X86_64_PREFIX="${PREFIX}/tmp/ios-simulator-x86_64" -export WATCHOS32_PREFIX="${PREFIX}/tmp/watchos32" -export WATCHOS64_32_PREFIX="${PREFIX}/tmp/watchos64_32" -export WATCHOS64_PREFIX="${PREFIX}/tmp/watchos64" -export WATCHOS_SIMULATOR_ARM64_PREFIX="${PREFIX}/tmp/watchos-simulator-arm64" -export WATCHOS_SIMULATOR_I386_PREFIX="${PREFIX}/tmp/watchos-simulator-i386" -export WATCHOS_SIMULATOR_X86_64_PREFIX="${PREFIX}/tmp/watchos-simulator-x86_64" -export TVOS_PREFIX="${PREFIX}/tmp/tvos" -export TVOS_SIMULATOR_ARM64_PREFIX="${PREFIX}/tmp/tvos-simulator-arm64" -export TVOS_SIMULATOR_X86_64_PREFIX="${PREFIX}/tmp/tvos-simulator-x86_64" -export VISIONOS_PREFIX="${PREFIX}/tmp/visionos" -export VISIONOS_SIMULATOR_PREFIX="${PREFIX}/tmp/visionos-simulator" -export CATALYST_ARM64_PREFIX="${PREFIX}/tmp/catalyst-arm64" -export CATALYST_X86_64_PREFIX="${PREFIX}/tmp/catalyst-x86_64" -export LOG_FILE="${PREFIX}/tmp/build_log" -export XCODEDIR="$(xcode-select -p)" - -export MACOS_VERSION_MIN=${MACOS_VERSION_MIN-"10.10"} -export IOS_SIMULATOR_VERSION_MIN=${IOS_SIMULATOR_VERSION_MIN-"9.0.0"} -export IOS_VERSION_MIN=${IOS_VERSION_MIN-"9.0.0"} -export WATCHOS_SIMULATOR_VERSION_MIN=${WATCHOS_SIMULATOR_VERSION_MIN-"4.0.0"} -export WATCHOS_VERSION_MIN=${WATCHOS_VERSION_MIN-"4.0.0"} -export TVOS_SIMULATOR_VERSION_MIN=${TVOS_SIMULATOR_VERSION_MIN-"9.0.0"} -export TVOS_VERSION_MIN=${TVOS_VERSION_MIN-"9.0.0"} - -echo -echo "Warnings related to headers being present but not usable are due to functions" -echo "that didn't exist in the specified minimum iOS version level." -echo "They can be safely ignored." -echo -echo "Define the LIBSODIUM_FULL_BUILD environment variable to build the full" -echo "library (including all deprecated/undocumented/low-level functions)." -echo -echo "Define the LIBSODIUM_SKIP_SIMULATORS environment variable to skip building" -echo "the simulators libraries (iOS, watchOS, tvOS, visionOS simulators)." -echo - -if [ -z "$LIBSODIUM_FULL_BUILD" ]; then - export LIBSODIUM_ENABLE_MINIMAL_FLAG="--enable-minimal" -else - export LIBSODIUM_ENABLE_MINIMAL_FLAG="" -fi - -APPLE_SILICON_SUPPORTED=false -echo 'int main(void){return 0;}' >comptest.c && cc --target=arm64-macos comptest.c 2>/dev/null && APPLE_SILICON_SUPPORTED=true -rm -f comptest.c - -NPROCESSORS=$(getconf NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null) -PROCESSORS=${NPROCESSORS:-3} - -swift_module_map() { - echo 'module Clibsodium {' - echo ' header "sodium.h"' - echo ' export *' - echo '}' -} - -build_ios() { - export BASEDIR="${XCODEDIR}/Platforms/iPhoneOS.platform/Developer" - export PATH="${BASEDIR}/usr/bin:$BASEDIR/usr/sbin:$PATH" - export SDK="${BASEDIR}/SDKs/iPhoneOS.sdk" - - ## 32-bit iOS - export CFLAGS="-O2 -mthumb -arch armv7 -isysroot ${SDK} -mios-version-min=${IOS_VERSION_MIN}" - export LDFLAGS="-mthumb -arch armv7 -isysroot ${SDK} -mios-version-min=${IOS_VERSION_MIN}" - - make distclean >/dev/null 2>&1 - ./configure --host=arm-apple-darwin10 --prefix="$IOS32_PREFIX" \ - ${LIBSODIUM_ENABLE_MINIMAL_FLAG} || exit 1 - make -j${PROCESSORS} install || exit 1 - - ## 32-bit armv7s iOS - export CFLAGS="-O2 -mthumb -arch armv7s -isysroot ${SDK} -mios-version-min=${IOS_VERSION_MIN}" - export LDFLAGS="-mthumb -arch armv7s -isysroot ${SDK} -mios-version-min=${IOS_VERSION_MIN}" - - make distclean >/dev/null 2>&1 - ./configure --host=arm-apple-darwin10 --prefix="$IOS32s_PREFIX" \ - ${LIBSODIUM_ENABLE_MINIMAL_FLAG} || exit 1 - make -j${PROCESSORS} install || exit 1 - - ## 64-bit iOS - export CFLAGS="-O2 -arch arm64 -isysroot ${SDK} -mios-version-min=${IOS_VERSION_MIN}" - export LDFLAGS="-arch arm64 -isysroot ${SDK} -mios-version-min=${IOS_VERSION_MIN}" - - make distclean >/dev/null 2>&1 - ./configure --host=arm-apple-darwin10 --prefix="$IOS64_PREFIX" \ - ${LIBSODIUM_ENABLE_MINIMAL_FLAG} || exit 1 - make -j${PROCESSORS} install || exit 1 -} - -mkdir -p "${PREFIX}/tmp" - -echo "Building for iOS... ($LOG_FILE)" -./autogen.sh -./configure -build_ios >"$LOG_FILE" 2>&1 || exit 1 - -echo "Adding the Clibsodium module map for Swift..." - -find "$PREFIX" -name "include" -type d -print | while read -r f; do - swift_module_map >"${f}/module.modulemap" -done - -echo "Bundling iOS targets..." - -mkdir -p "${PREFIX}/ios/lib" -cp -a "${IOS64_PREFIX}/include" "${PREFIX}/ios/" -for ext in a dylib; do - lipo -create \ - "$IOS32_PREFIX/lib/libsodium.${ext}" \ - "$IOS32s_PREFIX/lib/libsodium.${ext}" \ - "$IOS64_PREFIX/lib/libsodium.${ext}" \ - -output "$PREFIX/ios/lib/libsodium.${ext}" -done - -echo "Creating Clibsodium.xcframework..." - -rm -rf "${PREFIX}/Clibsodium.xcframework" - -XCFRAMEWORK_ARGS="" -for f in ios; do - XCFRAMEWORK_ARGS="${XCFRAMEWORK_ARGS} -library ${PREFIX}/${f}/lib/libsodium.a" - XCFRAMEWORK_ARGS="${XCFRAMEWORK_ARGS} -headers ${PREFIX}/${f}/include" -done -xcodebuild -create-xcframework \ - ${XCFRAMEWORK_ARGS} \ - -output "${PREFIX}/Clibsodium.xcframework" >/dev/null - -ls -ld -- "$PREFIX" -ls -l -- "$PREFIX" -ls -l -- "$PREFIX/Clibsodium.xcframework" - -echo "Done!" - -# Cleanup -rm -rf -- "$PREFIX/tmp" -make distclean >/dev/null diff --git a/external/libexpat b/external/libexpat deleted file mode 160000 index 038a0bf..0000000 --- a/external/libexpat +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 038a0bf44f940ad7f7df59cfa5ed6fcb2c63f64f diff --git a/external/libsodium b/external/libsodium deleted file mode 160000 index 3c6da4b..0000000 --- a/external/libsodium +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3c6da4b8c27c7d546746eadabc9e2dd6c1fdfc2c diff --git a/external/libzmq b/external/libzmq deleted file mode 160000 index 90b4f41..0000000 --- a/external/libzmq +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 90b4f410a07222fa2e9a5f53b454a09d4533e45a diff --git a/external/macos/build_unbound.sh b/external/macos/build_unbound.sh deleted file mode 100755 index d1c3825..0000000 --- a/external/macos/build_unbound.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -set -x -e - -. config.sh - -#### expat - -EXPAT_VERSION=R_2_4_8 -EXPAT_HASH="3bab6c09bbe8bf42d84b81563ddbcf4cca4be838" -EXPAT_SRC_DIR=${EXTERNAL_MACOS_SOURCE_DIR}/libexpat -rm -rf $EXPAT_SRC_DIR -if [ -d "$EXPAT_SRC_DIR" ]; then - echo "Unbound directory already exists." -else - echo "Cloning Unbound from $Unbound_URL" - mkdir -p ${EXPAT_SRC_DIR} || true - rm -rf ${EXPAT_SRC_DIR} - cp -r "${MONEROC_DIR}/external/libexpat" ${EXPAT_SRC_DIR} -fi -cd $EXPAT_SRC_DIR -cd $EXPAT_SRC_DIR/expat - -./buildconf.sh -./configure --enable-static --disable-shared --prefix=${EXTERNAL_MACOS_DIR} -make -make install - -#### unbound - -UNBOUND_VERSION=release-1.16.2 -UNBOUND_HASH="cbed768b8ff9bfcf11089a5f1699b7e5707f1ea5" -UNBOUND_URL="https://www.nlnetlabs.nl/downloads/unbound/unbound-${UNBOUND_VERSION}.tar.gz" -UNBOUND_DIR_PATH="${EXTERNAL_MACOS_SOURCE_DIR}/unbound-1.16.2" - -echo "============================ Unbound ============================" -rm -rf ${UNBOUND_DIR_PATH} -# Check if the directory already exists. -if [ -d "$UNBOUND_DIR_PATH" ]; then - echo "Unbound directory already exists." -else - echo "Cloning Unbound from $Unbound_URL" - mkdir -p ${UNBOUND_DIR_PATH} || true - rm -rf ${UNBOUND_DIR_PATH} - cp -r "${MONEROC_DIR}/external/unbound" ${UNBOUND_DIR_PATH} -fi -cd $UNBOUND_DIR_PATH -./configure --prefix="${EXTERNAL_MACOS_DIR}" \ - --with-ssl="${HOMEBREW_PREFIX}" \ - --with-libexpat="${EXTERNAL_MACOS_DIR}" \ - --enable-static \ - --disable-shared \ - --disable-flto -make -make install diff --git a/external/macos/config.sh b/external/macos/config.sh deleted file mode 100755 index 3c40625..0000000 --- a/external/macos/config.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -export MACOS_SCRIPTS_DIR="$(pwd)" -export CW_ROOT="${MACOS_SCRIPTS_DIR}" -export EXTERNAL_DIR="${CW_ROOT}/build" -export EXTERNAL_MACOS_DIR="${EXTERNAL_DIR}/MACOS" -export EXTERNAL_MACOS_SOURCE_DIR="${EXTERNAL_MACOS_DIR}/sources" -export EXTERNAL_MACOS_LIB_DIR="${EXTERNAL_MACOS_DIR}/lib" -export EXTERNAL_MACOS_INCLUDE_DIR="${EXTERNAL_MACOS_DIR}/include" -export MONEROC_DIR="$(pwd)/../.." - -mkdir -p "$EXTERNAL_MACOS_LIB_DIR" || true -mkdir -p "$EXTERNAL_MACOS_INCLUDE_DIR" || true \ No newline at end of file diff --git a/external/polyseed b/external/polyseed deleted file mode 160000 index 3ef3669..0000000 --- a/external/polyseed +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3ef366993258a6f1c837d4908e3e4afb2cfefff7 diff --git a/external/unbound b/external/unbound deleted file mode 160000 index cbed768..0000000 --- a/external/unbound +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cbed768b8ff9bfcf11089a5f1699b7e5707f1ea5 diff --git a/external/wownero-seed b/external/wownero-seed deleted file mode 160000 index d3f68be..0000000 --- a/external/wownero-seed +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d3f68be347facfeebbd8f68fd74982c705cb917b diff --git a/external/wownero-seed-0001-fix-duplicate-symbol-error.patch b/external/wownero-seed-0001-fix-duplicate-symbol-error.patch deleted file mode 120000 index b31660d..0000000 --- a/external/wownero-seed-0001-fix-duplicate-symbol-error.patch +++ /dev/null @@ -1 +0,0 @@ -../wownero/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch \ No newline at end of file diff --git a/impls/monero.dart/lib/monero.dart b/impls/monero.dart/lib/monero.dart index 7b6eac3..62264c1 100644 --- a/impls/monero.dart/lib/monero.dart +++ b/impls/monero.dart/lib/monero.dart @@ -2143,23 +2143,6 @@ int Wallet_daemonBlockChainHeight(wallet ptr) { return daemonBlockChainHeight; } -int Wallet_daemonBlockChainHeight_cached(wallet ptr) { - debugStart?.call('MONERO_Wallet_daemonBlockChainHeight_cached'); - lib ??= MoneroC(DynamicLibrary.open(libPath)); - final daemonBlockChainHeight = - lib!.MONERO_Wallet_daemonBlockChainHeight_cached(ptr); - debugEnd?.call('MONERO_Wallet_daemonBlockChainHeight_cached'); - return daemonBlockChainHeight; -} - -void Wallet_daemonBlockChainHeight_runThread(wallet ptr, int seconds) { - debugStart?.call('MONERO_Wallet_daemonBlockChainHeight_enableRefresh'); - lib ??= MoneroC(DynamicLibrary.open(libPath)); - final ret = lib!.MONERO_Wallet_daemonBlockChainHeight_runThread(ptr, seconds); - debugEnd?.call('MONERO_Wallet_daemonBlockChainHeight_enableRefresh'); - return ret; -} - bool Wallet_synchronized(wallet ptr) { debugStart?.call('MONERO_Wallet_synchronized'); lib ??= MoneroC(DynamicLibrary.open(libPath)); diff --git a/impls/monero.dart/lib/src/checksum_monero.dart b/impls/monero.dart/lib/src/checksum_monero.dart index 1092533..603eb21 100644 --- a/impls/monero.dart/lib/src/checksum_monero.dart +++ b/impls/monero.dart/lib/src/checksum_monero.dart @@ -1,4 +1,4 @@ // ignore_for_file: constant_identifier_names -const String wallet2_api_c_h_sha256 = "198a27486ce4f014b737f52ec2879a90838c3b3ca088de8dc7d55b79a10f4a5b"; -const String wallet2_api_c_cpp_sha256 = "22b0c9dd542fb55edfe9e17da67e97ddf5f80bcdd3e126e1bd64cce54234bde1-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; -const String wallet2_api_c_exp_sha256 = "c1f785d62709fd8b849063ecf4fe8854d9e3b05b568b9716de98d33e7bdaf522"; +const String wallet2_api_c_h_sha256 = "9e80c4b59a0509aa02fbf01e8df2881b89f82225d1765bfa7856cbdbaf7af116"; +const String wallet2_api_c_cpp_sha256 = "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; +const String wallet2_api_c_exp_sha256 = "d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d"; diff --git a/impls/monero.dart/lib/src/checksum_wownero.dart b/impls/monero.dart/lib/src/checksum_wownero.dart index 7d501c6..615a989 100644 --- a/impls/monero.dart/lib/src/checksum_wownero.dart +++ b/impls/monero.dart/lib/src/checksum_wownero.dart @@ -1,4 +1,4 @@ // ignore_for_file: constant_identifier_names -const String wallet2_api_c_h_sha256 = "8a8d386dd5d996c89a0586c55b295ef95ca584bf1ffa26255152b291910a0a77"; -const String wallet2_api_c_cpp_sha256 = "07d67f34a07869aaa4af6ca04e142dbad2fb1fba0e2ebdefd22bc333fd982e25-e25963cbc11ca0a0fe5eb34b9bd7c72e4f51b795"; -const String wallet2_api_c_exp_sha256 = "3673e40e1a7115552276d1d541f6e4d5a0fef47c40fff7b988f49923af84c8a4"; +const String wallet2_api_c_h_sha256 = "f99009d1ca1d1c783cc9aa0fb63f680d48753b88124fb5de2079c57b7e34c827"; +const String wallet2_api_c_cpp_sha256 = "880add77ec8c77d8054a6f21e996d6d08a37ef84e10df7220630426fd048b43c-dd46a31f3cab67b316e9239b15acf7d5cea60aa9"; +const String wallet2_api_c_exp_sha256 = "5f53ea8bbe66a5e5aa6cbc4ca00695900e08589cfd32062e88965a24252d05ba"; diff --git a/impls/monero.dart/lib/src/generated_bindings_monero.g.dart b/impls/monero.dart/lib/src/generated_bindings_monero.g.dart index cd2124b..77e2899 100644 --- a/impls/monero.dart/lib/src/generated_bindings_monero.g.dart +++ b/impls/monero.dart/lib/src/generated_bindings_monero.g.dart @@ -2763,39 +2763,6 @@ class MoneroC { _MONERO_Wallet_daemonBlockChainHeightPtr.asFunction< int Function(ffi.Pointer)>(); - int MONERO_Wallet_daemonBlockChainHeight_cached( - ffi.Pointer wallet_ptr, - ) { - return _MONERO_Wallet_daemonBlockChainHeight_cached( - wallet_ptr, - ); - } - - late final _MONERO_Wallet_daemonBlockChainHeight_cachedPtr = - _lookup)>>( - 'MONERO_Wallet_daemonBlockChainHeight_cached'); - late final _MONERO_Wallet_daemonBlockChainHeight_cached = - _MONERO_Wallet_daemonBlockChainHeight_cachedPtr.asFunction< - int Function(ffi.Pointer)>(); - - void MONERO_Wallet_daemonBlockChainHeight_runThread( - ffi.Pointer wallet_ptr, - int seconds, - ) { - return _MONERO_Wallet_daemonBlockChainHeight_runThread( - wallet_ptr, - seconds, - ); - } - - late final _MONERO_Wallet_daemonBlockChainHeight_runThreadPtr = _lookup< - ffi - .NativeFunction, ffi.Int)>>( - 'MONERO_Wallet_daemonBlockChainHeight_runThread'); - late final _MONERO_Wallet_daemonBlockChainHeight_runThread = - _MONERO_Wallet_daemonBlockChainHeight_runThreadPtr.asFunction< - void Function(ffi.Pointer, int)>(); - int MONERO_Wallet_daemonBlockChainTargetHeight( ffi.Pointer wallet_ptr, ) { diff --git a/impls/monero.dart/lib/src/generated_bindings_wownero.g.dart b/impls/monero.dart/lib/src/generated_bindings_wownero.g.dart index 2fe1eef..b7d1321 100644 --- a/impls/monero.dart/lib/src/generated_bindings_wownero.g.dart +++ b/impls/monero.dart/lib/src/generated_bindings_wownero.g.dart @@ -2740,39 +2740,6 @@ class WowneroC { _WOWNERO_Wallet_daemonBlockChainHeightPtr.asFunction< int Function(ffi.Pointer)>(); - int WOWNERO_Wallet_daemonBlockChainHeight_cached( - ffi.Pointer wallet_ptr, - ) { - return _WOWNERO_Wallet_daemonBlockChainHeight_cached( - wallet_ptr, - ); - } - - late final _WOWNERO_Wallet_daemonBlockChainHeight_cachedPtr = - _lookup)>>( - 'WOWNERO_Wallet_daemonBlockChainHeight_cached'); - late final _WOWNERO_Wallet_daemonBlockChainHeight_cached = - _WOWNERO_Wallet_daemonBlockChainHeight_cachedPtr.asFunction< - int Function(ffi.Pointer)>(); - - void WOWNERO_Wallet_daemonBlockChainHeight_runThread( - ffi.Pointer wallet_ptr, - int seconds, - ) { - return _WOWNERO_Wallet_daemonBlockChainHeight_runThread( - wallet_ptr, - seconds, - ); - } - - late final _WOWNERO_Wallet_daemonBlockChainHeight_runThreadPtr = _lookup< - ffi - .NativeFunction, ffi.Int)>>( - 'WOWNERO_Wallet_daemonBlockChainHeight_runThread'); - late final _WOWNERO_Wallet_daemonBlockChainHeight_runThread = - _WOWNERO_Wallet_daemonBlockChainHeight_runThreadPtr.asFunction< - void Function(ffi.Pointer, int)>(); - int WOWNERO_Wallet_daemonBlockChainTargetHeight( ffi.Pointer wallet_ptr, ) { diff --git a/impls/monero.dart/lib/wownero.dart b/impls/monero.dart/lib/wownero.dart index d355005..56bd43e 100644 --- a/impls/monero.dart/lib/wownero.dart +++ b/impls/monero.dart/lib/wownero.dart @@ -2108,24 +2108,6 @@ int Wallet_daemonBlockChainHeight(wallet ptr) { return daemonBlockChainHeight; } -int Wallet_daemonBlockChainHeight_cached(wallet ptr) { - debugStart?.call('WOWNERO_Wallet_daemonBlockChainHeight_cached'); - lib ??= WowneroC(DynamicLibrary.open(libPath)); - final daemonBlockChainHeight = - lib!.WOWNERO_Wallet_daemonBlockChainHeight_cached(ptr); - debugEnd?.call('WOWNERO_Wallet_daemonBlockChainHeight_cached'); - return daemonBlockChainHeight; -} - -void Wallet_daemonBlockChainHeight_runThread(wallet ptr, int seconds) { - debugStart?.call('WOWNERO_Wallet_daemonBlockChainHeight_enableRefresh'); - lib ??= WowneroC(DynamicLibrary.open(libPath)); - final ret = - lib!.WOWNERO_Wallet_daemonBlockChainHeight_runThread(ptr, seconds); - debugEnd?.call('WOWNERO_Wallet_daemonBlockChainHeight_enableRefresh'); - return ret; -} - bool Wallet_synchronized(wallet ptr) { debugStart?.call('WOWNERO_Wallet_synchronized'); lib ??= WowneroC(DynamicLibrary.open(libPath)); diff --git a/impls/monero.ts/checksum_monero.ts b/impls/monero.ts/checksum_monero.ts index b8e57cb..03bb061 100644 --- a/impls/monero.ts/checksum_monero.ts +++ b/impls/monero.ts/checksum_monero.ts @@ -1,5 +1,5 @@ export const moneroChecksum = { - wallet2_api_c_h_sha256: "198a27486ce4f014b737f52ec2879a90838c3b3ca088de8dc7d55b79a10f4a5b", - wallet2_api_c_cpp_sha256: "22b0c9dd542fb55edfe9e17da67e97ddf5f80bcdd3e126e1bd64cce54234bde1-b089f9ee69924882c5d14dd1a6991deb05d9d1cd", - wallet2_api_c_exp_sha256: "c1f785d62709fd8b849063ecf4fe8854d9e3b05b568b9716de98d33e7bdaf522", + wallet2_api_c_h_sha256: "9e80c4b59a0509aa02fbf01e8df2881b89f82225d1765bfa7856cbdbaf7af116", + wallet2_api_c_cpp_sha256: "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-b089f9ee69924882c5d14dd1a6991deb05d9d1cd", + wallet2_api_c_exp_sha256: "d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d", } diff --git a/impls/monero.ts/checksum_wownero.ts b/impls/monero.ts/checksum_wownero.ts index 8b2899c..2a6007f 100644 --- a/impls/monero.ts/checksum_wownero.ts +++ b/impls/monero.ts/checksum_wownero.ts @@ -1,5 +1,5 @@ export const wowneroChecksum = { - wallet2_api_c_h_sha256: "8a8d386dd5d996c89a0586c55b295ef95ca584bf1ffa26255152b291910a0a77", - wallet2_api_c_cpp_sha256: "07d67f34a07869aaa4af6ca04e142dbad2fb1fba0e2ebdefd22bc333fd982e25-e25963cbc11ca0a0fe5eb34b9bd7c72e4f51b795", - wallet2_api_c_exp_sha256: "3673e40e1a7115552276d1d541f6e4d5a0fef47c40fff7b988f49923af84c8a4", + wallet2_api_c_h_sha256: "f99009d1ca1d1c783cc9aa0fb63f680d48753b88124fb5de2079c57b7e34c827", + wallet2_api_c_cpp_sha256: "880add77ec8c77d8054a6f21e996d6d08a37ef84e10df7220630426fd048b43c-dd46a31f3cab67b316e9239b15acf7d5cea60aa9", + wallet2_api_c_exp_sha256: "5f53ea8bbe66a5e5aa6cbc4ca00695900e08589cfd32062e88965a24252d05ba", } diff --git a/impls/monero.ts/src/symbols.ts b/impls/monero.ts/src/symbols.ts index b2cf965..2c34a6e 100644 --- a/impls/monero.ts/src/symbols.ts +++ b/impls/monero.ts/src/symbols.ts @@ -1252,21 +1252,6 @@ export const moneroSymbols = { wallet_ptr: "pointer", ], }, - MONERO_Wallet_daemonBlockChainHeight_cached: { - nonblocking: true, - result: "u64", - parameters: ["pointer"] as [ - wallet_ptr: "pointer", - ], - }, - MONERO_Wallet_daemonBlockChainHeight_runThread: { - nonblocking: true, - result: "void", - parameters: ["pointer", "i32"] as [ - wallet_ptr: "pointer", - seconds: "i32", - ], - }, MONERO_Wallet_daemonBlockChainTargetHeight: { nonblocking: true, result: "u64", diff --git a/monero_libwallet2_api_c/CMakeLists.txt b/monero_libwallet2_api_c/CMakeLists.txt index 60be91c..dadafc6 100644 --- a/monero_libwallet2_api_c/CMakeLists.txt +++ b/monero_libwallet2_api_c/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.4.1) project(wallet2_api_c) -message(STATUS ABI_INFO = ${HOST_ABI}) +message(STATUS ABI_INFO = ${HOST_ABI}) set (CMAKE_CXX_STANDARD 11) @@ -8,14 +8,21 @@ if(${HOST_ABI} STREQUAL "x86_64-w64-mingw32") set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_LINKER "x86_64-w64-mingw32-ld") set(TARGET "x86_64-w64-mingw32") - # set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lssp") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lssp") elseif(${HOST_ABI} STREQUAL "i686-w64-mingw32") set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_LINKER "i686-w64-mingw32-ld") set(TARGET "i686-w64-mingw32") - # set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) -elseif(${HOST_ABI} STREQUAL "host-apple-ios") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lssp") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lssp") +elseif(${HOST_ABI} STREQUAL "host-apple-ios" OR + ${HOST_ABI} STREQUAL "aarch64-apple-ios") set(CMAKE_SYSTEM_NAME iOS) +elseif(${HOST_ABI} STREQUAL "host-apple-darwin" OR + ${HOST_ABI} STREQUAL "x86_64-host-apple-darwin" OR + ${HOST_ABI} STREQUAL "aarch64-host-apple-darwin") + set(CMAKE_SYSTEM_NAME Darwin) endif() if (${HOST_ABI} STREQUAL "host-apple-darwin" OR @@ -43,299 +50,10 @@ add_library( wallet2_api_c set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") -set(MONERO_DIR ${CMAKE_SOURCE_DIR}/../${MONERO_FLAVOR}) -set(EXTERNAL_LIBS_DIR ${MONERO_DIR}/contrib/depends/${HOST_ABI}) - -############ -# libsodium -############ - -add_library(sodium STATIC IMPORTED) -set_target_properties(sodium PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libsodium.a) - -############ -# OpenSSL -############ - -add_library(crypto STATIC IMPORTED) -set_target_properties(crypto PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libcrypto.a) - -add_library(ssl STATIC IMPORTED) -set_target_properties(ssl PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libssl.a) - -############ -# Boost -############ - -if(${HOST_ABI} STREQUAL "x86_64-linux-android" OR ${HOST_ABI} STREQUAL "aarch64-linux-android" OR ${HOST_ABI} STREQUAL "armv7a-linux-androideabi" OR ${HOST_ABI} STREQUAL "i686-linux-android") - set(CMAKE_LINKER ${HOST_ABI}-ld) - set(BOOST_WTF "-mt-s") - set(BOOST_WTF_PART "") -elseif(${HOST_ABI} STREQUAL "x86_64-w64-mingw32" OR ${HOST_ABI} STREQUAL "i686-w64-mingw32") - set(BOOST_WTF "-mt-s") - set(BOOST_WTF_PART "_win32") -elseif(${HOST_ABI} STREQUAL "host-apple-ios") - set(BOOST_WTF "") - set(BOOST_WTF_PART "") -else() - set(BOOST_WTF "-mt") - set(BOOST_WTF_PART "") -endif() - - -add_library(boost_chrono STATIC IMPORTED) -set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_chrono${BOOST_WTF}.a) - -# win extra -add_library(boost_locale STATIC IMPORTED) -set_target_properties(boost_locale PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_locale${BOOST_WTF}.a) - -# win extra -add_library(iconv-win STATIC IMPORTED) -set_target_properties(iconv-win PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libiconv.a) - -add_library(boost_date_time STATIC IMPORTED) -set_target_properties(boost_date_time PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_date_time${BOOST_WTF}.a) - -add_library(boost_filesystem STATIC IMPORTED) -set_target_properties(boost_filesystem PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_filesystem${BOOST_WTF}.a) - -add_library(boost_program_options STATIC IMPORTED) -set_target_properties(boost_program_options PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_program_options${BOOST_WTF}.a) - -add_library(boost_regex STATIC IMPORTED) -set_target_properties(boost_regex PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_regex${BOOST_WTF}.a) - -add_library(boost_serialization STATIC IMPORTED) -set_target_properties(boost_serialization PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_serialization${BOOST_WTF}.a) - -add_library(boost_system STATIC IMPORTED) -set_target_properties(boost_system PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_system${BOOST_WTF}.a) - -add_library(boost_thread STATIC IMPORTED) -set_target_properties(boost_thread PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_thread${BOOST_WTF_PART}${BOOST_WTF}.a) - -add_library(boost_wserialization STATIC IMPORTED) -set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libboost_wserialization${BOOST_WTF}.a) - -############# -# Polyseed -############# - -if(${HOST_ABI} STREQUAL "aarch64-meego-linux-gnu") - add_library(polyseed STATIC IMPORTED) - set_target_properties(polyseed PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib64/libpolyseed.a) -else() - add_library(polyseed STATIC IMPORTED) - set_target_properties(polyseed PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libpolyseed.a) -endif() - -add_library(polyseed-wrapper STATIC IMPORTED) -set_target_properties(polyseed-wrapper PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/polyseed/libpolyseed_wrapper.a) - -if(${HOST_ABI} STREQUAL "x86_64-w64-mingw32" OR ${HOST_ABI} STREQUAL "i686-w64-mingw32") - set(EXTRA_LIBS_POLYSEED polyseed-win) - add_library(polyseed-win STATIC IMPORTED) - set_target_properties(polyseed-win PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/external/polyseed/libpolyseed.dll.a) -endif() - -############# -# Wownero Seed -############# - -if (${MONERO_FLAVOR} STREQUAL "wownero") - set(EXTRA_LIBS_WOWNEROSEED wownero-seed) - if(${HOST_ABI} STREQUAL "aarch64-meego-linux-gnu") - add_library(wownero-seed STATIC IMPORTED) - set_target_properties(wownero-seed PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib64/libwownero-seed.a) - else() - add_library(wownero-seed STATIC IMPORTED) - set_target_properties(wownero-seed PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libwownero-seed.a) - endif() -endif() - -############# -# Utf8proc -############# - -add_library(utf8proc STATIC IMPORTED) -set_target_properties(utf8proc PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/external/utf8proc/libutf8proc.a) - -############# -# bc-ur -############# - -add_library(bc-ur STATIC IMPORTED) -set_target_properties(bc-ur PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/external/bc-ur/libbc-ur.a) - - -############# -# Monero -############# - -add_library(wallet_api STATIC IMPORTED) -set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/lib/libwallet_api.a) - -add_library(wallet STATIC IMPORTED) -set_target_properties(wallet PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/lib/libwallet.a) - -add_library(cryptonote_core STATIC IMPORTED) -set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/cryptonote_core/libcryptonote_core.a) - -add_library(cryptonote_basic STATIC IMPORTED) -set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/cryptonote_basic/libcryptonote_basic.a) - -add_library(mnemonics STATIC IMPORTED) -set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/mnemonics/libmnemonics.a) - -add_library(common STATIC IMPORTED) -set_target_properties(common PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/common/libcommon.a) - -add_library(cncrypto STATIC IMPORTED) -set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/crypto/libcncrypto.a) - -add_library(ringct STATIC IMPORTED) -set_target_properties(ringct PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/ringct/libringct.a) -add_library(ringct_basic STATIC IMPORTED) -set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/ringct/libringct_basic.a) -add_library(blockchain_db STATIC IMPORTED) -set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/blockchain_db/libblockchain_db.a) - -add_library(lmdb STATIC IMPORTED) -set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/external/db_drivers/liblmdb/liblmdb.a) - -add_library(easylogging STATIC IMPORTED) -set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/external/easylogging++/libeasylogging.a) - -add_library(unbound STATIC IMPORTED) -set_target_properties(unbound PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libunbound.a) - -add_library(epee STATIC IMPORTED) -set_target_properties(epee PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/contrib/epee/src/libepee.a) - -add_library(blocks STATIC IMPORTED) -set_target_properties(blocks PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/blocks/libblocks.a) - -add_library(checkpoints STATIC IMPORTED) -set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/checkpoints/libcheckpoints.a) - -add_library(device STATIC IMPORTED) -set_target_properties(device PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/device/libdevice.a) - -add_library(device_trezor STATIC IMPORTED) -set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/device_trezor/libdevice_trezor.a) - -add_library(multisig STATIC IMPORTED) -set_target_properties(multisig PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/multisig/libmultisig.a) - -add_library(version STATIC IMPORTED) -set_target_properties(version PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/libversion.a) - -add_library(net STATIC IMPORTED) -set_target_properties(net PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/net/libnet.a) - -add_library(hardforks STATIC IMPORTED) -set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/hardforks/libhardforks.a) - -if (${MONERO_FLAVOR} STREQUAL "monero") - set(RANDOMX_FLAVOR "randomx") -elseif(${MONERO_FLAVOR} STREQUAL "wownero") - set(RANDOMX_FLAVOR "randomwow") -endif() - -add_library(randomx STATIC IMPORTED) -set_target_properties(randomx PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/external/${RANDOMX_FLAVOR}/librandomx.a) - -add_library(rpc_base STATIC IMPORTED) -set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/rpc/librpc_base.a) - -# TODO(mrcyjanek): fix (x86_64 maybe?) -add_library(wallet-crypto STATIC IMPORTED) -set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/crypto/wallet/libwallet-crypto.a) - -add_library(cryptonote_format_utils_basic STATIC IMPORTED) -set_target_properties(cryptonote_format_utils_basic PROPERTIES IMPORTED_LOCATION - ${MONERO_DIR}/build/${HOST_ABI}/src/cryptonote_basic/libcryptonote_format_utils_basic.a) - -add_library(hidapi STATIC IMPORTED) -set_target_properties(hidapi PROPERTIES IMPORTED_LOCATION - ${EXTERNAL_LIBS_DIR}/lib/libhidapi.a) - -############# -# System -############# - -#find_library( log-lib log ) - -include_directories( ${EXTERNAL_LIBS_DIR}/include ) - -message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR}) - -if(${HOST_ABI} STREQUAL "x86_64-linux-gnu" OR - ${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR - ${HOST_ABI} STREQUAL "x86_64-linux-android") - set(EXTRA_LIBS "wallet-crypto") -else() - set(EXTRA_LIBS "") -endif() - -if (${HOST_ABI} STREQUAL "host-apple-darwin" OR - ${HOST_ABI} STREQUAL "x86_64-host-apple-darwin" OR - ${HOST_ABI} STREQUAL "aarch64-host-apple-darwin") - EXECUTE_PROCESS( COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE ) - if (NOT ${ARCHITECTURE} STREQUAL arm64) - set(EXTRA_LIBS "wallet-crypto") - endif() -endif() +set(MONERO_DIR ${CMAKE_SOURCE_DIR}/../${MONERO_FLAVOR}) +set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../contrib/depends/${HOST_ABI}) if (${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR ${HOST_ABI} STREQUAL "aarch64-apple-darwin11") set(EXTRA_LIBS_APPLE "-framework IOKit" "-framework CoreFoundation" "-framework Cocoa" hidapi) @@ -343,35 +61,31 @@ if (${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR ${HOST_ABI} STREQUAL "aarch6 elseif(${HOST_ABI} STREQUAL "host-apple-darwin" OR ${HOST_ABI} STREQUAL "x86_64-host-apple-darwin" OR ${HOST_ABI} STREQUAL "aarch64-host-apple-darwin") - set(EXTRA_LIBS_APPLE "-framework IOKit" "-framework CoreFoundation" "-framework Cocoa" apple_nghttp2) -elseif(${HOST_ABI} STREQUAL "host-apple-ios") + set(EXTRA_LIBS_APPLE "-framework IOKit" "-framework CoreFoundation" "-framework Cocoa" apple_nghttp2) +elseif(${HOST_ABI} STREQUAL "host-apple-ios" OR ${HOST_ABI} STREQUAL "aarch64-apple-ios" OR ${HOST_ABI} STREQUAL "arm64-apple-ios") set(EXTRA_LIBS_APPLE "-framework IOKit" "-framework CoreFoundation" iconv ) endif() if(${HOST_ABI} STREQUAL "x86_64-w64-mingw32" OR ${HOST_ABI} STREQUAL "i686-w64-mingw32") - set_target_properties(wallet2_api_c PROPERTIES SUFFIX ".dll") - set(EXTRA_LIBS_WINDOWS ssp wsock32 ws2_32 iconv-win iphlpapi crypt32 hidapi) target_link_options(wallet2_api_c PRIVATE -static-libgcc -static-libstdc++) endif() -if(${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR ${HOST_ABI} STREQUAL "aarch64-apple-darwin11" OR ${HOST_ABI} STREQUAL "host-apple-darwin" OR ${HOST_ABI} STREQUAL "x86_64-host-apple-darwin" OR ${HOST_ABI} STREQUAL "aarch64-host-apple-darwin" OR ${HOST_ABI} STREQUAL "host-apple-ios") +if(${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR ${HOST_ABI} STREQUAL "aarch64-apple-darwin11" OR ${HOST_ABI} STREQUAL "host-apple-darwin" OR ${HOST_ABI} STREQUAL "x86_64-host-apple-darwin" OR ${HOST_ABI} STREQUAL "aarch64-host-apple-darwin" OR ${HOST_ABI} STREQUAL "host-apple-ios" OR ${HOST_ABI} STREQUAL "aarch64-apple-ios") set_target_properties(wallet2_api_c PROPERTIES SUFFIX ".dylib") set_target_properties(wallet2_api_c PROPERTIES NO_SONAME 1) endif() -if (${MONERO_FLAVOR} STREQUAL "monero") - target_compile_definitions(wallet2_api_c PRIVATE FLAVOR_MONERO) - set(BCUR_ENABLED bc-ur) -elseif(${MONERO_FLAVOR} STREQUAL "wownero") - target_compile_definitions(wallet2_api_c PRIVATE FLAVOR_WOWNERO) -endif() - -if(NOT ${HOST_ABI} STREQUAL "x86_64-apple-darwin11" AND NOT ${HOST_ABI} STREQUAL "aarch64-apple-darwin11" AND NOT ${HOST_ABI} STREQUAL "host-apple-darwin" AND NOT ${HOST_ABI} STREQUAL "x86_64-host-apple-darwin" AND NOT ${HOST_ABI} STREQUAL "aarch64-host-apple-darwin" AND NOT ${HOST_ABI} STREQUAL "host-apple-ios") +if(NOT ${HOST_ABI} STREQUAL "x86_64-apple-darwin11" AND NOT ${HOST_ABI} STREQUAL "aarch64-apple-darwin11" AND NOT ${HOST_ABI} STREQUAL "aarch64-apple-darwin" AND NOT ${HOST_ABI} STREQUAL "x86_64-apple-darwin" AND NOT ${HOST_ABI} STREQUAL "host-apple-darwin" AND NOT ${HOST_ABI} STREQUAL "x86_64-host-apple-darwin" AND NOT ${HOST_ABI} STREQUAL "aarch64-host-apple-darwin" AND NOT ${HOST_ABI} STREQUAL "host-apple-ios" AND NOT ${HOST_ABI} STREQUAL "aarch64-apple-ios") set_target_properties(wallet2_api_c PROPERTIES LINK_FLAGS "-Wl,--exclude-libs,ALL") endif() -if(${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR ${HOST_ABI} STREQUAL "aarch64-apple-darwin11" OR ${HOST_ABI} STREQUAL "host-apple-darwin" OR ${HOST_ABI} STREQUAL "x86_64-host-apple-darwin" OR ${HOST_ABI} STREQUAL "aarch64-host-apple-darwin" OR ${HOST_ABI} STREQUAL "host-apple-ios") +add_subdirectory("${CMAKE_SOURCE_DIR}/../${MONERO_FLAVOR}" ${CMAKE_BINARY_DIR}/${MONERO_FLAVOR}_build EXCLUDE_FROM_ALL) + + + + +if(${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR ${HOST_ABI} STREQUAL "aarch64-apple-darwin11" OR ${HOST_ABI} STREQUAL "host-apple-darwin" OR ${HOST_ABI} STREQUAL "x86_64-host-apple-darwin" OR ${HOST_ABI} STREQUAL "aarch64-host-apple-darwin" OR ${HOST_ABI} STREQUAL "host-apple-ios" OR ${HOST_ABI} STREQUAL "aarch64-apple-ios") if (${MONERO_FLAVOR} STREQUAL "monero") set(EXPORTED_SYMBOLS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/monero_libwallet2_api_c.exp) elseif(${MONERO_FLAVOR} STREQUAL "wownero") @@ -382,60 +96,21 @@ if(${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR ${HOST_ABI} STREQUAL "aarch64 set_target_properties(${TARGET} PROPERTIES LINK_DEPENDS ${EXPORTED_SYMBOLS_FILE}) endif() -target_link_libraries( wallet2_api_c +if(${MONERO_FLAVOR} STREQUAL "wownero") + add_subdirectory(wownero-seed EXCLUDE_FROM_ALL) + set(EXTRA_LIBS_WOWNEROSEED wownero-seed) +endif() - wallet_api - wallet - cryptonote_core - cryptonote_basic - cryptonote_format_utils_basic - mnemonics - ringct - ringct_basic - net - common - cncrypto - blockchain_db - lmdb - easylogging - unbound - epee - blocks - checkpoints - device - device_trezor - multisig - version - randomx - hardforks - rpc_base - ${EXTRA_LIBS} +#get_cmake_property(_variableNames VARIABLES) +#list (SORT _variableNames) +#foreach (_variableName ${_variableNames}) +# message(STATUS "${_variableName}=${${_variableName}}") +#endforeach() - boost_chrono - boost_locale - boost_date_time - boost_filesystem - boost_program_options - boost_regex - boost_serialization - boost_system - boost_thread - boost_wserialization +target_link_libraries( wallet2_api_c - polyseed - polyseed-wrapper - ${EXTRA_LIBS_POLYSEED} + wallet_api ${EXTRA_LIBS_WOWNEROSEED} - utf8proc - - ${BCUR_ENABLED} - - ssl - crypto - - sodium - - ${EXTRA_LIBS_WINDOWS} - ${EXTRA_LIBS_APPLE} ${EXTRA_LIBS_ANDROID} - ) + ${wallet_api_LIB_DEPENDS} + ) \ No newline at end of file diff --git a/monero_libwallet2_api_c/monero_libwallet2_api_c.exp b/monero_libwallet2_api_c/monero_libwallet2_api_c.exp index 3b2931c..d42f14a 100644 --- a/monero_libwallet2_api_c/monero_libwallet2_api_c.exp +++ b/monero_libwallet2_api_c/monero_libwallet2_api_c.exp @@ -165,8 +165,6 @@ _MONERO_Wallet_blockChainHeight _MONERO_Wallet_approximateBlockChainHeight _MONERO_Wallet_estimateBlockChainHeight _MONERO_Wallet_daemonBlockChainHeight -_MONERO_Wallet_daemonBlockChainHeight_cached -_MONERO_Wallet_daemonBlockChainHeight_runThread _MONERO_Wallet_daemonBlockChainTargetHeight _MONERO_Wallet_synchronized _MONERO_Wallet_displayAmount diff --git a/monero_libwallet2_api_c/src/main/cpp/helpers.cpp b/monero_libwallet2_api_c/src/main/cpp/helpers.cpp index 8e45eba..324c12d 100644 --- a/monero_libwallet2_api_c/src/main/cpp/helpers.cpp +++ b/monero_libwallet2_api_c/src/main/cpp/helpers.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #ifdef __ANDROID__ #include @@ -260,4 +262,4 @@ std::vector splitStringUint(const std::string& str, const std::string& } tokens.push_back(std::stoull(content)); // Inserting the last token return tokens; -} +} \ No newline at end of file diff --git a/monero_libwallet2_api_c/src/main/cpp/helpers.hpp b/monero_libwallet2_api_c/src/main/cpp/helpers.hpp index c3a64e6..2c64394 100644 --- a/monero_libwallet2_api_c/src/main/cpp/helpers.hpp +++ b/monero_libwallet2_api_c/src/main/cpp/helpers.hpp @@ -2,6 +2,24 @@ #include #include #include +#include + +// Debug macros +#define DEBUG_START() \ + try { + +#define DEBUG_END() \ + } catch (const std::exception &e) { \ + std::cerr << "Exception caught in function: " << __FUNCTION__ \ + << " at " << __FILE__ << ":" << __LINE__ << std::endl \ + << "Message: " << e.what() << std::endl; \ + std::abort(); \ + } catch (...) { \ + std::cerr << "Unknown exception caught in function: " << __FUNCTION__ \ + << " at " << __FILE__ << ":" << __LINE__ << std::endl; \ + std::abort(); \ + } + const char* vectorToString(const std::vector& vec, const std::string separator); const char* vectorToString(const std::vector& vec, const std::string separator); diff --git a/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h b/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h index 4d3d39f..02869fe 100644 --- a/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h +++ b/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h @@ -1,6 +1,6 @@ #ifndef MONEROC_CHECKSUMS #define MONEROC_CHECKSUMS -const char * MONERO_wallet2_api_c_h_sha256 = "198a27486ce4f014b737f52ec2879a90838c3b3ca088de8dc7d55b79a10f4a5b"; -const char * MONERO_wallet2_api_c_cpp_sha256 = "22b0c9dd542fb55edfe9e17da67e97ddf5f80bcdd3e126e1bd64cce54234bde1-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; -const char * MONERO_wallet2_api_c_exp_sha256 = "c1f785d62709fd8b849063ecf4fe8854d9e3b05b568b9716de98d33e7bdaf522"; +const char * MONERO_wallet2_api_c_h_sha256 = "9e80c4b59a0509aa02fbf01e8df2881b89f82225d1765bfa7856cbdbaf7af116"; +const char * MONERO_wallet2_api_c_cpp_sha256 = "d229507db508e574bd2badf4819a38dbead8c16a84311ad32c22c887a6003439-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; +const char * MONERO_wallet2_api_c_exp_sha256 = "d0f95f1f3bc49f1f59fe4eb0b61826128d7d3bb75405d5a01a252d02db03097d"; #endif diff --git a/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp b/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp index 0cc2f9f..19192cf 100644 --- a/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp +++ b/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp @@ -63,252 +63,346 @@ extern "C" // PendingTransaction int MONERO_PendingTransaction_status(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->status(); + DEBUG_END() } const char* MONERO_PendingTransaction_errorString(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::string str = pendingTx->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool MONERO_PendingTransaction_commit(void* pendingTx_ptr, const char* filename, bool overwrite) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->commit(std::string(filename), overwrite); + DEBUG_END() } const char* MONERO_PendingTransaction_commitUR(void* pendingTx_ptr, int max_fragment_length) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::string str = pendingTx->commitUR(max_fragment_length); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } uint64_t MONERO_PendingTransaction_amount(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->amount(); + DEBUG_END() } uint64_t MONERO_PendingTransaction_dust(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->dust(); + DEBUG_END() } uint64_t MONERO_PendingTransaction_fee(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->fee(); + DEBUG_END() } const char* MONERO_PendingTransaction_txid(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector txid = pendingTx->txid(); return vectorToString(txid, std::string(separator)); + DEBUG_END() } uint64_t MONERO_PendingTransaction_txCount(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->txCount(); + DEBUG_END() } const char* MONERO_PendingTransaction_subaddrAccount(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector subaddrAccount = pendingTx->subaddrAccount(); return vectorToString(subaddrAccount, std::string(separator)); + DEBUG_END() } const char* MONERO_PendingTransaction_subaddrIndices(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector> subaddrIndices = pendingTx->subaddrIndices(); return vectorToString(subaddrIndices, std::string(separator)); + DEBUG_END() } const char* MONERO_PendingTransaction_multisigSignData(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::string str = pendingTx->multisigSignData(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void MONERO_PendingTransaction_signMultisigTx(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->signMultisigTx(); + DEBUG_END() } const char* MONERO_PendingTransaction_signersKeys(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector txid = pendingTx->signersKeys(); return vectorToString(txid, std::string(separator)); + DEBUG_END() } const char* MONERO_PendingTransaction_hex(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector txid = pendingTx->hex(); return vectorToString(txid, std::string(separator)); + DEBUG_END() } const char* MONERO_PendingTransaction_txKey(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector txid = pendingTx->txKey(); return vectorToString(txid, std::string(separator)); + DEBUG_END() } // UnsignedTransaction int MONERO_UnsignedTransaction_status(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return unsignedTx->status(); + DEBUG_END() } const char* MONERO_UnsignedTransaction_errorString(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); std::string str = unsignedTx->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_UnsignedTransaction_amount(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->amount(), std::string(separator)); + DEBUG_END() } const char* MONERO_UnsignedTransaction_fee(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->fee(), std::string(separator)); + DEBUG_END() } const char* MONERO_UnsignedTransaction_mixin(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->mixin(), std::string(separator)); + DEBUG_END() } const char* MONERO_UnsignedTransaction_confirmationMessage(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); std::string str = unsignedTx->confirmationMessage(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_UnsignedTransaction_paymentId(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->paymentId(), std::string(separator)); + DEBUG_END() } const char* MONERO_UnsignedTransaction_recipientAddress(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->recipientAddress(), std::string(separator)); + DEBUG_END() } uint64_t MONERO_UnsignedTransaction_minMixinCount(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return unsignedTx->minMixinCount(); + DEBUG_END() } uint64_t MONERO_UnsignedTransaction_txCount(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return unsignedTx->txCount(); + DEBUG_END() } bool MONERO_UnsignedTransaction_sign(void* unsignedTx_ptr, const char* signedFileName) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return unsignedTx->sign(std::string(signedFileName)); + DEBUG_END() } const char* MONERO_UnsignedTransaction_signUR(void* unsignedTx_ptr, int max_fragment_length) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); std::string str = unsignedTx->signUR(max_fragment_length); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // TransactionInfo int MONERO_TransactionInfo_direction(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->direction(); + DEBUG_END() } bool MONERO_TransactionInfo_isPending(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->isPending(); + DEBUG_END() } bool MONERO_TransactionInfo_isFailed(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->isFailed(); + DEBUG_END() } bool MONERO_TransactionInfo_isCoinbase(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->isCoinbase(); + DEBUG_END() } uint64_t MONERO_TransactionInfo_amount(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->amount(); + DEBUG_END() } uint64_t MONERO_TransactionInfo_fee(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->fee(); + DEBUG_END() } uint64_t MONERO_TransactionInfo_blockHeight(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->blockHeight(); + DEBUG_END() } const char* MONERO_TransactionInfo_description(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->description(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_TransactionInfo_subaddrIndex(void* txInfo_ptr, const char* separator) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::set subaddrIndex = txInfo->subaddrIndex(); return vectorToString(subaddrIndex, std::string(separator)); + DEBUG_END() } uint32_t MONERO_TransactionInfo_subaddrAccount(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->subaddrAccount(); + DEBUG_END() } const char* MONERO_TransactionInfo_label(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->label(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } uint64_t MONERO_TransactionInfo_confirmations(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->confirmations(); + DEBUG_END() } uint64_t MONERO_TransactionInfo_unlockTime(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->unlockTime(); + DEBUG_END() } const char* MONERO_TransactionInfo_hash(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->hash(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } uint64_t MONERO_TransactionInfo_timestamp(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->timestamp(); + DEBUG_END() } const char* MONERO_TransactionInfo_paymentId(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->paymentId(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } int MONERO_TransactionInfo_transfers_count(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->transfers().size(); + DEBUG_END() } uint64_t MONERO_TransactionInfo_transfers_amount(void* txInfo_ptr, int index) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->transfers()[index].amount; + DEBUG_END() } const char* MONERO_TransactionInfo_transfers_address(void* txInfo_ptr, int index) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->transfers()[index].address; const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } @@ -316,249 +410,329 @@ const char* MONERO_TransactionInfo_transfers_address(void* txInfo_ptr, int index // TransactionHistory int MONERO_TransactionHistory_count(void* txHistory_ptr) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return txHistory->count(); + DEBUG_END() } void* MONERO_TransactionHistory_transaction(void* txHistory_ptr, int index) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return reinterpret_cast(txHistory->transaction(index)); + DEBUG_END() } void* MONERO_TransactionHistory_transactionById(void* txHistory_ptr, const char* id) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return reinterpret_cast(txHistory->transaction(std::string(id))); + DEBUG_END() } void MONERO_TransactionHistory_refresh(void* txHistory_ptr) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return txHistory->refresh(); + DEBUG_END() } void MONERO_TransactionHistory_setTxNote(void* txHistory_ptr, const char* txid, const char* note) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return txHistory->setTxNote(std::string(txid), std::string(note)); + DEBUG_END() } // AddressBokRow // std::string extra; const char* MONERO_AddressBookRow_extra(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); std::string str = addressBookRow->extra; const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getAddress() const {return m_address;} const char* MONERO_AddressBookRow_getAddress(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); std::string str = addressBookRow->getAddress(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getDescription() const {return m_description;} const char* MONERO_AddressBookRow_getDescription(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); std::string str = addressBookRow->getDescription(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getPaymentId() const {return m_paymentId;} const char* MONERO_AddressBookRow_getPaymentId(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); std::string str = addressBookRow->getPaymentId(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::size_t getRowId() const {return m_rowId;} size_t MONERO_AddressBookRow_getRowId(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); return addressBookRow->getRowId(); + DEBUG_END() } // AddressBook // virtual std::vector getAll() const = 0; int MONERO_AddressBook_getAll_size(void* addressBook_ptr) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->getAll().size(); + DEBUG_END() } void* MONERO_AddressBook_getAll_byIndex(void* addressBook_ptr, int index) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->getAll()[index]; + DEBUG_END() } // virtual bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) = 0; bool MONERO_AddressBook_addRow(void* addressBook_ptr, const char* dst_addr , const char* payment_id, const char* description) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->addRow(std::string(dst_addr), std::string(payment_id), std::string(description)); + DEBUG_END() } // virtual bool deleteRow(std::size_t rowId) = 0; bool MONERO_AddressBook_deleteRow(void* addressBook_ptr, size_t rowId) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->deleteRow(rowId); + DEBUG_END() } // virtual bool setDescription(std::size_t index, const std::string &description) = 0; bool MONERO_AddressBook_setDescription(void* addressBook_ptr, size_t rowId, const char* description) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->setDescription(rowId, std::string(description)); + DEBUG_END() } // virtual void refresh() = 0; void MONERO_AddressBook_refresh(void* addressBook_ptr) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->refresh(); + DEBUG_END() } // virtual std::string errorString() const = 0; const char* MONERO_AddressBook_errorString(void* addressBook_ptr) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); std::string str = addressBook->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual int errorCode() const = 0; int MONERO_AddressBook_errorCode(void* addressBook_ptr) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->errorCode(); + DEBUG_END() } // virtual int lookupPaymentID(const std::string &payment_id) const = 0; int MONERO_AddressBook_lookupPaymentID(void* addressBook_ptr, const char* payment_id) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->lookupPaymentID(std::string(payment_id)); + DEBUG_END() } // CoinsInfo uint64_t MONERO_CoinsInfo_blockHeight(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->blockHeight(); + DEBUG_END() } // virtual std::string hash() const = 0; const char* MONERO_CoinsInfo_hash(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->hash(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual size_t internalOutputIndex() const = 0; size_t MONERO_CoinsInfo_internalOutputIndex(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->internalOutputIndex(); + DEBUG_END() } // virtual uint64_t globalOutputIndex() const = 0; uint64_t MONERO_CoinsInfo_globalOutputIndex(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->globalOutputIndex(); + DEBUG_END() } // virtual bool spent() const = 0; bool MONERO_CoinsInfo_spent(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->spent(); + DEBUG_END() } // virtual bool frozen() const = 0; bool MONERO_CoinsInfo_frozen(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->frozen(); + DEBUG_END() } // virtual uint64_t spentHeight() const = 0; uint64_t MONERO_CoinsInfo_spentHeight(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->spentHeight(); + DEBUG_END() } // virtual uint64_t amount() const = 0; uint64_t MONERO_CoinsInfo_amount(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->amount(); + DEBUG_END() } // virtual bool rct() const = 0; bool MONERO_CoinsInfo_rct(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->rct(); + DEBUG_END() } // virtual bool keyImageKnown() const = 0; bool MONERO_CoinsInfo_keyImageKnown(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->keyImageKnown(); + DEBUG_END() } // virtual size_t pkIndex() const = 0; size_t MONERO_CoinsInfo_pkIndex(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->pkIndex(); + DEBUG_END() } // virtual uint32_t subaddrIndex() const = 0; uint32_t MONERO_CoinsInfo_subaddrIndex(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->subaddrIndex(); + DEBUG_END() } // virtual uint32_t subaddrAccount() const = 0; uint32_t MONERO_CoinsInfo_subaddrAccount(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->subaddrAccount(); + DEBUG_END() } // virtual std::string address() const = 0; const char* MONERO_CoinsInfo_address(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->address(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual std::string addressLabel() const = 0; const char* MONERO_CoinsInfo_addressLabel(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->addressLabel(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual std::string keyImage() const = 0; const char* MONERO_CoinsInfo_keyImage(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->keyImage(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual uint64_t unlockTime() const = 0; uint64_t MONERO_CoinsInfo_unlockTime(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->unlockTime(); + DEBUG_END() } // virtual bool unlocked() const = 0; bool MONERO_CoinsInfo_unlocked(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->unlocked(); + DEBUG_END() } // virtual std::string pubKey() const = 0; const char* MONERO_CoinsInfo_pubKey(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->pubKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual bool coinbase() const = 0; bool MONERO_CoinsInfo_coinbase(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->coinbase(); + DEBUG_END() } // virtual std::string description() const = 0; const char* MONERO_CoinsInfo_description(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->description(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } @@ -567,173 +741,225 @@ const char* MONERO_CoinsInfo_description(void* coinsInfo_ptr) { // virtual ~Coins() = 0; // virtual int count() const = 0; int MONERO_Coins_count(void* coins_ptr) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->count(); + DEBUG_END() } // virtual CoinsInfo * coin(int index) const = 0; void* MONERO_Coins_coin(void* coins_ptr, int index) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->coin(index); + DEBUG_END() } int MONERO_Coins_getAll_size(void* coins_ptr) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->getAll().size(); + DEBUG_END() } void* MONERO_Coins_getAll_byIndex(void* coins_ptr, int index) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->getAll()[index]; + DEBUG_END() } // virtual std::vector getAll() const = 0; // virtual void refresh() = 0; void MONERO_Coins_refresh(void* coins_ptr) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->refresh(); + DEBUG_END() } // virtual void setFrozen(std::string public_key) = 0; void MONERO_Coins_setFrozenByPublicKey(void* coins_ptr, const char* public_key) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->setFrozen(std::string(public_key)); + DEBUG_END() } // virtual void setFrozen(int index) = 0; void MONERO_Coins_setFrozen(void* coins_ptr, int index) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->setFrozen(index); + DEBUG_END() } // virtual void thaw(int index) = 0; void MONERO_Coins_thaw(void* coins_ptr, int index) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->thaw(index); + DEBUG_END() } // virtual void thaw(std::string public_key) = 0; void MONERO_Coins_thawByPublicKey(void* coins_ptr, const char* public_key) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->thaw(std::string(public_key)); + DEBUG_END() } // virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; bool MONERO_Coins_isTransferUnlocked(void* coins_ptr, uint64_t unlockTime, uint64_t blockHeight) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->isTransferUnlocked(unlockTime, blockHeight); + DEBUG_END() } // virtual void setDescription(const std::string &public_key, const std::string &description) = 0; void MONERO_Coins_setDescription(void* coins_ptr, const char* public_key, const char* description) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); - coins->setDescription(std::string(public_key), std::string(description)); + return coins->setDescription(std::string(public_key), std::string(description)); + DEBUG_END() } // SubaddressRow // std::string extra; const char* MONERO_SubaddressRow_extra(void* subaddressRow_ptr) { + DEBUG_START() Monero::SubaddressRow *subaddressRow = reinterpret_cast(subaddressRow_ptr); std::string str = subaddressRow->extra; const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getAddress() const {return m_address;} const char* MONERO_SubaddressRow_getAddress(void* subaddressRow_ptr) { + DEBUG_START() Monero::SubaddressRow *subaddressRow = reinterpret_cast(subaddressRow_ptr); std::string str = subaddressRow->getAddress(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getLabel() const {return m_label;} const char* MONERO_SubaddressRow_getLabel(void* subaddressRow_ptr) { + DEBUG_START() Monero::SubaddressRow *subaddressRow = reinterpret_cast(subaddressRow_ptr); std::string str = subaddressRow->getLabel(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::size_t getRowId() const {return m_rowId;} size_t MONERO_SubaddressRow_getRowId(void* subaddressRow_ptr) { + DEBUG_START() Monero::SubaddressRow *subaddressRow = reinterpret_cast(subaddressRow_ptr); return subaddressRow->getRowId(); + DEBUG_END() } // Subaddress int MONERO_Subaddress_getAll_size(void* subaddress_ptr) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->getAll().size(); + DEBUG_END() } void* MONERO_Subaddress_getAll_byIndex(void* subaddress_ptr, int index) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->getAll()[index]; + DEBUG_END() } // virtual void addRow(uint32_t accountIndex, const std::string &label) = 0; void MONERO_Subaddress_addRow(void* subaddress_ptr, uint32_t accountIndex, const char* label) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->addRow(accountIndex, std::string(label)); + DEBUG_END() } // virtual void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0; void MONERO_Subaddress_setLabel(void* subaddress_ptr, uint32_t accountIndex, uint32_t addressIndex, const char* label) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->setLabel(accountIndex, addressIndex, std::string(label)); + DEBUG_END() } // virtual void refresh(uint32_t accountIndex) = 0; void MONERO_Subaddress_refresh(void* subaddress_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->refresh(accountIndex); + DEBUG_END() } // SubaddressAccountRow // std::string extra; const char* MONERO_SubaddressAccountRow_extra(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->extra; const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getAddress() const {return m_address;} const char* MONERO_SubaddressAccountRow_getAddress(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->getAddress(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getLabel() const {return m_label;} const char* MONERO_SubaddressAccountRow_getLabel(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->getLabel(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getBalance() const {return m_balance;} const char* MONERO_SubaddressAccountRow_getBalance(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->getBalance(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getUnlockedBalance() const {return m_unlockedBalance;} const char* MONERO_SubaddressAccountRow_getUnlockedBalance(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->getUnlockedBalance(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::size_t getRowId() const {return m_rowId;} size_t MONERO_SubaddressAccountRow_getRowId(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); return subaddressAccountRow->getRowId(); + DEBUG_END() } // struct SubaddressAccount @@ -741,50 +967,68 @@ size_t MONERO_SubaddressAccountRow_getRowId(void* subaddressAccountRow_ptr) { // virtual ~SubaddressAccount() = 0; // virtual std::vector getAll() const = 0; int MONERO_SubaddressAccount_getAll_size(void* subaddressAccount_ptr) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->getAll().size(); + DEBUG_END() } void* MONERO_SubaddressAccount_getAll_byIndex(void* subaddressAccount_ptr, int index) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->getAll()[index]; + DEBUG_END() } // virtual void addRow(const std::string &label) = 0; void MONERO_SubaddressAccount_addRow(void* subaddressAccount_ptr, const char* label) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->addRow(std::string(label)); + DEBUG_END() } // virtual void setLabel(uint32_t accountIndex, const std::string &label) = 0; void MONERO_SubaddressAccount_setLabel(void* subaddressAccount_ptr, uint32_t accountIndex, const char* label) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->setLabel(accountIndex, std::string(label)); + DEBUG_END() } // virtual void refresh() = 0; void MONERO_SubaddressAccount_refresh(void* subaddressAccount_ptr) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->refresh(); + DEBUG_END() } // MultisigState // bool isMultisig; bool MONERO_MultisigState_isMultisig(void* multisigState_ptr) { + DEBUG_START() Monero::MultisigState *multisigState = reinterpret_cast(multisigState_ptr); return multisigState->isMultisig; + DEBUG_END() } // bool isReady; bool MONERO_MultisigState_isReady(void* multisigState_ptr) { + DEBUG_START() Monero::MultisigState *multisigState = reinterpret_cast(multisigState_ptr); return multisigState->isReady; + DEBUG_END() } // uint32_t threshold; uint32_t MONERO_MultisigState_threshold(void* multisigState_ptr) { + DEBUG_START() Monero::MultisigState *multisigState = reinterpret_cast(multisigState_ptr); return multisigState->threshold; + DEBUG_END() } // uint32_t total; uint32_t MONERO_MultisigState_total(void* multisigState_ptr) { + DEBUG_START() Monero::MultisigState *multisigState = reinterpret_cast(multisigState_ptr); return multisigState->total; + DEBUG_END() } // DeviceProgress @@ -792,364 +1036,468 @@ uint32_t MONERO_MultisigState_total(void* multisigState_ptr) { // virtual double progress() const { return m_progress; } bool MONERO_DeviceProgress_progress(void* deviceProgress_ptr) { + DEBUG_START() Monero::DeviceProgress *deviceProgress = reinterpret_cast(deviceProgress_ptr); return deviceProgress->progress(); + DEBUG_END() } // virtual bool indeterminate() const { return m_indeterminate; } bool MONERO_DeviceProgress_indeterminate(void* deviceProgress_ptr) { + DEBUG_START() Monero::DeviceProgress *deviceProgress = reinterpret_cast(deviceProgress_ptr); return deviceProgress->indeterminate(); + DEBUG_END() } // Wallet const char* MONERO_Wallet_seed(void* wallet_ptr, const char* seed_offset) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->seed(std::string(seed_offset)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_getSeedLanguage(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getSeedLanguage(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void MONERO_Wallet_setSeedLanguage(void* wallet_ptr, const char* arg) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setSeedLanguage(std::string(arg)); + DEBUG_END() } int MONERO_Wallet_status(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->status(); + DEBUG_END() } const char* MONERO_Wallet_errorString(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool MONERO_Wallet_setPassword(void* wallet_ptr, const char* password) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setPassword(std::string(password)); + DEBUG_END() } const char* MONERO_Wallet_getPassword(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getPassword(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool MONERO_Wallet_setDevicePin(void* wallet_ptr, const char* pin) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setDevicePin(std::string(pin)); + DEBUG_END() } bool MONERO_Wallet_setDevicePassphrase(void* wallet_ptr, const char* passphrase) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setDevicePassphrase(std::string(passphrase)); + DEBUG_END() } const char* MONERO_Wallet_address(void* wallet_ptr, uint64_t accountIndex, uint64_t addressIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->address(accountIndex, addressIndex); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_path(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->path(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } int MONERO_Wallet_nettype(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->nettype(); + DEBUG_END() } uint8_t MONERO_Wallet_useForkRules(void* wallet_ptr, uint8_t version, int64_t early_blocks) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->useForkRules(version, early_blocks); + DEBUG_END() } const char* MONERO_Wallet_integratedAddress(void* wallet_ptr, const char* payment_id) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->integratedAddress(std::string(payment_id)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_secretViewKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->secretViewKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_publicViewKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->publicViewKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_secretSpendKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->secretSpendKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_publicSpendKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->publicSpendKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_publicMultisigSignerKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->publicMultisigSignerKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void MONERO_Wallet_stop(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); - wallet->stop(); + return wallet->stop(); + DEBUG_END() } bool MONERO_Wallet_store(void* wallet_ptr, const char* path) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->store(std::string(path)); + DEBUG_END() } const char* MONERO_Wallet_filename(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->filename(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_keysFilename(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->keysFilename(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false, const std::string &proxy_address = "") = 0; bool MONERO_Wallet_init(void* wallet_ptr, const char* daemon_address, uint64_t upper_transaction_size_limit, const char* daemon_username, const char* daemon_password, bool use_ssl, bool lightWallet, const char* proxy_address) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->init(std::string(daemon_address), upper_transaction_size_limit, std::string(daemon_username), std::string(daemon_password), use_ssl, lightWallet, std::string(proxy_address)); + DEBUG_END() } bool MONERO_Wallet_createWatchOnly(void* wallet_ptr, const char* path, const char* password, const char* language) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->createWatchOnly(std::string(path), std::string(password), std::string(language)); + DEBUG_END() } void MONERO_Wallet_setRefreshFromBlockHeight(void* wallet_ptr, uint64_t refresh_from_block_height) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setRefreshFromBlockHeight(refresh_from_block_height); + DEBUG_END() } uint64_t MONERO_Wallet_getRefreshFromBlockHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getRefreshFromBlockHeight(); + DEBUG_END() } void MONERO_Wallet_setRecoveringFromSeed(void* wallet_ptr, bool recoveringFromSeed) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setRecoveringFromSeed(recoveringFromSeed); + DEBUG_END() } void MONERO_Wallet_setRecoveringFromDevice(void* wallet_ptr, bool recoveringFromDevice) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setRecoveringFromDevice(recoveringFromDevice); + DEBUG_END() } void MONERO_Wallet_setSubaddressLookahead(void* wallet_ptr, uint32_t major, uint32_t minor) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setSubaddressLookahead(major, minor); + DEBUG_END() } bool MONERO_Wallet_connectToDaemon(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->connectToDaemon(); + DEBUG_END() } int MONERO_Wallet_connected(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->connected(); + DEBUG_END() } void MONERO_Wallet_setTrustedDaemon(void* wallet_ptr, bool arg) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setTrustedDaemon(arg); + DEBUG_END() } bool MONERO_Wallet_trustedDaemon(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->trustedDaemon(); + DEBUG_END() } bool MONERO_Wallet_setProxy(void* wallet_ptr, const char* address) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setProxy(std::string(address)); + DEBUG_END() } uint64_t MONERO_Wallet_balance(void* wallet_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->balance(accountIndex); + DEBUG_END() } uint64_t MONERO_Wallet_unlockedBalance(void* wallet_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->unlockedBalance(accountIndex); + DEBUG_END() } uint64_t MONERO_Wallet_viewOnlyBalance(void* wallet_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->viewOnlyBalance(accountIndex); + DEBUG_END() } // TODO bool MONERO_Wallet_watchOnly(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->watchOnly(); + DEBUG_END() } bool MONERO_Wallet_isDeterministic(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->isDeterministic(); + DEBUG_END() } uint64_t MONERO_Wallet_blockChainHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->blockChainHeight(); + DEBUG_END() } uint64_t MONERO_Wallet_approximateBlockChainHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->approximateBlockChainHeight(); + DEBUG_END() } uint64_t MONERO_Wallet_estimateBlockChainHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->estimateBlockChainHeight(); + DEBUG_END() } uint64_t MONERO_Wallet_daemonBlockChainHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->daemonBlockChainHeight(); -} - -uint64_t daemonBlockChainHeight_cached = 0; - -uint64_t MONERO_Wallet_daemonBlockChainHeight_cached(void* wallet_ptr) { - return daemonBlockChainHeight_cached; -} - -void MONERO_Wallet_daemonBlockChainHeight_runThread(void* wallet_ptr, int seconds) { - std::cout << "DEPRECATED: this was used as an experiment, and will be removed in newer release. use ${COIN}_cw_* listener functions instead." << std::endl; - while (true) { - Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); - daemonBlockChainHeight_cached = wallet->daemonBlockChainHeight(); - sleep(seconds); - std::cout << "MONERO: TICK: MONERO_Wallet_daemonBlockChainHeight_runThread(" << seconds << "): " << daemonBlockChainHeight_cached << std::endl; - } + DEBUG_END() } uint64_t MONERO_Wallet_daemonBlockChainTargetHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->daemonBlockChainTargetHeight(); + DEBUG_END() } bool MONERO_Wallet_synchronized(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->synchronized(); + DEBUG_END() } const char* MONERO_Wallet_displayAmount(uint64_t amount) { + DEBUG_START() std::string str = Monero::Wallet::displayAmount(amount); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // static uint64_t amountFromString(const std::string &amount); uint64_t MONERO_Wallet_amountFromString(const char* amount) { + DEBUG_START() return Monero::Wallet::amountFromString(amount); + DEBUG_END() } // static uint64_t amountFromDouble(double amount); uint64_t MONERO_Wallet_amountFromDouble(double amount) { + DEBUG_START() return Monero::Wallet::amountFromDouble(amount); + DEBUG_END() } // static std::string genPaymentId(); const char* MONERO_Wallet_genPaymentId() { + DEBUG_START() std::string str = Monero::Wallet::genPaymentId(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // static bool paymentIdValid(const std::string &paiment_id); bool MONERO_Wallet_paymentIdValid(const char* paiment_id) { + DEBUG_START() return Monero::Wallet::paymentIdValid(std::string(paiment_id)); + DEBUG_END() } bool MONERO_Wallet_addressValid(const char* str, int nettype) { + DEBUG_START() // Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return Monero::Wallet::addressValid(std::string(str), nettype); + DEBUG_END() } bool MONERO_Wallet_keyValid(const char* secret_key_string, const char* address_string, bool isViewKey, int nettype) { + DEBUG_START() std::string error; return Monero::Wallet::keyValid(std::string(secret_key_string), std::string(address_string), isViewKey, nettype, error); + DEBUG_END() } const char* MONERO_Wallet_keyValid_error(const char* secret_key_string, const char* address_string, bool isViewKey, int nettype) { + DEBUG_START() std::string str; Monero::Wallet::keyValid(std::string(secret_key_string), std::string(address_string), isViewKey, nettype, str); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() + } const char* MONERO_Wallet_paymentIdFromAddress(const char* strarg, int nettype) { + DEBUG_START() std::string str = Monero::Wallet::paymentIdFromAddress(std::string(strarg), nettype); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } uint64_t MONERO_Wallet_maximumAllowedAmount() { + DEBUG_START() return Monero::Wallet::maximumAllowedAmount(); + DEBUG_END() } void MONERO_Wallet_init3(void* wallet_ptr, const char* argv0, const char* default_log_base_name, const char* log_path, bool console) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->init(argv0, default_log_base_name, log_path, console); + DEBUG_END() } const char* MONERO_Wallet_getPolyseed(void* wallet_ptr, const char* passphrase) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string seed = ""; std::string _passphrase = std::string(passphrase); @@ -1159,9 +1507,11 @@ const char* MONERO_Wallet_getPolyseed(void* wallet_ptr, const char* passphrase) char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // static bool createPolyseed(std::string &seed_words, std::string &err, const std::string &language = "English"); const char* MONERO_Wallet_createPolyseed(const char* language) { + DEBUG_START() std::string seed_words = ""; std::string err; Monero::Wallet::createPolyseed(seed_words, err, std::string(language)); @@ -1172,104 +1522,142 @@ const char* MONERO_Wallet_createPolyseed(const char* language) { char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void MONERO_Wallet_startRefresh(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->startRefresh(); + DEBUG_END() } void MONERO_Wallet_pauseRefresh(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->pauseRefresh(); + DEBUG_END() } bool MONERO_Wallet_refresh(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->refresh(); + DEBUG_END() } void MONERO_Wallet_refreshAsync(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->refreshAsync(); + DEBUG_END() } bool MONERO_Wallet_rescanBlockchain(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->rescanBlockchain(); + DEBUG_END() } void MONERO_Wallet_rescanBlockchainAsync(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->rescanBlockchainAsync(); + DEBUG_END() } void MONERO_Wallet_setAutoRefreshInterval(void* wallet_ptr, int millis) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setAutoRefreshInterval(millis); + DEBUG_END() } int MONERO_Wallet_autoRefreshInterval(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->autoRefreshInterval(); + DEBUG_END() } void MONERO_Wallet_addSubaddressAccount(void* wallet_ptr, const char* label) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->addSubaddressAccount(std::string(label)); + DEBUG_END() } size_t MONERO_Wallet_numSubaddressAccounts(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->numSubaddressAccounts(); + DEBUG_END() } size_t MONERO_Wallet_numSubaddresses(void* wallet_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->numSubaddresses(accountIndex); + DEBUG_END() } void MONERO_Wallet_addSubaddress(void* wallet_ptr, uint32_t accountIndex, const char* label) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->addSubaddress(accountIndex, std::string(label)); + DEBUG_END() } const char* MONERO_Wallet_getSubaddressLabel(void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getSubaddressLabel(accountIndex, addressIndex); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void MONERO_Wallet_setSubaddressLabel(void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex, const char* label) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setSubaddressLabel(accountIndex, addressIndex, std::string(label)); + DEBUG_END() } void* MONERO_Wallet_multisig(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); Monero::MultisigState *mstate_ptr = new Monero::MultisigState(wallet->multisig()); return reinterpret_cast(mstate_ptr); + DEBUG_END() } const char* MONERO_Wallet_getMultisigInfo(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getMultisigInfo(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_makeMultisig(void* wallet_ptr, const char* info, const char* info_separator, uint32_t threshold) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->makeMultisig(splitStringVector(std::string(info), std::string(info_separator)), threshold); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_exchangeMultisigKeys(void* wallet_ptr, const char* info, const char* info_separator, bool force_update_use_with_caution) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->exchangeMultisigKeys(splitStringVector(std::string(info), std::string(info_separator)), force_update_use_with_caution); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_exportMultisigImages(void* wallet_ptr, const char* separator) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str; wallet->exportMultisigImages(str); @@ -1277,21 +1665,28 @@ const char* MONERO_Wallet_exportMultisigImages(void* wallet_ptr, const char* sep char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } size_t MONERO_Wallet_importMultisigImages(void* wallet_ptr, const char* info, const char* info_separator) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->importMultisigImages(splitStringVector(std::string(info), std::string(info_separator))); + DEBUG_END() } size_t MONERO_Wallet_hasMultisigPartialKeyImages(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->hasMultisigPartialKeyImages(); + DEBUG_END() } void* MONERO_Wallet_restoreMultisigTransaction(void* wallet_ptr, const char* signData) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return reinterpret_cast(wallet->restoreMultisigTransaction(std::string(signData))); + DEBUG_END() } @@ -1310,6 +1705,7 @@ void* MONERO_Wallet_createTransactionMultDest(void* wallet_ptr, const char* dst_ int pendingTransactionPriority, uint32_t subaddr_account, const char* preferredInputs, const char* preferredInputs_separator) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::vector dst_addr = splitStringVector(std::string(dst_addr_list), std::string(dst_addr_list_separator)); @@ -1328,6 +1724,7 @@ void* MONERO_Wallet_createTransactionMultDest(void* wallet_ptr, const char* dst_ subaddr_indices, preferred_inputs ); + DEBUG_END() } void* MONERO_Wallet_createTransaction(void* wallet_ptr, const char* dst_addr, const char* payment_id, @@ -1335,6 +1732,7 @@ void* MONERO_Wallet_createTransaction(void* wallet_ptr, const char* dst_addr, co int pendingTransactionPriority, uint32_t subaddr_account, const char* preferredInputs, const char* separator) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); Monero::optional optAmount; if (amount != 0) { @@ -1346,244 +1744,334 @@ void* MONERO_Wallet_createTransaction(void* wallet_ptr, const char* dst_addr, co optAmount, mixin_count, PendingTransaction_Priority_fromInt(pendingTransactionPriority), subaddr_account, subaddr_indices, preferred_inputs); + DEBUG_END() } void* MONERO_Wallet_loadUnsignedTx(void* wallet_ptr, const char* fileName) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->loadUnsignedTx(std::string(fileName)); + DEBUG_END() } void* MONERO_Wallet_loadUnsignedTxUR(void* wallet_ptr, const char* input) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->loadUnsignedTxUR(std::string(input)); + DEBUG_END() } bool MONERO_Wallet_submitTransaction(void* wallet_ptr, const char* fileName) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->submitTransaction(std::string(fileName)); + DEBUG_END() } bool MONERO_Wallet_submitTransactionUR(void* wallet_ptr, const char* input) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->submitTransactionUR(std::string(input)); + DEBUG_END() } bool MONERO_Wallet_hasUnknownKeyImages(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->hasUnknownKeyImages(); + DEBUG_END() } bool MONERO_Wallet_exportKeyImages(void* wallet_ptr, const char* filename, bool all) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->exportKeyImages(std::string(filename), all); + DEBUG_END() } const char* MONERO_Wallet_exportKeyImagesUR(void* wallet_ptr, size_t max_fragment_length, bool all) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->exportKeyImagesUR(max_fragment_length, all); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool MONERO_Wallet_importKeyImages(void* wallet_ptr, const char* filename) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->importKeyImages(std::string(filename)); + DEBUG_END() } bool MONERO_Wallet_importKeyImagesUR(void* wallet_ptr, const char* input) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->importKeyImagesUR(std::string(input)); + DEBUG_END() } bool MONERO_Wallet_exportOutputs(void* wallet_ptr, const char* filename, bool all) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->exportOutputs(std::string(filename), all); + DEBUG_END() } const char* MONERO_Wallet_exportOutputsUR(void* wallet_ptr, size_t max_fragment_length, bool all) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->exportOutputsUR(max_fragment_length, all); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool MONERO_Wallet_importOutputs(void* wallet_ptr, const char* filename) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->importOutputs(std::string(filename)); + DEBUG_END() } bool MONERO_Wallet_importOutputsUR(void* wallet_ptr, const char* input) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->importOutputsUR(std::string(input)); + DEBUG_END() } // virtual bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) = 0; bool MONERO_Wallet_setupBackgroundSync(void* wallet_ptr, int background_sync_type, const char* wallet_password, const char* background_cache_password) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setupBackgroundSync(Monero::Wallet::BackgroundSyncType::BackgroundSync_CustomPassword, std::string(wallet_password), std::string(background_cache_password)); + DEBUG_END() } // virtual BackgroundSyncType getBackgroundSyncType() const = 0; int MONERO_Wallet_getBackgroundSyncType(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getBackgroundSyncType(); + DEBUG_END() } // virtual bool startBackgroundSync() = 0; bool MONERO_Wallet_startBackgroundSync(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->startBackgroundSync(); + DEBUG_END() } // virtual bool stopBackgroundSync(const std::string &wallet_password) = 0; bool MONERO_Wallet_stopBackgroundSync(void* wallet_ptr, const char* wallet_password) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->stopBackgroundSync(std::string(wallet_password)); + DEBUG_END() } // virtual bool isBackgroundSyncing() const = 0; bool MONERO_Wallet_isBackgroundSyncing(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->hasUnknownKeyImages(); + DEBUG_END() } // virtual bool isBackgroundWallet() const = 0; bool MONERO_Wallet_isBackgroundWallet(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->isBackgroundWallet(); + DEBUG_END() } void* MONERO_Wallet_history(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->history(); + DEBUG_END() } void* MONERO_Wallet_addressBook(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->addressBook(); + DEBUG_END() } // virtual Coins * coins() = 0; void* MONERO_Wallet_coins(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->coins(); + DEBUG_END() } // virtual Subaddress * subaddress() = 0; void* MONERO_Wallet_subaddress(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->subaddress(); + DEBUG_END() } // virtual SubaddressAccount * subaddressAccount() = 0; void* MONERO_Wallet_subaddressAccount(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->subaddressAccount(); + DEBUG_END() } // virtual uint32_t defaultMixin() const = 0; uint32_t MONERO_Wallet_defaultMixin(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->defaultMixin(); + DEBUG_END() } // virtual void setDefaultMixin(uint32_t arg) = 0; void MONERO_Wallet_setDefaultMixin(void* wallet_ptr, uint32_t arg) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setDefaultMixin(arg); + DEBUG_END() } // virtual bool setCacheAttribute(const std::string &key, const std::string &val) = 0; bool MONERO_Wallet_setCacheAttribute(void* wallet_ptr, const char* key, const char* val) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setCacheAttribute(std::string(key), std::string(val)); + DEBUG_END() } // virtual std::string getCacheAttribute(const std::string &key) const = 0; const char* MONERO_Wallet_getCacheAttribute(void* wallet_ptr, const char* key) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getCacheAttribute(std::string(key)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual bool setUserNote(const std::string &txid, const std::string ¬e) = 0; bool MONERO_Wallet_setUserNote(void* wallet_ptr, const char* txid, const char* note) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setUserNote(std::string(txid), std::string(note)); + DEBUG_END() } // virtual std::string getUserNote(const std::string &txid) const = 0; const char* MONERO_Wallet_getUserNote(void* wallet_ptr, const char* txid) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getUserNote(std::string(txid)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_getTxKey(void* wallet_ptr, const char* txid) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getTxKey(std::string(txid)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* MONERO_Wallet_signMessage(void* wallet_ptr, const char* message, const char* address) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->signMessage(std::string(message), std::string(address)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool MONERO_Wallet_verifySignedMessage(void* wallet_ptr, const char* message, const char* address, const char* signature) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); bool v = wallet->verifySignedMessage(std::string(message), std::string(address), std::string(signature)); return v; + DEBUG_END() } bool MONERO_Wallet_rescanSpent(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->rescanSpent(); + DEBUG_END() } void MONERO_Wallet_setOffline(void* wallet_ptr, bool offline) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setOffline(offline); + DEBUG_END() } // virtual bool isOffline() const = 0; bool MONERO_Wallet_isOffline(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->isOffline(); + DEBUG_END() } void MONERO_Wallet_segregatePreForkOutputs(void* wallet_ptr, bool segregate) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->segregatePreForkOutputs(segregate); + DEBUG_END() } // virtual void segregationHeight(uint64_t height) = 0; void MONERO_Wallet_segregationHeight(void* wallet_ptr, uint64_t height) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->segregationHeight(height); + DEBUG_END() } // virtual void keyReuseMitigation2(bool mitigation) = 0; void MONERO_Wallet_keyReuseMitigation2(void* wallet_ptr, bool mitigation) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->keyReuseMitigation2(mitigation); + DEBUG_END() } // virtual bool lightWalletLogin(bool &isNewWallet) const = 0; // virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0; // virtual bool lockKeysFile() = 0; bool MONERO_Wallet_lockKeysFile(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->lockKeysFile(); + DEBUG_END() } // virtual bool unlockKeysFile() = 0; bool MONERO_Wallet_unlockKeysFile(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->unlockKeysFile(); + DEBUG_END() } // virtual bool isKeysFileLocked() = 0; bool MONERO_Wallet_isKeysFileLocked(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->isKeysFileLocked(); + DEBUG_END() } // virtual Device getDeviceType() const = 0; int MONERO_Wallet_getDeviceType(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getDeviceType(); + DEBUG_END() } // virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) = 0; uint64_t MONERO_Wallet_coldKeyImageSync(void* wallet_ptr, uint64_t spent, uint64_t unspent) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->coldKeyImageSync(spent, unspent); + DEBUG_END() } // virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) = 0; const char* MONERO_Wallet_deviceShowAddress(void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = ""; wallet->deviceShowAddress(accountIndex, addressIndex, str); @@ -1591,25 +2079,34 @@ const char* MONERO_Wallet_deviceShowAddress(void* wallet_ptr, uint32_t accountIn char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual bool reconnectDevice() = 0; bool MONERO_Wallet_reconnectDevice(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->reconnectDevice(); + DEBUG_END() }; uint64_t MONERO_Wallet_getBytesReceived(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getBytesReceived(); + DEBUG_END() } uint64_t MONERO_Wallet_getBytesSent(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getBytesSent(); + DEBUG_END() } bool MONERO_Wallet_getStateIsConnected(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getStateIsConnected(); + DEBUG_END() } unsigned char* MONERO_Wallet_getSendToDevice(void* wallet_ptr) { @@ -1618,8 +2115,10 @@ unsigned char* MONERO_Wallet_getSendToDevice(void* wallet_ptr) { } size_t MONERO_Wallet_getSendToDeviceLength(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getSendToDeviceLength(); + DEBUG_END() } unsigned char* MONERO_Wallet_getReceivedFromDevice(void* wallet_ptr) { @@ -1628,31 +2127,42 @@ unsigned char* MONERO_Wallet_getReceivedFromDevice(void* wallet_ptr) { } size_t MONERO_Wallet_getReceivedFromDeviceLength(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getReceivedFromDeviceLength(); + DEBUG_END() } bool MONERO_Wallet_getWaitsForDeviceSend(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getWaitsForDeviceSend(); + DEBUG_END() } bool MONERO_Wallet_getWaitsForDeviceReceive(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getWaitsForDeviceReceive(); + DEBUG_END() } void MONERO_Wallet_setDeviceReceivedData(void* wallet_ptr, unsigned char* data, size_t len) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setDeviceReceivedData(data, len); + DEBUG_END() } void MONERO_Wallet_setDeviceSendData(void* wallet_ptr, unsigned char* data, size_t len) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setDeviceSendData(data, len); + DEBUG_END() } void* MONERO_WalletManager_createWallet(void* wm_ptr, const char* path, const char* password, const char* language, int networkType) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = wm->createWallet( std::string(path), @@ -1660,17 +2170,21 @@ void* MONERO_WalletManager_createWallet(void* wm_ptr, const char* path, const ch std::string(language), static_cast(networkType)); return reinterpret_cast(wallet); + DEBUG_END() } void* MONERO_WalletManager_openWallet(void* wm_ptr, const char* path, const char* password, int networkType) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = wm->openWallet( std::string(path), std::string(password), static_cast(networkType)); return reinterpret_cast(wallet); + DEBUG_END() } void* MONERO_WalletManager_recoveryWallet(void* wm_ptr, const char* path, const char* password, const char* mnemonic, int networkType, uint64_t restoreHeight, uint64_t kdfRounds, const char* seedOffset) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); // (const std::string &path, const std::string &password, const std::string &mnemonic, // NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1, @@ -1684,6 +2198,7 @@ void* MONERO_WalletManager_recoveryWallet(void* wm_ptr, const char* path, const kdfRounds, std::string(seedOffset)); return reinterpret_cast(wallet); + DEBUG_END() } // virtual Wallet * createWalletFromKeys(const std::string &path, // const std::string &password, @@ -1695,6 +2210,7 @@ void* MONERO_WalletManager_recoveryWallet(void* wm_ptr, const char* path, const // const std::string &spendKeyString = "", // uint64_t kdf_rounds = 1) = 0; void* MONERO_WalletManager_createWalletFromKeys(void* wm_ptr, const char* path, const char* password, const char* language, int nettype, uint64_t restoreHeight, const char* addressString, const char* viewKeyString, const char* spendKeyString, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = wm->createWalletFromKeys( std::string(path), @@ -1706,9 +2222,11 @@ void* MONERO_WalletManager_createWalletFromKeys(void* wm_ptr, const char* path, std::string(viewKeyString), std::string(spendKeyString)); return reinterpret_cast(wallet); + DEBUG_END() } void* MONERO_WalletManager_createWalletFromDevice(void* wm_ptr, const char* path, const char* password, int nettype, const char* deviceName, uint64_t restoreHeight, const char* subaddressLookahead, const char* viewKeyString, const char* spendKeyString, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = wm->createWalletFromDevice(std::string(path), std::string(password), @@ -1718,11 +2236,13 @@ void* MONERO_WalletManager_createWalletFromDevice(void* wm_ptr, const char* path std::string(subaddressLookahead), kdf_rounds); return reinterpret_cast(wallet); + DEBUG_END() } void* MONERO_WalletManager_createDeterministicWalletFromSpendKey(void* wm_ptr, const char* path, const char* password, const char* language, int nettype, uint64_t restoreHeight, const char* spendKeyString, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = wm->createDeterministicWalletFromSpendKey( std::string(path), @@ -1734,11 +2254,13 @@ void* MONERO_WalletManager_createDeterministicWalletFromSpendKey(void* wm_ptr, c kdf_rounds ); return reinterpret_cast(wallet); + DEBUG_END() } void* MONERO_WalletManager_createWalletFromPolyseed(void* wm_ptr, const char* path, const char* password, int nettype, const char* mnemonic, const char* passphrase, bool newWallet, uint64_t restore_height, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->createWalletFromPolyseed(std::string(path), std::string(password), @@ -1748,125 +2270,166 @@ void* MONERO_WalletManager_createWalletFromPolyseed(void* wm_ptr, const char* pa newWallet, restore_height, kdf_rounds); + DEBUG_END() } bool MONERO_WalletManager_closeWallet(void* wm_ptr, void* wallet_ptr, bool store) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wm->closeWallet(wallet, store); + DEBUG_END() } bool MONERO_WalletManager_walletExists(void* wm_ptr, const char* path) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->walletExists(std::string(path)); + DEBUG_END() } // virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const = 0; bool MONERO_WalletManager_verifyWalletPassword(void* wm_ptr, const char* keys_file_name, const char* password, bool no_spend_key, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->verifyWalletPassword(std::string(keys_file_name), std::string(password), no_spend_key, kdf_rounds); + DEBUG_END() } // virtual bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const = 0; int MONERO_WalletManager_queryWalletDevice(void* wm_ptr, const char* keys_file_name, const char* password, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet::Device device_type; wm->queryWalletDevice(device_type, std::string(keys_file_name), std::string(password), kdf_rounds); return device_type; + DEBUG_END() } // virtual std::vector findWallets(const std::string &path) = 0; const char* MONERO_WalletManager_findWallets(void* wm_ptr, const char* path, const char* separator) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return vectorToString(wm->findWallets(std::string(path)), std::string(separator)); + DEBUG_END() } const char* MONERO_WalletManager_errorString(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); std::string str = wm->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void MONERO_WalletManager_setDaemonAddress(void* wm_ptr, const char* address) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->setDaemonAddress(std::string(address)); + DEBUG_END() } bool MONERO_WalletManager_setProxy(void* wm_ptr, const char* address) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->setProxy(std::string(address)); + DEBUG_END() } // virtual bool connected(uint32_t *version = NULL) = 0; // virtual uint64_t blockchainHeight() = 0; uint64_t MONERO_WalletManager_blockchainHeight(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->blockchainHeight(); + DEBUG_END() } // virtual uint64_t blockchainTargetHeight() = 0; uint64_t MONERO_WalletManager_blockchainTargetHeight(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->blockchainTargetHeight(); + DEBUG_END() } // virtual uint64_t networkDifficulty() = 0; uint64_t MONERO_WalletManager_networkDifficulty(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->networkDifficulty(); + DEBUG_END() } // virtual double miningHashRate() = 0; double MONERO_WalletManager_miningHashRate(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->miningHashRate(); + DEBUG_END() } // virtual uint64_t blockTarget() = 0; uint64_t MONERO_WalletManager_blockTarget(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->blockTarget(); + DEBUG_END() } // virtual bool isMining() = 0; bool MONERO_WalletManager_isMining(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->isMining(); + DEBUG_END() } // virtual bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) = 0; bool MONERO_WalletManager_startMining(void* wm_ptr, const char* address, uint32_t threads, bool backgroundMining, bool ignoreBattery) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->startMining(std::string(address), threads, backgroundMining, ignoreBattery); + DEBUG_END() } // virtual bool stopMining() = 0; bool MONERO_WalletManager_stopMining(void* wm_ptr, const char* address) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->stopMining(); + DEBUG_END() } // virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0; const char* MONERO_WalletManager_resolveOpenAlias(void* wm_ptr, const char* address, bool dnssec_valid) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); std::string str = wm->resolveOpenAlias(std::string(address), dnssec_valid); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // WalletManagerFactory void* MONERO_WalletManagerFactory_getWalletManager() { + DEBUG_START() Monero::WalletManager *wm = Monero::WalletManagerFactory::getWalletManager(); return reinterpret_cast(wm); + DEBUG_END() } void MONERO_WalletManagerFactory_setLogLevel(int level) { - Monero::WalletManagerFactory::setLogLevel(level); + DEBUG_START() + return Monero::WalletManagerFactory::setLogLevel(level); + DEBUG_END() } void MONERO_WalletManagerFactory_setLogCategories(const char* categories) { - Monero::WalletManagerFactory::setLogCategories(std::string(categories)); + DEBUG_START() + return Monero::WalletManagerFactory::setLogCategories(std::string(categories)); + DEBUG_END() } // DEBUG functions @@ -1901,19 +2464,21 @@ void* MONERO_DEBUG_test4(uint64_t x) { } const char* MONERO_DEBUG_test5() { - const char *text = "This is a const char* text"; + const char *text = "This is a const char* text"; return text; } const char* MONERO_DEBUG_test5_std() { - std::string text ("This is a std::string text"); + std::string text("This is a std::string text"); const char *text2 = "This is a text"; return text2; } bool MONERO_DEBUG_isPointerNull(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return (wallet != NULL); + DEBUG_END() } // cake wallet world @@ -1993,35 +2558,47 @@ struct MONERO_cw_WalletListener : Monero::WalletListener }; void* MONERO_cw_getWalletListener(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); MONERO_cw_WalletListener *listener = new MONERO_cw_WalletListener(); wallet->setListener(listener); return reinterpret_cast(listener); + DEBUG_END() } void MONERO_cw_WalletListener_resetNeedToRefresh(void* cw_walletListener_ptr) { + DEBUG_START() MONERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); listener->cw_resetNeedToRefresh(); + DEBUG_END() } bool MONERO_cw_WalletListener_isNeedToRefresh(void* cw_walletListener_ptr) { + DEBUG_START() MONERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); return listener->cw_isNeedToRefresh(); + DEBUG_END() }; bool MONERO_cw_WalletListener_isNewTransactionExist(void* cw_walletListener_ptr) { + DEBUG_START() MONERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); return listener->cw_isNeedToRefresh(); + DEBUG_END() }; void MONERO_cw_WalletListener_resetIsNewTransactionExist(void* cw_walletListener_ptr) { + DEBUG_START() MONERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); listener->cw_isNeedToRefresh(); + DEBUG_END() }; uint64_t MONERO_cw_WalletListener_height(void* cw_walletListener_ptr) { + DEBUG_START() MONERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); return listener->cw_isNeedToRefresh(); + DEBUG_END() }; const char* MONERO_checksum_wallet2_api_c_h() { diff --git a/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h b/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h index 143da40..17c647c 100644 --- a/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h +++ b/monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h @@ -604,8 +604,6 @@ extern ADDAPI uint64_t MONERO_Wallet_approximateBlockChainHeight(void* wallet_pt extern ADDAPI uint64_t MONERO_Wallet_estimateBlockChainHeight(void* wallet_ptr); // virtual uint64_t daemonBlockChainHeight() const = 0; extern ADDAPI uint64_t MONERO_Wallet_daemonBlockChainHeight(void* wallet_ptr); -extern ADDAPI uint64_t MONERO_Wallet_daemonBlockChainHeight_cached(void* wallet_ptr); -extern ADDAPI void MONERO_Wallet_daemonBlockChainHeight_runThread(void* wallet_ptr, int seconds); // virtual uint64_t daemonBlockChainTargetHeight() const = 0; extern ADDAPI uint64_t MONERO_Wallet_daemonBlockChainTargetHeight(void* wallet_ptr); // virtual bool synchronized() const = 0; diff --git a/patches/monero/0001-polyseed.patch b/patches/monero/0001-polyseed.patch deleted file mode 100644 index 8164403..0000000 --- a/patches/monero/0001-polyseed.patch +++ /dev/null @@ -1,1273 +0,0 @@ -From 8324040dfb89bd90b414ea685b2aa758a461fbc4 Mon Sep 17 00:00:00 2001 -From: tobtoht -Date: Tue, 12 Mar 2024 09:42:37 +0100 -Subject: [PATCH 01/16] polyseed - -Co-authored-by: Czarek Nakamoto ---- - .gitmodules | 6 + - CMakeLists.txt | 4 +- - contrib/epee/include/wipeable_string.h | 7 + - contrib/epee/src/wipeable_string.cpp | 10 ++ - external/CMakeLists.txt | 2 + - src/CMakeLists.txt | 1 + - src/cryptonote_basic/CMakeLists.txt | 1 + - src/cryptonote_basic/account.cpp | 23 +++- - src/cryptonote_basic/account.h | 6 + - src/cryptonote_config.h | 2 + - src/polyseed/CMakeLists.txt | 25 ++++ - src/polyseed/pbkdf2.c | 85 ++++++++++++ - src/polyseed/pbkdf2.h | 46 +++++++ - src/polyseed/polyseed.cpp | 182 +++++++++++++++++++++++++ - src/polyseed/polyseed.hpp | 167 +++++++++++++++++++++++ - src/wallet/api/wallet.cpp | 71 ++++++++++ - src/wallet/api/wallet.h | 10 ++ - src/wallet/api/wallet2_api.h | 25 ++++ - src/wallet/api/wallet_manager.cpp | 9 ++ - src/wallet/api/wallet_manager.h | 10 ++ - src/wallet/wallet2.cpp | 102 ++++++++++++-- - src/wallet/wallet2.h | 30 +++- - 22 files changed, 805 insertions(+), 19 deletions(-) - create mode 100644 src/polyseed/CMakeLists.txt - create mode 100644 src/polyseed/pbkdf2.c - create mode 100644 src/polyseed/pbkdf2.h - create mode 100644 src/polyseed/polyseed.cpp - create mode 100644 src/polyseed/polyseed.hpp - -diff --git a/.gitmodules b/.gitmodules -index 721cce3b4..73a23fb35 100644 ---- a/.gitmodules -+++ b/.gitmodules -@@ -10,6 +10,12 @@ - [submodule "external/randomx"] - path = external/randomx - url = https://github.com/tevador/RandomX -+[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 -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 8fb03ba1f..63b8c5079 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -369,6 +369,8 @@ if(NOT MANUAL_SUBMODULES) - check_submodule(external/trezor-common) - check_submodule(external/randomx) - check_submodule(external/supercop) -+ check_submodule(external/polyseed) -+ check_submodule(external/utf8proc) - endif() - endif() - -@@ -458,7 +460,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) -diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h -index 65977cd97..594e15de4 100644 ---- a/contrib/epee/include/wipeable_string.h -+++ b/contrib/epee/include/wipeable_string.h -@@ -34,6 +34,7 @@ - #include - #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); -diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp -index b016f2f48..f2f365b1b 100644 ---- a/contrib/epee/src/wipeable_string.cpp -+++ b/contrib/epee/src/wipeable_string.cpp -@@ -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]; -+} -+ - } -diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 5b7f69a56..1b9761d70 100644 ---- a/external/CMakeLists.txt -+++ b/external/CMakeLists.txt -@@ -70,3 +70,5 @@ add_subdirectory(db_drivers) - add_subdirectory(easylogging++) - add_subdirectory(qrcodegen) - add_subdirectory(randomx EXCLUDE_FROM_ALL) -+add_subdirectory(polyseed EXCLUDE_FROM_ALL) -+add_subdirectory(utf8proc EXCLUDE_FROM_ALL) -\ No newline at end of file -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 3335d3c21..06b708cf0 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -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) -diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt -index 1414be1b2..414936a05 100644 ---- a/src/cryptonote_basic/CMakeLists.txt -+++ b/src/cryptonote_basic/CMakeLists.txt -@@ -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} -diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 2ac455fda..4931c3740 100644 ---- a/src/cryptonote_basic/account.cpp -+++ b/src/cryptonote_basic/account.cpp -@@ -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(); - } - //----------------------------------------------------------------- - crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) -@@ -244,6 +250,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 &multisig_keys) - { - m_keys.m_account_address.m_spend_public_key = spend_public_key; -diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h -index 2ee9545d4..0099ebfe7 100644 ---- a/src/cryptonote_basic/account.h -+++ b/src/cryptonote_basic/account.h -@@ -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 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 &multisig_keys); - const account_keys& get_keys() const; - std::string get_public_address_str(network_type nettype) const; -diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h -index 61146a114..8e1a07110 100644 ---- a/src/cryptonote_config.h -+++ b/src/cryptonote_config.h -@@ -207,6 +207,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 -diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt -new file mode 100644 -index 000000000..cca4eb746 ---- /dev/null -+++ b/src/polyseed/CMakeLists.txt -@@ -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} -+) -diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c -new file mode 100644 -index 000000000..1c45f4708 ---- /dev/null -+++ b/src/polyseed/pbkdf2.c -@@ -0,0 +1,85 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// 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 -+ -+#include -+#include -+ -+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); -+} -\ No newline at end of file -diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h -new file mode 100644 -index 000000000..f6253b9d7 ---- /dev/null -+++ b/src/polyseed/pbkdf2.h -@@ -0,0 +1,46 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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 -+#include -+ -+#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 -\ No newline at end of file -diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp -new file mode 100644 -index 000000000..231a48a94 ---- /dev/null -+++ b/src/polyseed/polyseed.cpp -@@ -0,0 +1,182 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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 -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+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(str), 0, buffer, POLYSEED_STR_SIZE, options); -+ if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) { -+ throw std::runtime_error("Unicode normalization failed"); -+ } -+ -+ result = utf8proc_reencode(buffer, result, options); -+ if (result < 0 || result > POLYSEED_STR_SIZE) { -+ throw std::runtime_error("Unicode normalization failed"); -+ } -+ -+ strcpy(norm, reinterpret_cast(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)); -+ } -+ -+ 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 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& 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 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); -+ } -+} -diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp -new file mode 100644 -index 000000000..2c8c777a7 ---- /dev/null -+++ b/src/polyseed/polyseed.hpp -@@ -0,0 +1,167 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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 -+#include -+#include -+#include -+#include -+#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& 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 -+ 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 -\ No newline at end of file -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index fc4f89128..d96ea97ea 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -690,6 +690,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(m_wallet->get_device_type()); -@@ -798,6 +820,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> Wallet::getPolyseedLanguages() -+{ -+ std::vector> languages; -+ -+ auto langs = polyseed::get_langs(); -+ for (const auto &lang : langs) { -+ languages.emplace_back(std::pair(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(); -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index ec2d7e9b3..787215ab3 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -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 *) {} -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 71991df0d..9ea753083 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -700,6 +700,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> getPolyseedLanguages(); -+ - /** - * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) - */ -@@ -1256,6 +1260,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 -diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp -index e81b8f83a..c79fe25d6 100644 ---- a/src/wallet/api/wallet_manager.cpp -+++ b/src/wallet/api/wallet_manager.cpp -@@ -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(wallet); -diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h -index a223e1df9..28fcd36c9 100644 ---- a/src/wallet/api/wallet_manager.h -+++ b/src/wallet/api/wallet_manager.h -@@ -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; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 64f486e71..9e95f44d6 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -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" - { -@@ -1260,7 +1261,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())); - } -@@ -1438,10 +1440,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; - } - //---------------------------------------------------------------------------------------------------- -@@ -4630,6 +4647,9 @@ boost::optional wallet2::get_keys_file_data(const epee: - 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()); -+ - // Serialize the JSON object - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); -@@ -4777,6 +4797,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; - } - else if(json.IsObject()) - { -@@ -5013,6 +5034,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_credits_target = field_credits_target; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false); - m_enable_multisig = field_enable_multisig; -+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false); -+ m_polyseed = field_polyseed; - } - else - { -@@ -5285,6 +5308,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. -@@ -5412,7 +5477,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 -@@ -5436,7 +5501,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) -@@ -13133,7 +13198,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 - { - // time of v2 fork - const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? 1520937818 : 1458748658; -@@ -13142,7 +13207,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const - // avg seconds per block - const int seconds_per_block = DIFFICULTY_TARGET_V2; - // Calculated blockchain height -- uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block; -+ uint64_t approx_blockchain_height = fork_block + ((t > 0 ? t : time(NULL)) - fork_time)/seconds_per_block; - // testnet and stagenet got some huge rollbacks, so the estimation is way off - static const uint64_t approximate_rolled_back_blocks = m_nettype == TESTNET ? 342100 : m_nettype == STAGENET ? 60000 : 30000; - if ((m_nettype == TESTNET || m_nettype == STAGENET) && approx_blockchain_height > approximate_rolled_back_blocks) -@@ -14860,15 +14925,6 @@ 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) - { -- uint32_t version; -- if (!check_connection(&version)) -- { -- throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); -- } -- if (version < MAKE_CORE_RPC_VERSION(1, 6)) -- { -- 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; -@@ -14877,7 +14933,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui - { - 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)) -+ { -+ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); -+ } -+ if (version < MAKE_CORE_RPC_VERSION(1, 6)) -+ { -+ throw std::runtime_error("this function requires RPC version 1.6 or higher"); -+ } -+ - std::string err; - uint64_t height_min = 0; - uint64_t height_max = get_daemon_blockchain_height(err) - 1; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index c38d77675..91ec72e0f 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -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" -@@ -854,6 +855,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 -@@ -1018,6 +1033,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. - */ -@@ -1466,8 +1490,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 select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); - std::vector select_available_outputs(const std::function &f); - std::vector select_available_unmixable_outputs(); -@@ -1559,6 +1583,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 &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(); - -@@ -1874,6 +1899,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 m_multisig_signers; --- -2.39.2 - diff --git a/patches/monero/0001-wallet-background-sync-with-just-the-view-key.patch b/patches/monero/0001-wallet-background-sync-with-just-the-view-key.patch new file mode 100644 index 0000000..849c8be --- /dev/null +++ b/patches/monero/0001-wallet-background-sync-with-just-the-view-key.patch @@ -0,0 +1,4317 @@ +From 0efd53a750d0827a500b036dc30ab190689e7e13 Mon Sep 17 00:00:00 2001 +From: j-berman +Date: Thu, 13 Oct 2022 18:33:33 -0700 +Subject: [PATCH 01/14] wallet: background sync with just the view key + +- When background syncing, the wallet wipes the spend key +from memory and processes all new transactions. The wallet saves +all receives, spends, and "plausible" spends of receives the +wallet does not know key images for. +- When background sync disabled, the wallet processes all +background synced txs and then clears the background sync cache. +- Adding "plausible" spends to the background sync cache ensures +that the wallet does not need to query the daemon to see if any +received outputs were spent while background sync was enabled. +This would harm privacy especially for users of 3rd party daemons. +- To enable the feature in the CLI wallet, the user can set +background-sync to reuse-wallet-password or +custom-background-password and the wallet automatically syncs in +the background when the wallet locks, then processes all +background synced txs when the wallet is unlocked. +- The custom-background-password option enables the user to +open a distinct background wallet that only has a view key saved +and can be opened/closed/synced separately from the main wallet. +When the main wallet opens, it processes the background wallet's +cache. +- To enable the feature in the RPC wallet, there is a new +`/setup_background_sync` endpoint. +- HW, multsig and view-only wallets cannot background sync. +--- + src/cryptonote_basic/account.cpp | 11 + + src/cryptonote_basic/account.h | 1 + + src/cryptonote_config.h | 2 + + src/simplewallet/simplewallet.cpp | 204 +++- + src/simplewallet/simplewallet.h | 1 + + src/wallet/api/wallet.cpp | 213 +++- + src/wallet/api/wallet.h | 12 + + src/wallet/api/wallet2_api.h | 42 + + src/wallet/wallet2.cpp | 1030 ++++++++++++++++-- + src/wallet/wallet2.h | 156 ++- + src/wallet/wallet_errors.h | 39 + + src/wallet/wallet_rpc_server.cpp | 162 +++ + src/wallet/wallet_rpc_server.h | 6 + + src/wallet/wallet_rpc_server_commands_defs.h | 64 ++ + src/wallet/wallet_rpc_server_error_codes.h | 2 + + tests/functional_tests/transfer.py | 400 ++++++- + tests/functional_tests/util_resources.py | 25 + + tests/functional_tests/wallet.py | 43 +- + tests/unit_tests/wipeable_string.cpp | 12 + + utils/python-rpc/framework/wallet.py | 42 + + 20 files changed, 2337 insertions(+), 130 deletions(-) + +diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp +index 2ac455f..4e87d44 100644 +--- a/src/cryptonote_basic/account.cpp ++++ b/src/cryptonote_basic/account.cpp +@@ -152,6 +152,17 @@ DISABLE_VS_WARNINGS(4244 4345) + m_keys.m_multisig_keys.clear(); + } + //----------------------------------------------------------------- ++ void account_base::set_spend_key(const crypto::secret_key& spend_secret_key) ++ { ++ // make sure derived spend public key matches saved public spend key ++ crypto::public_key spend_public_key; ++ crypto::secret_key_to_public_key(spend_secret_key, spend_public_key); ++ CHECK_AND_ASSERT_THROW_MES(m_keys.m_account_address.m_spend_public_key == spend_public_key, ++ "Unexpected derived public spend key"); ++ ++ m_keys.m_spend_secret_key = spend_secret_key; ++ } ++ //----------------------------------------------------------------- + crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) + { + crypto::secret_key first = generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, recovery_key, recover); +diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h +index 2ee9545..93d1d28 100644 +--- a/src/cryptonote_basic/account.h ++++ b/src/cryptonote_basic/account.h +@@ -95,6 +95,7 @@ namespace cryptonote + bool store(const std::string& file_path); + + void forget_spend_key(); ++ void set_spend_key(const crypto::secret_key& spend_secret_key); + const std::vector &get_multisig_keys() const { return m_keys.m_multisig_keys; } + + void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); } +diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h +index 61146a1..f9e6a6c 100644 +--- a/src/cryptonote_config.h ++++ b/src/cryptonote_config.h +@@ -241,6 +241,8 @@ namespace config + const unsigned char HASH_KEY_ENCRYPTED_PAYMENT_ID = 0x8d; + const unsigned char HASH_KEY_WALLET = 0x8c; + const unsigned char HASH_KEY_WALLET_CACHE = 0x8d; ++ const unsigned char HASH_KEY_BACKGROUND_CACHE = 0x8e; ++ const unsigned char HASH_KEY_BACKGROUND_KEYS_FILE = 0x8f; + const unsigned char HASH_KEY_RPC_PAYMENT_NONCE = 0x58; + const unsigned char HASH_KEY_MEMORY = 'k'; + const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp +index b9e30f9..2c51337 100644 +--- a/src/simplewallet/simplewallet.cpp ++++ b/src/simplewallet/simplewallet.cpp +@@ -155,6 +155,17 @@ typedef cryptonote::simple_wallet sw; + } \ + } while(0) + ++#define CHECK_IF_BACKGROUND_SYNCING(msg) \ ++ do \ ++ { \ ++ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) \ ++ { \ ++ std::string type = m_wallet->is_background_wallet() ? "background wallet" : "background syncing wallet"; \ ++ fail_msg_writer() << boost::format(tr("%s %s")) % type % msg; \ ++ return false; \ ++ } \ ++ } while (0) ++ + static std::string get_human_readable_timespan(std::chrono::seconds seconds); + static std::string get_human_readable_timespan(uint64_t seconds); + +@@ -325,7 +336,7 @@ namespace + auto pwd_container = tools::password_container::prompt(verify, prompt); + if (!pwd_container) + { +- tools::fail_msg_writer() << sw::tr("failed to read wallet password"); ++ tools::fail_msg_writer() << sw::tr("failed to read password"); + } + return pwd_container; + } +@@ -335,6 +346,11 @@ namespace + return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify); + } + ++ boost::optional background_sync_cache_password_prompter(bool verify) ++ { ++ return password_prompter(verify ? sw::tr("Enter a custom password for the background sync cache") : sw::tr("Background sync cache password"), verify); ++ } ++ + inline std::string interpret_rpc_response(bool ok, const std::string& status) + { + std::string err; +@@ -452,6 +468,41 @@ namespace + return "invalid"; + } + ++ const struct ++ { ++ const char *name; ++ tools::wallet2::BackgroundSyncType background_sync_type; ++ } background_sync_type_names[] = ++ { ++ { "off", tools::wallet2::BackgroundSyncOff }, ++ { "reuse-wallet-password", tools::wallet2::BackgroundSyncReusePassword }, ++ { "custom-background-password", tools::wallet2::BackgroundSyncCustomPassword }, ++ }; ++ ++ bool parse_background_sync_type(const std::string &s, tools::wallet2::BackgroundSyncType &background_sync_type) ++ { ++ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) ++ { ++ if (s == background_sync_type_names[n].name) ++ { ++ background_sync_type = background_sync_type_names[n].background_sync_type; ++ return true; ++ } ++ } ++ fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse background sync type"); ++ return false; ++ } ++ ++ std::string get_background_sync_type_name(tools::wallet2::BackgroundSyncType type) ++ { ++ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) ++ { ++ if (type == background_sync_type_names[n].background_sync_type) ++ return background_sync_type_names[n].name; ++ } ++ return "invalid"; ++ } ++ + std::string get_version_string(uint32_t version) + { + return boost::lexical_cast(version >> 16) + "." + boost::lexical_cast(version & 0xffff); +@@ -805,6 +856,7 @@ bool simple_wallet::spendkey(const std::vector &args/* = std::vecto + fail_msg_writer() << tr("wallet is watch-only and has no spend key"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("has no spend key"); + // don't log + PAUSE_READLINE(); + if (m_wallet->key_on_device()) { +@@ -836,6 +888,7 @@ bool simple_wallet::print_seed(bool encrypted) + fail_msg_writer() << tr("wallet is watch-only and has no seed"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("has no seed"); + + multisig = m_wallet->multisig(&ready); + if (multisig) +@@ -913,6 +966,7 @@ bool simple_wallet::seed_set_language(const std::vector &args/* = s + fail_msg_writer() << tr("wallet is watch-only and has no seed"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("has no seed"); + + epee::wipeable_string password; + { +@@ -1059,6 +1113,7 @@ bool simple_wallet::prepare_multisig_main(const std::vector &args, + fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig"); + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot be made multisig"); + + if(m_wallet->get_num_transfer_details()) + { +@@ -2195,6 +2250,7 @@ bool simple_wallet::save_known_rings(const std::vector &args) + + bool simple_wallet::freeze_thaw(const std::vector &args, bool freeze) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot freeze/thaw"); + if (args.empty()) + { + fail_msg_writer() << boost::format(tr("usage: %s |")) % (freeze ? "freeze" : "thaw"); +@@ -2234,6 +2290,7 @@ bool simple_wallet::thaw(const std::vector &args) + + bool simple_wallet::frozen(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot see frozen key images"); + if (args.empty()) + { + size_t ntd = m_wallet->get_num_transfer_details(); +@@ -3005,6 +3062,57 @@ bool simple_wallet::set_track_uses(const std::vector &args/* = std: + return true; + } + ++bool simple_wallet::setup_background_sync(const std::vector &args/* = std::vector()*/) ++{ ++ if (m_wallet->multisig()) ++ { ++ fail_msg_writer() << tr("background sync not implemented for multisig wallet"); ++ return true; ++ } ++ if (m_wallet->watch_only()) ++ { ++ fail_msg_writer() << tr("background sync not implemented for watch only wallet"); ++ return true; ++ } ++ if (m_wallet->key_on_device()) ++ { ++ fail_msg_writer() << tr("command not supported by HW wallet"); ++ return true; ++ } ++ ++ tools::wallet2::BackgroundSyncType background_sync_type; ++ if (!parse_background_sync_type(args[1], background_sync_type)) ++ { ++ fail_msg_writer() << tr("invalid option"); ++ return true; ++ } ++ ++ const auto pwd_container = get_and_verify_password(); ++ if (!pwd_container) ++ return true; ++ ++ try ++ { ++ boost::optional background_cache_password = boost::none; ++ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) ++ { ++ const auto background_pwd_container = background_sync_cache_password_prompter(true); ++ if (!background_pwd_container) ++ return true; ++ background_cache_password = background_pwd_container->password(); ++ } ++ ++ LOCK_IDLE_SCOPE(); ++ m_wallet->setup_background_sync(background_sync_type, pwd_container->password(), background_cache_password); ++ } ++ catch (const std::exception &e) ++ { ++ fail_msg_writer() << tr("Error setting background sync type: ") << e.what(); ++ } ++ ++ return true; ++} ++ + bool simple_wallet::set_show_wallet_name_when_locked(const std::vector &args/* = std::vector()*/) + { + const auto pwd_container = get_and_verify_password(); +@@ -3237,6 +3345,7 @@ bool simple_wallet::apropos(const std::vector &args) + + bool simple_wallet::scan_tx(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot scan tx"); + if (args.empty()) + { + PRINT_USAGE(USAGE_SCAN_TX); +@@ -3458,6 +3567,8 @@ simple_wallet::simple_wallet() + " Ignore outputs of amount below this threshold when spending.\n " + "track-uses <1|0>\n " + " Whether to keep track of owned outputs uses.\n " ++ "background-sync \n " ++ " Set this to enable scanning in the background with just the view key while the wallet is locked.\n " + "setup-background-mining <1|0>\n " + " Whether to enable background mining. Set this to support the network and to get a chance to receive new monero.\n " + "device-name \n " +@@ -3876,6 +3987,7 @@ bool simple_wallet::set_variable(const std::vector &args) + success_msg_writer() << "ignore-outputs-above = " << cryptonote::print_money(m_wallet->ignore_outputs_above()); + success_msg_writer() << "ignore-outputs-below = " << cryptonote::print_money(m_wallet->ignore_outputs_below()); + success_msg_writer() << "track-uses = " << m_wallet->track_uses(); ++ success_msg_writer() << "background-sync = " << get_background_sync_type_name(m_wallet->background_sync_type()); + success_msg_writer() << "setup-background-mining = " << setup_background_mining_string; + success_msg_writer() << "device-name = " << m_wallet->device_name(); + success_msg_writer() << "export-format = " << (m_wallet->export_format() == tools::wallet2::ExportFormat::Ascii ? "ascii" : "binary"); +@@ -3894,6 +4006,7 @@ bool simple_wallet::set_variable(const std::vector &args) + } + else + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot change wallet settings"); + + #define CHECK_SIMPLE_VARIABLE(name, f, help) do \ + if (args[0] == name) { \ +@@ -3947,6 +4060,7 @@ bool simple_wallet::set_variable(const std::vector &args) + CHECK_SIMPLE_VARIABLE("ignore-outputs-above", set_ignore_outputs_above, tr("amount")); + CHECK_SIMPLE_VARIABLE("ignore-outputs-below", set_ignore_outputs_below, tr("amount")); + CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1")); ++ CHECK_SIMPLE_VARIABLE("background-sync", setup_background_sync, tr("off (default); reuse-wallet-password (reuse the wallet password to encrypt the background cache); custom-background-password (use a custom background password to encrypt the background cache)")); + CHECK_SIMPLE_VARIABLE("show-wallet-name-when-locked", set_show_wallet_name_when_locked, tr("1 or 0")); + CHECK_SIMPLE_VARIABLE("inactivity-lock-timeout", set_inactivity_lock_timeout, tr("unsigned integer (seconds, 0 to disable)")); + CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no")); +@@ -4900,7 +5014,10 @@ std::string simple_wallet::get_mnemonic_language() + //---------------------------------------------------------------------------------------------------- + boost::optional simple_wallet::get_and_verify_password() const + { +- auto pwd_container = default_password_prompter(m_wallet_file.empty()); ++ const bool verify = m_wallet_file.empty(); ++ auto pwd_container = (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) ++ ? background_sync_cache_password_prompter(verify) ++ : default_password_prompter(verify); + if (!pwd_container) + return boost::none; + +@@ -5203,6 +5320,8 @@ boost::optional simple_wallet::open_wallet(const boost::p + prefix = tr("Opened watch-only wallet"); + else if (m_wallet->multisig(&ready, &threshold, &total)) + prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); ++ else if (m_wallet->is_background_wallet()) ++ prefix = tr("Opened background wallet"); + else + prefix = tr("Opened wallet"); + message_writer(console_color_white, true) << +@@ -5411,6 +5530,10 @@ void simple_wallet::stop_background_mining() + //---------------------------------------------------------------------------------------------------- + void simple_wallet::check_background_mining(const epee::wipeable_string &password) + { ++ // Background mining can be toggled from the main wallet ++ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) ++ return; ++ + tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); + if (setup == tools::wallet2::BackgroundMiningNo) + { +@@ -6275,6 +6398,7 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::rescan_spent(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot rescan spent"); + if (!m_wallet->is_trusted_daemon()) + { + fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); +@@ -6532,10 +6656,27 @@ void simple_wallet::check_for_inactivity_lock(bool user) + " || ||" << std::endl << + "" << std::endl; + } ++ ++ bool started_background_sync = false; ++ if (!m_wallet->is_background_wallet() && ++ m_wallet->background_sync_type() != tools::wallet2::BackgroundSyncOff) ++ { ++ LOCK_IDLE_SCOPE(); ++ m_wallet->start_background_sync(); ++ started_background_sync = true; ++ } ++ + while (1) + { + const char *inactivity_msg = user ? "" : tr("Locked due to inactivity."); +- tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << tr("The wallet password is required to unlock the console."); ++ tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << ( ++ (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) ++ ? tr("The background password is required to unlock the console.") ++ : tr("The wallet password is required to unlock the console.") ++ ); ++ ++ if (m_wallet->is_background_syncing()) ++ tools::msg_writer() << tr("\nSyncing in the background while locked...") << std::endl; + + const bool show_wallet_name = m_wallet->show_wallet_name_when_locked(); + if (show_wallet_name) +@@ -6548,8 +6689,16 @@ void simple_wallet::check_for_inactivity_lock(bool user) + } + try + { +- if (get_and_verify_password()) ++ const auto pwd_container = get_and_verify_password(); ++ if (pwd_container) ++ { ++ if (started_background_sync) ++ { ++ LOCK_IDLE_SCOPE(); ++ m_wallet->stop_background_sync(pwd_container->password()); ++ } + break; ++ } + } + catch (...) { /* do nothing, just let the loop loop */ } + } +@@ -6576,6 +6725,7 @@ bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector &args_, bool called_by_mms) + { + // "transfer [index=[,,...]] [] []
[]" ++ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); + if (!try_connect_to_daemon()) + return false; + +@@ -7004,6 +7154,7 @@ bool simple_wallet::transfer_main(const std::vector &args_, bool ca + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::transfer(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); + if (args_.size() < 1) + { + PRINT_USAGE(USAGE_TRANSFER); +@@ -7016,6 +7167,7 @@ bool simple_wallet::transfer(const std::vector &args_) + + bool simple_wallet::sweep_unmixable(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + if (!try_connect_to_daemon()) + return true; + +@@ -7123,6 +7275,7 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + auto print_usage = [this, account, below]() + { + if (below) +@@ -7404,6 +7557,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vect + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_single(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + if (!try_connect_to_daemon()) + return true; + +@@ -7642,12 +7796,14 @@ bool simple_wallet::sweep_single(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_all(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + sweep_main(m_current_subaddress_account, 0, args_); + return true; + } + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_account(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + auto local_args = args_; + if (local_args.empty()) + { +@@ -7668,6 +7824,7 @@ bool simple_wallet::sweep_account(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_below(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + uint64_t below = 0; + if (args_.size() < 1) + { +@@ -7686,6 +7843,7 @@ bool simple_wallet::sweep_below(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::donate(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot donate"); + std::vector local_args = args_; + if(local_args.empty() || local_args.size() > 5) + { +@@ -7747,6 +7905,7 @@ bool simple_wallet::donate(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot load tx"); + // gather info to ask the user + uint64_t amount = 0, amount_to_dests = 0, change = 0; + size_t min_ring_size = ~0; +@@ -7927,6 +8086,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) + fail_msg_writer() << tr("This is a watch only wallet"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot sign transfer"); + + bool export_raw = false; + std::string unsigned_filename = "unsigned_monero_tx"; +@@ -8034,6 +8194,8 @@ std::string get_tx_key_stream(crypto::secret_key tx_key, std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get tx key"); ++ + std::vector local_args = args_; + + if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR) +@@ -8074,6 +8236,8 @@ bool simple_wallet::get_tx_key(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::set_tx_key(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot set tx key"); ++ + std::vector local_args = args_; + + if(local_args.size() != 2 && local_args.size() != 3) { +@@ -8150,6 +8314,8 @@ bool simple_wallet::set_tx_key(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_tx_proof(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get tx proof"); ++ + if (args.size() != 2 && args.size() != 3) + { + PRINT_USAGE(USAGE_GET_TX_PROOF); +@@ -8356,6 +8522,7 @@ bool simple_wallet::check_tx_proof(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_spend_proof(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get spend proof"); + if (m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command not supported by HW wallet"); +@@ -8440,6 +8607,7 @@ bool simple_wallet::check_spend_proof(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_reserve_proof(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get reserve proof"); + if (m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command not supported by HW wallet"); +@@ -9126,6 +9294,8 @@ bool simple_wallet::unspent_outputs(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::rescan_blockchain(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot rescan"); ++ + uint64_t start_height = 0; + ResetType reset_type = ResetSoft; + +@@ -9423,6 +9593,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + if (command == "new") + { + // create a new account and switch to it ++ CHECK_IF_BACKGROUND_SYNCING("cannot create new account"); + std::string label = boost::join(local_args, " "); + if (label.empty()) + label = tr("(Untitled account)"); +@@ -9453,6 +9624,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + else if (command == "label" && local_args.size() >= 1) + { + // set label of the specified account ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); + uint32_t index_major; + if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0])) + { +@@ -9474,6 +9646,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + } + else if (command == "tag" && local_args.size() >= 2) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); + const std::string tag = local_args[0]; + std::set account_indices; + for (size_t i = 1; i < local_args.size(); ++i) +@@ -9498,6 +9671,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + } + else if (command == "untag" && local_args.size() >= 1) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); + std::set account_indices; + for (size_t i = 0; i < local_args.size(); ++i) + { +@@ -9521,6 +9695,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + } + else if (command == "tag_description" && local_args.size() >= 1) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); + const std::string tag = local_args[0]; + std::string description; + if (local_args.size() > 1) +@@ -9638,6 +9813,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: + } + else if (local_args[0] == "new") + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); + local_args.erase(local_args.begin()); + std::string label; + if (local_args.size() > 0) +@@ -9650,6 +9826,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: + } + else if (local_args[0] == "mnew") + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot add addresses"); + local_args.erase(local_args.begin()); + if (local_args.size() != 1) + { +@@ -9675,6 +9852,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: + } + else if (local_args[0] == "one-off") + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); + local_args.erase(local_args.begin()); + std::string label; + if (local_args.size() != 2) +@@ -9693,6 +9871,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: + } + else if (local_args.size() >= 2 && local_args[0] == "label") + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify address"); + if (!epee::string_tools::get_xtype_from_string(index, local_args[1])) + { + fail_msg_writer() << tr("failed to parse index: ") << local_args[1]; +@@ -9839,6 +10018,8 @@ bool simple_wallet::print_integrated_address(const std::vector &arg + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::address_book(const std::vector &args/* = std::vector()*/) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get address book"); ++ + if (args.size() == 0) + { + } +@@ -9899,6 +10080,8 @@ bool simple_wallet::address_book(const std::vector &args/* = std::v + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::set_tx_note(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot set tx note"); ++ + if (args.size() == 0) + { + PRINT_USAGE(USAGE_SET_TX_NOTE); +@@ -9927,6 +10110,8 @@ bool simple_wallet::set_tx_note(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_tx_note(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get tx note"); ++ + if (args.size() != 1) + { + PRINT_USAGE(USAGE_GET_TX_NOTE); +@@ -9952,6 +10137,8 @@ bool simple_wallet::get_tx_note(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::set_description(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot set description"); ++ + // 0 arguments allowed, for setting the description to empty string + + std::string description = ""; +@@ -9968,6 +10155,8 @@ bool simple_wallet::set_description(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_description(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get description"); ++ + if (args.size() != 0) + { + PRINT_USAGE(USAGE_GET_DESCRIPTION); +@@ -10026,6 +10215,8 @@ bool simple_wallet::wallet_info(const std::vector &args) + type = tr("Watch only"); + else if (m_wallet->multisig(&ready, &threshold, &total)) + type = (boost::format(tr("%u/%u multisig%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); ++ else if (m_wallet->is_background_wallet()) ++ type = tr("Background wallet"); + else + type = tr("Normal"); + message_writer() << tr("Type: ") << type; +@@ -10037,6 +10228,7 @@ bool simple_wallet::wallet_info(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sign(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sign"); + if (m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command not supported by HW wallet"); +@@ -10144,6 +10336,7 @@ bool simple_wallet::export_key_images(const std::vector &args_) + fail_msg_writer() << tr("command not supported by HW wallet"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot export key images"); + auto args = args_; + + if (m_wallet->watch_only()) +@@ -10197,6 +10390,7 @@ bool simple_wallet::import_key_images(const std::vector &args) + fail_msg_writer() << tr("command not supported by HW wallet"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot import key images"); + if (!m_wallet->is_trusted_daemon()) + { + fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); +@@ -10305,6 +10499,7 @@ bool simple_wallet::export_outputs(const std::vector &args_) + fail_msg_writer() << tr("command not supported by HW wallet"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot export outputs"); + auto args = args_; + + bool all = false; +@@ -10354,6 +10549,7 @@ bool simple_wallet::import_outputs(const std::vector &args) + fail_msg_writer() << tr("command not supported by HW wallet"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot import outputs"); + if (args.size() != 1) + { + PRINT_USAGE(USAGE_IMPORT_OUTPUTS); +diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h +index 652708f..159da2c 100644 +--- a/src/simplewallet/simplewallet.h ++++ b/src/simplewallet/simplewallet.h +@@ -147,6 +147,7 @@ namespace cryptonote + bool set_ignore_outputs_above(const std::vector &args = std::vector()); + bool set_ignore_outputs_below(const std::vector &args = std::vector()); + bool set_track_uses(const std::vector &args = std::vector()); ++ bool setup_background_sync(const std::vector &args = std::vector()); + bool set_show_wallet_name_when_locked(const std::vector &args = std::vector()); + bool set_inactivity_lock_timeout(const std::vector &args = std::vector()); + bool set_setup_background_mining(const std::vector &args = std::vector()); +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index fc4f891..e9f76f4 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -54,6 +54,40 @@ using namespace cryptonote; + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" + ++#define LOCK_REFRESH() \ ++ bool refresh_enabled = m_refreshEnabled; \ ++ m_refreshEnabled = false; \ ++ m_wallet->stop(); \ ++ m_refreshCV.notify_one(); \ ++ boost::mutex::scoped_lock lock(m_refreshMutex); \ ++ boost::mutex::scoped_lock lock2(m_refreshMutex2); \ ++ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ ++ /* m_refreshMutex's still locked here */ \ ++ if (refresh_enabled) \ ++ startRefresh(); \ ++ }) ++ ++#define PRE_VALIDATE_BACKGROUND_SYNC() \ ++ do \ ++ { \ ++ clearStatus(); \ ++ if (m_wallet->key_on_device()) \ ++ { \ ++ setStatusError(tr("HW wallet cannot use background sync")); \ ++ return false; \ ++ } \ ++ if (m_wallet->watch_only()) \ ++ { \ ++ setStatusError(tr("View only wallet cannot use background sync")); \ ++ return false; \ ++ } \ ++ if (m_wallet->multisig()) \ ++ { \ ++ setStatusError(tr("Multisig wallet cannot use background sync")); \ ++ return false; \ ++ } \ ++ } while (0) ++ + namespace Monero { + + namespace { +@@ -792,6 +826,8 @@ bool WalletImpl::close(bool store) + + std::string WalletImpl::seed(const std::string& seed_offset) const + { ++ if (checkBackgroundSync("cannot get seed")) ++ return std::string(); + epee::wipeable_string seed; + if (m_wallet) + m_wallet->get_seed(seed, seed_offset); +@@ -805,6 +841,8 @@ std::string WalletImpl::getSeedLanguage() const + + void WalletImpl::setSeedLanguage(const std::string &arg) + { ++ if (checkBackgroundSync("cannot set seed language")) ++ return; + m_wallet->set_seed_language(arg); + } + +@@ -828,6 +866,8 @@ void WalletImpl::statusWithErrorString(int& status, std::string& errorString) co + + bool WalletImpl::setPassword(const std::string &password) + { ++ if (checkBackgroundSync("cannot change password")) ++ return false; + clearStatus(); + try { + m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password); +@@ -988,6 +1028,8 @@ bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_ + + void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height) + { ++ if (checkBackgroundSync("cannot change refresh height")) ++ return; + m_wallet->set_refresh_from_block_height(refresh_from_block_height); + } + +@@ -1105,6 +1147,8 @@ void WalletImpl::refreshAsync() + + bool WalletImpl::rescanBlockchain() + { ++ if (checkBackgroundSync("cannot rescan blockchain")) ++ return false; + clearStatus(); + m_refreshShouldRescan = true; + doRefresh(); +@@ -1113,6 +1157,8 @@ bool WalletImpl::rescanBlockchain() + + void WalletImpl::rescanBlockchainAsync() + { ++ if (checkBackgroundSync("cannot rescan blockchain")) ++ return; + m_refreshShouldRescan = true; + refreshAsync(); + } +@@ -1136,7 +1182,7 @@ int WalletImpl::autoRefreshInterval() const + UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_filename) { + clearStatus(); + UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); +- if (!m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ ++ if (checkBackgroundSync("cannot load tx") || !m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ + setStatusError(tr("Failed to load unsigned transactions")); + transaction->m_status = UnsignedTransaction::Status::Status_Error; + transaction->m_errorString = errorString(); +@@ -1156,6 +1202,8 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file + + bool WalletImpl::submitTransaction(const string &fileName) { + clearStatus(); ++ if (checkBackgroundSync("cannot submit tx")) ++ return false; + std::unique_ptr transaction(new PendingTransactionImpl(*this)); + + bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx); +@@ -1179,6 +1227,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) + setStatusError(tr("Wallet is view only")); + return false; + } ++ if (checkBackgroundSync("cannot export key images")) ++ return false; + + try + { +@@ -1199,6 +1249,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) + + bool WalletImpl::importKeyImages(const string &filename) + { ++ if (checkBackgroundSync("cannot import key images")) ++ return false; + if (!trustedDaemon()) { + setStatusError(tr("Key images can only be imported with a trusted daemon")); + return false; +@@ -1222,6 +1274,8 @@ bool WalletImpl::importKeyImages(const string &filename) + + bool WalletImpl::exportOutputs(const string &filename, bool all) + { ++ if (checkBackgroundSync("cannot export outputs")) ++ return false; + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets.")) + filename); +@@ -1252,6 +1306,8 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) + + bool WalletImpl::importOutputs(const string &filename) + { ++ if (checkBackgroundSync("cannot import outputs")) ++ return false; + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets.")) + filename); +@@ -1284,6 +1340,8 @@ bool WalletImpl::importOutputs(const string &filename) + + bool WalletImpl::scanTransactions(const std::vector &txids) + { ++ if (checkBackgroundSync("cannot scan transactions")) ++ return false; + if (txids.empty()) + { + setStatusError(string(tr("Failed to scan transactions: no transaction ids provided."))); +@@ -1322,8 +1380,86 @@ bool WalletImpl::scanTransactions(const std::vector &txids) + return true; + } + ++bool WalletImpl::setupBackgroundSync(const Wallet::BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) ++{ ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ ++ tools::wallet2::BackgroundSyncType bgs_type; ++ switch (background_sync_type) ++ { ++ case Wallet::BackgroundSync_Off: bgs_type = tools::wallet2::BackgroundSyncOff; break; ++ case Wallet::BackgroundSync_ReusePassword: bgs_type = tools::wallet2::BackgroundSyncReusePassword; break; ++ case Wallet::BackgroundSync_CustomPassword: bgs_type = tools::wallet2::BackgroundSyncCustomPassword; break; ++ default: setStatusError(tr("Unknown background sync type")); return false; ++ } ++ ++ boost::optional bgc_password = background_cache_password ++ ? boost::optional(*background_cache_password) ++ : boost::none; ++ ++ LOCK_REFRESH(); ++ m_wallet->setup_background_sync(bgs_type, wallet_password, bgc_password); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Failed to setup background sync: " << e.what()); ++ setStatusError(string(tr("Failed to setup background sync: ")) + e.what()); ++ return false; ++ } ++ return true; ++} ++ ++Wallet::BackgroundSyncType WalletImpl::getBackgroundSyncType() const ++{ ++ switch (m_wallet->background_sync_type()) ++ { ++ case tools::wallet2::BackgroundSyncOff: return Wallet::BackgroundSync_Off; ++ case tools::wallet2::BackgroundSyncReusePassword: return Wallet::BackgroundSync_ReusePassword; ++ case tools::wallet2::BackgroundSyncCustomPassword: return Wallet::BackgroundSync_CustomPassword; ++ default: setStatusError(tr("Unknown background sync type")); return Wallet::BackgroundSync_Off; ++ } ++} ++ ++bool WalletImpl::startBackgroundSync() ++{ ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ LOCK_REFRESH(); ++ m_wallet->start_background_sync(); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Failed to start background sync: " << e.what()); ++ setStatusError(string(tr("Failed to start background sync: ")) + e.what()); ++ return false; ++ } ++ return true; ++} ++ ++bool WalletImpl::stopBackgroundSync(const std::string &wallet_password) ++{ ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ LOCK_REFRESH(); ++ m_wallet->stop_background_sync(epee::wipeable_string(wallet_password)); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Failed to stop background sync: " << e.what()); ++ setStatusError(string(tr("Failed to stop background sync: ")) + e.what()); ++ return false; ++ } ++ return true; ++} ++ + void WalletImpl::addSubaddressAccount(const std::string& label) + { ++ if (checkBackgroundSync("cannot add account")) ++ return; + m_wallet->add_subaddress_account(label); + } + size_t WalletImpl::numSubaddressAccounts() const +@@ -1336,10 +1472,14 @@ size_t WalletImpl::numSubaddresses(uint32_t accountIndex) const + } + void WalletImpl::addSubaddress(uint32_t accountIndex, const std::string& label) + { ++ if (checkBackgroundSync("cannot add subbaddress")) ++ return; + m_wallet->add_subaddress(accountIndex, label); + } + std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const + { ++ if (checkBackgroundSync("cannot get subbaddress label")) ++ return ""; + try + { + return m_wallet->get_subaddress_label({accountIndex, addressIndex}); +@@ -1353,6 +1493,8 @@ std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addre + } + void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) + { ++ if (checkBackgroundSync("cannot set subbaddress label")) ++ return; + try + { + return m_wallet->set_subaddress_label({accountIndex, addressIndex}, label); +@@ -1366,12 +1508,16 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex + + MultisigState WalletImpl::multisig() const { + MultisigState state; ++ if (checkBackgroundSync("cannot use multisig")) ++ return state; + state.isMultisig = m_wallet->multisig(&state.isReady, &state.threshold, &state.total); + + return state; + } + + string WalletImpl::getMultisigInfo() const { ++ if (checkBackgroundSync("cannot use multisig")) ++ return string(); + try { + clearStatus(); + return m_wallet->get_multisig_first_kex_msg(); +@@ -1384,6 +1530,8 @@ string WalletImpl::getMultisigInfo() const { + } + + string WalletImpl::makeMultisig(const vector& info, const uint32_t threshold) { ++ if (checkBackgroundSync("cannot make multisig")) ++ return string(); + try { + clearStatus(); + +@@ -1524,6 +1672,9 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector extra; + std::string extra_nonce; + vector dsts; +@@ -1690,6 +1841,9 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() + PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); + + do { ++ if (checkBackgroundSync("cannot sweep")) ++ break; ++ + try { + transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions(); + pendingTxPostProcess(transaction); +@@ -1823,11 +1977,15 @@ uint32_t WalletImpl::defaultMixin() const + + void WalletImpl::setDefaultMixin(uint32_t arg) + { ++ if (checkBackgroundSync("cannot set default mixin")) ++ return; + m_wallet->default_mixin(arg); + } + + bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val) + { ++ if (checkBackgroundSync("cannot set cache attribute")) ++ return false; + m_wallet->set_attribute(key, val); + return true; + } +@@ -1841,6 +1999,8 @@ std::string WalletImpl::getCacheAttribute(const std::string &key) const + + bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) + { ++ if (checkBackgroundSync("cannot set user note")) ++ return false; + cryptonote::blobdata txid_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) + return false; +@@ -1852,6 +2012,8 @@ bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) + + std::string WalletImpl::getUserNote(const std::string &txid) const + { ++ if (checkBackgroundSync("cannot get user note")) ++ return ""; + cryptonote::blobdata txid_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) + return ""; +@@ -1862,6 +2024,9 @@ std::string WalletImpl::getUserNote(const std::string &txid) const + + std::string WalletImpl::getTxKey(const std::string &txid_str) const + { ++ if (checkBackgroundSync("cannot get tx key")) ++ return ""; ++ + crypto::hash txid; + if(!epee::string_tools::hex_to_pod(txid_str, txid)) + { +@@ -1946,6 +2111,9 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str, + + std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const + { ++ if (checkBackgroundSync("cannot get tx proof")) ++ return ""; ++ + crypto::hash txid; + if (!epee::string_tools::hex_to_pod(txid_str, txid)) + { +@@ -2002,6 +2170,9 @@ bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &ad + } + + std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::string &message) const { ++ if (checkBackgroundSync("cannot get spend proof")) ++ return ""; ++ + crypto::hash txid; + if(!epee::string_tools::hex_to_pod(txid_str, txid)) + { +@@ -2044,6 +2215,9 @@ bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string + } + + std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const { ++ if (checkBackgroundSync("cannot get reserve proof")) ++ return ""; ++ + try + { + clearStatus(); +@@ -2090,6 +2264,9 @@ bool WalletImpl::checkReserveProof(const std::string &address, const std::string + + std::string WalletImpl::signMessage(const std::string &message, const std::string &address) + { ++ if (checkBackgroundSync("cannot sign message")) ++ return ""; ++ + if (address.empty()) { + return m_wallet->sign(message, tools::wallet2::sign_with_spend_key); + } +@@ -2217,6 +2394,16 @@ bool WalletImpl::isDeterministic() const + return m_wallet->is_deterministic(); + } + ++bool WalletImpl::isBackgroundSyncing() const ++{ ++ return m_wallet->is_background_syncing(); ++} ++ ++bool WalletImpl::isBackgroundWallet() const ++{ ++ return m_wallet->is_background_wallet(); ++} ++ + void WalletImpl::clearStatus() const + { + boost::lock_guard l(m_statusMutex); +@@ -2285,9 +2472,7 @@ void WalletImpl::doRefresh() + if(rescan) + m_wallet->rescan_blockchain(false); + m_wallet->refresh(trustedDaemon()); +- if (!m_synchronized) { +- m_synchronized = true; +- } ++ m_synchronized = m_wallet->is_synced(); + // assuming if we have empty history, it wasn't initialized yet + // for further history changes client need to update history in + // "on_money_received" and "on_money_sent" callbacks +@@ -2391,6 +2576,24 @@ bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_a + return true; + } + ++bool WalletImpl::checkBackgroundSync(const std::string &message) const ++{ ++ clearStatus(); ++ if (m_wallet->is_background_wallet()) ++ { ++ LOG_ERROR("Background wallets " + message); ++ setStatusError(tr("Background wallets ") + message); ++ return true; ++ } ++ if (m_wallet->is_background_syncing()) ++ { ++ LOG_ERROR(message + " while background syncing"); ++ setStatusError(message + tr(" while background syncing. Stop background syncing first.")); ++ return true; ++ } ++ return false; ++} ++ + bool WalletImpl::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 &unknown_parameters, std::string &error) + { + return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); +@@ -2409,6 +2612,8 @@ std::string WalletImpl::getDefaultDataDir() const + bool WalletImpl::rescanSpent() + { + clearStatus(); ++ if (checkBackgroundSync("cannot rescan spent")) ++ return false; + if (!trustedDaemon()) { + setStatusError(tr("Rescan spent can only be used with a trusted daemon")); + return false; +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index ec2d7e9..1f199a7 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -171,6 +171,13 @@ public: + bool importOutputs(const std::string &filename) override; + bool scanTransactions(const std::vector &txids) override; + ++ bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; ++ BackgroundSyncType getBackgroundSyncType() const override; ++ bool startBackgroundSync() override; ++ bool stopBackgroundSync(const std::string &wallet_password) override; ++ bool isBackgroundSyncing() const override; ++ bool isBackgroundWallet() const override; ++ + virtual void disposeTransaction(PendingTransaction * t) override; + virtual uint64_t estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const override; +@@ -239,6 +246,7 @@ private: + bool isNewWallet() const; + void pendingTxPostProcess(PendingTransactionImpl * pending); + bool doInit(const std::string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit = 0, bool ssl = false); ++ bool checkBackgroundSync(const std::string &message) const; + + private: + friend class PendingTransactionImpl; +@@ -253,6 +261,10 @@ private: + mutable boost::mutex m_statusMutex; + mutable int m_status; + mutable std::string m_errorString; ++ // TODO: harden password handling in the wallet API, see relevant discussion ++ // https://github.com/monero-project/monero-gui/issues/1537 ++ // https://github.com/feather-wallet/feather/issues/72#issuecomment-1405602142 ++ // https://github.com/monero-project/monero/pull/8619#issuecomment-1632951461 + std::string m_password; + std::unique_ptr m_history; + std::unique_ptr m_wallet2Callback; +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 71991df..e349df1 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -445,6 +445,12 @@ struct Wallet + ConnectionStatus_WrongVersion + }; + ++ enum BackgroundSyncType { ++ BackgroundSync_Off = 0, ++ BackgroundSync_ReusePassword = 1, ++ BackgroundSync_CustomPassword = 2 ++ }; ++ + virtual ~Wallet() = 0; + virtual std::string seed(const std::string& seed_offset = "") const = 0; + virtual std::string getSeedLanguage() const = 0; +@@ -936,6 +942,42 @@ struct Wallet + */ + virtual bool scanTransactions(const std::vector &txids) = 0; + ++ /*! ++ * \brief setupBackgroundSync - setup background sync mode with just a view key ++ * \param background_sync_type - the mode the wallet background syncs in ++ * \param wallet_password ++ * \param background_cache_password - custom password to encrypt background cache, only needed for custom password background sync type ++ * \return - true on success ++ */ ++ virtual bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) = 0; ++ ++ /*! ++ * \brief getBackgroundSyncType - get mode the wallet background syncs in ++ * \return - the type, or off if type is unknown ++ */ ++ virtual BackgroundSyncType getBackgroundSyncType() const = 0; ++ ++ /** ++ * @brief startBackgroundSync - sync the chain in the background with just view key ++ */ ++ virtual bool startBackgroundSync() = 0; ++ ++ /** ++ * @brief stopBackgroundSync - bring back spend key and process background synced txs ++ * \param wallet_password ++ */ ++ virtual bool stopBackgroundSync(const std::string &wallet_password) = 0; ++ ++ /** ++ * @brief isBackgroundSyncing - returns true if the wallet is background syncing ++ */ ++ virtual bool isBackgroundSyncing() const = 0; ++ ++ /** ++ * @brief isBackgroundWallet - returns true if the wallet is a background wallet ++ */ ++ virtual bool isBackgroundWallet() const = 0; ++ + virtual TransactionHistory * history() = 0; + virtual AddressBook * addressBook() = 0; + virtual Subaddress * subaddress() = 0; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index ad8c361..8146014 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -157,6 +157,8 @@ static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; + + static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1"; + ++static const std::string BACKGROUND_WALLET_SUFFIX = ".background"; ++ + boost::mutex tools::wallet2::default_daemon_address_lock; + std::string tools::wallet2::default_daemon_address = ""; + +@@ -1009,14 +1011,14 @@ uint64_t num_priv_multisig_keys_post_setup(uint64_t threshold, uint64_t total) + * @param keys_data_key the chacha key that encrypts wallet keys files + * @return crypto::chacha_key the chacha key that encrypts the wallet cache files + */ +-crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key) ++crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key, const unsigned char domain_separator) + { + static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key"); + + crypto::chacha_key cache_key; + epee::mlocked> cache_key_data; + memcpy(cache_key_data.data(), &keys_data_key, HASH_SIZE); +- cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE; ++ cache_key_data[HASH_SIZE] = domain_separator; + cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&) cache_key); + + return cache_key; +@@ -1104,7 +1106,7 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional lock(lockers_lock); + if (lockers++ > 0) + locked = false; +- if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only()) ++ if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only() || w.is_background_syncing()) + { + locked = false; + return; +@@ -1221,6 +1223,11 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std + m_ignore_outputs_above(MONEY_SUPPLY), + m_ignore_outputs_below(0), + m_track_uses(false), ++ m_is_background_wallet(false), ++ m_background_sync_type(BackgroundSyncOff), ++ m_background_syncing(false), ++ m_processing_background_cache(false), ++ m_custom_background_key(boost::none), + m_show_wallet_name_when_locked(false), + m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT), + m_setup_background_mining(BackgroundMiningMaybe), +@@ -1860,6 +1867,9 @@ bool has_nonrequested_tx_at_height_or_above_requested(uint64_t height, const std + //---------------------------------------------------------------------------------------------------- + void wallet2::scan_tx(const std::unordered_set &txids) + { ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "cannot scan tx from background wallet"); ++ + // Get the transactions from daemon in batches sorted lowest height to highest + tx_entry_data txs_to_scan = get_tx_entries(txids); + if (txs_to_scan.tx_entries.empty()) +@@ -2167,11 +2177,11 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons + THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); + + // if keys are encrypted, ask for password +- if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k) ++ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k && !m_background_syncing) + { + static critical_section password_lock; + CRITICAL_REGION_LOCAL(password_lock); +- if (!m_encrypt_keys_after_refresh) ++ if (!m_encrypt_keys_after_refresh && !m_processing_background_cache) + { + boost::optional pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received"); + THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero")); +@@ -2183,7 +2193,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons + crypto::public_key output_public_key; + THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key"); + +- if (m_multisig) ++ if (m_multisig || m_background_syncing/*no spend key*/) + { + tx_scan_info.in_ephemeral.pub = output_public_key; + tx_scan_info.in_ephemeral.sec = crypto::null_skey; +@@ -2440,6 +2450,22 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error, + "transactions outputs size=" + std::to_string(tx.vout.size()) + + " not match with daemon response size=" + std::to_string(o_indices.size())); ++ ++ // we're going to re-process this receive when background sync is disabled ++ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) ++ { ++ size_t bgs_idx = m_background_sync_data.txs.size(); ++ background_synced_tx_t bgs_tx = { ++ .index_in_background_sync_data = bgs_idx, ++ .tx = tx, ++ .output_indices = o_indices, ++ .height = height, ++ .block_timestamp = ts, ++ .double_spend_seen = double_spend_seen ++ }; ++ LOG_PRINT_L2("Adding received tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); ++ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); ++ } + } + + for(size_t o: outs) +@@ -2465,7 +2491,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + td.m_tx = (const cryptonote::transaction_prefix&)tx; + td.m_txid = txid; + td.m_key_image = tx_scan_info[o].ki; +- td.m_key_image_known = !m_watch_only && !m_multisig; ++ td.m_key_image_known = !m_watch_only && !m_multisig && !m_background_syncing; + if (!td.m_key_image_known) + { + // we might have cold signed, and have a mapping to key images +@@ -2655,10 +2681,25 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + 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); ++ ++ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) ++ { ++ size_t bgs_idx = m_background_sync_data.txs.size(); ++ background_synced_tx_t bgs_tx = { ++ .index_in_background_sync_data = bgs_idx, ++ .tx = tx, ++ .output_indices = o_indices, ++ .height = height, ++ .block_timestamp = ts, ++ .double_spend_seen = double_spend_seen ++ }; ++ LOG_PRINT_L2("Adding spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); ++ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); ++ } + } + } + +- if (!pool && m_track_uses) ++ if (!pool && (m_track_uses || (m_background_syncing && it == m_key_images.end()))) + { + PERF_TIMER(track_uses); + const uint64_t amount = in_to_key.amount; +@@ -2672,7 +2713,27 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + { + size_t idx = i->second; + THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Output tracker cache index out of range"); +- m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); ++ ++ if (m_track_uses) ++ m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); ++ ++ // We'll re-process all txs which *might* be spends when we disable ++ // background sync and retrieve the spend key. We don't know if an ++ // output is a spend in this tx if we don't know its key image. ++ if (m_background_syncing && !m_transfers[idx].m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) ++ { ++ size_t bgs_idx = m_background_sync_data.txs.size(); ++ background_synced_tx_t bgs_tx = { ++ .index_in_background_sync_data = bgs_idx, ++ .tx = tx, ++ .output_indices = o_indices, ++ .height = height, ++ .block_timestamp = ts, ++ .double_spend_seen = double_spend_seen ++ }; ++ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); ++ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); ++ } + } + } + } +@@ -2682,7 +2743,24 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + continue; + for (uint64_t offset: offsets) + if (offset == td.m_global_output_index) +- td.m_uses.push_back(std::make_pair(height, txid)); ++ { ++ if (m_track_uses) ++ td.m_uses.push_back(std::make_pair(height, txid)); ++ if (m_background_syncing && !td.m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) ++ { ++ size_t bgs_idx = m_background_sync_data.txs.size(); ++ background_synced_tx_t bgs_tx = { ++ .index_in_background_sync_data = bgs_idx, ++ .tx = tx, ++ .output_indices = o_indices, ++ .height = height, ++ .block_timestamp = ts, ++ .double_spend_seen = double_spend_seen ++ }; ++ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); ++ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); ++ } ++ } + } + } + } +@@ -3055,8 +3133,8 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh + req.start_height = start_height; + req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; + +- req.requested_info = first ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; +- if (try_incremental) ++ req.requested_info = (first && !m_background_syncing) ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; ++ if (try_incremental && !m_background_syncing) + req.pool_info_since = m_pool_info_query_time; + + { +@@ -3083,7 +3161,7 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh + << ", height " << blocks_start_height + blocks.size() << ", node height " << res.current_height + << ", pool info " << static_cast(res.pool_info_extent)); + +- if (first) ++ if (first && !m_background_syncing) + { + if (res.pool_info_extent != COMMAND_RPC_GET_BLOCKS_FAST::NONE) + { +@@ -3595,6 +3673,9 @@ void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash + // incremental update anymore, because with that we might miss some txs altogether. + void wallet2::update_pool_state(std::vector> &process_txs, bool refreshed, bool try_incremental) + { ++ process_txs.clear(); ++ if (m_background_syncing) ++ return; + bool updated = false; + if (m_pool_info_query_time != 0 && try_incremental) + { +@@ -4166,6 +4247,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo + } + + m_first_refresh_done = true; ++ if (m_background_syncing || m_is_background_wallet) ++ m_background_sync_data.first_refresh_done = true; + + LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all(false)) << ", unlocked: " << print_money(unlocked_balance_all(false))); + } +@@ -4251,6 +4334,14 @@ wallet2::detached_blockchain_data wallet2::detach_blockchain(uint64_t height, st + td.m_uses.pop_back(); + } + ++ for (auto it = m_background_sync_data.txs.begin(); it != m_background_sync_data.txs.end(); ) ++ { ++ if(height <= it->second.height) ++ it = m_background_sync_data.txs.erase(it); ++ else ++ ++it; ++ } ++ + if (output_tracker_cache) + output_tracker_cache->clear(); + +@@ -4325,8 +4416,12 @@ void wallet2::handle_reorg(uint64_t height, std::map m_blockchain.offset(), + error::wallet_internal_error, "Daemon claims reorg below last checkpoint"); ++ + detached_blockchain_data dbd = detach_blockchain(height, output_tracker_cache); + ++ if (m_background_syncing && height < m_background_sync_data.start_height) ++ m_background_sync_data.start_height = height; ++ + if (m_callback) + m_callback->on_reorg(height, dbd.detached_blockchain.size(), dbd.detached_tx_hashes.size()); + } +@@ -4336,6 +4431,7 @@ bool wallet2::deinit() + if(m_is_initialized) { + m_is_initialized = false; + unlock_keys_file(); ++ unlock_background_keys_file(); + m_account.deinit(); + } + return true; +@@ -4362,6 +4458,7 @@ bool wallet2::clear() + m_device_last_key_image_sync = 0; + m_pool_info_query_time = 0; + m_skip_to_height = 0; ++ m_background_sync_data = background_sync_data_t{}; + return true; + } + //---------------------------------------------------------------------------------------------------- +@@ -4380,13 +4477,30 @@ void wallet2::clear_soft(bool keep_key_images) + m_scanned_pool_txs[1].clear(); + m_pool_info_query_time = 0; + m_skip_to_height = 0; ++ m_background_sync_data = background_sync_data_t{}; + + cryptonote::block b; + generate_genesis(b); + m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); + } +- ++//---------------------------------------------------------------------------------------------------- ++void wallet2::clear_user_data() ++{ ++ for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) ++ i->second.m_dests.clear(); ++ for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) ++ i->second.m_dests.clear(); ++ for (auto i = m_transfers.begin(); i != m_transfers.end(); ++i) ++ i->m_frozen = false; ++ m_tx_keys.clear(); ++ m_tx_notes.clear(); ++ m_address_book.clear(); ++ m_subaddress_labels.clear(); ++ m_attributes.clear(); ++ m_account_tags = std::pair, std::vector>(); ++} ++//---------------------------------------------------------------------------------------------------- + /*! + * \brief Stores wallet information to wallet file. + * \param keys_file_name Name of wallet file +@@ -4398,16 +4512,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable + { + boost::optional keys_file_data = get_keys_file_data(password, watch_only); + CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); +- ++ return store_keys_file_data(keys_file_name, keys_file_data.get()); ++} ++//---------------------------------------------------------------------------------------------------- ++bool wallet2::store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only, bool background_keys_file) ++{ ++ boost::optional keys_file_data = get_keys_file_data(key, watch_only, background_keys_file); ++ CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); ++ return store_keys_file_data(keys_file_name, keys_file_data.get(), background_keys_file); ++} ++//---------------------------------------------------------------------------------------------------- ++bool wallet2::store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file) ++{ + std::string tmp_file_name = keys_file_name + ".new"; + std::string buf; +- bool r = ::serialization::dump_binary(keys_file_data.get(), buf); ++ bool r = ::serialization::dump_binary(keys_file_data, buf); + r = r && save_to_file(tmp_file_name, buf); + CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name); + +- unlock_keys_file(); ++ if (!background_keys_file) ++ unlock_keys_file(); ++ else ++ unlock_background_keys_file(); ++ + std::error_code e = tools::replace_file(tmp_file_name, keys_file_name); +- lock_keys_file(); ++ ++ if (!background_keys_file) ++ lock_keys_file(); ++ else ++ lock_background_keys_file(keys_file_name); + + if (e) { + boost::filesystem::remove(tmp_file_name); +@@ -4419,26 +4552,27 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable + } + //---------------------------------------------------------------------------------------------------- + boost::optional wallet2::get_keys_file_data(const epee::wipeable_string& password, bool watch_only) ++{ ++ crypto::chacha_key key; ++ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); ++ verify_password_with_cached_key(key); ++ return get_keys_file_data(key, watch_only); ++} ++//---------------------------------------------------------------------------------------------------- ++boost::optional wallet2::get_keys_file_data(const crypto::chacha_key& key, bool watch_only, bool background_keys_file) + { + epee::byte_slice account_data; + std::string multisig_signers; + std::string multisig_derivations; + cryptonote::account_base account = m_account; + +- crypto::chacha_key key; +- crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); +- +- // We use m_cache_key as a deterministic test to see if given key corresponds to original password +- const crypto::chacha_key cache_key = derive_cache_key(key); +- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); +- + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) + { + account.encrypt_viewkey(key); + account.decrypt_keys(key); + } + +- if (watch_only) ++ if (watch_only || background_keys_file) + account.forget_spend_key(); + + account.encrypt_keys(key); +@@ -4573,6 +4707,9 @@ boost::optional wallet2::get_keys_file_data(const epee: + value2.SetInt(m_track_uses ? 1 : 0); + json.AddMember("track_uses", value2, json.GetAllocator()); + ++ value2.SetInt(m_background_sync_type); ++ json.AddMember("background_sync_type", value2, json.GetAllocator()); ++ + value2.SetInt(m_show_wallet_name_when_locked ? 1 : 0); + json.AddMember("show_wallet_name_when_locked", value2, json.GetAllocator()); + +@@ -4630,6 +4767,12 @@ boost::optional wallet2::get_keys_file_data(const epee: + value2.SetInt(m_enable_multisig ? 1 : 0); + json.AddMember("enable_multisig", value2, json.GetAllocator()); + ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key) ++ { ++ value.SetString(reinterpret_cast(m_custom_background_key.get().data()), m_custom_background_key.get().size()); ++ json.AddMember("custom_background_key", value, json.GetAllocator()); ++ } ++ + // Serialize the JSON object + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); +@@ -4656,13 +4799,81 @@ void wallet2::setup_keys(const epee::wipeable_string &password) + m_account.decrypt_viewkey(key); + } + +- m_cache_key = derive_cache_key(key); ++ m_cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); + + get_ringdb_key(); + } + //---------------------------------------------------------------------------------------------------- ++void validate_background_cache_password_usage(const tools::wallet2::BackgroundSyncType background_sync_type, const boost::optional &background_cache_password, const bool multisig, const bool watch_only, const bool key_on_device) ++{ ++ THROW_WALLET_EXCEPTION_IF(multisig || watch_only || key_on_device, error::wallet_internal_error, multisig ++ ? "Background sync not implemented for multisig wallets" : watch_only ++ ? "Background sync not implemented for view only wallets" ++ : "Background sync not implemented for HW wallets"); ++ ++ switch (background_sync_type) ++ { ++ case tools::wallet2::BackgroundSyncOff: ++ { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "background sync is not enabled"); ++ break; ++ } ++ case tools::wallet2::BackgroundSyncReusePassword: ++ { ++ THROW_WALLET_EXCEPTION_IF(background_cache_password, error::wallet_internal_error, ++ "unexpected custom background cache password"); ++ break; ++ } ++ case tools::wallet2::BackgroundSyncCustomPassword: ++ { ++ THROW_WALLET_EXCEPTION_IF(!background_cache_password, error::wallet_internal_error, ++ "expected custom background cache password"); ++ break; ++ } ++ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); ++ } ++} ++//---------------------------------------------------------------------------------------------------- ++void get_custom_background_key(const epee::wipeable_string &password, crypto::chacha_key &custom_background_key, const uint64_t kdf_rounds) ++{ ++ crypto::chacha_key key; ++ crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds); ++ custom_background_key = derive_cache_key(key, config::HASH_KEY_BACKGROUND_KEYS_FILE); ++} ++//---------------------------------------------------------------------------------------------------- ++const crypto::chacha_key wallet2::get_cache_key() ++{ ++ if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing) ++ { ++ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); ++ // Domain separate keys used to encrypt background keys file and cache ++ return derive_cache_key(m_custom_background_key.get(), config::HASH_KEY_BACKGROUND_CACHE); ++ } ++ else ++ { ++ return m_cache_key; ++ } ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::verify_password_with_cached_key(const epee::wipeable_string &password) ++{ ++ crypto::chacha_key key; ++ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); ++ verify_password_with_cached_key(key); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::verify_password_with_cached_key(const crypto::chacha_key &key) ++{ ++ // We use m_cache_key as a deterministic test to see if given key corresponds to original password ++ const crypto::chacha_key cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); ++ THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); ++} ++//---------------------------------------------------------------------------------------------------- + void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password) + { ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "cannot change password from background wallet"); ++ + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) + decrypt_keys(original_password); + setup_keys(new_password); +@@ -4721,8 +4932,24 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + std::string account_data; + account_data.resize(keys_file_data.account_data.size()); + crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); +- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) ++ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); ++ if (try_v0_format) + crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); ++ ++ // Check if it's a background keys file if both of the above formats fail ++ { ++ m_is_background_wallet = false; ++ m_background_syncing = false; ++ cryptonote::account_base account_data_check; ++ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) ++ { ++ get_custom_background_key(password, key, m_kdf_rounds); ++ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); ++ m_is_background_wallet = !json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); ++ m_background_syncing = m_is_background_wallet; // start a background wallet background syncing ++ } ++ } ++ + // The contents should be JSON if the wallet follows the new format. + if (json.Parse(account_data.c_str()).HasParseError()) + { +@@ -4760,6 +4987,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + m_ignore_outputs_above = MONEY_SUPPLY; + m_ignore_outputs_below = 0; + m_track_uses = false; ++ m_background_sync_type = BackgroundSyncOff; + m_show_wallet_name_when_locked = false; + m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT; + m_setup_background_mining = BackgroundMiningMaybe; +@@ -4777,6 +5005,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_custom_background_key = boost::none; + } + else if(json.IsObject()) + { +@@ -5013,6 +5242,39 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + m_credits_target = field_credits_target; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false); + m_enable_multisig = field_enable_multisig; ++ ++ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff); ++ m_background_sync_type = field_background_sync_type; ++ ++ // Load encryption key used to encrypt background cache ++ crypto::chacha_key custom_background_key; ++ m_custom_background_key = boost::none; ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_is_background_wallet) ++ { ++ if (!json.HasMember("custom_background_key")) ++ { ++ LOG_ERROR("Field custom_background_key not found in JSON"); ++ return false; ++ } ++ else if (!json["custom_background_key"].IsString()) ++ { ++ LOG_ERROR("Field custom_background_key found in JSON, but not String"); ++ return false; ++ } ++ else if (json["custom_background_key"].GetStringLength() != sizeof(crypto::chacha_key)) ++ { ++ LOG_ERROR("Field custom_background_key found in JSON, but not correct length"); ++ return false; ++ } ++ const char *field_custom_background_key = json["custom_background_key"].GetString(); ++ memcpy(custom_background_key.data(), field_custom_background_key, sizeof(crypto::chacha_key)); ++ m_custom_background_key = boost::optional(custom_background_key); ++ LOG_PRINT_L1("Loaded custom background key derived from custom password"); ++ } ++ else if (json.HasMember("custom_background_key")) ++ { ++ LOG_ERROR("Unexpected field custom_background_key found in JSON"); ++ } + } + else + { +@@ -5076,12 +5338,17 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + const cryptonote::account_keys& keys = m_account.get_keys(); + hw::device &hwdev = m_account.get_device(); + r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); +- if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) ++ if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD && !m_is_background_wallet) + r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); + + if (r) +- setup_keys(password); ++ { ++ if (!m_is_background_wallet) ++ setup_keys(password); ++ else ++ m_custom_background_key = boost::optional(key); ++ } + + return true; + } +@@ -5096,11 +5363,12 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password + * + */ +-bool wallet2::verify_password(const epee::wipeable_string& password) ++bool wallet2::verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out) + { + // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). + unlock_keys_file(); +- bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds); ++ const bool no_spend_key = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; ++ bool r = verify_password(m_keys_file, password, no_spend_key, m_account.get_device(), m_kdf_rounds, spend_key_out); + lock_keys_file(); + return r; + } +@@ -5118,7 +5386,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) + * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password + * + */ +-bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) ++bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out) + { + rapidjson::Document json; + wallet2::keys_file_data keys_file_data; +@@ -5135,9 +5403,22 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip + std::string account_data; + account_data.resize(keys_file_data.account_data.size()); + crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); +- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) ++ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); ++ if (try_v0_format) + crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + ++ // Check if it's a background keys file if both of the above formats fail ++ { ++ cryptonote::account_base account_data_check; ++ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) ++ { ++ get_custom_background_key(password, key, kdf_rounds); ++ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); ++ const bool is_background_wallet = json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); ++ no_spend_key = no_spend_key || is_background_wallet; ++ } ++ } ++ + // The contents should be JSON if the wallet follows the new format. + if (json.Parse(account_data.c_str()).HasParseError()) + { +@@ -5162,6 +5443,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip + r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); + if(!no_spend_key) + r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); ++ spend_key_out = (!no_spend_key && r) ? keys.m_spend_secret_key : crypto::null_skey; + return r; + } + +@@ -5173,9 +5455,7 @@ void wallet2::encrypt_keys(const crypto::chacha_key &key) + + void wallet2::decrypt_keys(const crypto::chacha_key &key) + { +- // We use m_cache_key as a deterministic test to see if given key corresponds to original password +- const crypto::chacha_key cache_key = derive_cache_key(key); +- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); ++ verify_password_with_cached_key(key); + + m_account.encrypt_viewkey(key); + m_account.decrypt_keys(key); +@@ -5851,11 +6131,30 @@ void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_strin + { + if (wallet_name.empty()) + return; ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "cannot change wallet settings from background wallet"); + prepare_file_names(wallet_name); + boost::system::error_code ignored_ec; + THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file); + bool r = store_keys(m_keys_file, password, m_watch_only); + THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); ++ ++ // Update the background keys file when we rewrite the main wallet keys file ++ if (m_background_sync_type == BackgroundSyncCustomPassword && m_custom_background_key) ++ { ++ const std::string background_keys_filename = make_background_keys_file_name(wallet_name); ++ if (!lock_background_keys_file(background_keys_filename)) ++ { ++ LOG_ERROR("Background keys file " << background_keys_filename << " is opened by another wallet program and cannot be rewritten"); ++ return; // not fatal, background keys file will just have different wallet settings ++ } ++ store_background_keys(m_custom_background_key.get()); ++ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); ++ } ++ else if (m_background_sync_type == BackgroundSyncReusePassword) ++ { ++ reset_background_sync_data(m_background_sync_data); ++ } + } + /*! + * \brief Writes to a file named based on the normal wallet (doesn't generate key, assumes it's already there) +@@ -5889,6 +6188,16 @@ bool wallet2::wallet_valid_path_format(const std::string& file_path) + return !file_path.empty(); + } + //---------------------------------------------------------------------------------------------------- ++std::string wallet2::make_background_wallet_file_name(const std::string &wallet_file) ++{ ++ return wallet_file + BACKGROUND_WALLET_SUFFIX; ++} ++//---------------------------------------------------------------------------------------------------- ++std::string wallet2::make_background_keys_file_name(const std::string &wallet_file) ++{ ++ return make_background_wallet_file_name(wallet_file) + ".keys"; ++} ++//---------------------------------------------------------------------------------------------------- + bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) + { + cryptonote::blobdata payment_id_data; +@@ -6124,10 +6433,81 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass + THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, "failed to load keys from buffer"); + } + +- wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password); ++ wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_is_background_wallet, password); + + //keys loaded ok! + //try to load wallet cache. but even if we failed, it is not big problem ++ load_wallet_cache(use_fs, cache_buf); ++ ++ if (!m_persistent_rpc_client_id) ++ set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); ++ ++ // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. ++ // Here we erase these multisig keys if they're zero'd out to free up space. ++ for (auto &td : m_transfers) ++ { ++ auto mk_it = td.m_multisig_k.begin(); ++ while (mk_it != td.m_multisig_k.end()) ++ { ++ if (*mk_it == rct::zero()) ++ mk_it = td.m_multisig_k.erase(mk_it); ++ else ++ ++mk_it; ++ } ++ } ++ ++ cryptonote::block genesis; ++ generate_genesis(genesis); ++ crypto::hash genesis_hash = get_block_hash(genesis); ++ ++ if (m_blockchain.empty()) ++ { ++ m_blockchain.push_back(genesis_hash); ++ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); ++ } ++ else ++ { ++ check_genesis(genesis_hash); ++ } ++ ++ trim_hashchain(); ++ ++ if (get_num_subaddress_accounts() == 0) ++ add_subaddress_account(tr("Primary account")); ++ ++ try ++ { ++ find_and_save_rings(false); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to save rings, will try again next time"); ++ } ++ ++ try ++ { ++ if (use_fs) ++ m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to initialize MMS, it will be unusable"); ++ } ++ ++ try ++ { ++ if (use_fs) ++ process_background_cache_on_open(); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to process background cache on open: " << e.what()); ++ } ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::load_wallet_cache(const bool use_fs, const std::string& cache_buf) ++{ ++ boost::system::error_code e; + bool cache_missing = use_fs ? (!boost::filesystem::exists(m_wallet_file, e) || e) : cache_buf.empty(); + if (cache_missing) + { +@@ -6141,7 +6521,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass + bool r = true; + if (use_fs) + { +- load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); ++ r = load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); + THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); + } + +@@ -6154,7 +6534,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"'); + std::string cache_data; + cache_data.resize(cache_file_data.cache_data.size()); +- crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]); ++ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), get_cache_key(), cache_file_data.iv, &cache_data[0]); + + try { + bool loaded = false; +@@ -6244,60 +6624,76 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass + m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key, + error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); + } ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::process_background_cache_on_open() ++{ ++ if (m_wallet_file.empty()) ++ return; ++ if (m_background_syncing || m_is_background_wallet) ++ return; ++ if (m_background_sync_type == BackgroundSyncOff) ++ return; + +- if (!m_persistent_rpc_client_id) +- set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); ++ if (m_background_sync_type == BackgroundSyncReusePassword) ++ { ++ const background_sync_data_t background_sync_data = m_background_sync_data; ++ const hashchain blockchain = m_blockchain; ++ process_background_cache(background_sync_data, blockchain, m_last_block_reward); + +- // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. +- // Here we erase these multisig keys if they're zero'd out to free up space. +- for (auto &td : m_transfers) ++ // Reset the background cache after processing ++ reset_background_sync_data(m_background_sync_data); ++ } ++ else if (m_background_sync_type == BackgroundSyncCustomPassword) + { +- auto mk_it = td.m_multisig_k.begin(); +- while (mk_it != td.m_multisig_k.end()) ++ // If the background wallet files don't exist, recreate them ++ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); ++ const std::string background_wallet_file = make_background_wallet_file_name(m_wallet_file); ++ const bool background_keys_file_exists = boost::filesystem::exists(background_keys_file); ++ const bool background_wallet_exists = boost::filesystem::exists(background_wallet_file); ++ ++ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_keys_file), error::background_wallet_already_open, background_wallet_file); ++ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); ++ ++ if (!background_keys_file_exists) + { +- if (*mk_it == rct::zero()) +- mk_it = td.m_multisig_k.erase(mk_it); +- else +- ++mk_it; ++ MDEBUG("Background keys file not found, restoring"); ++ store_background_keys(m_custom_background_key.get()); + } +- } + +- cryptonote::block genesis; +- generate_genesis(genesis); +- crypto::hash genesis_hash = get_block_hash(genesis); ++ if (!background_wallet_exists) ++ { ++ MDEBUG("Background cache not found, restoring"); ++ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); ++ return; ++ } + +- if (m_blockchain.empty()) +- { +- m_blockchain.push_back(genesis_hash); +- m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); +- } +- else +- { +- check_genesis(genesis_hash); +- } ++ MDEBUG("Loading background cache"); + +- trim_hashchain(); ++ // Set up a minimal background wallet2 instance ++ std::unique_ptr background_w2(new wallet2(m_nettype)); ++ background_w2->m_is_background_wallet = true; ++ background_w2->m_background_syncing = true; ++ background_w2->m_background_sync_type = m_background_sync_type; ++ background_w2->m_custom_background_key = m_custom_background_key; + +- if (get_num_subaddress_accounts() == 0) +- add_subaddress_account(tr("Primary account")); ++ cryptonote::account_base account = m_account; ++ account.forget_spend_key(); ++ background_w2->m_account = account; + +- try +- { +- find_and_save_rings(false); +- } +- catch (const std::exception &e) +- { +- MERROR("Failed to save rings, will try again next time"); +- } +- +- try +- { +- if (use_fs) +- m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); ++ // Load background cache from file ++ background_w2->clear(); ++ background_w2->prepare_file_names(background_wallet_file); ++ background_w2->load_wallet_cache(true/*use_fs*/); ++ ++ process_background_cache(background_w2->m_background_sync_data, background_w2->m_blockchain, background_w2->m_last_block_reward); ++ ++ // Reset the background cache after processing ++ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); + } +- catch (const std::exception &e) ++ else + { +- MERROR("Failed to initialize MMS, it will be unusable"); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); + } + } + //---------------------------------------------------------------------------------------------------- +@@ -6379,6 +6775,8 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas + same_file = canonical_old_path == canonical_new_path; + } + ++ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet && !same_file, error::wallet_internal_error, ++ "Cannot save background wallet files to a different location"); + + if (!same_file) + { +@@ -6395,6 +6793,21 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas + } + } + } ++ else if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing && !m_is_background_wallet) ++ { ++ // We're background syncing, so store the wallet cache as a background cache ++ // keeping the background sync data ++ try ++ { ++ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); ++ store_background_cache(m_custom_background_key.get(), false/*do_reset_background_sync_data*/); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to store background cache while background syncing: " << e.what()); ++ } ++ return; ++ } + + // get wallet cache data + boost::optional cache_file_data = get_cache_file_data(); +@@ -6488,6 +6901,22 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas + // store should only exist if the MMS is really active + m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file); + } ++ ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_background_syncing && !m_is_background_wallet) ++ { ++ // Update the background wallet cache when we store the main wallet cache ++ // Note: if background syncing when this is called, it means the background ++ // wallet is open and was already stored above ++ try ++ { ++ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); ++ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to update background cache: " << e.what()); ++ } ++ } + } + //---------------------------------------------------------------------------------------------------- + boost::optional wallet2::get_cache_file_data() +@@ -6505,7 +6934,7 @@ boost::optional wallet2::get_cache_file_data() + std::string cipher; + cipher.resize(cache_file_data.get().cache_data.size()); + cache_file_data.get().iv = crypto::rand(); +- crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), m_cache_key, cache_file_data.get().iv, &cipher[0]); ++ crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), get_cache_key(), cache_file_data.get().iv, &cipher[0]); + cache_file_data.get().cache_data = cipher; + return cache_file_data; + } +@@ -8581,6 +9010,34 @@ bool wallet2::is_keys_file_locked() const + return m_keys_file_locker->locked(); + } + ++bool wallet2::lock_background_keys_file(const std::string &background_keys_file) ++{ ++ if (background_keys_file.empty() || !boost::filesystem::exists(background_keys_file)) ++ return true; ++ if (m_background_keys_file_locker && m_background_keys_file_locker->locked()) ++ return true; ++ m_background_keys_file_locker.reset(new tools::file_locker(background_keys_file)); ++ return m_background_keys_file_locker->locked(); ++} ++ ++bool wallet2::unlock_background_keys_file() ++{ ++ if (!m_background_keys_file_locker) ++ { ++ MDEBUG("background keys file locker is not set"); ++ return false; ++ } ++ m_background_keys_file_locker.reset(); ++ return true; ++} ++ ++bool wallet2::is_background_keys_file_locked() const ++{ ++ if (!m_background_keys_file_locker) ++ return false; ++ return m_background_keys_file_locker->locked(); ++} ++ + bool wallet2::tx_add_fake_output(std::vector> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set &valid_public_keys_cache) const + { + if (!unlocked) // don't add locked outs +@@ -13913,6 +14370,413 @@ bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool o + return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none); + } + ++/* ++ In background sync mode, we use just the view key when the wallet is scanning ++ to identify all txs where: ++ ++ 1. We received an output. ++ 2. We spent an output. ++ 3. We *may* have spent a received output but we didn't know for sure because ++ the spend key was not loaded while background sync was enabled. ++ ++ When the user is ready to use the spend key again, we call this function to ++ process all those background synced transactions with the spend key loaded, ++ so that we can properly generate key images for the transactions which we ++ we were not able to do so for while background sync was enabled. This allows ++ us to determine *all* receives and spends the user completed while the wallet ++ had background sync enabled. Once this function completes, we can continue ++ scanning from where the background sync left off. ++ ++ Txs of type 3 (txs which we *may* have spent received output(s)) are txs where ++ 1+ rings contain an output that the user received and the wallet does not know ++ the associated key image for that output. We don't know if the user spent in ++ this type of tx or not. This function will generate key images for all outputs ++ we don't know key images for, and then check if those outputs were spent in ++ the txs of type 3. ++ ++ By storing this type of "plausible spend tx" when scanning in background sync ++ mode, we avoid the need to query the daemon with key images when background ++ sync mode is disabled to see if those key images were spent. This would ++ reveal key images to 3rd party nodes for users who don't run their own. ++ Although this is not a perfect solution to avoid revealing key images to a 3rd ++ party node (since tx submission trivially reveals key images to a node), it's ++ probably better than revealing *unused* key images to a 3rd party node, which ++ would enable the 3rd party to deduce that a tx is spending an output at least ++ X old when the key image is included in the chain. ++*/ ++void wallet2::process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_synced_chain, uint64_t last_block_reward) ++{ ++ // We expect the spend key to be in a decrypted state while ++ // m_processing_background_cache is true ++ m_processing_background_cache = true; ++ auto done_processing = epee::misc_utils::create_scope_leave_handler([&, this]() { ++ m_processing_background_cache = false; ++ }); ++ ++ if (m_background_syncing || m_multisig || m_watch_only || key_on_device()) ++ return; ++ ++ if (!background_sync_data.first_refresh_done) ++ { ++ MDEBUG("Skipping processing background cache, background cache has not synced yet"); ++ return; ++ } ++ ++ // Skip processing if wallet cache is synced higher than background cache ++ const uint64_t current_height = m_blockchain.size(); ++ const uint64_t background_height = background_synced_chain.size(); ++ MDEBUG("Background cache height " << background_height << " , wallet height " << current_height); ++ if (current_height > background_height) ++ { ++ MWARNING("Skipping processing background cache, synced height is higher than background cache"); ++ return; ++ } ++ ++ if (m_refresh_from_block_height < background_sync_data.wallet_refresh_from_block_height || ++ m_subaddress_lookahead_major > background_sync_data.subaddress_lookahead_major || ++ m_subaddress_lookahead_minor > background_sync_data.subaddress_lookahead_minor || ++ m_refresh_type < background_sync_data.wallet_refresh_type) ++ { ++ MWARNING("Skipping processing background cache, background wallet sync settings did not match main wallet's"); ++ MDEBUG("Wallet settings: " << ++ ", m_refresh_from_block_height: " << m_refresh_from_block_height << " vs " << background_sync_data.wallet_refresh_from_block_height << ++ ", m_subaddress_lookahead_major: " << m_subaddress_lookahead_major << " vs " << background_sync_data.subaddress_lookahead_major << ++ ", m_subaddress_lookahead_minor: " << m_subaddress_lookahead_minor << " vs " << background_sync_data.subaddress_lookahead_minor << ++ ", m_refresh_type: " << m_refresh_type << " vs " << background_sync_data.wallet_refresh_type); ++ return; ++ } ++ ++ // Sort background synced txs in the order they appeared in the cache so that ++ // we process them in the order they appeared in the chain. Thus if tx2 spends ++ // from tx1, we will know because tx1 is processed before tx2. ++ std::vector> sorted_bgs_cache(background_sync_data.txs.begin(), background_sync_data.txs.end()); ++ std::sort(sorted_bgs_cache.begin(), sorted_bgs_cache.end(), ++ [](const std::pair& l, const std::pair& r) ++ { ++ uint64_t left_index = l.second.index_in_background_sync_data; ++ uint64_t right_index = r.second.index_in_background_sync_data; ++ THROW_WALLET_EXCEPTION_IF( ++ (left_index < right_index && l.second.height > r.second.height) || ++ (left_index > right_index && l.second.height < r.second.height), ++ error::wallet_internal_error, "Unexpected background sync data order"); ++ return left_index < right_index; ++ }); ++ ++ // All txs in the background cache should have height >= sync start height, ++ // but not fatal if not ++ if (!sorted_bgs_cache.empty() && sorted_bgs_cache[0].second.height < background_sync_data.start_height) ++ MWARNING("First tx in background cache has height (" << sorted_bgs_cache[0].second.height << ") lower than sync start height (" << background_sync_data.start_height << ")"); ++ ++ // We want to process all background synced txs in order to make sure ++ // the wallet state updates correctly. First we remove all txs from the wallet ++ // from before the background sync start height, then re-process them in ++ // chronological order. The background cache should contain a superset of ++ // *all* the wallet's txs from after the background sync start height. ++ MDEBUG("Processing " << background_sync_data.txs.size() << " background synced txs starting from height " << background_sync_data.start_height); ++ detached_blockchain_data dbd = detach_blockchain(background_sync_data.start_height); ++ ++ for (const auto &bgs_tx : sorted_bgs_cache) ++ { ++ MDEBUG("Processing background synced tx " << bgs_tx.first); ++ ++ process_new_transaction(bgs_tx.first, bgs_tx.second.tx, bgs_tx.second.output_indices, bgs_tx.second.height, 0, bgs_tx.second.block_timestamp, ++ cryptonote::is_coinbase(bgs_tx.second.tx), false/*pool*/, bgs_tx.second.double_spend_seen, {}, {}, true/*ignore_callbacks*/); ++ ++ // Re-set destination addresses if they were previously set ++ if (m_confirmed_txs.find(bgs_tx.first) != m_confirmed_txs.end() && ++ dbd.detached_confirmed_txs_dests.find(bgs_tx.first) != dbd.detached_confirmed_txs_dests.end()) ++ { ++ m_confirmed_txs[bgs_tx.first].m_dests = std::move(dbd.detached_confirmed_txs_dests[bgs_tx.first]); ++ } ++ } ++ ++ m_blockchain = background_synced_chain; ++ m_last_block_reward = last_block_reward; ++ ++ MDEBUG("Finished processing background sync data"); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::reset_background_sync_data(background_sync_data_t &background_sync_data) ++{ ++ background_sync_data.first_refresh_done = false; ++ background_sync_data.start_height = get_blockchain_current_height(); ++ background_sync_data.txs.clear(); ++ ++ background_sync_data.wallet_refresh_from_block_height = m_refresh_from_block_height; ++ background_sync_data.subaddress_lookahead_major = m_subaddress_lookahead_major; ++ background_sync_data.subaddress_lookahead_minor = m_subaddress_lookahead_minor; ++ background_sync_data.wallet_refresh_type = m_refresh_type; ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data) ++{ ++ MDEBUG("Storing background cache (do_reset_background_sync_data=" << do_reset_background_sync_data << ")"); ++ ++ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, ++ "Can only write a background cache when using a custom background password"); ++ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, ++ "No wallet file known, can't store background cache"); ++ ++ std::unique_ptr background_w2(new wallet2(m_nettype)); ++ background_w2->prepare_file_names(make_background_wallet_file_name(m_wallet_file)); ++ ++ // Make sure background wallet is opened by this wallet ++ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_w2->m_keys_file), ++ error::background_wallet_already_open, background_w2->m_wallet_file); ++ ++ // Load a background wallet2 instance using this wallet2 instance ++ std::string this_wallet2; ++ bool r = ::serialization::dump_binary(*this, this_wallet2); ++ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to serialize wallet cache"); ++ ++ background_w2->clear(); ++ r = ::serialization::parse_binary(this_wallet2, *background_w2); ++ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to deserialize wallet cache"); ++ ++ // Clear sensitive data from background cache not needed to sync ++ background_w2->clear_user_data(); ++ ++ background_w2->m_is_background_wallet = true; ++ if (do_reset_background_sync_data) ++ reset_background_sync_data(background_w2->m_background_sync_data); ++ else ++ background_w2->m_background_sync_data = m_background_sync_data; ++ background_w2->m_background_syncing = true; ++ ++ background_w2->m_custom_background_key = boost::optional(custom_background_key); ++ background_w2->m_background_sync_type = m_background_sync_type; ++ background_w2->store(); ++ ++ MDEBUG("Background cache stored (" << background_w2->m_transfers.size() << " transfers, " << background_w2->m_background_sync_data.txs.size() << " background synced txs)"); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::store_background_keys(const crypto::chacha_key &custom_background_key) ++{ ++ MDEBUG("Storing background keys"); ++ ++ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, ++ "No wallet file known, can't store background keys"); ++ ++ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); ++ bool r = store_keys(background_keys_file, custom_background_key, false/*watch_only*/, true/*background_keys_file*/); ++ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, background_keys_file); ++ THROW_WALLET_EXCEPTION_IF(!is_background_keys_file_locked(), error::wallet_internal_error, background_keys_file + "\" should be locked"); ++ ++ // GUI uses the address file to differentiate non-mainnet wallets in the UI ++ const std::string background_address_file = make_background_wallet_file_name(m_wallet_file) + ".address.txt"; ++ if (m_nettype != MAINNET && !boost::filesystem::exists(background_address_file)) ++ { ++ r = save_to_file(background_address_file, m_account.get_public_address_str(m_nettype), true); ++ if (!r) MERROR("String with address text not saved"); ++ } ++ ++ MDEBUG("Background keys stored"); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password) ++{ ++ MDEBUG("Storing background sync wallet"); ++ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, ++ "Can only write a background sync wallet when using a custom background password"); ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "Can't write background sync wallet from an existing background cache"); ++ THROW_WALLET_EXCEPTION_IF(wallet_password == background_cache_password, ++ error::background_custom_password_same_as_wallet_password); ++ ++ // Set the background encryption key ++ crypto::chacha_key custom_background_key; ++ get_custom_background_key(background_cache_password, custom_background_key, m_kdf_rounds); ++ ++ // Keep the background encryption key in memory so the main wallet can update ++ // the background cache when it stores the main wallet cache ++ m_custom_background_key = boost::optional(custom_background_key); ++ ++ if (m_wallet_file.empty() || m_keys_file.empty()) ++ return; ++ ++ // Save background keys file, then background cache, then update main wallet settings ++ store_background_keys(custom_background_key); ++ store_background_cache(custom_background_key, true/*do_reset_background_sync_data*/); ++ bool r = store_keys(m_keys_file, wallet_password, false/*watch_only*/); ++ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); ++ ++ MDEBUG("Background sync wallet saved successfully"); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password) ++{ ++ MDEBUG("Setting background sync to type " << background_sync_type); ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "Can't set background sync type from an existing background cache"); ++ verify_password_with_cached_key(wallet_password); ++ ++ if (background_sync_type != BackgroundSyncOff) ++ validate_background_cache_password_usage(background_sync_type, background_cache_password, m_multisig, m_watch_only, key_on_device()); ++ ++ THROW_WALLET_EXCEPTION_IF(background_sync_type == BackgroundSyncCustomPassword && wallet_password == background_cache_password, ++ error::background_custom_password_same_as_wallet_password); ++ ++ if (m_background_sync_type == background_sync_type && background_sync_type != BackgroundSyncCustomPassword) ++ return; // No need to make any changes ++ ++ if (!m_wallet_file.empty()) ++ { ++ // Delete existing background files if they already exist ++ const std::string old_background_wallet_file = make_background_wallet_file_name(m_wallet_file); ++ const std::string old_background_keys_file = make_background_keys_file_name(m_wallet_file); ++ const std::string old_background_address_file = old_background_wallet_file + ".address.txt"; ++ ++ // Make sure no other program is using the background wallet ++ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(old_background_keys_file), ++ error::background_wallet_already_open, old_background_wallet_file); ++ ++ if (boost::filesystem::exists(old_background_wallet_file)) ++ if (!boost::filesystem::remove(old_background_wallet_file)) ++ LOG_ERROR("Error deleting background wallet file: " << old_background_wallet_file); ++ ++ if (boost::filesystem::exists(old_background_keys_file)) ++ if (!boost::filesystem::remove(old_background_keys_file)) ++ LOG_ERROR("Error deleting background keys file: " << old_background_keys_file); ++ ++ if (boost::filesystem::exists(old_background_address_file)) ++ if (!boost::filesystem::remove(old_background_address_file)) ++ LOG_ERROR("Error deleting background address file: " << old_background_address_file); ++ } ++ ++ m_background_sync_type = background_sync_type; ++ m_custom_background_key = boost::none; ++ ++ // Write the new files ++ switch (background_sync_type) ++ { ++ case BackgroundSyncOff: ++ case BackgroundSyncReusePassword: rewrite(m_wallet_file, wallet_password); break; ++ case BackgroundSyncCustomPassword: write_background_sync_wallet(wallet_password, background_cache_password.get()); break; ++ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); ++ } ++ ++ MDEBUG("Done setting background sync type"); ++} ++//---------------------------------------------------------------------------------------------------- ++/* ++ When background syncing, the wallet scans using just the view key, without ++ keeping the spend key in decrypted state. When a user returns to the wallet ++ and decrypts the spend key, the wallet processes the background synced txs, ++ then the wallet picks up scanning normally right where the background sync ++ left off. ++*/ ++void wallet2::start_background_sync() ++{ ++ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, ++ "must setup background sync first before using background sync"); ++ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, ++ "Can't start background syncing from a background wallet (it is always background syncing)"); ++ ++ MDEBUG("Starting background sync"); ++ ++ if (m_background_syncing) ++ { ++ MDEBUG("Already background syncing"); ++ return; ++ } ++ ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) ++ { ++ // Save the current state of the wallet cache. Only necessary when using a ++ // custom background password which uses distinct background wallet to sync. ++ // When reusing wallet password to sync we reuse the main wallet cache. ++ store(); ++ ++ // Wipe user data from the background wallet cache not needed to sync. ++ // Only wipe user data from background cache if wallet cache is stored ++ // on disk; otherwise we could lose the data. ++ clear_user_data(); ++ ++ // Wipe m_cache_key since it can be used to decrypt main wallet cache ++ m_cache_key.scrub(); ++ } ++ ++ reset_background_sync_data(m_background_sync_data); ++ m_background_syncing = true; ++ ++ // Wipe the spend key from memory ++ m_account.forget_spend_key(); ++ ++ MDEBUG("Background sync started at height " << m_background_sync_data.start_height); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key) ++{ ++ MDEBUG("Stopping background sync"); ++ ++ // Verify provided password and spend secret key. If no spend secret key is ++ // provided, recover it from the wallet keys file ++ crypto::secret_key recovered_spend_key = crypto::null_skey; ++ if (!m_wallet_file.empty()) ++ { ++ THROW_WALLET_EXCEPTION_IF(!verify_password(wallet_password, recovered_spend_key), error::invalid_password); ++ } ++ else ++ { ++ verify_password_with_cached_key(wallet_password); ++ } ++ ++ if (spend_secret_key != crypto::null_skey) ++ { ++ THROW_WALLET_EXCEPTION_IF(!m_wallet_file.empty() && spend_secret_key != recovered_spend_key, ++ error::invalid_spend_key); ++ MDEBUG("Setting spend secret key with the provided key"); ++ recovered_spend_key = spend_secret_key; ++ } ++ ++ // Verify private spend key derives to wallet's public spend key ++ const auto verify_spend_key = [this](crypto::secret_key &recovered_spend_key) -> bool ++ { ++ crypto::public_key spend_public_key; ++ return recovered_spend_key != crypto::null_skey && ++ crypto::secret_key_to_public_key(recovered_spend_key, spend_public_key) && ++ m_account.get_keys().m_account_address.m_spend_public_key == spend_public_key; ++ }; ++ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); ++ ++ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, ++ "must setup background sync first before using background sync"); ++ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, ++ "Can't stop background syncing from a background wallet"); ++ ++ if (!m_background_syncing) ++ return; ++ ++ // Copy background cache, we're about to overwrite it ++ const background_sync_data_t background_sync_data = m_background_sync_data; ++ const hashchain background_synced_chain = m_blockchain; ++ const uint64_t last_block_reward = m_last_block_reward; ++ ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) ++ { ++ // Reload the wallet from disk ++ load(m_wallet_file, wallet_password); ++ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); ++ } ++ m_background_syncing = false; ++ ++ // Set the plaintext spend key ++ m_account.set_spend_key(recovered_spend_key); ++ ++ // Encrypt the spend key when done if needed ++ epee::misc_utils::auto_scope_leave_caller keys_reencryptor; ++ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) ++ keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]{encrypt_keys(wallet_password);}); ++ ++ // Now we can use the decrypted spend key to process background cache ++ process_background_cache(background_sync_data, background_synced_chain, last_block_reward); ++ ++ // Reset the background cache after processing ++ reset_background_sync_data(m_background_sync_data); ++ ++ MDEBUG("Background sync stopped"); ++} ++//---------------------------------------------------------------------------------------------------- + wallet2::payment_container wallet2::export_payments() const + { + payment_container payments; +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index 24366f6..b1dc4f7 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -256,6 +256,20 @@ private: + BackgroundMiningNo = 2, + }; + ++ enum BackgroundSyncType { ++ BackgroundSyncOff = 0, ++ BackgroundSyncReusePassword = 1, ++ BackgroundSyncCustomPassword = 2, ++ }; ++ ++ static BackgroundSyncType background_sync_type_from_str(const std::string &background_sync_type_str) ++ { ++ if (background_sync_type_str == "off") return BackgroundSyncOff; ++ if (background_sync_type_str == "reuse-wallet-password") return BackgroundSyncReusePassword; ++ if (background_sync_type_str == "custom-background-password") return BackgroundSyncCustomPassword; ++ throw std::logic_error("Unknown background sync type"); ++ }; ++ + enum ExportFormat { + Binary = 0, + Ascii, +@@ -282,7 +296,12 @@ private: + //! Just parses variables. + static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter); + +- static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); ++ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) ++ { ++ crypto::secret_key spend_key = crypto::null_skey; ++ return verify_password(keys_file_name, password, no_spend_key, hwdev, kdf_rounds, spend_key); ++ }; ++ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out); + static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1); + + wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false, std::unique_ptr http_client_factory = std::unique_ptr(new net::http::client_factory())); +@@ -792,6 +811,54 @@ private: + END_SERIALIZE() + }; + ++ struct background_synced_tx_t ++ { ++ uint64_t index_in_background_sync_data; ++ cryptonote::transaction tx; ++ std::vector output_indices; ++ uint64_t height; ++ uint64_t block_timestamp; ++ bool double_spend_seen; ++ ++ BEGIN_SERIALIZE_OBJECT() ++ VERSION_FIELD(0) ++ VARINT_FIELD(index_in_background_sync_data) ++ ++ // prune tx; don't need to keep signature data ++ if (!tx.serialize_base(ar)) ++ return false; ++ ++ FIELD(output_indices) ++ VARINT_FIELD(height) ++ VARINT_FIELD(block_timestamp) ++ FIELD(double_spend_seen) ++ END_SERIALIZE() ++ }; ++ ++ struct background_sync_data_t ++ { ++ bool first_refresh_done = false; ++ uint64_t start_height = 0; ++ serializable_unordered_map txs; ++ ++ // Relevant wallet settings ++ uint64_t wallet_refresh_from_block_height; ++ size_t subaddress_lookahead_major; ++ size_t subaddress_lookahead_minor; ++ RefreshType wallet_refresh_type; ++ ++ BEGIN_SERIALIZE_OBJECT() ++ VERSION_FIELD(0) ++ FIELD(first_refresh_done) ++ FIELD(start_height) ++ FIELD(txs) ++ FIELD(wallet_refresh_from_block_height) ++ VARINT_FIELD(subaddress_lookahead_major) ++ VARINT_FIELD(subaddress_lookahead_minor) ++ VARINT_FIELD(wallet_refresh_type) ++ END_SERIALIZE() ++ }; ++ + typedef std::tuple get_outs_entry; + + struct parsed_block +@@ -974,7 +1041,8 @@ private: + /*! + * \brief verifies given password is correct for default wallet keys file + */ +- bool verify_password(const epee::wipeable_string& password); ++ bool verify_password(const epee::wipeable_string& password) {crypto::secret_key key = crypto::null_skey; return verify_password(password, key);}; ++ bool verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out); + cryptonote::account_base& get_account(){return m_account;} + const cryptonote::account_base& get_account()const{return m_account;} + +@@ -1069,6 +1137,7 @@ private: + cryptonote::network_type nettype() const { return m_nettype; } + bool watch_only() const { return m_watch_only; } + bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; ++ bool is_background_wallet() const { return m_is_background_wallet; } + bool has_multisig_partial_key_images() const; + bool has_unknown_key_images() const; + bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string()) const; +@@ -1276,11 +1345,17 @@ private: + return; + } + a & m_has_ever_refreshed_from_node; ++ if(ver < 31) ++ { ++ m_background_sync_data = background_sync_data_t{}; ++ return; ++ } ++ a & m_background_sync_data; + } + + BEGIN_SERIALIZE_OBJECT() + MAGIC_FIELD("monero wallet cache") +- VERSION_FIELD(1) ++ VERSION_FIELD(2) + FIELD(m_blockchain) + FIELD(m_transfers) + FIELD(m_account_public_address) +@@ -1312,6 +1387,12 @@ private: + return true; + } + FIELD(m_has_ever_refreshed_from_node) ++ if (version < 2) ++ { ++ m_background_sync_data = background_sync_data_t{}; ++ return true; ++ } ++ FIELD(m_background_sync_data) + END_SERIALIZE() + + /*! +@@ -1327,6 +1408,8 @@ private: + * \return Whether path is valid format + */ + static bool wallet_valid_path_format(const std::string& file_path); ++ static std::string make_background_wallet_file_name(const std::string &wallet_file); ++ static std::string make_background_keys_file_name(const std::string &wallet_file); + static bool parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); + static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id); + static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); +@@ -1375,6 +1458,9 @@ private: + void ignore_outputs_below(uint64_t value) { m_ignore_outputs_below = value; } + bool track_uses() const { return m_track_uses; } + void track_uses(bool value) { m_track_uses = value; } ++ BackgroundSyncType background_sync_type() const { return m_background_sync_type; } ++ void setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password); ++ bool is_background_syncing() const { return m_background_syncing; } + bool show_wallet_name_when_locked() const { return m_show_wallet_name_when_locked; } + void show_wallet_name_when_locked(bool value) { m_show_wallet_name_when_locked = value; } + BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; } +@@ -1689,6 +1775,9 @@ private: + uint64_t get_bytes_sent() const; + uint64_t get_bytes_received() const; + ++ void start_background_sync(); ++ void stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key = crypto::null_skey); ++ + // MMS ------------------------------------------------------------------------------------------------- + mms::message_store& get_message_store() { return m_message_store; }; + const mms::message_store& get_message_store() const { return m_message_store; }; +@@ -1724,6 +1813,9 @@ private: + * \return Whether it was successful. + */ + bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false); ++ bool store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); ++ boost::optional get_keys_file_data(const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); ++ bool store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file = false); + /*! + * \brief Load wallet keys information from wallet file. + * \param keys_file_name Name of wallet file +@@ -1737,6 +1829,7 @@ private: + */ + bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password); + bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional& keys_to_encrypt); ++ 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 &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, 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, size_t tx_cache_data_offset, std::map, size_t> *output_tracker_cache = NULL); +@@ -1745,6 +1838,15 @@ private: + void get_short_chain_history(std::list& ids, uint64_t granularity = 1) const; + bool clear(); + void clear_soft(bool keep_key_images=false); ++ /* ++ * clear_user_data clears data created by the user, which is mostly data ++ * that a view key cannot identify on chain. This function was initially ++ * added to ensure that a "background" wallet (a wallet that syncs with just ++ * a view key hot in memory) does not have any sensitive data loaded that it ++ * does not need in order to sync. Future devs should take care to ensure ++ * that this function deletes data that is not useful for background syncing ++ */ ++ void clear_user_data(); + void pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, uint64_t ¤t_height); + void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); + void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); +@@ -1796,10 +1898,23 @@ private: + bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector &outs); + crypto::chacha_key get_ringdb_key(); + void setup_keys(const epee::wipeable_string &password); ++ const crypto::chacha_key get_cache_key(); ++ void verify_password_with_cached_key(const epee::wipeable_string &password); ++ void verify_password_with_cached_key(const crypto::chacha_key &key); + size_t get_transfer_details(const crypto::key_image &ki) const; + tx_entry_data get_tx_entries(const std::unordered_set &txids); + void sort_scan_tx_entries(std::vector &unsorted_tx_entries); + void process_scan_txs(const tx_entry_data &txs_to_scan, const tx_entry_data &txs_to_reprocess, const std::unordered_set &tx_hashes_to_reprocess, detached_blockchain_data &dbd); ++ void write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password); ++ void process_background_cache_on_open(); ++ void process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_chain, uint64_t last_block_reward); ++ void reset_background_sync_data(background_sync_data_t &background_sync_data); ++ void store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data = true); ++ void store_background_keys(const crypto::chacha_key &custom_background_key); ++ ++ bool lock_background_keys_file(const std::string &background_keys_file); ++ bool unlock_background_keys_file(); ++ bool is_background_keys_file_locked() const; + + void register_devices(); + hw::device& lookup_device(const std::string & device_descriptor); +@@ -1914,6 +2029,8 @@ private: + uint64_t m_ignore_outputs_above; + uint64_t m_ignore_outputs_below; + bool m_track_uses; ++ bool m_is_background_wallet; ++ BackgroundSyncType m_background_sync_type; + bool m_show_wallet_name_when_locked; + uint32_t m_inactivity_lock_timeout; + BackgroundMiningSetupType m_setup_background_mining; +@@ -1959,6 +2076,7 @@ private: + + uint64_t m_last_block_reward; + std::unique_ptr m_keys_file_locker; ++ std::unique_ptr m_background_keys_file_locker; + + mms::message_store m_message_store; + bool m_original_keys_available; +@@ -1966,6 +2084,7 @@ private: + crypto::secret_key m_original_view_secret_key; + + crypto::chacha_key m_cache_key; ++ boost::optional m_custom_background_key = boost::none; + std::shared_ptr m_encrypt_keys_after_refresh; + + bool m_unattended; +@@ -1981,9 +2100,13 @@ private: + + static boost::mutex default_daemon_address_lock; + static std::string default_daemon_address; ++ ++ bool m_background_syncing; ++ bool m_processing_background_cache; ++ background_sync_data_t m_background_sync_data; + }; + } +-BOOST_CLASS_VERSION(tools::wallet2, 30) ++BOOST_CLASS_VERSION(tools::wallet2, 31) + BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) + BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) + BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) +@@ -1999,6 +2122,8 @@ BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1) + BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4) + BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) + BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 1) ++BOOST_CLASS_VERSION(tools::wallet2::background_synced_tx_t, 0) ++BOOST_CLASS_VERSION(tools::wallet2::background_sync_data_t, 0) + + namespace boost + { +@@ -2497,6 +2622,29 @@ namespace boost + return; + a & x.multisig_sigs; + } ++ ++ template ++ inline void serialize(Archive& a, tools::wallet2::background_synced_tx_t &x, const boost::serialization::version_type ver) ++ { ++ a & x.index_in_background_sync_data; ++ a & x.tx; ++ a & x.output_indices; ++ a & x.height; ++ a & x.block_timestamp; ++ a & x.double_spend_seen; ++ } ++ ++ template ++ inline void serialize(Archive& a, tools::wallet2::background_sync_data_t &x, const boost::serialization::version_type ver) ++ { ++ a & x.first_refresh_done; ++ a & x.start_height; ++ a & x.txs.parent(); ++ a & x.wallet_refresh_from_block_height; ++ a & x.subaddress_lookahead_major; ++ a & x.subaddress_lookahead_minor; ++ a & x.wallet_refresh_type; ++ } + } + } + +diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h +index c077313..c54cd34 100644 +--- a/src/wallet/wallet_errors.h ++++ b/src/wallet/wallet_errors.h +@@ -63,6 +63,7 @@ namespace tools + // invalid_password + // invalid_priority + // invalid_multisig_seed ++ // invalid_spend_key + // refresh_error * + // acc_outs_lookup_error + // block_parse_error +@@ -97,6 +98,9 @@ namespace tools + // wallet_files_doesnt_correspond + // scan_tx_error * + // wont_reprocess_recent_txs_via_untrusted_daemon ++ // background_sync_error * ++ // background_wallet_already_open ++ // background_custom_password_same_as_wallet_password + // + // * - class with protected ctor + +@@ -304,6 +308,16 @@ namespace tools + std::string to_string() const { return wallet_logic_error::to_string(); } + }; + ++ struct invalid_spend_key : public wallet_logic_error ++ { ++ explicit invalid_spend_key(std::string&& loc) ++ : wallet_logic_error(std::move(loc), "invalid spend key") ++ { ++ } ++ ++ std::string to_string() const { return wallet_logic_error::to_string(); } ++ }; ++ + //---------------------------------------------------------------------------------------------------- + struct invalid_pregenerated_random : public wallet_logic_error + { +@@ -947,6 +961,31 @@ namespace tools + } + }; + //---------------------------------------------------------------------------------------------------- ++ struct background_sync_error : public wallet_logic_error ++ { ++ protected: ++ explicit background_sync_error(std::string&& loc, const std::string& message) ++ : wallet_logic_error(std::move(loc), message) ++ { ++ } ++ }; ++ //---------------------------------------------------------------------------------------------------- ++ struct background_wallet_already_open : public background_sync_error ++ { ++ explicit background_wallet_already_open(std::string&& loc, const std::string& background_wallet_file) ++ : background_sync_error(std::move(loc), "background wallet " + background_wallet_file + " is already opened by another wallet program") ++ { ++ } ++ }; ++ //---------------------------------------------------------------------------------------------------- ++ struct background_custom_password_same_as_wallet_password : public background_sync_error ++ { ++ explicit background_custom_password_same_as_wallet_password(std::string&& loc) ++ : background_sync_error(std::move(loc), "custom background password must be different than wallet password") ++ { ++ } ++ }; ++ //---------------------------------------------------------------------------------------------------- + + #if !defined(_MSC_VER) + +diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp +index b141994..d24b4c5 100644 +--- a/src/wallet/wallet_rpc_server.cpp ++++ b/src/wallet/wallet_rpc_server.cpp +@@ -73,6 +73,54 @@ using namespace epee; + } \ + } while(0) + ++#define CHECK_IF_BACKGROUND_SYNCING() \ ++ do \ ++ { \ ++ if (!m_wallet) { return not_open(er); } \ ++ if (m_wallet->is_background_wallet()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET; \ ++ er.message = "This command is disabled for background wallets."; \ ++ return false; \ ++ } \ ++ if (m_wallet->is_background_syncing()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING; \ ++ er.message = "This command is disabled while background syncing. Stop background syncing to use this command."; \ ++ return false; \ ++ } \ ++ } while(0) ++ ++#define PRE_VALIDATE_BACKGROUND_SYNC() \ ++ do \ ++ { \ ++ if (!m_wallet) { return not_open(er); } \ ++ if (m_restricted) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_DENIED; \ ++ er.message = "Command unavailable in restricted mode."; \ ++ return false; \ ++ } \ ++ if (m_wallet->key_on_device()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ ++ er.message = "Command not supported by HW wallet"; \ ++ return false; \ ++ } \ ++ if (m_wallet->multisig()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ ++ er.message = "Multisig wallet cannot enable background sync"; \ ++ return false; \ ++ } \ ++ if (m_wallet->watch_only()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; \ ++ er.message = "Watch-only wallet cannot enable background sync"; \ ++ return false; \ ++ } \ ++ } while (0) ++ + namespace + { + const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; +@@ -291,6 +339,9 @@ namespace tools + { + if (!m_wallet) + return; ++ // Background mining can be toggled from the main wallet ++ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) ++ return; + + tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); + if (setup == tools::wallet2::BackgroundMiningNo) +@@ -582,6 +633,7 @@ namespace tools + bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + if (req.count < 1 || req.count > 64) { +@@ -619,6 +671,7 @@ namespace tools + bool wallet_rpc_server::on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_subaddress_label(req.index, req.label); +@@ -681,6 +734,7 @@ namespace tools + bool wallet_rpc_server::on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->add_subaddress_account(req.label); +@@ -698,6 +752,7 @@ namespace tools + bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_subaddress_label({req.account_index, 0}, req.label); +@@ -713,6 +768,7 @@ namespace tools + bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + const std::pair, std::vector> account_tags = m_wallet->get_account_tags(); + for (const std::pair& p : account_tags.first) + { +@@ -732,6 +788,7 @@ namespace tools + bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_account_tag(req.accounts, req.tag); +@@ -747,6 +804,7 @@ namespace tools + bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_account_tag(req.accounts, ""); +@@ -762,6 +820,7 @@ namespace tools + bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_account_tag_description(req.tag, req.description); +@@ -792,6 +851,7 @@ namespace tools + bool wallet_rpc_server::on_freeze(const wallet_rpc::COMMAND_RPC_FREEZE::request& req, wallet_rpc::COMMAND_RPC_FREEZE::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + if (req.key_image.empty()) +@@ -820,6 +880,7 @@ namespace tools + bool wallet_rpc_server::on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + if (req.key_image.empty()) +@@ -848,6 +909,7 @@ namespace tools + bool wallet_rpc_server::on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + if (req.key_image.empty()) +@@ -875,6 +937,8 @@ namespace tools + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::validate_transfer(const std::list& destinations, const std::string& payment_id, std::vector& dsts, std::vector& extra, bool at_least_one_destination, epee::json_rpc::error& er) + { ++ CHECK_IF_BACKGROUND_SYNCING(); ++ + crypto::hash8 integrated_payment_id = crypto::null_hash8; + std::string extra_nonce; + for (auto it = destinations.begin(); it != destinations.end(); it++) +@@ -1204,6 +1268,7 @@ namespace tools + } + + CHECK_MULTISIG_ENABLED(); ++ CHECK_IF_BACKGROUND_SYNCING(); + + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) +@@ -1285,6 +1350,7 @@ namespace tools + er.message = "command not supported by watch-only wallet"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + if(req.unsigned_txset.empty() && req.multisig_txset.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; +@@ -1554,6 +1620,7 @@ namespace tools + } + + CHECK_MULTISIG_ENABLED(); ++ CHECK_IF_BACKGROUND_SYNCING(); + + try + { +@@ -2115,6 +2182,7 @@ namespace tools + er.message = "The wallet is watch-only. Cannot retrieve seed."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + if (!m_wallet->is_deterministic()) + { + er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC; +@@ -2143,6 +2211,7 @@ namespace tools + er.message = "The wallet is watch-only. Cannot retrieve spend key."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key); + res.key = std::string(key.data(), key.size()); + } +@@ -2164,6 +2233,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + try + { +@@ -2177,6 +2247,79 @@ namespace tools + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ ++ bool wallet_rpc_server::on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) ++ { ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ const tools::wallet2::BackgroundSyncType background_sync_type = tools::wallet2::background_sync_type_from_str(req.background_sync_type); ++ boost::optional background_cache_password = boost::none; ++ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) ++ background_cache_password = boost::optional(req.background_cache_password); ++ m_wallet->setup_background_sync(background_sync_type, req.wallet_password, background_cache_password); ++ } ++ catch (...) ++ { ++ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); ++ return false; ++ } ++ return true; ++ } ++ //------------------------------------------------------------------------------------------------------------------------------ ++ bool wallet_rpc_server::on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) ++ { ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ m_wallet->start_background_sync(); ++ } ++ catch (...) ++ { ++ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); ++ return false; ++ } ++ return true; ++ } ++ //------------------------------------------------------------------------------------------------------------------------------ ++ bool wallet_rpc_server::on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) ++ { ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ crypto::secret_key spend_secret_key = crypto::null_skey; ++ ++ // Load the spend key from seed if seed is provided ++ if (!req.seed.empty()) ++ { ++ crypto::secret_key recovery_key; ++ std::string language; ++ ++ if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, language)) ++ { ++ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; ++ er.message = "Electrum-style word list failed verification"; ++ return false; ++ } ++ ++ if (!req.seed_offset.empty()) ++ recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset); ++ ++ // generate spend key ++ cryptonote::account_base account; ++ account.generate(recovery_key, true, false); ++ spend_secret_key = account.get_keys().m_spend_secret_key; ++ } ++ ++ m_wallet->stop_background_sync(req.wallet_password, spend_secret_key); ++ } ++ catch (...) ++ { ++ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); ++ return false; ++ } ++ return true; ++ } ++ //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); +@@ -2186,6 +2329,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + tools::wallet2::message_signature_type_t signature_type = tools::wallet2::sign_with_spend_key; + if (req.signature_type == "spend" || req.signature_type == "") +@@ -2278,6 +2422,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + if (req.txids.size() != req.notes.size()) + { +@@ -2350,6 +2495,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + m_wallet->set_attribute(req.key, req.value); + +@@ -2377,6 +2523,7 @@ namespace tools + bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + + crypto::hash txid; + if (!epee::string_tools::hex_to_pod(req.txid, txid)) +@@ -2468,6 +2615,7 @@ namespace tools + bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + + crypto::hash txid; + if (!epee::string_tools::hex_to_pod(req.txid, txid)) +@@ -2584,6 +2732,7 @@ namespace tools + bool wallet_rpc_server::on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + + boost::optional> account_minreserve; + if (!req.all) +@@ -2826,6 +2975,7 @@ namespace tools + er.message = "command not supported by HW wallet"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + try + { +@@ -2855,6 +3005,7 @@ namespace tools + er.message = "command not supported by HW wallet"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob)) +@@ -2880,6 +3031,7 @@ namespace tools + bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + std::pair>> ski = m_wallet->export_key_images(req.all); +@@ -2916,6 +3068,7 @@ namespace tools + er.message = "This command requires a trusted daemon."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + std::vector> ski; +@@ -2984,6 +3137,7 @@ namespace tools + bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + const auto ab = m_wallet->get_address_book(); + if (req.entries.empty()) + { +@@ -3029,6 +3183,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + cryptonote::address_parse_info info; + er.message = ""; +@@ -3071,6 +3226,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + const auto ab = m_wallet->get_address_book(); + if (req.index >= ab.size()) +@@ -3133,6 +3289,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + const auto ab = m_wallet->get_address_book(); + if (req.index >= ab.size()) +@@ -3203,6 +3360,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + std::unordered_set txids; + std::list::const_iterator i = req.txids.begin(); +@@ -3242,6 +3400,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->rescan_spent(); +@@ -3506,6 +3665,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + if (m_wallet->verify_password(req.old_password)) + { + try +@@ -4033,6 +4193,7 @@ namespace tools + er.message = "wallet is watch-only and cannot be made multisig"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + res.multisig_info = m_wallet->get_multisig_first_kex_msg(); + return true; +@@ -4060,6 +4221,7 @@ namespace tools + er.message = "wallet is watch-only and cannot be made multisig"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + try + { +diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h +index 3308d17..c2329aa 100644 +--- a/src/wallet/wallet_rpc_server.h ++++ b/src/wallet/wallet_rpc_server.h +@@ -160,6 +160,9 @@ namespace tools + MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES) + MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT) + MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) ++ MAP_JON_RPC_WE("setup_background_sync", on_setup_background_sync, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC) ++ MAP_JON_RPC_WE("start_background_sync", on_start_background_sync, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC) ++ MAP_JON_RPC_WE("stop_background_sync", on_stop_background_sync, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC) + END_JSON_RPC_MAP() + END_URI_MAP2() + +@@ -251,6 +254,9 @@ namespace tools + bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); ++ bool on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); ++ bool on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); ++ bool on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + + //json rpc v2 + bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); +diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h +index b6098d9..a44b56e 100644 +--- a/src/wallet/wallet_rpc_server_commands_defs.h ++++ b/src/wallet/wallet_rpc_server_commands_defs.h +@@ -2696,5 +2696,69 @@ namespace wallet_rpc + typedef epee::misc_utils::struct_init response; + }; + ++ struct COMMAND_RPC_SETUP_BACKGROUND_SYNC ++ { ++ struct request_t ++ { ++ std::string background_sync_type; ++ std::string wallet_password; ++ std::string background_cache_password; ++ ++ BEGIN_KV_SERIALIZE_MAP() ++ KV_SERIALIZE(background_sync_type) ++ KV_SERIALIZE(wallet_password) ++ KV_SERIALIZE_OPT(background_cache_password, (std::string)"") ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init request; ++ ++ struct response_t ++ { ++ BEGIN_KV_SERIALIZE_MAP() ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init response; ++ }; ++ ++ struct COMMAND_RPC_START_BACKGROUND_SYNC ++ { ++ struct request_t ++ { ++ BEGIN_KV_SERIALIZE_MAP() ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init request; ++ ++ struct response_t ++ { ++ BEGIN_KV_SERIALIZE_MAP() ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init response; ++ }; ++ ++ struct COMMAND_RPC_STOP_BACKGROUND_SYNC ++ { ++ struct request_t ++ { ++ std::string wallet_password; ++ std::string seed; ++ std::string seed_offset; ++ ++ BEGIN_KV_SERIALIZE_MAP() ++ KV_SERIALIZE(wallet_password) ++ KV_SERIALIZE_OPT(seed, (std::string)"") ++ KV_SERIALIZE_OPT(seed_offset, (std::string)"") ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init request; ++ ++ struct response_t ++ { ++ BEGIN_KV_SERIALIZE_MAP() ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init response; ++ }; + } + } +diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h +index 541d29f..4756c19 100644 +--- a/src/wallet/wallet_rpc_server_error_codes.h ++++ b/src/wallet/wallet_rpc_server_error_codes.h +@@ -81,3 +81,5 @@ + #define WALLET_RPC_ERROR_CODE_DISABLED -48 + #define WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED -49 + #define WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME -50 ++#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET -51 ++#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING -52 +diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py +index 4063911..60eb09a 100755 +--- a/tests/functional_tests/transfer.py ++++ b/tests/functional_tests/transfer.py +@@ -30,6 +30,7 @@ + + from __future__ import print_function + import json ++import util_resources + import pprint + from deepdiff import DeepDiff + pp = pprint.PrettyPrinter(indent=2) +@@ -46,6 +47,17 @@ seeds = [ + 'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid', + ] + ++def diff_transfers(actual_transfers, expected_transfers, ignore_order = True): ++ # The payments containers aren't ordered; re-scanning can lead to diff orders ++ diff = DeepDiff(actual_transfers, expected_transfers, ignore_order = ignore_order) ++ if diff != {}: ++ pp.pprint(diff) ++ assert diff == {} ++ ++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) ++ + class TransferTest(): + def run_test(self): + self.reset() +@@ -63,6 +75,8 @@ class TransferTest(): + self.check_is_key_image_spent() + self.check_scan_tx() + self.check_subtract_fee_from_outputs() ++ self.check_background_sync() ++ self.check_background_sync_reorg_recovery() + + def reset(self): + print('Resetting blockchain') +@@ -840,12 +854,6 @@ class TransferTest(): + + print('Testing scan_tx') + +- def diff_transfers(actual_transfers, expected_transfers): +- diff = DeepDiff(actual_transfers, expected_transfers) +- if diff != {}: +- pp.pprint(diff) +- assert diff == {} +- + # set up sender_wallet + sender_wallet = self.wallet[0] + try: sender_wallet.close_wallet() +@@ -1127,5 +1135,385 @@ class TransferTest(): + except AssertionError: + pass + ++ def check_background_sync(self): ++ daemon = Daemon() ++ ++ print('Testing background sync') ++ ++ # Some helper functions ++ def stop_with_wrong_inputs(wallet, wallet_password, seed = ''): ++ invalid = False ++ try: wallet.stop_background_sync(wallet_password = wallet_password, seed = seed) ++ except: invalid = True ++ assert invalid ++ ++ def open_with_wrong_password(wallet, filename, password): ++ invalid_password = False ++ try: wallet.open_wallet(filename, password = password) ++ 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) ++ assert wallet.get_balance().balance == expected_balance ++ ++ # Set up sender_wallet. Prepare to sweep single output to receiver. ++ # 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] ++ 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] ++ assert len(unlocked) > 0 ++ ki = unlocked[0].key_image ++ amount = unlocked[0].amount ++ spent_txid = unlocked[0].tx_hash ++ sender_wallet.refresh() ++ res = sender_wallet.get_transfers() ++ out_len = 0 if 'out' not in res else len(res.out) ++ sender_starting_balance = sender_wallet.get_balance().balance ++ ++ # Background sync type options ++ reuse_password = sender_wallet.background_sync_options.reuse_password ++ custom_password = sender_wallet.background_sync_options.custom_password ++ ++ # set up receiver_wallet ++ receiver_wallet = self.wallet[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']) ++ receiver_starting_balance = receiver_wallet.get_balance().balance ++ ++ # transfer from sender_wallet to receiver_wallet ++ dst = '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW' ++ res = sender_wallet.sweep_single(dst, key_image = ki) ++ assert len(res.tx_hash) == 32*2 ++ txid = res.tx_hash ++ assert res.fee > 0 ++ fee = res.fee ++ assert res.amount == amount - fee ++ ++ expected_sender_balance = sender_starting_balance - amount ++ expected_receiver_balance = receiver_starting_balance + (amount - fee) ++ ++ print('Checking background sync on outgoing wallet') ++ sender_wallet.setup_background_sync(background_sync_type = reuse_password) ++ sender_wallet.start_background_sync() ++ # Mine block to an uninvolved wallet ++ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) ++ # sender should still be able to scan the transfer normally because we ++ # spent an output that had a known key image ++ sender_wallet.refresh() ++ transfers = sender_wallet.get_transfers() ++ assert 'pending' not in transfers or len(transfers.pending) == 0 ++ assert 'pool' not in transfers or len (transfers.pool) == 0 ++ assert len(transfers.out) == out_len + 1 ++ tx = [x for x in transfers.out if x.txid == txid] ++ assert len(tx) == 1 ++ tx = tx[0] ++ assert tx.amount == amount - fee ++ assert tx.fee == fee ++ assert len(tx.destinations) == 1 ++ assert tx.destinations[0].amount == amount - fee ++ assert tx.destinations[0].address == dst ++ incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all') ++ assert len([x for x in incoming_transfers.transfers if x.tx_hash == spent_txid and x.key_image == ki and x.spent]) == 1 ++ assert sender_wallet.get_balance().balance == expected_sender_balance ++ ++ # Restore and check background syncing outgoing wallet ++ restore_wallet(sender_wallet, seeds[0]) ++ sender_wallet.setup_background_sync(background_sync_type = reuse_password) ++ sender_wallet.start_background_sync() ++ sender_wallet.refresh() ++ for i, out_tx in enumerate(transfers.out): ++ if 'destinations' in out_tx: ++ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore ++ # sender's balance should be higher because can't detect spends while ++ # background sync enabled, only receives ++ background_bal = sender_wallet.get_balance().balance ++ assert background_bal > expected_sender_balance ++ background_transfers = sender_wallet.get_transfers() ++ assert 'out' not in background_transfers or len(background_transfers.out) == 0 ++ assert 'in' in background_transfers and len(background_transfers['in']) > 0 ++ background_incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all') ++ assert len(background_incoming_transfers) == len(incoming_transfers) ++ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0 ++ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == spent_txid]) == 1 ++ ++ # Try to stop background sync with the wrong seed ++ stop_with_wrong_inputs(sender_wallet, wallet_password = '', seed = seeds[1]) ++ ++ # Stop background sync and check transfers update correctly ++ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0]) ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ ++ # 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], '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() ++ sender_wallet.refresh() ++ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) ++ stop_with_wrong_inputs(sender_wallet, 'wrong_password') ++ sender_wallet.stop_background_sync(wallet_password = 'test_password') ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ ++ # Close wallet while background syncing, then reopen ++ for background_sync_type in [reuse_password, custom_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() ++ sender_wallet.refresh() ++ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) ++ sender_wallet.close_wallet() ++ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password') ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ # It should reopen with spend key loaded and correctly scan all transfers ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ ++ # Close wallet while syncing normally, then reopen ++ for background_sync_type in [reuse_password, custom_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() ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ sender_wallet.close_wallet() ++ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password') ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ ++ # 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], '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) ++ assert util_resources.file_exists('test1.background') ++ assert util_resources.file_exists('test1.background.keys') ++ sender_wallet.close_wallet() ++ open_with_wrong_password(sender_wallet, 'test1.background', 'test_password') ++ sender_wallet.open_wallet('test1.background', password = background_cache_password) ++ sender_wallet.refresh() ++ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) ++ sender_wallet.close_wallet() ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ 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], '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) ++ sender_wallet.close_wallet() ++ sender_wallet.open_wallet('test1.background', password = 'background_password') ++ 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], 'test1', 'test_password') ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ same_password = False ++ try: sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = 'test_password') ++ except: same_password = True ++ assert same_password ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ ++ # Turn off background sync ++ for background_sync_type in [reuse_password, custom_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: ++ assert util_resources.file_exists('test1.background') ++ assert util_resources.file_exists('test1.background.keys') ++ sender_wallet.close_wallet() ++ assert util_resources.file_exists('test1.background') ++ assert util_resources.file_exists('test1.background.keys') ++ else: ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ sender_wallet.close_wallet() ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ sender_wallet.setup_background_sync(background_sync_type = sender_wallet.background_sync_options.off, wallet_password = 'test_password') ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ sender_wallet.close_wallet() ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ ++ # Sanity check against outgoing wallet restored at 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) ++ ++ print('Checking background sync on incoming wallet') ++ receiver_wallet.setup_background_sync(background_sync_type = reuse_password) ++ receiver_wallet.start_background_sync() ++ receiver_wallet.refresh() ++ transfers = receiver_wallet.get_transfers() ++ assert 'pending' not in transfers or len(transfers.pending) == 0 ++ assert 'pool' not in transfers or len (transfers.pool) == 0 ++ assert len(transfers['in']) == in_len + 1 ++ tx = [x for x in transfers['in'] if x.txid == txid] ++ assert len(tx) == 1 ++ tx = tx[0] ++ assert tx.amount == amount - fee ++ assert tx.fee == fee ++ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') ++ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image == '' and not x.spent]) == 1 ++ assert receiver_wallet.get_balance().balance == expected_receiver_balance ++ ++ # Restore and check background syncing incoming wallet ++ restore_wallet(receiver_wallet, seeds[1]) ++ receiver_wallet.setup_background_sync(background_sync_type = reuse_password) ++ receiver_wallet.start_background_sync() ++ receiver_wallet.refresh() ++ if 'out' in transfers: ++ for i, out_tx in enumerate(transfers.out): ++ if 'destinations' in out_tx: ++ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore ++ background_bal = receiver_wallet.get_balance().balance ++ assert background_bal >= expected_receiver_balance ++ background_transfers = receiver_wallet.get_transfers() ++ assert 'out' not in background_transfers or len(background_transfers.out) == 0 ++ assert 'in' in background_transfers and len(background_transfers['in']) > 0 ++ background_incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') ++ assert len(background_incoming_transfers) == len(incoming_transfers) ++ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0 ++ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == txid]) == 1 ++ ++ # Stop background sync and check transfers update correctly ++ receiver_wallet.stop_background_sync(wallet_password = '', seed = seeds[1]) ++ diff_transfers(receiver_wallet.get_transfers(), transfers) ++ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') ++ assert len(background_incoming_transfers) == len(incoming_transfers) ++ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image != '' and not x.spent]) == 1 ++ 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], '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() ++ assert_correct_transfers(receiver_wallet, background_transfers, background_incoming_transfers, background_bal) ++ stop_with_wrong_inputs(receiver_wallet, 'wrong_password') ++ receiver_wallet.stop_background_sync(wallet_password = 'test_password') ++ 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], '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() ++ diff_transfers(receiver_wallet.get_transfers(), background_transfers) ++ diff_incoming_transfers(receiver_wallet.incoming_transfers(transfer_type = 'all'), background_incoming_transfers) ++ assert receiver_wallet.get_balance().balance == background_bal ++ receiver_wallet.close_wallet() ++ receiver_wallet.open_wallet('test2', password = 'test_password') ++ # It should reopen with spend key loaded and correctly scan all transfers ++ assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance) ++ ++ # Sanity check against incoming wallet restored at 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) ++ ++ # Clean up ++ util_resources.remove_wallet_files('test1') ++ util_resources.remove_wallet_files('test2') ++ for i in range(2): ++ self.wallet[i].close_wallet() ++ self.wallet[i].restore_deterministic_wallet(seed = seeds[i]) ++ ++ def check_background_sync_reorg_recovery(self): ++ daemon = Daemon() ++ ++ print('Testing background sync reorg recovery') ++ ++ # Disconnect daemon from peers ++ daemon.out_peers(0) ++ ++ # Background sync type options ++ sender_wallet = self.wallet[0] ++ reuse_password = sender_wallet.background_sync_options.reuse_password ++ custom_password = sender_wallet.background_sync_options.custom_password ++ ++ for background_sync_type in [reuse_password, custom_password]: ++ # Set up wallet saved to disk ++ sender_wallet.close_wallet() ++ util_resources.remove_wallet_files('test1') ++ sender_wallet.restore_deterministic_wallet(seed = seeds[0], filename = 'test1', password = '') ++ sender_wallet.auto_refresh(enable = False) ++ sender_wallet.refresh() ++ sender_starting_balance = sender_wallet.get_balance().balance ++ ++ # Send tx and mine a block ++ amount = 1000000000000 ++ assert sender_starting_balance > amount ++ dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': amount} ++ res = sender_wallet.transfer([dst]) ++ assert len(res.tx_hash) == 32*2 ++ txid = res.tx_hash ++ ++ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) ++ ++ # Make sure the wallet can see the tx ++ sender_wallet.refresh() ++ transfers = sender_wallet.get_transfers() ++ assert 'pool' not in transfers or len (transfers.pool) == 0 ++ tx = [x for x in transfers.out if x.txid == txid] ++ assert len(tx) == 1 ++ tx = tx[0] ++ assert sender_wallet.get_balance().balance < (sender_starting_balance - amount) ++ ++ # Pop the block while background syncing ++ 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 = '', background_cache_password = background_cache_password) ++ sender_wallet.start_background_sync() ++ daemon.pop_blocks(1) ++ daemon.flush_txpool() ++ ++ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) ++ ++ # Make sure the wallet can no longer see the tx ++ sender_wallet.refresh() ++ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0]) ++ transfers = sender_wallet.get_transfers() ++ no_tx = [x for x in transfers.out if x.txid == txid] ++ assert len(no_tx) == 0 ++ assert sender_wallet.get_balance().balance == sender_starting_balance ++ ++ # Clean up ++ daemon.out_peers(12) ++ util_resources.remove_wallet_files('test1') ++ self.wallet[0].close_wallet() ++ self.wallet[0].restore_deterministic_wallet(seed = seeds[0]) ++ + if __name__ == '__main__': + TransferTest().run_test() +diff --git a/tests/functional_tests/util_resources.py b/tests/functional_tests/util_resources.py +index e030312..3ca6fdb 100755 +--- a/tests/functional_tests/util_resources.py ++++ b/tests/functional_tests/util_resources.py +@@ -37,6 +37,8 @@ + from __future__ import print_function + import subprocess + import psutil ++import os ++import errno + + def available_ram_gb(): + ram_bytes = psutil.virtual_memory().available +@@ -51,3 +53,26 @@ def get_time_pi_seconds(cores, app_dir='.'): + miliseconds = int(decoded) + + return miliseconds / 1000.0 ++ ++def remove_file(name): ++ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] ++ assert WALLET_DIRECTORY != '' ++ try: ++ os.unlink(WALLET_DIRECTORY + '/' + name) ++ except OSError as e: ++ if e.errno != errno.ENOENT: ++ raise ++ ++def get_file_path(name): ++ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] ++ assert WALLET_DIRECTORY != '' ++ return WALLET_DIRECTORY + '/' + name ++ ++def remove_wallet_files(name): ++ for suffix in ['', '.keys', '.background', '.background.keys', '.address.txt']: ++ remove_file(name + suffix) ++ ++def file_exists(name): ++ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] ++ assert WALLET_DIRECTORY != '' ++ return os.path.isfile(WALLET_DIRECTORY + '/' + name) +diff --git a/tests/functional_tests/wallet.py b/tests/functional_tests/wallet.py +index 1ad05c9..8182cec 100755 +--- a/tests/functional_tests/wallet.py ++++ b/tests/functional_tests/wallet.py +@@ -34,8 +34,7 @@ + + from __future__ import print_function + import sys +-import os +-import errno ++import util_resources + + from framework.wallet import Wallet + from framework.daemon import Daemon +@@ -54,24 +53,6 @@ class WalletTest(): + self.change_password() + self.store() + +- def remove_file(self, name): +- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] +- assert WALLET_DIRECTORY != '' +- try: +- os.unlink(WALLET_DIRECTORY + '/' + name) +- except OSError as e: +- if e.errno != errno.ENOENT: +- raise +- +- def remove_wallet_files(self, name): +- for suffix in ['', '.keys']: +- self.remove_file(name + suffix) +- +- def file_exists(self, name): +- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] +- assert WALLET_DIRECTORY != '' +- return os.path.isfile(WALLET_DIRECTORY + '/' + name) +- + def reset(self): + print('Resetting blockchain') + daemon = Daemon() +@@ -333,7 +314,7 @@ class WalletTest(): + try: wallet.close_wallet() + except: pass + +- self.remove_wallet_files('test1') ++ util_resources.remove_wallet_files('test1') + + seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' + res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') +@@ -359,7 +340,7 @@ class WalletTest(): + + wallet.close_wallet() + +- self.remove_wallet_files('test1') ++ util_resources.remove_wallet_files('test1') + + def store(self): + print('Testing store') +@@ -369,22 +350,26 @@ class WalletTest(): + try: wallet.close_wallet() + except: pass + +- self.remove_wallet_files('test1') ++ util_resources.remove_wallet_files('test1') + + seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' + res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert res.seed == seed + +- self.remove_file('test1') +- assert self.file_exists('test1.keys') +- assert not self.file_exists('test1') ++ util_resources.remove_file('test1') ++ assert util_resources.file_exists('test1.keys') ++ assert not util_resources.file_exists('test1') + wallet.store() +- assert self.file_exists('test1.keys') +- assert self.file_exists('test1') ++ assert util_resources.file_exists('test1.keys') ++ assert util_resources.file_exists('test1') + + wallet.close_wallet() +- self.remove_wallet_files('test1') ++ ++ wallet.open_wallet(filename = 'test1', password = '') ++ wallet.close_wallet() ++ ++ util_resources.remove_wallet_files('test1') + + + if __name__ == '__main__': +diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp +index ef6964f..25121a0 100644 +--- a/tests/unit_tests/wipeable_string.cpp ++++ b/tests/unit_tests/wipeable_string.cpp +@@ -211,3 +211,15 @@ TEST(wipeable_string, to_hex) + ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"", 0)) == epee::wipeable_string("")); + ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"abc", 3)) == epee::wipeable_string("616263")); + } ++ ++TEST(wipeable_string, to_string) ++{ ++ // Converting a wipeable_string to a string defeats the purpose of wipeable_string, ++ // but nice to know this works ++ std::string str; ++ { ++ epee::wipeable_string wipeable_str("foo"); ++ str = std::string(wipeable_str.data(), wipeable_str.size()); ++ } ++ ASSERT_TRUE(str == std::string("foo")); ++} +diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py +index 1e10e1f..bff33a5 100644 +--- a/utils/python-rpc/framework/wallet.py ++++ b/utils/python-rpc/framework/wallet.py +@@ -1138,3 +1138,45 @@ class Wallet(object): + 'id': '0' + } + return self.rpc.send_json_rpc_request(frozen) ++ ++ class BackgroundSyncOptions(object): ++ def __init__(self): ++ self.off = 'off' ++ self.reuse_password = 'reuse-wallet-password' ++ self.custom_password = 'custom-background-password' ++ background_sync_options = BackgroundSyncOptions() ++ ++ def setup_background_sync(self, background_sync_type = background_sync_options.off, wallet_password = '', background_cache_password = ''): ++ setup_background_sync = { ++ 'method': 'setup_background_sync', ++ 'jsonrpc': '2.0', ++ 'params' : { ++ 'background_sync_type': background_sync_type, ++ 'wallet_password': wallet_password, ++ 'background_cache_password': background_cache_password, ++ }, ++ 'id': '0' ++ } ++ return self.rpc.send_json_rpc_request(setup_background_sync) ++ ++ def start_background_sync(self): ++ start_background_sync = { ++ 'method': 'start_background_sync', ++ 'jsonrpc': '2.0', ++ 'params' : {}, ++ 'id': '0' ++ } ++ return self.rpc.send_json_rpc_request(start_background_sync) ++ ++ def stop_background_sync(self, wallet_password = '', seed = '', seed_offset = ''): ++ stop_background_sync = { ++ 'method': 'stop_background_sync', ++ 'jsonrpc': '2.0', ++ 'params' : { ++ 'wallet_password': wallet_password, ++ 'seed': seed, ++ 'seed_offset': seed_offset, ++ }, ++ 'id': '0' ++ } ++ return self.rpc.send_json_rpc_request(stop_background_sync) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/monero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch new file mode 100644 index 0000000..2b13c05 --- /dev/null +++ b/patches/monero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch @@ -0,0 +1,34 @@ +From c7b6e26d36d2428e5747e2b8287ae3719fa443e3 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Tue, 2 Apr 2024 16:51:56 +0200 +Subject: [PATCH 02/14] fix missing ___clear_cache when targetting iOS + +--- + .gitmodules | 3 ++- + external/randomx | 2 +- + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/.gitmodules b/.gitmodules +index 721cce3..ffb73fe 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -9,7 +9,8 @@ + url = https://github.com/trezor/trezor-common.git + [submodule "external/randomx"] + path = external/randomx +- url = https://github.com/tevador/RandomX ++ url = https://github.com/MrCyjaneK/RandomX ++ branch = cyjan-fix-ios + [submodule "external/supercop"] + path = external/supercop + url = https://github.com/monero-project/supercop +diff --git a/external/randomx b/external/randomx +index 102f8ac..ce72c9b 160000 +--- a/external/randomx ++++ b/external/randomx +@@ -1 +1 @@ +-Subproject commit 102f8acf90a7649ada410de5499a7ec62e49e1da ++Subproject commit ce72c9bb9cb799e0d9171094b9abb009e04c5bfc +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0002-wallet-background-sync-with-just-the-view-key.patch b/patches/monero/0002-wallet-background-sync-with-just-the-view-key.patch deleted file mode 100644 index 16fe77f..0000000 --- a/patches/monero/0002-wallet-background-sync-with-just-the-view-key.patch +++ /dev/null @@ -1,4316 +0,0 @@ -From 56fed429a25773c760b8490b3e0fb908c832e6e5 Mon Sep 17 00:00:00 2001 -From: j-berman -Date: Thu, 13 Oct 2022 18:33:33 -0700 -Subject: [PATCH 02/16] wallet: background sync with just the view key - -- When background syncing, the wallet wipes the spend key -from memory and processes all new transactions. The wallet saves -all receives, spends, and "plausible" spends of receives the -wallet does not know key images for. -- When background sync disabled, the wallet processes all -background synced txs and then clears the background sync cache. -- Adding "plausible" spends to the background sync cache ensures -that the wallet does not need to query the daemon to see if any -received outputs were spent while background sync was enabled. -This would harm privacy especially for users of 3rd party daemons. -- To enable the feature in the CLI wallet, the user can set -background-sync to reuse-wallet-password or -custom-background-password and the wallet automatically syncs in -the background when the wallet locks, then processes all -background synced txs when the wallet is unlocked. -- The custom-background-password option enables the user to -open a distinct background wallet that only has a view key saved -and can be opened/closed/synced separately from the main wallet. -When the main wallet opens, it processes the background wallet's -cache. -- To enable the feature in the RPC wallet, there is a new -`/setup_background_sync` endpoint. -- HW, multsig and view-only wallets cannot background sync. ---- - src/cryptonote_basic/account.cpp | 11 + - src/cryptonote_basic/account.h | 1 + - src/cryptonote_config.h | 2 + - src/simplewallet/simplewallet.cpp | 203 +++- - src/simplewallet/simplewallet.h | 1 + - src/wallet/api/wallet.cpp | 213 +++- - src/wallet/api/wallet.h | 12 + - src/wallet/api/wallet2_api.h | 42 + - src/wallet/wallet2.cpp | 1029 ++++++++++++++++-- - src/wallet/wallet2.h | 156 ++- - src/wallet/wallet_errors.h | 39 + - src/wallet/wallet_rpc_server.cpp | 162 +++ - src/wallet/wallet_rpc_server.h | 6 + - src/wallet/wallet_rpc_server_commands_defs.h | 64 ++ - src/wallet/wallet_rpc_server_error_codes.h | 2 + - tests/functional_tests/transfer.py | 400 ++++++- - tests/functional_tests/util_resources.py | 25 + - tests/functional_tests/wallet.py | 43 +- - tests/unit_tests/wipeable_string.cpp | 12 + - utils/python-rpc/framework/wallet.py | 42 + - 20 files changed, 2335 insertions(+), 130 deletions(-) - -diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 4931c3740..2d556f285 100644 ---- a/src/cryptonote_basic/account.cpp -+++ b/src/cryptonote_basic/account.cpp -@@ -158,6 +158,17 @@ DISABLE_VS_WARNINGS(4244 4345) - m_keys.m_passphrase.wipe(); - } - //----------------------------------------------------------------- -+ void account_base::set_spend_key(const crypto::secret_key& spend_secret_key) -+ { -+ // make sure derived spend public key matches saved public spend key -+ crypto::public_key spend_public_key; -+ crypto::secret_key_to_public_key(spend_secret_key, spend_public_key); -+ CHECK_AND_ASSERT_THROW_MES(m_keys.m_account_address.m_spend_public_key == spend_public_key, -+ "Unexpected derived public spend key"); -+ -+ m_keys.m_spend_secret_key = spend_secret_key; -+ } -+ //----------------------------------------------------------------- - crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) - { - crypto::secret_key first = generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, recovery_key, recover); -diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h -index 0099ebfe7..1f76febce 100644 ---- a/src/cryptonote_basic/account.h -+++ b/src/cryptonote_basic/account.h -@@ -101,6 +101,7 @@ namespace cryptonote - bool store(const std::string& file_path); - - void forget_spend_key(); -+ void set_spend_key(const crypto::secret_key& spend_secret_key); - const std::vector &get_multisig_keys() const { return m_keys.m_multisig_keys; } - - void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); } -diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h -index 8e1a07110..3af3a63a1 100644 ---- a/src/cryptonote_config.h -+++ b/src/cryptonote_config.h -@@ -243,6 +243,8 @@ namespace config - const unsigned char HASH_KEY_ENCRYPTED_PAYMENT_ID = 0x8d; - const unsigned char HASH_KEY_WALLET = 0x8c; - const unsigned char HASH_KEY_WALLET_CACHE = 0x8d; -+ const unsigned char HASH_KEY_BACKGROUND_CACHE = 0x8e; -+ const unsigned char HASH_KEY_BACKGROUND_KEYS_FILE = 0x8f; - const unsigned char HASH_KEY_RPC_PAYMENT_NONCE = 0x58; - const unsigned char HASH_KEY_MEMORY = 'k'; - const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp -index b9e30f9d9..f8ed6659c 100644 ---- a/src/simplewallet/simplewallet.cpp -+++ b/src/simplewallet/simplewallet.cpp -@@ -155,6 +155,17 @@ typedef cryptonote::simple_wallet sw; - } \ - } while(0) - -+#define CHECK_IF_BACKGROUND_SYNCING(msg) \ -+ do \ -+ { \ -+ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) \ -+ { \ -+ std::string type = m_wallet->is_background_wallet() ? "background wallet" : "background syncing wallet"; \ -+ fail_msg_writer() << boost::format(tr("%s %s")) % type % msg; \ -+ return false; \ -+ } \ -+ } while (0) -+ - static std::string get_human_readable_timespan(std::chrono::seconds seconds); - static std::string get_human_readable_timespan(uint64_t seconds); - -@@ -325,7 +336,7 @@ namespace - auto pwd_container = tools::password_container::prompt(verify, prompt); - if (!pwd_container) - { -- tools::fail_msg_writer() << sw::tr("failed to read wallet password"); -+ tools::fail_msg_writer() << sw::tr("failed to read password"); - } - return pwd_container; - } -@@ -335,6 +346,11 @@ namespace - return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify); - } - -+ boost::optional background_sync_cache_password_prompter(bool verify) -+ { -+ return password_prompter(verify ? sw::tr("Enter a custom password for the background sync cache") : sw::tr("Background sync cache password"), verify); -+ } -+ - inline std::string interpret_rpc_response(bool ok, const std::string& status) - { - std::string err; -@@ -452,6 +468,41 @@ namespace - return "invalid"; - } - -+ const struct -+ { -+ const char *name; -+ tools::wallet2::BackgroundSyncType background_sync_type; -+ } background_sync_type_names[] = -+ { -+ { "off", tools::wallet2::BackgroundSyncOff }, -+ { "reuse-wallet-password", tools::wallet2::BackgroundSyncReusePassword }, -+ { "custom-background-password", tools::wallet2::BackgroundSyncCustomPassword }, -+ }; -+ -+ bool parse_background_sync_type(const std::string &s, tools::wallet2::BackgroundSyncType &background_sync_type) -+ { -+ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) -+ { -+ if (s == background_sync_type_names[n].name) -+ { -+ background_sync_type = background_sync_type_names[n].background_sync_type; -+ return true; -+ } -+ } -+ fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse background sync type"); -+ return false; -+ } -+ -+ std::string get_background_sync_type_name(tools::wallet2::BackgroundSyncType type) -+ { -+ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) -+ { -+ if (type == background_sync_type_names[n].background_sync_type) -+ return background_sync_type_names[n].name; -+ } -+ return "invalid"; -+ } -+ - std::string get_version_string(uint32_t version) - { - return boost::lexical_cast(version >> 16) + "." + boost::lexical_cast(version & 0xffff); -@@ -805,6 +856,7 @@ bool simple_wallet::spendkey(const std::vector &args/* = std::vecto - fail_msg_writer() << tr("wallet is watch-only and has no spend key"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("has no spend key"); - // don't log - PAUSE_READLINE(); - if (m_wallet->key_on_device()) { -@@ -836,6 +888,7 @@ bool simple_wallet::print_seed(bool encrypted) - fail_msg_writer() << tr("wallet is watch-only and has no seed"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("has no seed"); - - multisig = m_wallet->multisig(&ready); - if (multisig) -@@ -913,6 +966,7 @@ bool simple_wallet::seed_set_language(const std::vector &args/* = s - fail_msg_writer() << tr("wallet is watch-only and has no seed"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("has no seed"); - - epee::wipeable_string password; - { -@@ -1059,6 +1113,7 @@ bool simple_wallet::prepare_multisig_main(const std::vector &args, - fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig"); - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot be made multisig"); - - if(m_wallet->get_num_transfer_details()) - { -@@ -2195,6 +2250,7 @@ bool simple_wallet::save_known_rings(const std::vector &args) - - bool simple_wallet::freeze_thaw(const std::vector &args, bool freeze) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot freeze/thaw"); - if (args.empty()) - { - fail_msg_writer() << boost::format(tr("usage: %s |")) % (freeze ? "freeze" : "thaw"); -@@ -2234,6 +2290,7 @@ bool simple_wallet::thaw(const std::vector &args) - - bool simple_wallet::frozen(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot see frozen key images"); - if (args.empty()) - { - size_t ntd = m_wallet->get_num_transfer_details(); -@@ -3005,6 +3062,56 @@ bool simple_wallet::set_track_uses(const std::vector &args/* = std: - return true; - } - -+bool simple_wallet::setup_background_sync(const std::vector &args/* = std::vector()*/) -+{ -+ if (m_wallet->multisig()) -+ { -+ fail_msg_writer() << tr("background sync not implemented for multisig wallet"); -+ return true; -+ } -+ if (m_wallet->watch_only()) -+ { -+ fail_msg_writer() << tr("background sync not implemented for watch only wallet"); -+ return true; -+ } -+ if (m_wallet->key_on_device()) -+ { -+ fail_msg_writer() << tr("command not supported by HW wallet"); -+ return true; -+ } -+ -+ const auto pwd_container = get_and_verify_password(); -+ if (pwd_container) -+ { -+ tools::wallet2::BackgroundSyncType background_sync_type; -+ if (!parse_background_sync_type(args[1], background_sync_type)) -+ { -+ fail_msg_writer() << tr("invalid option"); -+ return true; -+ } -+ -+ try -+ { -+ boost::optional background_cache_password = boost::none; -+ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) -+ { -+ const auto background_pwd_container = background_sync_cache_password_prompter(true); -+ if (!background_pwd_container) -+ return true; -+ background_cache_password = background_pwd_container->password(); -+ } -+ -+ LOCK_IDLE_SCOPE(); -+ m_wallet->setup_background_sync(background_sync_type, pwd_container->password(), background_cache_password); -+ } -+ catch (const std::exception &e) -+ { -+ fail_msg_writer() << tr("Error setting background sync type: ") << e.what(); -+ } -+ } -+ return true; -+} -+ - bool simple_wallet::set_show_wallet_name_when_locked(const std::vector &args/* = std::vector()*/) - { - const auto pwd_container = get_and_verify_password(); -@@ -3237,6 +3344,7 @@ bool simple_wallet::apropos(const std::vector &args) - - bool simple_wallet::scan_tx(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot scan tx"); - if (args.empty()) - { - PRINT_USAGE(USAGE_SCAN_TX); -@@ -3458,6 +3566,8 @@ simple_wallet::simple_wallet() - " Ignore outputs of amount below this threshold when spending.\n " - "track-uses <1|0>\n " - " Whether to keep track of owned outputs uses.\n " -+ "background-sync \n " -+ " Set this to enable scanning in the background with just the view key while the wallet is locked.\n " - "setup-background-mining <1|0>\n " - " Whether to enable background mining. Set this to support the network and to get a chance to receive new monero.\n " - "device-name \n " -@@ -3876,6 +3986,7 @@ bool simple_wallet::set_variable(const std::vector &args) - success_msg_writer() << "ignore-outputs-above = " << cryptonote::print_money(m_wallet->ignore_outputs_above()); - success_msg_writer() << "ignore-outputs-below = " << cryptonote::print_money(m_wallet->ignore_outputs_below()); - success_msg_writer() << "track-uses = " << m_wallet->track_uses(); -+ success_msg_writer() << "background-sync = " << get_background_sync_type_name(m_wallet->background_sync_type()); - success_msg_writer() << "setup-background-mining = " << setup_background_mining_string; - success_msg_writer() << "device-name = " << m_wallet->device_name(); - success_msg_writer() << "export-format = " << (m_wallet->export_format() == tools::wallet2::ExportFormat::Ascii ? "ascii" : "binary"); -@@ -3894,6 +4005,7 @@ bool simple_wallet::set_variable(const std::vector &args) - } - else - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot change wallet settings"); - - #define CHECK_SIMPLE_VARIABLE(name, f, help) do \ - if (args[0] == name) { \ -@@ -3947,6 +4059,7 @@ bool simple_wallet::set_variable(const std::vector &args) - CHECK_SIMPLE_VARIABLE("ignore-outputs-above", set_ignore_outputs_above, tr("amount")); - CHECK_SIMPLE_VARIABLE("ignore-outputs-below", set_ignore_outputs_below, tr("amount")); - CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1")); -+ CHECK_SIMPLE_VARIABLE("background-sync", setup_background_sync, tr("off (default); reuse-wallet-password (reuse the wallet password to encrypt the background cache); custom-background-password (use a custom background password to encrypt the background cache)")); - CHECK_SIMPLE_VARIABLE("show-wallet-name-when-locked", set_show_wallet_name_when_locked, tr("1 or 0")); - CHECK_SIMPLE_VARIABLE("inactivity-lock-timeout", set_inactivity_lock_timeout, tr("unsigned integer (seconds, 0 to disable)")); - CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no")); -@@ -4900,7 +5013,10 @@ std::string simple_wallet::get_mnemonic_language() - //---------------------------------------------------------------------------------------------------- - boost::optional simple_wallet::get_and_verify_password() const - { -- auto pwd_container = default_password_prompter(m_wallet_file.empty()); -+ const bool verify = m_wallet_file.empty(); -+ auto pwd_container = (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) -+ ? background_sync_cache_password_prompter(verify) -+ : default_password_prompter(verify); - if (!pwd_container) - return boost::none; - -@@ -5203,6 +5319,8 @@ boost::optional simple_wallet::open_wallet(const boost::p - prefix = tr("Opened watch-only wallet"); - else if (m_wallet->multisig(&ready, &threshold, &total)) - prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); -+ else if (m_wallet->is_background_wallet()) -+ prefix = tr("Opened background wallet"); - else - prefix = tr("Opened wallet"); - message_writer(console_color_white, true) << -@@ -5411,6 +5529,10 @@ void simple_wallet::stop_background_mining() - //---------------------------------------------------------------------------------------------------- - void simple_wallet::check_background_mining(const epee::wipeable_string &password) - { -+ // Background mining can be toggled from the main wallet -+ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) -+ return; -+ - tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); - if (setup == tools::wallet2::BackgroundMiningNo) - { -@@ -6275,6 +6397,7 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::rescan_spent(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot rescan spent"); - if (!m_wallet->is_trusted_daemon()) - { - fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); -@@ -6532,10 +6655,27 @@ void simple_wallet::check_for_inactivity_lock(bool user) - " || ||" << std::endl << - "" << std::endl; - } -+ -+ bool started_background_sync = false; -+ if (!m_wallet->is_background_wallet() && -+ m_wallet->background_sync_type() != tools::wallet2::BackgroundSyncOff) -+ { -+ LOCK_IDLE_SCOPE(); -+ m_wallet->start_background_sync(); -+ started_background_sync = true; -+ } -+ - while (1) - { - const char *inactivity_msg = user ? "" : tr("Locked due to inactivity."); -- tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << tr("The wallet password is required to unlock the console."); -+ tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << ( -+ (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) -+ ? tr("The background password is required to unlock the console.") -+ : tr("The wallet password is required to unlock the console.") -+ ); -+ -+ if (m_wallet->is_background_syncing()) -+ tools::msg_writer() << tr("\nSyncing in the background while locked...") << std::endl; - - const bool show_wallet_name = m_wallet->show_wallet_name_when_locked(); - if (show_wallet_name) -@@ -6548,8 +6688,16 @@ void simple_wallet::check_for_inactivity_lock(bool user) - } - try - { -- if (get_and_verify_password()) -+ const auto pwd_container = get_and_verify_password(); -+ if (pwd_container) -+ { -+ if (started_background_sync) -+ { -+ LOCK_IDLE_SCOPE(); -+ m_wallet->stop_background_sync(pwd_container->password()); -+ } - break; -+ } - } - catch (...) { /* do nothing, just let the loop loop */ } - } -@@ -6576,6 +6724,7 @@ bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector &args_, bool called_by_mms) - { - // "transfer [index=[,,...]] [] []
[]" -+ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); - if (!try_connect_to_daemon()) - return false; - -@@ -7004,6 +7153,7 @@ bool simple_wallet::transfer_main(const std::vector &args_, bool ca - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::transfer(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); - if (args_.size() < 1) - { - PRINT_USAGE(USAGE_TRANSFER); -@@ -7016,6 +7166,7 @@ bool simple_wallet::transfer(const std::vector &args_) - - bool simple_wallet::sweep_unmixable(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - if (!try_connect_to_daemon()) - return true; - -@@ -7123,6 +7274,7 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - auto print_usage = [this, account, below]() - { - if (below) -@@ -7404,6 +7556,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vect - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_single(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - if (!try_connect_to_daemon()) - return true; - -@@ -7642,12 +7795,14 @@ bool simple_wallet::sweep_single(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_all(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - sweep_main(m_current_subaddress_account, 0, args_); - return true; - } - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_account(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - auto local_args = args_; - if (local_args.empty()) - { -@@ -7668,6 +7823,7 @@ bool simple_wallet::sweep_account(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_below(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - uint64_t below = 0; - if (args_.size() < 1) - { -@@ -7686,6 +7842,7 @@ bool simple_wallet::sweep_below(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::donate(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot donate"); - std::vector local_args = args_; - if(local_args.empty() || local_args.size() > 5) - { -@@ -7747,6 +7904,7 @@ bool simple_wallet::donate(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot load tx"); - // gather info to ask the user - uint64_t amount = 0, amount_to_dests = 0, change = 0; - size_t min_ring_size = ~0; -@@ -7927,6 +8085,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) - fail_msg_writer() << tr("This is a watch only wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot sign transfer"); - - bool export_raw = false; - std::string unsigned_filename = "unsigned_monero_tx"; -@@ -8034,6 +8193,8 @@ std::string get_tx_key_stream(crypto::secret_key tx_key, std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get tx key"); -+ - std::vector local_args = args_; - - if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR) -@@ -8074,6 +8235,8 @@ bool simple_wallet::get_tx_key(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::set_tx_key(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot set tx key"); -+ - std::vector local_args = args_; - - if(local_args.size() != 2 && local_args.size() != 3) { -@@ -8150,6 +8313,8 @@ bool simple_wallet::set_tx_key(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_tx_proof(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get tx proof"); -+ - if (args.size() != 2 && args.size() != 3) - { - PRINT_USAGE(USAGE_GET_TX_PROOF); -@@ -8356,6 +8521,7 @@ bool simple_wallet::check_tx_proof(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_spend_proof(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get spend proof"); - if (m_wallet->key_on_device()) - { - fail_msg_writer() << tr("command not supported by HW wallet"); -@@ -8440,6 +8606,7 @@ bool simple_wallet::check_spend_proof(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_reserve_proof(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get reserve proof"); - if (m_wallet->key_on_device()) - { - fail_msg_writer() << tr("command not supported by HW wallet"); -@@ -9126,6 +9293,8 @@ bool simple_wallet::unspent_outputs(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::rescan_blockchain(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot rescan"); -+ - uint64_t start_height = 0; - ResetType reset_type = ResetSoft; - -@@ -9423,6 +9592,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - if (command == "new") - { - // create a new account and switch to it -+ CHECK_IF_BACKGROUND_SYNCING("cannot create new account"); - std::string label = boost::join(local_args, " "); - if (label.empty()) - label = tr("(Untitled account)"); -@@ -9453,6 +9623,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - else if (command == "label" && local_args.size() >= 1) - { - // set label of the specified account -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - uint32_t index_major; - if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0])) - { -@@ -9474,6 +9645,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - } - else if (command == "tag" && local_args.size() >= 2) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - const std::string tag = local_args[0]; - std::set account_indices; - for (size_t i = 1; i < local_args.size(); ++i) -@@ -9498,6 +9670,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - } - else if (command == "untag" && local_args.size() >= 1) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - std::set account_indices; - for (size_t i = 0; i < local_args.size(); ++i) - { -@@ -9521,6 +9694,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - } - else if (command == "tag_description" && local_args.size() >= 1) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - const std::string tag = local_args[0]; - std::string description; - if (local_args.size() > 1) -@@ -9638,6 +9812,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args[0] == "new") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); - local_args.erase(local_args.begin()); - std::string label; - if (local_args.size() > 0) -@@ -9650,6 +9825,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args[0] == "mnew") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot add addresses"); - local_args.erase(local_args.begin()); - if (local_args.size() != 1) - { -@@ -9675,6 +9851,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args[0] == "one-off") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); - local_args.erase(local_args.begin()); - std::string label; - if (local_args.size() != 2) -@@ -9693,6 +9870,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args.size() >= 2 && local_args[0] == "label") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify address"); - if (!epee::string_tools::get_xtype_from_string(index, local_args[1])) - { - fail_msg_writer() << tr("failed to parse index: ") << local_args[1]; -@@ -9839,6 +10017,8 @@ bool simple_wallet::print_integrated_address(const std::vector &arg - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::address_book(const std::vector &args/* = std::vector()*/) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get address book"); -+ - if (args.size() == 0) - { - } -@@ -9899,6 +10079,8 @@ bool simple_wallet::address_book(const std::vector &args/* = std::v - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::set_tx_note(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot set tx note"); -+ - if (args.size() == 0) - { - PRINT_USAGE(USAGE_SET_TX_NOTE); -@@ -9927,6 +10109,8 @@ bool simple_wallet::set_tx_note(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_tx_note(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get tx note"); -+ - if (args.size() != 1) - { - PRINT_USAGE(USAGE_GET_TX_NOTE); -@@ -9952,6 +10136,8 @@ bool simple_wallet::get_tx_note(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::set_description(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot set description"); -+ - // 0 arguments allowed, for setting the description to empty string - - std::string description = ""; -@@ -9968,6 +10154,8 @@ bool simple_wallet::set_description(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_description(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get description"); -+ - if (args.size() != 0) - { - PRINT_USAGE(USAGE_GET_DESCRIPTION); -@@ -10026,6 +10214,8 @@ bool simple_wallet::wallet_info(const std::vector &args) - type = tr("Watch only"); - else if (m_wallet->multisig(&ready, &threshold, &total)) - type = (boost::format(tr("%u/%u multisig%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); -+ else if (m_wallet->is_background_wallet()) -+ type = tr("Background wallet"); - else - type = tr("Normal"); - message_writer() << tr("Type: ") << type; -@@ -10037,6 +10227,7 @@ bool simple_wallet::wallet_info(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sign(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sign"); - if (m_wallet->key_on_device()) - { - fail_msg_writer() << tr("command not supported by HW wallet"); -@@ -10144,6 +10335,7 @@ bool simple_wallet::export_key_images(const std::vector &args_) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot export key images"); - auto args = args_; - - if (m_wallet->watch_only()) -@@ -10197,6 +10389,7 @@ bool simple_wallet::import_key_images(const std::vector &args) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot import key images"); - if (!m_wallet->is_trusted_daemon()) - { - fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); -@@ -10305,6 +10498,7 @@ bool simple_wallet::export_outputs(const std::vector &args_) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot export outputs"); - auto args = args_; - - bool all = false; -@@ -10354,6 +10548,7 @@ bool simple_wallet::import_outputs(const std::vector &args) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot import outputs"); - if (args.size() != 1) - { - PRINT_USAGE(USAGE_IMPORT_OUTPUTS); -diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h -index 652708f5a..159da2c45 100644 ---- a/src/simplewallet/simplewallet.h -+++ b/src/simplewallet/simplewallet.h -@@ -147,6 +147,7 @@ namespace cryptonote - bool set_ignore_outputs_above(const std::vector &args = std::vector()); - bool set_ignore_outputs_below(const std::vector &args = std::vector()); - bool set_track_uses(const std::vector &args = std::vector()); -+ bool setup_background_sync(const std::vector &args = std::vector()); - bool set_show_wallet_name_when_locked(const std::vector &args = std::vector()); - bool set_inactivity_lock_timeout(const std::vector &args = std::vector()); - bool set_setup_background_mining(const std::vector &args = std::vector()); -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index d96ea97ea..7d430b655 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -54,6 +54,40 @@ using namespace cryptonote; - #undef MONERO_DEFAULT_LOG_CATEGORY - #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" - -+#define LOCK_REFRESH() \ -+ bool refresh_enabled = m_refreshEnabled; \ -+ m_refreshEnabled = false; \ -+ m_wallet->stop(); \ -+ m_refreshCV.notify_one(); \ -+ boost::mutex::scoped_lock lock(m_refreshMutex); \ -+ boost::mutex::scoped_lock lock2(m_refreshMutex2); \ -+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ -+ /* m_refreshMutex's still locked here */ \ -+ if (refresh_enabled) \ -+ startRefresh(); \ -+ }) -+ -+#define PRE_VALIDATE_BACKGROUND_SYNC() \ -+ do \ -+ { \ -+ clearStatus(); \ -+ if (m_wallet->key_on_device()) \ -+ { \ -+ setStatusError(tr("HW wallet cannot use background sync")); \ -+ return false; \ -+ } \ -+ if (m_wallet->watch_only()) \ -+ { \ -+ setStatusError(tr("View only wallet cannot use background sync")); \ -+ return false; \ -+ } \ -+ if (m_wallet->multisig()) \ -+ { \ -+ setStatusError(tr("Multisig wallet cannot use background sync")); \ -+ return false; \ -+ } \ -+ } while (0) -+ - namespace Monero { - - namespace { -@@ -814,6 +848,8 @@ bool WalletImpl::close(bool store) - - std::string WalletImpl::seed(const std::string& seed_offset) const - { -+ if (checkBackgroundSync("cannot get seed")) -+ return std::string(); - epee::wipeable_string seed; - if (m_wallet) - m_wallet->get_seed(seed, seed_offset); -@@ -876,6 +912,8 @@ std::string WalletImpl::getSeedLanguage() const - - void WalletImpl::setSeedLanguage(const std::string &arg) - { -+ if (checkBackgroundSync("cannot set seed language")) -+ return; - m_wallet->set_seed_language(arg); - } - -@@ -899,6 +937,8 @@ void WalletImpl::statusWithErrorString(int& status, std::string& errorString) co - - bool WalletImpl::setPassword(const std::string &password) - { -+ if (checkBackgroundSync("cannot change password")) -+ return false; - clearStatus(); - try { - m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password); -@@ -1059,6 +1099,8 @@ bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_ - - void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height) - { -+ if (checkBackgroundSync("cannot change refresh height")) -+ return; - m_wallet->set_refresh_from_block_height(refresh_from_block_height); - } - -@@ -1176,6 +1218,8 @@ void WalletImpl::refreshAsync() - - bool WalletImpl::rescanBlockchain() - { -+ if (checkBackgroundSync("cannot rescan blockchain")) -+ return false; - clearStatus(); - m_refreshShouldRescan = true; - doRefresh(); -@@ -1184,6 +1228,8 @@ bool WalletImpl::rescanBlockchain() - - void WalletImpl::rescanBlockchainAsync() - { -+ if (checkBackgroundSync("cannot rescan blockchain")) -+ return; - m_refreshShouldRescan = true; - refreshAsync(); - } -@@ -1207,7 +1253,7 @@ int WalletImpl::autoRefreshInterval() const - UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_filename) { - clearStatus(); - UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); -- if (!m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ -+ if (checkBackgroundSync("cannot load tx") || !m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ - setStatusError(tr("Failed to load unsigned transactions")); - transaction->m_status = UnsignedTransaction::Status::Status_Error; - transaction->m_errorString = errorString(); -@@ -1227,6 +1273,8 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file - - bool WalletImpl::submitTransaction(const string &fileName) { - clearStatus(); -+ if (checkBackgroundSync("cannot submit tx")) -+ return false; - std::unique_ptr transaction(new PendingTransactionImpl(*this)); - - bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx); -@@ -1250,6 +1298,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) - setStatusError(tr("Wallet is view only")); - return false; - } -+ if (checkBackgroundSync("cannot export key images")) -+ return false; - - try - { -@@ -1270,6 +1320,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) - - bool WalletImpl::importKeyImages(const string &filename) - { -+ if (checkBackgroundSync("cannot import key images")) -+ return false; - if (!trustedDaemon()) { - setStatusError(tr("Key images can only be imported with a trusted daemon")); - return false; -@@ -1293,6 +1345,8 @@ bool WalletImpl::importKeyImages(const string &filename) - - bool WalletImpl::exportOutputs(const string &filename, bool all) - { -+ if (checkBackgroundSync("cannot export outputs")) -+ return false; - if (m_wallet->key_on_device()) - { - setStatusError(string(tr("Not supported on HW wallets.")) + filename); -@@ -1323,6 +1377,8 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) - - bool WalletImpl::importOutputs(const string &filename) - { -+ if (checkBackgroundSync("cannot import outputs")) -+ return false; - if (m_wallet->key_on_device()) - { - setStatusError(string(tr("Not supported on HW wallets.")) + filename); -@@ -1355,6 +1411,8 @@ bool WalletImpl::importOutputs(const string &filename) - - bool WalletImpl::scanTransactions(const std::vector &txids) - { -+ if (checkBackgroundSync("cannot scan transactions")) -+ return false; - if (txids.empty()) - { - setStatusError(string(tr("Failed to scan transactions: no transaction ids provided."))); -@@ -1393,8 +1451,86 @@ bool WalletImpl::scanTransactions(const std::vector &txids) - return true; - } - -+bool WalletImpl::setupBackgroundSync(const Wallet::BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) -+{ -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ -+ tools::wallet2::BackgroundSyncType bgs_type; -+ switch (background_sync_type) -+ { -+ case Wallet::BackgroundSync_Off: bgs_type = tools::wallet2::BackgroundSyncOff; break; -+ case Wallet::BackgroundSync_ReusePassword: bgs_type = tools::wallet2::BackgroundSyncReusePassword; break; -+ case Wallet::BackgroundSync_CustomPassword: bgs_type = tools::wallet2::BackgroundSyncCustomPassword; break; -+ default: setStatusError(tr("Unknown background sync type")); return false; -+ } -+ -+ boost::optional bgc_password = background_cache_password -+ ? boost::optional(*background_cache_password) -+ : boost::none; -+ -+ LOCK_REFRESH(); -+ m_wallet->setup_background_sync(bgs_type, wallet_password, bgc_password); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to setup background sync: " << e.what()); -+ setStatusError(string(tr("Failed to setup background sync: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ -+Wallet::BackgroundSyncType WalletImpl::getBackgroundSyncType() const -+{ -+ switch (m_wallet->background_sync_type()) -+ { -+ case tools::wallet2::BackgroundSyncOff: return Wallet::BackgroundSync_Off; -+ case tools::wallet2::BackgroundSyncReusePassword: return Wallet::BackgroundSync_ReusePassword; -+ case tools::wallet2::BackgroundSyncCustomPassword: return Wallet::BackgroundSync_CustomPassword; -+ default: setStatusError(tr("Unknown background sync type")); return Wallet::BackgroundSync_Off; -+ } -+} -+ -+bool WalletImpl::startBackgroundSync() -+{ -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ LOCK_REFRESH(); -+ m_wallet->start_background_sync(); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to start background sync: " << e.what()); -+ setStatusError(string(tr("Failed to start background sync: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ -+bool WalletImpl::stopBackgroundSync(const std::string &wallet_password) -+{ -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ LOCK_REFRESH(); -+ m_wallet->stop_background_sync(epee::wipeable_string(wallet_password)); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to stop background sync: " << e.what()); -+ setStatusError(string(tr("Failed to stop background sync: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ - void WalletImpl::addSubaddressAccount(const std::string& label) - { -+ if (checkBackgroundSync("cannot add account")) -+ return; - m_wallet->add_subaddress_account(label); - } - size_t WalletImpl::numSubaddressAccounts() const -@@ -1407,10 +1543,14 @@ size_t WalletImpl::numSubaddresses(uint32_t accountIndex) const - } - void WalletImpl::addSubaddress(uint32_t accountIndex, const std::string& label) - { -+ if (checkBackgroundSync("cannot add subbaddress")) -+ return; - m_wallet->add_subaddress(accountIndex, label); - } - std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const - { -+ if (checkBackgroundSync("cannot get subbaddress label")) -+ return ""; - try - { - return m_wallet->get_subaddress_label({accountIndex, addressIndex}); -@@ -1424,6 +1564,8 @@ std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addre - } - void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) - { -+ if (checkBackgroundSync("cannot set subbaddress label")) -+ return; - try - { - return m_wallet->set_subaddress_label({accountIndex, addressIndex}, label); -@@ -1437,12 +1579,16 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex - - MultisigState WalletImpl::multisig() const { - MultisigState state; -+ if (checkBackgroundSync("cannot use multisig")) -+ return state; - state.isMultisig = m_wallet->multisig(&state.isReady, &state.threshold, &state.total); - - return state; - } - - string WalletImpl::getMultisigInfo() const { -+ if (checkBackgroundSync("cannot use multisig")) -+ return string(); - try { - clearStatus(); - return m_wallet->get_multisig_first_kex_msg(); -@@ -1455,6 +1601,8 @@ string WalletImpl::getMultisigInfo() const { - } - - string WalletImpl::makeMultisig(const vector& info, const uint32_t threshold) { -+ if (checkBackgroundSync("cannot make multisig")) -+ return string(); - try { - clearStatus(); - -@@ -1595,6 +1743,9 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector extra; - std::string extra_nonce; - vector dsts; -@@ -1761,6 +1912,9 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() - PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); - - do { -+ if (checkBackgroundSync("cannot sweep")) -+ break; -+ - try { - transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions(); - pendingTxPostProcess(transaction); -@@ -1894,11 +2048,15 @@ uint32_t WalletImpl::defaultMixin() const - - void WalletImpl::setDefaultMixin(uint32_t arg) - { -+ if (checkBackgroundSync("cannot set default mixin")) -+ return; - m_wallet->default_mixin(arg); - } - - bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val) - { -+ if (checkBackgroundSync("cannot set cache attribute")) -+ return false; - m_wallet->set_attribute(key, val); - return true; - } -@@ -1912,6 +2070,8 @@ std::string WalletImpl::getCacheAttribute(const std::string &key) const - - bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) - { -+ if (checkBackgroundSync("cannot set user note")) -+ return false; - cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) - return false; -@@ -1923,6 +2083,8 @@ bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) - - std::string WalletImpl::getUserNote(const std::string &txid) const - { -+ if (checkBackgroundSync("cannot get user note")) -+ return ""; - cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) - return ""; -@@ -1933,6 +2095,9 @@ std::string WalletImpl::getUserNote(const std::string &txid) const - - std::string WalletImpl::getTxKey(const std::string &txid_str) const - { -+ if (checkBackgroundSync("cannot get tx key")) -+ return ""; -+ - crypto::hash txid; - if(!epee::string_tools::hex_to_pod(txid_str, txid)) - { -@@ -2017,6 +2182,9 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str, - - std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const - { -+ if (checkBackgroundSync("cannot get tx proof")) -+ return ""; -+ - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(txid_str, txid)) - { -@@ -2073,6 +2241,9 @@ bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &ad - } - - std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::string &message) const { -+ if (checkBackgroundSync("cannot get spend proof")) -+ return ""; -+ - crypto::hash txid; - if(!epee::string_tools::hex_to_pod(txid_str, txid)) - { -@@ -2115,6 +2286,9 @@ bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string - } - - std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const { -+ if (checkBackgroundSync("cannot get reserve proof")) -+ return ""; -+ - try - { - clearStatus(); -@@ -2161,6 +2335,9 @@ bool WalletImpl::checkReserveProof(const std::string &address, const std::string - - std::string WalletImpl::signMessage(const std::string &message, const std::string &address) - { -+ if (checkBackgroundSync("cannot sign message")) -+ return ""; -+ - if (address.empty()) { - return m_wallet->sign(message, tools::wallet2::sign_with_spend_key); - } -@@ -2288,6 +2465,16 @@ bool WalletImpl::isDeterministic() const - return m_wallet->is_deterministic(); - } - -+bool WalletImpl::isBackgroundSyncing() const -+{ -+ return m_wallet->is_background_syncing(); -+} -+ -+bool WalletImpl::isBackgroundWallet() const -+{ -+ return m_wallet->is_background_wallet(); -+} -+ - void WalletImpl::clearStatus() const - { - boost::lock_guard l(m_statusMutex); -@@ -2356,9 +2543,7 @@ void WalletImpl::doRefresh() - if(rescan) - m_wallet->rescan_blockchain(false); - m_wallet->refresh(trustedDaemon()); -- if (!m_synchronized) { -- m_synchronized = true; -- } -+ m_synchronized = m_wallet->is_synced(); - // assuming if we have empty history, it wasn't initialized yet - // for further history changes client need to update history in - // "on_money_received" and "on_money_sent" callbacks -@@ -2462,6 +2647,24 @@ bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_a - return true; - } - -+bool WalletImpl::checkBackgroundSync(const std::string &message) const -+{ -+ clearStatus(); -+ if (m_wallet->is_background_wallet()) -+ { -+ LOG_ERROR("Background wallets " + message); -+ setStatusError(tr("Background wallets ") + message); -+ return true; -+ } -+ if (m_wallet->is_background_syncing()) -+ { -+ LOG_ERROR(message + " while background syncing"); -+ setStatusError(message + tr(" while background syncing. Stop background syncing first.")); -+ return true; -+ } -+ return false; -+} -+ - bool WalletImpl::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 &unknown_parameters, std::string &error) - { - return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); -@@ -2480,6 +2683,8 @@ std::string WalletImpl::getDefaultDataDir() const - bool WalletImpl::rescanSpent() - { - clearStatus(); -+ if (checkBackgroundSync("cannot rescan spent")) -+ return false; - if (!trustedDaemon()) { - setStatusError(tr("Rescan spent can only be used with a trusted daemon")); - return false; -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 787215ab3..9acd2871f 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -181,6 +181,13 @@ public: - bool importOutputs(const std::string &filename) override; - bool scanTransactions(const std::vector &txids) override; - -+ bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; -+ BackgroundSyncType getBackgroundSyncType() const override; -+ bool startBackgroundSync() override; -+ bool stopBackgroundSync(const std::string &wallet_password) override; -+ bool isBackgroundSyncing() const override; -+ bool isBackgroundWallet() const override; -+ - virtual void disposeTransaction(PendingTransaction * t) override; - virtual uint64_t estimateTransactionFee(const std::vector> &destinations, - PendingTransaction::Priority priority) const override; -@@ -249,6 +256,7 @@ private: - bool isNewWallet() const; - void pendingTxPostProcess(PendingTransactionImpl * pending); - bool doInit(const std::string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit = 0, bool ssl = false); -+ bool checkBackgroundSync(const std::string &message) const; - - private: - friend class PendingTransactionImpl; -@@ -263,6 +271,10 @@ private: - mutable boost::mutex m_statusMutex; - mutable int m_status; - mutable std::string m_errorString; -+ // TODO: harden password handling in the wallet API, see relevant discussion -+ // https://github.com/monero-project/monero-gui/issues/1537 -+ // https://github.com/feather-wallet/feather/issues/72#issuecomment-1405602142 -+ // https://github.com/monero-project/monero/pull/8619#issuecomment-1632951461 - std::string m_password; - std::unique_ptr m_history; - std::unique_ptr m_wallet2Callback; -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 9ea753083..4268b656e 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -445,6 +445,12 @@ struct Wallet - ConnectionStatus_WrongVersion - }; - -+ enum BackgroundSyncType { -+ BackgroundSync_Off = 0, -+ BackgroundSync_ReusePassword = 1, -+ BackgroundSync_CustomPassword = 2 -+ }; -+ - virtual ~Wallet() = 0; - virtual std::string seed(const std::string& seed_offset = "") const = 0; - virtual std::string getSeedLanguage() const = 0; -@@ -940,6 +946,42 @@ struct Wallet - */ - virtual bool scanTransactions(const std::vector &txids) = 0; - -+ /*! -+ * \brief setupBackgroundSync - setup background sync mode with just a view key -+ * \param background_sync_type - the mode the wallet background syncs in -+ * \param wallet_password -+ * \param background_cache_password - custom password to encrypt background cache, only needed for custom password background sync type -+ * \return - true on success -+ */ -+ virtual bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) = 0; -+ -+ /*! -+ * \brief getBackgroundSyncType - get mode the wallet background syncs in -+ * \return - the type, or off if type is unknown -+ */ -+ virtual BackgroundSyncType getBackgroundSyncType() const = 0; -+ -+ /** -+ * @brief startBackgroundSync - sync the chain in the background with just view key -+ */ -+ virtual bool startBackgroundSync() = 0; -+ -+ /** -+ * @brief stopBackgroundSync - bring back spend key and process background synced txs -+ * \param wallet_password -+ */ -+ virtual bool stopBackgroundSync(const std::string &wallet_password) = 0; -+ -+ /** -+ * @brief isBackgroundSyncing - returns true if the wallet is background syncing -+ */ -+ virtual bool isBackgroundSyncing() const = 0; -+ -+ /** -+ * @brief isBackgroundWallet - returns true if the wallet is a background wallet -+ */ -+ virtual bool isBackgroundWallet() const = 0; -+ - virtual TransactionHistory * history() = 0; - virtual AddressBook * addressBook() = 0; - virtual Subaddress * subaddress() = 0; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 9e95f44d6..f2381740a 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -158,6 +158,8 @@ static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; - - static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1"; - -+static const std::string BACKGROUND_WALLET_SUFFIX = ".background"; -+ - boost::mutex tools::wallet2::default_daemon_address_lock; - std::string tools::wallet2::default_daemon_address = ""; - -@@ -1010,14 +1012,14 @@ uint64_t num_priv_multisig_keys_post_setup(uint64_t threshold, uint64_t total) - * @param keys_data_key the chacha key that encrypts wallet keys files - * @return crypto::chacha_key the chacha key that encrypts the wallet cache files - */ --crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key) -+crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key, const unsigned char domain_separator) - { - static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key"); - - crypto::chacha_key cache_key; - epee::mlocked> cache_key_data; - memcpy(cache_key_data.data(), &keys_data_key, HASH_SIZE); -- cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE; -+ cache_key_data[HASH_SIZE] = domain_separator; - cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&) cache_key); - - return cache_key; -@@ -1105,7 +1107,7 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional lock(lockers_lock); - if (lockers++ > 0) - locked = false; -- if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only()) -+ if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only() || w.is_background_syncing()) - { - locked = false; - return; -@@ -1222,6 +1224,11 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - m_ignore_outputs_above(MONEY_SUPPLY), - m_ignore_outputs_below(0), - m_track_uses(false), -+ m_is_background_wallet(false), -+ m_background_sync_type(BackgroundSyncOff), -+ m_background_syncing(false), -+ m_processing_background_cache(false), -+ m_custom_background_key(boost::none), - m_show_wallet_name_when_locked(false), - m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT), - m_setup_background_mining(BackgroundMiningMaybe), -@@ -1877,6 +1884,9 @@ bool has_nonrequested_tx_at_height_or_above_requested(uint64_t height, const std - //---------------------------------------------------------------------------------------------------- - void wallet2::scan_tx(const std::unordered_set &txids) - { -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "cannot scan tx from background wallet"); -+ - // Get the transactions from daemon in batches sorted lowest height to highest - tx_entry_data txs_to_scan = get_tx_entries(txids); - if (txs_to_scan.tx_entries.empty()) -@@ -2184,11 +2194,11 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons - THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); - - // if keys are encrypted, ask for password -- if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k) -+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k && !m_background_syncing) - { - static critical_section password_lock; - CRITICAL_REGION_LOCAL(password_lock); -- if (!m_encrypt_keys_after_refresh) -+ if (!m_encrypt_keys_after_refresh && !m_processing_background_cache) - { - boost::optional pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received"); - THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero")); -@@ -2200,7 +2210,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons - crypto::public_key output_public_key; - THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key"); - -- if (m_multisig) -+ if (m_multisig || m_background_syncing/*no spend key*/) - { - tx_scan_info.in_ephemeral.pub = output_public_key; - tx_scan_info.in_ephemeral.sec = crypto::null_skey; -@@ -2457,6 +2467,22 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error, - "transactions outputs size=" + std::to_string(tx.vout.size()) + - " not match with daemon response size=" + std::to_string(o_indices.size())); -+ -+ // we're going to re-process this receive when background sync is disabled -+ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding received tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } - } - - for(size_t o: outs) -@@ -2482,7 +2508,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - td.m_tx = (const cryptonote::transaction_prefix&)tx; - td.m_txid = txid; - td.m_key_image = tx_scan_info[o].ki; -- td.m_key_image_known = !m_watch_only && !m_multisig; -+ td.m_key_image_known = !m_watch_only && !m_multisig && !m_background_syncing; - if (!td.m_key_image_known) - { - // we might have cold signed, and have a mapping to key images -@@ -2672,10 +2698,25 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - 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); -+ -+ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } - } - } - -- if (!pool && m_track_uses) -+ if (!pool && (m_track_uses || (m_background_syncing && it == m_key_images.end()))) - { - PERF_TIMER(track_uses); - const uint64_t amount = in_to_key.amount; -@@ -2689,7 +2730,27 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - { - size_t idx = i->second; - THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Output tracker cache index out of range"); -- m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); -+ -+ if (m_track_uses) -+ m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); -+ -+ // We'll re-process all txs which *might* be spends when we disable -+ // background sync and retrieve the spend key. We don't know if an -+ // output is a spend in this tx if we don't know its key image. -+ if (m_background_syncing && !m_transfers[idx].m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } - } - } - } -@@ -2699,7 +2760,24 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - continue; - for (uint64_t offset: offsets) - if (offset == td.m_global_output_index) -- td.m_uses.push_back(std::make_pair(height, txid)); -+ { -+ if (m_track_uses) -+ td.m_uses.push_back(std::make_pair(height, txid)); -+ if (m_background_syncing && !td.m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } -+ } - } - } - } -@@ -3072,8 +3150,8 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh - req.start_height = start_height; - req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; - -- req.requested_info = first ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; -- if (try_incremental) -+ req.requested_info = (first && !m_background_syncing) ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; -+ if (try_incremental && !m_background_syncing) - req.pool_info_since = m_pool_info_query_time; - - { -@@ -3100,7 +3178,7 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh - << ", height " << blocks_start_height + blocks.size() << ", node height " << res.current_height - << ", pool info " << static_cast(res.pool_info_extent)); - -- if (first) -+ if (first && !m_background_syncing) - { - if (res.pool_info_extent != COMMAND_RPC_GET_BLOCKS_FAST::NONE) - { -@@ -3612,6 +3690,9 @@ void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash - // incremental update anymore, because with that we might miss some txs altogether. - void wallet2::update_pool_state(std::vector> &process_txs, bool refreshed, bool try_incremental) - { -+ process_txs.clear(); -+ if (m_background_syncing) -+ return; - bool updated = false; - if (m_pool_info_query_time != 0 && try_incremental) - { -@@ -4183,6 +4264,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo - } - - m_first_refresh_done = true; -+ if (m_background_syncing || m_is_background_wallet) -+ m_background_sync_data.first_refresh_done = true; - - LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all(false)) << ", unlocked: " << print_money(unlocked_balance_all(false))); - } -@@ -4268,6 +4351,14 @@ wallet2::detached_blockchain_data wallet2::detach_blockchain(uint64_t height, st - td.m_uses.pop_back(); - } - -+ for (auto it = m_background_sync_data.txs.begin(); it != m_background_sync_data.txs.end(); ) -+ { -+ if(height <= it->second.height) -+ it = m_background_sync_data.txs.erase(it); -+ else -+ ++it; -+ } -+ - if (output_tracker_cache) - output_tracker_cache->clear(); - -@@ -4342,8 +4433,12 @@ void wallet2::handle_reorg(uint64_t height, std::map m_blockchain.offset(), - error::wallet_internal_error, "Daemon claims reorg below last checkpoint"); -+ - detached_blockchain_data dbd = detach_blockchain(height, output_tracker_cache); - -+ if (m_background_syncing && height < m_background_sync_data.start_height) -+ m_background_sync_data.start_height = height; -+ - if (m_callback) - m_callback->on_reorg(height, dbd.detached_blockchain.size(), dbd.detached_tx_hashes.size()); - } -@@ -4353,6 +4448,7 @@ bool wallet2::deinit() - if(m_is_initialized) { - m_is_initialized = false; - unlock_keys_file(); -+ unlock_background_keys_file(); - m_account.deinit(); - } - return true; -@@ -4379,6 +4475,7 @@ bool wallet2::clear() - m_device_last_key_image_sync = 0; - m_pool_info_query_time = 0; - m_skip_to_height = 0; -+ m_background_sync_data = background_sync_data_t{}; - return true; - } - //---------------------------------------------------------------------------------------------------- -@@ -4397,13 +4494,30 @@ void wallet2::clear_soft(bool keep_key_images) - m_scanned_pool_txs[1].clear(); - m_pool_info_query_time = 0; - m_skip_to_height = 0; -+ m_background_sync_data = background_sync_data_t{}; - - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); - } -- -+//---------------------------------------------------------------------------------------------------- -+void wallet2::clear_user_data() -+{ -+ for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) -+ i->second.m_dests.clear(); -+ for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) -+ i->second.m_dests.clear(); -+ for (auto i = m_transfers.begin(); i != m_transfers.end(); ++i) -+ i->m_frozen = false; -+ m_tx_keys.clear(); -+ m_tx_notes.clear(); -+ m_address_book.clear(); -+ m_subaddress_labels.clear(); -+ m_attributes.clear(); -+ m_account_tags = std::pair, std::vector>(); -+} -+//---------------------------------------------------------------------------------------------------- - /*! - * \brief Stores wallet information to wallet file. - * \param keys_file_name Name of wallet file -@@ -4415,16 +4529,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable - { - boost::optional keys_file_data = get_keys_file_data(password, watch_only); - CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); -- -+ return store_keys_file_data(keys_file_name, keys_file_data.get()); -+} -+//---------------------------------------------------------------------------------------------------- -+bool wallet2::store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only, bool background_keys_file) -+{ -+ boost::optional keys_file_data = get_keys_file_data(key, watch_only, background_keys_file); -+ CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); -+ return store_keys_file_data(keys_file_name, keys_file_data.get(), background_keys_file); -+} -+//---------------------------------------------------------------------------------------------------- -+bool wallet2::store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file) -+{ - std::string tmp_file_name = keys_file_name + ".new"; - std::string buf; -- bool r = ::serialization::dump_binary(keys_file_data.get(), buf); -+ bool r = ::serialization::dump_binary(keys_file_data, buf); - r = r && save_to_file(tmp_file_name, buf); - CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name); - -- unlock_keys_file(); -+ if (!background_keys_file) -+ unlock_keys_file(); -+ else -+ unlock_background_keys_file(); -+ - std::error_code e = tools::replace_file(tmp_file_name, keys_file_name); -- lock_keys_file(); -+ -+ if (!background_keys_file) -+ lock_keys_file(); -+ else -+ lock_background_keys_file(keys_file_name); - - if (e) { - boost::filesystem::remove(tmp_file_name); -@@ -4436,26 +4569,27 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable - } - //---------------------------------------------------------------------------------------------------- - boost::optional wallet2::get_keys_file_data(const epee::wipeable_string& password, bool watch_only) -+{ -+ crypto::chacha_key key; -+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); -+ verify_password_with_cached_key(key); -+ return get_keys_file_data(key, watch_only); -+} -+//---------------------------------------------------------------------------------------------------- -+boost::optional wallet2::get_keys_file_data(const crypto::chacha_key& key, bool watch_only, bool background_keys_file) - { - epee::byte_slice account_data; - std::string multisig_signers; - std::string multisig_derivations; - cryptonote::account_base account = m_account; - -- crypto::chacha_key key; -- crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); -- -- // We use m_cache_key as a deterministic test to see if given key corresponds to original password -- const crypto::chacha_key cache_key = derive_cache_key(key); -- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); -- - if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) - { - account.encrypt_viewkey(key); - account.decrypt_keys(key); - } - -- if (watch_only) -+ if (watch_only || background_keys_file) - account.forget_spend_key(); - - account.encrypt_keys(key); -@@ -4590,6 +4724,9 @@ boost::optional wallet2::get_keys_file_data(const epee: - value2.SetInt(m_track_uses ? 1 : 0); - json.AddMember("track_uses", value2, json.GetAllocator()); - -+ value2.SetInt(m_background_sync_type); -+ json.AddMember("background_sync_type", value2, json.GetAllocator()); -+ - value2.SetInt(m_show_wallet_name_when_locked ? 1 : 0); - json.AddMember("show_wallet_name_when_locked", value2, json.GetAllocator()); - -@@ -4649,6 +4786,11 @@ boost::optional wallet2::get_keys_file_data(const epee: - - 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(m_custom_background_key.get().data()), m_custom_background_key.get().size()); -+ json.AddMember("custom_background_key", value, json.GetAllocator()); -+ } - - // Serialize the JSON object - rapidjson::StringBuffer buffer; -@@ -4676,13 +4818,81 @@ void wallet2::setup_keys(const epee::wipeable_string &password) - m_account.decrypt_viewkey(key); - } - -- m_cache_key = derive_cache_key(key); -+ m_cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); - - get_ringdb_key(); - } - //---------------------------------------------------------------------------------------------------- -+void validate_background_cache_password_usage(const tools::wallet2::BackgroundSyncType background_sync_type, const boost::optional &background_cache_password, const bool multisig, const bool watch_only, const bool key_on_device) -+{ -+ THROW_WALLET_EXCEPTION_IF(multisig || watch_only || key_on_device, error::wallet_internal_error, multisig -+ ? "Background sync not implemented for multisig wallets" : watch_only -+ ? "Background sync not implemented for view only wallets" -+ : "Background sync not implemented for HW wallets"); -+ -+ switch (background_sync_type) -+ { -+ case tools::wallet2::BackgroundSyncOff: -+ { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "background sync is not enabled"); -+ break; -+ } -+ case tools::wallet2::BackgroundSyncReusePassword: -+ { -+ THROW_WALLET_EXCEPTION_IF(background_cache_password, error::wallet_internal_error, -+ "unexpected custom background cache password"); -+ break; -+ } -+ case tools::wallet2::BackgroundSyncCustomPassword: -+ { -+ THROW_WALLET_EXCEPTION_IF(!background_cache_password, error::wallet_internal_error, -+ "expected custom background cache password"); -+ break; -+ } -+ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); -+ } -+} -+//---------------------------------------------------------------------------------------------------- -+void get_custom_background_key(const epee::wipeable_string &password, crypto::chacha_key &custom_background_key, const uint64_t kdf_rounds) -+{ -+ crypto::chacha_key key; -+ crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds); -+ custom_background_key = derive_cache_key(key, config::HASH_KEY_BACKGROUND_KEYS_FILE); -+} -+//---------------------------------------------------------------------------------------------------- -+const crypto::chacha_key wallet2::get_cache_key() -+{ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing) -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ // Domain separate keys used to encrypt background keys file and cache -+ return derive_cache_key(m_custom_background_key.get(), config::HASH_KEY_BACKGROUND_CACHE); -+ } -+ else -+ { -+ return m_cache_key; -+ } -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::verify_password_with_cached_key(const epee::wipeable_string &password) -+{ -+ crypto::chacha_key key; -+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); -+ verify_password_with_cached_key(key); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::verify_password_with_cached_key(const crypto::chacha_key &key) -+{ -+ // We use m_cache_key as a deterministic test to see if given key corresponds to original password -+ const crypto::chacha_key cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); -+ THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); -+} -+//---------------------------------------------------------------------------------------------------- - void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password) - { -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "cannot change password from background wallet"); -+ - if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) - decrypt_keys(original_password); - setup_keys(new_password); -@@ -4741,8 +4951,24 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - std::string account_data; - account_data.resize(keys_file_data.account_data.size()); - crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) -+ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); -+ if (try_v0_format) - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -+ -+ // Check if it's a background keys file if both of the above formats fail -+ { -+ m_is_background_wallet = false; -+ m_background_syncing = false; -+ cryptonote::account_base account_data_check; -+ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) -+ { -+ get_custom_background_key(password, key, m_kdf_rounds); -+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -+ m_is_background_wallet = !json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); -+ m_background_syncing = m_is_background_wallet; // start a background wallet background syncing -+ } -+ } -+ - // The contents should be JSON if the wallet follows the new format. - if (json.Parse(account_data.c_str()).HasParseError()) - { -@@ -4780,6 +5006,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_ignore_outputs_above = MONEY_SUPPLY; - m_ignore_outputs_below = 0; - m_track_uses = false; -+ m_background_sync_type = BackgroundSyncOff; - m_show_wallet_name_when_locked = false; - m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT; - m_setup_background_mining = BackgroundMiningMaybe; -@@ -4798,6 +5025,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_enable_multisig = false; - m_allow_mismatched_daemon_version = false; - m_polyseed = false; -+ m_custom_background_key = boost::none; - } - else if(json.IsObject()) - { -@@ -5036,6 +5264,39 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_enable_multisig = field_enable_multisig; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false); - m_polyseed = field_polyseed; -+ -+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff); -+ m_background_sync_type = field_background_sync_type; -+ -+ // Load encryption key used to encrypt background cache -+ crypto::chacha_key custom_background_key; -+ m_custom_background_key = boost::none; -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_is_background_wallet) -+ { -+ if (!json.HasMember("custom_background_key")) -+ { -+ LOG_ERROR("Field custom_background_key not found in JSON"); -+ return false; -+ } -+ else if (!json["custom_background_key"].IsString()) -+ { -+ LOG_ERROR("Field custom_background_key found in JSON, but not String"); -+ return false; -+ } -+ else if (json["custom_background_key"].GetStringLength() != sizeof(crypto::chacha_key)) -+ { -+ LOG_ERROR("Field custom_background_key found in JSON, but not correct length"); -+ return false; -+ } -+ const char *field_custom_background_key = json["custom_background_key"].GetString(); -+ memcpy(custom_background_key.data(), field_custom_background_key, sizeof(crypto::chacha_key)); -+ m_custom_background_key = boost::optional(custom_background_key); -+ LOG_PRINT_L1("Loaded custom background key derived from custom password"); -+ } -+ else if (json.HasMember("custom_background_key")) -+ { -+ LOG_ERROR("Unexpected field custom_background_key found in JSON"); -+ } - } - else - { -@@ -5099,12 +5360,17 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - const cryptonote::account_keys& keys = m_account.get_keys(); - hw::device &hwdev = m_account.get_device(); - r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); -- if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) -+ if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD && !m_is_background_wallet) - r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); - - if (r) -- setup_keys(password); -+ { -+ if (!m_is_background_wallet) -+ setup_keys(password); -+ else -+ m_custom_background_key = boost::optional(key); -+ } - - return true; - } -@@ -5119,11 +5385,12 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password - * - */ --bool wallet2::verify_password(const epee::wipeable_string& password) -+bool wallet2::verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out) - { - // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). - unlock_keys_file(); -- bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds); -+ const bool no_spend_key = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; -+ bool r = verify_password(m_keys_file, password, no_spend_key, m_account.get_device(), m_kdf_rounds, spend_key_out); - lock_keys_file(); - return r; - } -@@ -5141,7 +5408,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) - * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password - * - */ --bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) -+bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out) - { - rapidjson::Document json; - wallet2::keys_file_data keys_file_data; -@@ -5158,9 +5425,22 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip - std::string account_data; - account_data.resize(keys_file_data.account_data.size()); - crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) -+ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); -+ if (try_v0_format) - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); - -+ // Check if it's a background keys file if both of the above formats fail -+ { -+ cryptonote::account_base account_data_check; -+ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) -+ { -+ get_custom_background_key(password, key, kdf_rounds); -+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -+ const bool is_background_wallet = json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); -+ no_spend_key = no_spend_key || is_background_wallet; -+ } -+ } -+ - // The contents should be JSON if the wallet follows the new format. - if (json.Parse(account_data.c_str()).HasParseError()) - { -@@ -5185,6 +5465,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip - r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); - if(!no_spend_key) - r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); -+ spend_key_out = (!no_spend_key && r) ? keys.m_spend_secret_key : crypto::null_skey; - return r; - } - -@@ -5196,9 +5477,7 @@ void wallet2::encrypt_keys(const crypto::chacha_key &key) - - void wallet2::decrypt_keys(const crypto::chacha_key &key) - { -- // We use m_cache_key as a deterministic test to see if given key corresponds to original password -- const crypto::chacha_key cache_key = derive_cache_key(key); -- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); -+ verify_password_with_cached_key(key); - - m_account.encrypt_viewkey(key); - m_account.decrypt_keys(key); -@@ -5916,11 +6195,30 @@ void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_strin - { - if (wallet_name.empty()) - return; -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "cannot change wallet settings from background wallet"); - prepare_file_names(wallet_name); - boost::system::error_code ignored_ec; - THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file); - bool r = store_keys(m_keys_file, password, m_watch_only); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); -+ -+ // Update the background keys file when we rewrite the main wallet keys file -+ if (m_background_sync_type == BackgroundSyncCustomPassword && m_custom_background_key) -+ { -+ const std::string background_keys_filename = make_background_keys_file_name(wallet_name); -+ if (!lock_background_keys_file(background_keys_filename)) -+ { -+ LOG_ERROR("Background keys file " << background_keys_filename << " is opened by another wallet program and cannot be rewritten"); -+ return; // not fatal, background keys file will just have different wallet settings -+ } -+ store_background_keys(m_custom_background_key.get()); -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); -+ } -+ else if (m_background_sync_type == BackgroundSyncReusePassword) -+ { -+ reset_background_sync_data(m_background_sync_data); -+ } - } - /*! - * \brief Writes to a file named based on the normal wallet (doesn't generate key, assumes it's already there) -@@ -5954,6 +6252,16 @@ bool wallet2::wallet_valid_path_format(const std::string& file_path) - return !file_path.empty(); - } - //---------------------------------------------------------------------------------------------------- -+std::string wallet2::make_background_wallet_file_name(const std::string &wallet_file) -+{ -+ return wallet_file + BACKGROUND_WALLET_SUFFIX; -+} -+//---------------------------------------------------------------------------------------------------- -+std::string wallet2::make_background_keys_file_name(const std::string &wallet_file) -+{ -+ return make_background_wallet_file_name(wallet_file) + ".keys"; -+} -+//---------------------------------------------------------------------------------------------------- - bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) - { - cryptonote::blobdata payment_id_data; -@@ -6189,10 +6497,81 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, "failed to load keys from buffer"); - } - -- wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password); -+ wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_is_background_wallet, password); - - //keys loaded ok! - //try to load wallet cache. but even if we failed, it is not big problem -+ load_wallet_cache(use_fs, cache_buf); -+ -+ if (!m_persistent_rpc_client_id) -+ set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); -+ -+ // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. -+ // Here we erase these multisig keys if they're zero'd out to free up space. -+ for (auto &td : m_transfers) -+ { -+ auto mk_it = td.m_multisig_k.begin(); -+ while (mk_it != td.m_multisig_k.end()) -+ { -+ if (*mk_it == rct::zero()) -+ mk_it = td.m_multisig_k.erase(mk_it); -+ else -+ ++mk_it; -+ } -+ } -+ -+ cryptonote::block genesis; -+ generate_genesis(genesis); -+ crypto::hash genesis_hash = get_block_hash(genesis); -+ -+ if (m_blockchain.empty()) -+ { -+ m_blockchain.push_back(genesis_hash); -+ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); -+ } -+ else -+ { -+ check_genesis(genesis_hash); -+ } -+ -+ trim_hashchain(); -+ -+ if (get_num_subaddress_accounts() == 0) -+ add_subaddress_account(tr("Primary account")); -+ -+ try -+ { -+ find_and_save_rings(false); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to save rings, will try again next time"); -+ } -+ -+ try -+ { -+ if (use_fs) -+ m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to initialize MMS, it will be unusable"); -+ } -+ -+ try -+ { -+ if (use_fs) -+ process_background_cache_on_open(); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to process background cache on open: " << e.what()); -+ } -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::load_wallet_cache(const bool use_fs, const std::string& cache_buf) -+{ -+ boost::system::error_code e; - bool cache_missing = use_fs ? (!boost::filesystem::exists(m_wallet_file, e) || e) : cache_buf.empty(); - if (cache_missing) - { -@@ -6206,7 +6585,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - bool r = true; - if (use_fs) - { -- load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); -+ r = load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); - THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); - } - -@@ -6219,7 +6598,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"'); - std::string cache_data; - cache_data.resize(cache_file_data.cache_data.size()); -- crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]); -+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), get_cache_key(), cache_file_data.iv, &cache_data[0]); - - try { - bool loaded = false; -@@ -6309,60 +6688,76 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key, - error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); - } -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::process_background_cache_on_open() -+{ -+ if (m_wallet_file.empty()) -+ return; -+ if (m_background_syncing || m_is_background_wallet) -+ return; -+ if (m_background_sync_type == BackgroundSyncOff) -+ return; - -- if (!m_persistent_rpc_client_id) -- set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); -+ if (m_background_sync_type == BackgroundSyncReusePassword) -+ { -+ const background_sync_data_t background_sync_data = m_background_sync_data; -+ const hashchain blockchain = m_blockchain; -+ process_background_cache(background_sync_data, blockchain, m_last_block_reward); - -- // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. -- // Here we erase these multisig keys if they're zero'd out to free up space. -- for (auto &td : m_transfers) -+ // Reset the background cache after processing -+ reset_background_sync_data(m_background_sync_data); -+ } -+ else if (m_background_sync_type == BackgroundSyncCustomPassword) - { -- auto mk_it = td.m_multisig_k.begin(); -- while (mk_it != td.m_multisig_k.end()) -+ // If the background wallet files don't exist, recreate them -+ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); -+ const std::string background_wallet_file = make_background_wallet_file_name(m_wallet_file); -+ const bool background_keys_file_exists = boost::filesystem::exists(background_keys_file); -+ const bool background_wallet_exists = boost::filesystem::exists(background_wallet_file); -+ -+ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_keys_file), error::background_wallet_already_open, background_wallet_file); -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ -+ if (!background_keys_file_exists) - { -- if (*mk_it == rct::zero()) -- mk_it = td.m_multisig_k.erase(mk_it); -- else -- ++mk_it; -+ MDEBUG("Background keys file not found, restoring"); -+ store_background_keys(m_custom_background_key.get()); - } -- } - -- cryptonote::block genesis; -- generate_genesis(genesis); -- crypto::hash genesis_hash = get_block_hash(genesis); -+ if (!background_wallet_exists) -+ { -+ MDEBUG("Background cache not found, restoring"); -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); -+ return; -+ } - -- if (m_blockchain.empty()) -- { -- m_blockchain.push_back(genesis_hash); -- m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); -- } -- else -- { -- check_genesis(genesis_hash); -- } -+ MDEBUG("Loading background cache"); - -- trim_hashchain(); -+ // Set up a minimal background wallet2 instance -+ std::unique_ptr background_w2(new wallet2(m_nettype)); -+ background_w2->m_is_background_wallet = true; -+ background_w2->m_background_syncing = true; -+ background_w2->m_background_sync_type = m_background_sync_type; -+ background_w2->m_custom_background_key = m_custom_background_key; - -- if (get_num_subaddress_accounts() == 0) -- add_subaddress_account(tr("Primary account")); -+ cryptonote::account_base account = m_account; -+ account.forget_spend_key(); -+ background_w2->m_account = account; - -- try -- { -- find_and_save_rings(false); -- } -- catch (const std::exception &e) -- { -- MERROR("Failed to save rings, will try again next time"); -- } -- -- try -- { -- if (use_fs) -- m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); -+ // Load background cache from file -+ background_w2->clear(); -+ background_w2->prepare_file_names(background_wallet_file); -+ background_w2->load_wallet_cache(true/*use_fs*/); -+ -+ process_background_cache(background_w2->m_background_sync_data, background_w2->m_blockchain, background_w2->m_last_block_reward); -+ -+ // Reset the background cache after processing -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); - } -- catch (const std::exception &e) -+ else - { -- MERROR("Failed to initialize MMS, it will be unusable"); -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); - } - } - //---------------------------------------------------------------------------------------------------- -@@ -6444,6 +6839,8 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas - same_file = canonical_old_path == canonical_new_path; - } - -+ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet && !same_file, error::wallet_internal_error, -+ "Cannot save background wallet files to a different location"); - - if (!same_file) - { -@@ -6460,6 +6857,21 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas - } - } - } -+ else if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing && !m_is_background_wallet) -+ { -+ // We're background syncing, so store the wallet cache as a background cache -+ // keeping the background sync data -+ try -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ store_background_cache(m_custom_background_key.get(), false/*do_reset_background_sync_data*/); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to store background cache while background syncing: " << e.what()); -+ } -+ return; -+ } - - // get wallet cache data - boost::optional cache_file_data = get_cache_file_data(); -@@ -6553,6 +6965,22 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas - // store should only exist if the MMS is really active - m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file); - } -+ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_background_syncing && !m_is_background_wallet) -+ { -+ // Update the background wallet cache when we store the main wallet cache -+ // Note: if background syncing when this is called, it means the background -+ // wallet is open and was already stored above -+ try -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to update background cache: " << e.what()); -+ } -+ } - } - //---------------------------------------------------------------------------------------------------- - boost::optional wallet2::get_cache_file_data() -@@ -6570,7 +6998,7 @@ boost::optional wallet2::get_cache_file_data() - std::string cipher; - cipher.resize(cache_file_data.get().cache_data.size()); - cache_file_data.get().iv = crypto::rand(); -- crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), m_cache_key, cache_file_data.get().iv, &cipher[0]); -+ crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), get_cache_key(), cache_file_data.get().iv, &cipher[0]); - cache_file_data.get().cache_data = cipher; - return cache_file_data; - } -@@ -8646,6 +9074,34 @@ bool wallet2::is_keys_file_locked() const - return m_keys_file_locker->locked(); - } - -+bool wallet2::lock_background_keys_file(const std::string &background_keys_file) -+{ -+ if (background_keys_file.empty() || !boost::filesystem::exists(background_keys_file)) -+ return true; -+ if (m_background_keys_file_locker && m_background_keys_file_locker->locked()) -+ return true; -+ m_background_keys_file_locker.reset(new tools::file_locker(background_keys_file)); -+ return m_background_keys_file_locker->locked(); -+} -+ -+bool wallet2::unlock_background_keys_file() -+{ -+ if (!m_background_keys_file_locker) -+ { -+ MDEBUG("background keys file locker is not set"); -+ return false; -+ } -+ m_background_keys_file_locker.reset(); -+ return true; -+} -+ -+bool wallet2::is_background_keys_file_locked() const -+{ -+ if (!m_background_keys_file_locker) -+ return false; -+ return m_background_keys_file_locker->locked(); -+} -+ - bool wallet2::tx_add_fake_output(std::vector> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set &valid_public_keys_cache) const - { - if (!unlocked) // don't add locked outs -@@ -13978,6 +14434,413 @@ bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool o - return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none); - } - -+/* -+ In background sync mode, we use just the view key when the wallet is scanning -+ to identify all txs where: -+ -+ 1. We received an output. -+ 2. We spent an output. -+ 3. We *may* have spent a received output but we didn't know for sure because -+ the spend key was not loaded while background sync was enabled. -+ -+ When the user is ready to use the spend key again, we call this function to -+ process all those background synced transactions with the spend key loaded, -+ so that we can properly generate key images for the transactions which we -+ we were not able to do so for while background sync was enabled. This allows -+ us to determine *all* receives and spends the user completed while the wallet -+ had background sync enabled. Once this function completes, we can continue -+ scanning from where the background sync left off. -+ -+ Txs of type 3 (txs which we *may* have spent received output(s)) are txs where -+ 1+ rings contain an output that the user received and the wallet does not know -+ the associated key image for that output. We don't know if the user spent in -+ this type of tx or not. This function will generate key images for all outputs -+ we don't know key images for, and then check if those outputs were spent in -+ the txs of type 3. -+ -+ By storing this type of "plausible spend tx" when scanning in background sync -+ mode, we avoid the need to query the daemon with key images when background -+ sync mode is disabled to see if those key images were spent. This would -+ reveal key images to 3rd party nodes for users who don't run their own. -+ Although this is not a perfect solution to avoid revealing key images to a 3rd -+ party node (since tx submission trivially reveals key images to a node), it's -+ probably better than revealing *unused* key images to a 3rd party node, which -+ would enable the 3rd party to deduce that a tx is spending an output at least -+ X old when the key image is included in the chain. -+*/ -+void wallet2::process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_synced_chain, uint64_t last_block_reward) -+{ -+ // We expect the spend key to be in a decrypted state while -+ // m_processing_background_cache is true -+ m_processing_background_cache = true; -+ auto done_processing = epee::misc_utils::create_scope_leave_handler([&, this]() { -+ m_processing_background_cache = false; -+ }); -+ -+ if (m_background_syncing || m_multisig || m_watch_only || key_on_device()) -+ return; -+ -+ if (!background_sync_data.first_refresh_done) -+ { -+ MDEBUG("Skipping processing background cache, background cache has not synced yet"); -+ return; -+ } -+ -+ // Skip processing if wallet cache is synced higher than background cache -+ const uint64_t current_height = m_blockchain.size(); -+ const uint64_t background_height = background_synced_chain.size(); -+ MDEBUG("Background cache height " << background_height << " , wallet height " << current_height); -+ if (current_height > background_height) -+ { -+ MWARNING("Skipping processing background cache, synced height is higher than background cache"); -+ return; -+ } -+ -+ if (m_refresh_from_block_height < background_sync_data.wallet_refresh_from_block_height || -+ m_subaddress_lookahead_major > background_sync_data.subaddress_lookahead_major || -+ m_subaddress_lookahead_minor > background_sync_data.subaddress_lookahead_minor || -+ m_refresh_type < background_sync_data.wallet_refresh_type) -+ { -+ MWARNING("Skipping processing background cache, background wallet sync settings did not match main wallet's"); -+ MDEBUG("Wallet settings: " << -+ ", m_refresh_from_block_height: " << m_refresh_from_block_height << " vs " << background_sync_data.wallet_refresh_from_block_height << -+ ", m_subaddress_lookahead_major: " << m_subaddress_lookahead_major << " vs " << background_sync_data.subaddress_lookahead_major << -+ ", m_subaddress_lookahead_minor: " << m_subaddress_lookahead_minor << " vs " << background_sync_data.subaddress_lookahead_minor << -+ ", m_refresh_type: " << m_refresh_type << " vs " << background_sync_data.wallet_refresh_type); -+ return; -+ } -+ -+ // Sort background synced txs in the order they appeared in the cache so that -+ // we process them in the order they appeared in the chain. Thus if tx2 spends -+ // from tx1, we will know because tx1 is processed before tx2. -+ std::vector> sorted_bgs_cache(background_sync_data.txs.begin(), background_sync_data.txs.end()); -+ std::sort(sorted_bgs_cache.begin(), sorted_bgs_cache.end(), -+ [](const std::pair& l, const std::pair& r) -+ { -+ uint64_t left_index = l.second.index_in_background_sync_data; -+ uint64_t right_index = r.second.index_in_background_sync_data; -+ THROW_WALLET_EXCEPTION_IF( -+ (left_index < right_index && l.second.height > r.second.height) || -+ (left_index > right_index && l.second.height < r.second.height), -+ error::wallet_internal_error, "Unexpected background sync data order"); -+ return left_index < right_index; -+ }); -+ -+ // All txs in the background cache should have height >= sync start height, -+ // but not fatal if not -+ if (!sorted_bgs_cache.empty() && sorted_bgs_cache[0].second.height < background_sync_data.start_height) -+ MWARNING("First tx in background cache has height (" << sorted_bgs_cache[0].second.height << ") lower than sync start height (" << background_sync_data.start_height << ")"); -+ -+ // We want to process all background synced txs in order to make sure -+ // the wallet state updates correctly. First we remove all txs from the wallet -+ // from before the background sync start height, then re-process them in -+ // chronological order. The background cache should contain a superset of -+ // *all* the wallet's txs from after the background sync start height. -+ MDEBUG("Processing " << background_sync_data.txs.size() << " background synced txs starting from height " << background_sync_data.start_height); -+ detached_blockchain_data dbd = detach_blockchain(background_sync_data.start_height); -+ -+ for (const auto &bgs_tx : sorted_bgs_cache) -+ { -+ MDEBUG("Processing background synced tx " << bgs_tx.first); -+ -+ process_new_transaction(bgs_tx.first, bgs_tx.second.tx, bgs_tx.second.output_indices, bgs_tx.second.height, 0, bgs_tx.second.block_timestamp, -+ cryptonote::is_coinbase(bgs_tx.second.tx), false/*pool*/, bgs_tx.second.double_spend_seen, {}, {}, true/*ignore_callbacks*/); -+ -+ // Re-set destination addresses if they were previously set -+ if (m_confirmed_txs.find(bgs_tx.first) != m_confirmed_txs.end() && -+ dbd.detached_confirmed_txs_dests.find(bgs_tx.first) != dbd.detached_confirmed_txs_dests.end()) -+ { -+ m_confirmed_txs[bgs_tx.first].m_dests = std::move(dbd.detached_confirmed_txs_dests[bgs_tx.first]); -+ } -+ } -+ -+ m_blockchain = background_synced_chain; -+ m_last_block_reward = last_block_reward; -+ -+ MDEBUG("Finished processing background sync data"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::reset_background_sync_data(background_sync_data_t &background_sync_data) -+{ -+ background_sync_data.first_refresh_done = false; -+ background_sync_data.start_height = get_blockchain_current_height(); -+ background_sync_data.txs.clear(); -+ -+ background_sync_data.wallet_refresh_from_block_height = m_refresh_from_block_height; -+ background_sync_data.subaddress_lookahead_major = m_subaddress_lookahead_major; -+ background_sync_data.subaddress_lookahead_minor = m_subaddress_lookahead_minor; -+ background_sync_data.wallet_refresh_type = m_refresh_type; -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data) -+{ -+ MDEBUG("Storing background cache (do_reset_background_sync_data=" << do_reset_background_sync_data << ")"); -+ -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, -+ "Can only write a background cache when using a custom background password"); -+ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, -+ "No wallet file known, can't store background cache"); -+ -+ std::unique_ptr background_w2(new wallet2(m_nettype)); -+ background_w2->prepare_file_names(make_background_wallet_file_name(m_wallet_file)); -+ -+ // Make sure background wallet is opened by this wallet -+ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_w2->m_keys_file), -+ error::background_wallet_already_open, background_w2->m_wallet_file); -+ -+ // Load a background wallet2 instance using this wallet2 instance -+ std::string this_wallet2; -+ bool r = ::serialization::dump_binary(*this, this_wallet2); -+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to serialize wallet cache"); -+ -+ background_w2->clear(); -+ r = ::serialization::parse_binary(this_wallet2, *background_w2); -+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to deserialize wallet cache"); -+ -+ // Clear sensitive data from background cache not needed to sync -+ background_w2->clear_user_data(); -+ -+ background_w2->m_is_background_wallet = true; -+ if (do_reset_background_sync_data) -+ reset_background_sync_data(background_w2->m_background_sync_data); -+ else -+ background_w2->m_background_sync_data = m_background_sync_data; -+ background_w2->m_background_syncing = true; -+ -+ background_w2->m_custom_background_key = boost::optional(custom_background_key); -+ background_w2->m_background_sync_type = m_background_sync_type; -+ background_w2->store(); -+ -+ MDEBUG("Background cache stored (" << background_w2->m_transfers.size() << " transfers, " << background_w2->m_background_sync_data.txs.size() << " background synced txs)"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::store_background_keys(const crypto::chacha_key &custom_background_key) -+{ -+ MDEBUG("Storing background keys"); -+ -+ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, -+ "No wallet file known, can't store background keys"); -+ -+ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); -+ bool r = store_keys(background_keys_file, custom_background_key, false/*watch_only*/, true/*background_keys_file*/); -+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, background_keys_file); -+ THROW_WALLET_EXCEPTION_IF(!is_background_keys_file_locked(), error::wallet_internal_error, background_keys_file + "\" should be locked"); -+ -+ // GUI uses the address file to differentiate non-mainnet wallets in the UI -+ const std::string background_address_file = make_background_wallet_file_name(m_wallet_file) + ".address.txt"; -+ if (m_nettype != MAINNET && !boost::filesystem::exists(background_address_file)) -+ { -+ r = save_to_file(background_address_file, m_account.get_public_address_str(m_nettype), true); -+ if (!r) MERROR("String with address text not saved"); -+ } -+ -+ MDEBUG("Background keys stored"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password) -+{ -+ MDEBUG("Storing background sync wallet"); -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, -+ "Can only write a background sync wallet when using a custom background password"); -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "Can't write background sync wallet from an existing background cache"); -+ THROW_WALLET_EXCEPTION_IF(wallet_password == background_cache_password, -+ error::background_custom_password_same_as_wallet_password); -+ -+ // Set the background encryption key -+ crypto::chacha_key custom_background_key; -+ get_custom_background_key(background_cache_password, custom_background_key, m_kdf_rounds); -+ -+ // Keep the background encryption key in memory so the main wallet can update -+ // the background cache when it stores the main wallet cache -+ m_custom_background_key = boost::optional(custom_background_key); -+ -+ if (m_wallet_file.empty() || m_keys_file.empty()) -+ return; -+ -+ // Save background keys file, then background cache, then update main wallet settings -+ store_background_keys(custom_background_key); -+ store_background_cache(custom_background_key, true/*do_reset_background_sync_data*/); -+ bool r = store_keys(m_keys_file, wallet_password, false/*watch_only*/); -+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); -+ -+ MDEBUG("Background sync wallet saved successfully"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password) -+{ -+ MDEBUG("Setting background sync to type " << background_sync_type); -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "Can't set background sync type from an existing background cache"); -+ verify_password_with_cached_key(wallet_password); -+ -+ if (background_sync_type != BackgroundSyncOff) -+ validate_background_cache_password_usage(background_sync_type, background_cache_password, m_multisig, m_watch_only, key_on_device()); -+ -+ THROW_WALLET_EXCEPTION_IF(background_sync_type == BackgroundSyncCustomPassword && wallet_password == background_cache_password, -+ error::background_custom_password_same_as_wallet_password); -+ -+ if (m_background_sync_type == background_sync_type && background_sync_type != BackgroundSyncCustomPassword) -+ return; // No need to make any changes -+ -+ if (!m_wallet_file.empty()) -+ { -+ // Delete existing background files if they already exist -+ const std::string old_background_wallet_file = make_background_wallet_file_name(m_wallet_file); -+ const std::string old_background_keys_file = make_background_keys_file_name(m_wallet_file); -+ const std::string old_background_address_file = old_background_wallet_file + ".address.txt"; -+ -+ // Make sure no other program is using the background wallet -+ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(old_background_keys_file), -+ error::background_wallet_already_open, old_background_wallet_file); -+ -+ if (boost::filesystem::exists(old_background_wallet_file)) -+ if (!boost::filesystem::remove(old_background_wallet_file)) -+ LOG_ERROR("Error deleting background wallet file: " << old_background_wallet_file); -+ -+ if (boost::filesystem::exists(old_background_keys_file)) -+ if (!boost::filesystem::remove(old_background_keys_file)) -+ LOG_ERROR("Error deleting background keys file: " << old_background_keys_file); -+ -+ if (boost::filesystem::exists(old_background_address_file)) -+ if (!boost::filesystem::remove(old_background_address_file)) -+ LOG_ERROR("Error deleting background address file: " << old_background_address_file); -+ } -+ -+ m_background_sync_type = background_sync_type; -+ m_custom_background_key = boost::none; -+ -+ // Write the new files -+ switch (background_sync_type) -+ { -+ case BackgroundSyncOff: -+ case BackgroundSyncReusePassword: rewrite(m_wallet_file, wallet_password); break; -+ case BackgroundSyncCustomPassword: write_background_sync_wallet(wallet_password, background_cache_password.get()); break; -+ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); -+ } -+ -+ MDEBUG("Done setting background sync type"); -+} -+//---------------------------------------------------------------------------------------------------- -+/* -+ When background syncing, the wallet scans using just the view key, without -+ keeping the spend key in decrypted state. When a user returns to the wallet -+ and decrypts the spend key, the wallet processes the background synced txs, -+ then the wallet picks up scanning normally right where the background sync -+ left off. -+*/ -+void wallet2::start_background_sync() -+{ -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, -+ "must setup background sync first before using background sync"); -+ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, -+ "Can't start background syncing from a background wallet (it is always background syncing)"); -+ -+ MDEBUG("Starting background sync"); -+ -+ if (m_background_syncing) -+ { -+ MDEBUG("Already background syncing"); -+ return; -+ } -+ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) -+ { -+ // Save the current state of the wallet cache. Only necessary when using a -+ // custom background password which uses distinct background wallet to sync. -+ // When reusing wallet password to sync we reuse the main wallet cache. -+ store(); -+ -+ // Wipe user data from the background wallet cache not needed to sync. -+ // Only wipe user data from background cache if wallet cache is stored -+ // on disk; otherwise we could lose the data. -+ clear_user_data(); -+ -+ // Wipe m_cache_key since it can be used to decrypt main wallet cache -+ m_cache_key.scrub(); -+ } -+ -+ reset_background_sync_data(m_background_sync_data); -+ m_background_syncing = true; -+ -+ // Wipe the spend key from memory -+ m_account.forget_spend_key(); -+ -+ MDEBUG("Background sync started at height " << m_background_sync_data.start_height); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key) -+{ -+ MDEBUG("Stopping background sync"); -+ -+ // Verify provided password and spend secret key. If no spend secret key is -+ // provided, recover it from the wallet keys file -+ crypto::secret_key recovered_spend_key = crypto::null_skey; -+ if (!m_wallet_file.empty()) -+ { -+ THROW_WALLET_EXCEPTION_IF(!verify_password(wallet_password, recovered_spend_key), error::invalid_password); -+ } -+ else -+ { -+ verify_password_with_cached_key(wallet_password); -+ } -+ -+ if (spend_secret_key != crypto::null_skey) -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_wallet_file.empty() && spend_secret_key != recovered_spend_key, -+ error::invalid_spend_key); -+ MDEBUG("Setting spend secret key with the provided key"); -+ recovered_spend_key = spend_secret_key; -+ } -+ -+ // Verify private spend key derives to wallet's public spend key -+ const auto verify_spend_key = [this](crypto::secret_key &recovered_spend_key) -> bool -+ { -+ crypto::public_key spend_public_key; -+ return recovered_spend_key != crypto::null_skey && -+ crypto::secret_key_to_public_key(recovered_spend_key, spend_public_key) && -+ m_account.get_keys().m_account_address.m_spend_public_key == spend_public_key; -+ }; -+ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); -+ -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, -+ "must setup background sync first before using background sync"); -+ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, -+ "Can't stop background syncing from a background wallet"); -+ -+ if (!m_background_syncing) -+ return; -+ -+ // Copy background cache, we're about to overwrite it -+ const background_sync_data_t background_sync_data = m_background_sync_data; -+ const hashchain background_synced_chain = m_blockchain; -+ const uint64_t last_block_reward = m_last_block_reward; -+ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) -+ { -+ // Reload the wallet from disk -+ load(m_wallet_file, wallet_password); -+ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); -+ } -+ m_background_syncing = false; -+ -+ // Set the plaintext spend key -+ m_account.set_spend_key(recovered_spend_key); -+ -+ // Encrypt the spend key when done if needed -+ epee::misc_utils::auto_scope_leave_caller keys_reencryptor; -+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) -+ keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]{encrypt_keys(wallet_password);}); -+ -+ // Now we can use the decrypted spend key to process background cache -+ process_background_cache(background_sync_data, background_synced_chain, last_block_reward); -+ -+ // Reset the background cache after processing -+ reset_background_sync_data(m_background_sync_data); -+ -+ MDEBUG("Background sync stopped"); -+} -+//---------------------------------------------------------------------------------------------------- - wallet2::payment_container wallet2::export_payments() const - { - payment_container payments; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 91ec72e0f..56cc118f4 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -257,6 +257,20 @@ private: - BackgroundMiningNo = 2, - }; - -+ enum BackgroundSyncType { -+ BackgroundSyncOff = 0, -+ BackgroundSyncReusePassword = 1, -+ BackgroundSyncCustomPassword = 2, -+ }; -+ -+ static BackgroundSyncType background_sync_type_from_str(const std::string &background_sync_type_str) -+ { -+ if (background_sync_type_str == "off") return BackgroundSyncOff; -+ if (background_sync_type_str == "reuse-wallet-password") return BackgroundSyncReusePassword; -+ if (background_sync_type_str == "custom-background-password") return BackgroundSyncCustomPassword; -+ throw std::logic_error("Unknown background sync type"); -+ }; -+ - enum ExportFormat { - Binary = 0, - Ascii, -@@ -283,7 +297,12 @@ private: - //! Just parses variables. - static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter); - -- static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); -+ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) -+ { -+ crypto::secret_key spend_key = crypto::null_skey; -+ return verify_password(keys_file_name, password, no_spend_key, hwdev, kdf_rounds, spend_key); -+ }; -+ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out); - static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1); - - wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false, std::unique_ptr http_client_factory = std::unique_ptr(new net::http::client_factory())); -@@ -793,6 +812,54 @@ private: - END_SERIALIZE() - }; - -+ struct background_synced_tx_t -+ { -+ uint64_t index_in_background_sync_data; -+ cryptonote::transaction tx; -+ std::vector output_indices; -+ uint64_t height; -+ uint64_t block_timestamp; -+ bool double_spend_seen; -+ -+ BEGIN_SERIALIZE_OBJECT() -+ VERSION_FIELD(0) -+ VARINT_FIELD(index_in_background_sync_data) -+ -+ // prune tx; don't need to keep signature data -+ if (!tx.serialize_base(ar)) -+ return false; -+ -+ FIELD(output_indices) -+ VARINT_FIELD(height) -+ VARINT_FIELD(block_timestamp) -+ FIELD(double_spend_seen) -+ END_SERIALIZE() -+ }; -+ -+ struct background_sync_data_t -+ { -+ bool first_refresh_done = false; -+ uint64_t start_height = 0; -+ serializable_unordered_map txs; -+ -+ // Relevant wallet settings -+ uint64_t wallet_refresh_from_block_height; -+ size_t subaddress_lookahead_major; -+ size_t subaddress_lookahead_minor; -+ RefreshType wallet_refresh_type; -+ -+ BEGIN_SERIALIZE_OBJECT() -+ VERSION_FIELD(0) -+ FIELD(first_refresh_done) -+ FIELD(start_height) -+ FIELD(txs) -+ FIELD(wallet_refresh_from_block_height) -+ VARINT_FIELD(subaddress_lookahead_major) -+ VARINT_FIELD(subaddress_lookahead_minor) -+ VARINT_FIELD(wallet_refresh_type) -+ END_SERIALIZE() -+ }; -+ - typedef std::tuple get_outs_entry; - - struct parsed_block -@@ -989,7 +1056,8 @@ private: - /*! - * \brief verifies given password is correct for default wallet keys file - */ -- bool verify_password(const epee::wipeable_string& password); -+ bool verify_password(const epee::wipeable_string& password) {crypto::secret_key key = crypto::null_skey; return verify_password(password, key);}; -+ bool verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out); - cryptonote::account_base& get_account(){return m_account;} - const cryptonote::account_base& get_account()const{return m_account;} - -@@ -1093,6 +1161,7 @@ private: - cryptonote::network_type nettype() const { return m_nettype; } - bool watch_only() const { return m_watch_only; } - bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; -+ bool is_background_wallet() const { return m_is_background_wallet; } - bool has_multisig_partial_key_images() const; - bool has_unknown_key_images() const; - bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string()) const; -@@ -1300,11 +1369,17 @@ private: - return; - } - a & m_has_ever_refreshed_from_node; -+ if(ver < 31) -+ { -+ m_background_sync_data = background_sync_data_t{}; -+ return; -+ } -+ a & m_background_sync_data; - } - - BEGIN_SERIALIZE_OBJECT() - MAGIC_FIELD("monero wallet cache") -- VERSION_FIELD(1) -+ VERSION_FIELD(2) - FIELD(m_blockchain) - FIELD(m_transfers) - FIELD(m_account_public_address) -@@ -1336,6 +1411,12 @@ private: - return true; - } - FIELD(m_has_ever_refreshed_from_node) -+ if (version < 2) -+ { -+ m_background_sync_data = background_sync_data_t{}; -+ return true; -+ } -+ FIELD(m_background_sync_data) - END_SERIALIZE() - - /*! -@@ -1351,6 +1432,8 @@ private: - * \return Whether path is valid format - */ - static bool wallet_valid_path_format(const std::string& file_path); -+ static std::string make_background_wallet_file_name(const std::string &wallet_file); -+ static std::string make_background_keys_file_name(const std::string &wallet_file); - static bool parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); - static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id); - static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); -@@ -1399,6 +1482,9 @@ private: - void ignore_outputs_below(uint64_t value) { m_ignore_outputs_below = value; } - bool track_uses() const { return m_track_uses; } - void track_uses(bool value) { m_track_uses = value; } -+ BackgroundSyncType background_sync_type() const { return m_background_sync_type; } -+ void setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password); -+ bool is_background_syncing() const { return m_background_syncing; } - bool show_wallet_name_when_locked() const { return m_show_wallet_name_when_locked; } - void show_wallet_name_when_locked(bool value) { m_show_wallet_name_when_locked = value; } - BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; } -@@ -1714,6 +1800,9 @@ private: - uint64_t get_bytes_sent() const; - uint64_t get_bytes_received() const; - -+ void start_background_sync(); -+ void stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key = crypto::null_skey); -+ - // MMS ------------------------------------------------------------------------------------------------- - mms::message_store& get_message_store() { return m_message_store; }; - const mms::message_store& get_message_store() const { return m_message_store; }; -@@ -1749,6 +1838,9 @@ private: - * \return Whether it was successful. - */ - bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false); -+ bool store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); -+ boost::optional get_keys_file_data(const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); -+ bool store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file = false); - /*! - * \brief Load wallet keys information from wallet file. - * \param keys_file_name Name of wallet file -@@ -1762,6 +1854,7 @@ private: - */ - bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password); - bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional& keys_to_encrypt); -+ 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 &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, 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, size_t tx_cache_data_offset, std::map, size_t> *output_tracker_cache = NULL); -@@ -1770,6 +1863,15 @@ private: - void get_short_chain_history(std::list& ids, uint64_t granularity = 1) const; - bool clear(); - void clear_soft(bool keep_key_images=false); -+ /* -+ * clear_user_data clears data created by the user, which is mostly data -+ * that a view key cannot identify on chain. This function was initially -+ * added to ensure that a "background" wallet (a wallet that syncs with just -+ * a view key hot in memory) does not have any sensitive data loaded that it -+ * does not need in order to sync. Future devs should take care to ensure -+ * that this function deletes data that is not useful for background syncing -+ */ -+ void clear_user_data(); - void pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, uint64_t ¤t_height); - void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); - void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); -@@ -1821,10 +1923,23 @@ private: - bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector &outs); - crypto::chacha_key get_ringdb_key(); - void setup_keys(const epee::wipeable_string &password); -+ const crypto::chacha_key get_cache_key(); -+ void verify_password_with_cached_key(const epee::wipeable_string &password); -+ void verify_password_with_cached_key(const crypto::chacha_key &key); - size_t get_transfer_details(const crypto::key_image &ki) const; - tx_entry_data get_tx_entries(const std::unordered_set &txids); - void sort_scan_tx_entries(std::vector &unsorted_tx_entries); - void process_scan_txs(const tx_entry_data &txs_to_scan, const tx_entry_data &txs_to_reprocess, const std::unordered_set &tx_hashes_to_reprocess, detached_blockchain_data &dbd); -+ void write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password); -+ void process_background_cache_on_open(); -+ void process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_chain, uint64_t last_block_reward); -+ void reset_background_sync_data(background_sync_data_t &background_sync_data); -+ void store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data = true); -+ void store_background_keys(const crypto::chacha_key &custom_background_key); -+ -+ bool lock_background_keys_file(const std::string &background_keys_file); -+ bool unlock_background_keys_file(); -+ bool is_background_keys_file_locked() const; - - void register_devices(); - hw::device& lookup_device(const std::string & device_descriptor); -@@ -1940,6 +2055,8 @@ private: - uint64_t m_ignore_outputs_above; - uint64_t m_ignore_outputs_below; - bool m_track_uses; -+ bool m_is_background_wallet; -+ BackgroundSyncType m_background_sync_type; - bool m_show_wallet_name_when_locked; - uint32_t m_inactivity_lock_timeout; - BackgroundMiningSetupType m_setup_background_mining; -@@ -1985,6 +2102,7 @@ private: - - uint64_t m_last_block_reward; - std::unique_ptr m_keys_file_locker; -+ std::unique_ptr m_background_keys_file_locker; - - mms::message_store m_message_store; - bool m_original_keys_available; -@@ -1992,6 +2110,7 @@ private: - crypto::secret_key m_original_view_secret_key; - - crypto::chacha_key m_cache_key; -+ boost::optional m_custom_background_key = boost::none; - std::shared_ptr m_encrypt_keys_after_refresh; - - bool m_unattended; -@@ -2007,9 +2126,13 @@ private: - - static boost::mutex default_daemon_address_lock; - static std::string default_daemon_address; -+ -+ bool m_background_syncing; -+ bool m_processing_background_cache; -+ background_sync_data_t m_background_sync_data; - }; - } --BOOST_CLASS_VERSION(tools::wallet2, 30) -+BOOST_CLASS_VERSION(tools::wallet2, 31) - BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) - BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) - BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) -@@ -2025,6 +2148,8 @@ BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1) - BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4) - BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) - BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 1) -+BOOST_CLASS_VERSION(tools::wallet2::background_synced_tx_t, 0) -+BOOST_CLASS_VERSION(tools::wallet2::background_sync_data_t, 0) - - namespace boost - { -@@ -2523,6 +2648,29 @@ namespace boost - return; - a & x.multisig_sigs; - } -+ -+ template -+ inline void serialize(Archive& a, tools::wallet2::background_synced_tx_t &x, const boost::serialization::version_type ver) -+ { -+ a & x.index_in_background_sync_data; -+ a & x.tx; -+ a & x.output_indices; -+ a & x.height; -+ a & x.block_timestamp; -+ a & x.double_spend_seen; -+ } -+ -+ template -+ inline void serialize(Archive& a, tools::wallet2::background_sync_data_t &x, const boost::serialization::version_type ver) -+ { -+ a & x.first_refresh_done; -+ a & x.start_height; -+ a & x.txs.parent(); -+ a & x.wallet_refresh_from_block_height; -+ a & x.subaddress_lookahead_major; -+ a & x.subaddress_lookahead_minor; -+ a & x.wallet_refresh_type; -+ } - } - } - -diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h -index c077313d4..c54cd3499 100644 ---- a/src/wallet/wallet_errors.h -+++ b/src/wallet/wallet_errors.h -@@ -63,6 +63,7 @@ namespace tools - // invalid_password - // invalid_priority - // invalid_multisig_seed -+ // invalid_spend_key - // refresh_error * - // acc_outs_lookup_error - // block_parse_error -@@ -97,6 +98,9 @@ namespace tools - // wallet_files_doesnt_correspond - // scan_tx_error * - // wont_reprocess_recent_txs_via_untrusted_daemon -+ // background_sync_error * -+ // background_wallet_already_open -+ // background_custom_password_same_as_wallet_password - // - // * - class with protected ctor - -@@ -304,6 +308,16 @@ namespace tools - std::string to_string() const { return wallet_logic_error::to_string(); } - }; - -+ struct invalid_spend_key : public wallet_logic_error -+ { -+ explicit invalid_spend_key(std::string&& loc) -+ : wallet_logic_error(std::move(loc), "invalid spend key") -+ { -+ } -+ -+ std::string to_string() const { return wallet_logic_error::to_string(); } -+ }; -+ - //---------------------------------------------------------------------------------------------------- - struct invalid_pregenerated_random : public wallet_logic_error - { -@@ -947,6 +961,31 @@ namespace tools - } - }; - //---------------------------------------------------------------------------------------------------- -+ struct background_sync_error : public wallet_logic_error -+ { -+ protected: -+ explicit background_sync_error(std::string&& loc, const std::string& message) -+ : wallet_logic_error(std::move(loc), message) -+ { -+ } -+ }; -+ //---------------------------------------------------------------------------------------------------- -+ struct background_wallet_already_open : public background_sync_error -+ { -+ explicit background_wallet_already_open(std::string&& loc, const std::string& background_wallet_file) -+ : background_sync_error(std::move(loc), "background wallet " + background_wallet_file + " is already opened by another wallet program") -+ { -+ } -+ }; -+ //---------------------------------------------------------------------------------------------------- -+ struct background_custom_password_same_as_wallet_password : public background_sync_error -+ { -+ explicit background_custom_password_same_as_wallet_password(std::string&& loc) -+ : background_sync_error(std::move(loc), "custom background password must be different than wallet password") -+ { -+ } -+ }; -+ //---------------------------------------------------------------------------------------------------- - - #if !defined(_MSC_VER) - -diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp -index b1419949f..d24b4c563 100644 ---- a/src/wallet/wallet_rpc_server.cpp -+++ b/src/wallet/wallet_rpc_server.cpp -@@ -73,6 +73,54 @@ using namespace epee; - } \ - } while(0) - -+#define CHECK_IF_BACKGROUND_SYNCING() \ -+ do \ -+ { \ -+ if (!m_wallet) { return not_open(er); } \ -+ if (m_wallet->is_background_wallet()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET; \ -+ er.message = "This command is disabled for background wallets."; \ -+ return false; \ -+ } \ -+ if (m_wallet->is_background_syncing()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING; \ -+ er.message = "This command is disabled while background syncing. Stop background syncing to use this command."; \ -+ return false; \ -+ } \ -+ } while(0) -+ -+#define PRE_VALIDATE_BACKGROUND_SYNC() \ -+ do \ -+ { \ -+ if (!m_wallet) { return not_open(er); } \ -+ if (m_restricted) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_DENIED; \ -+ er.message = "Command unavailable in restricted mode."; \ -+ return false; \ -+ } \ -+ if (m_wallet->key_on_device()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ -+ er.message = "Command not supported by HW wallet"; \ -+ return false; \ -+ } \ -+ if (m_wallet->multisig()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ -+ er.message = "Multisig wallet cannot enable background sync"; \ -+ return false; \ -+ } \ -+ if (m_wallet->watch_only()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; \ -+ er.message = "Watch-only wallet cannot enable background sync"; \ -+ return false; \ -+ } \ -+ } while (0) -+ - namespace - { - const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; -@@ -291,6 +339,9 @@ namespace tools - { - if (!m_wallet) - return; -+ // Background mining can be toggled from the main wallet -+ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) -+ return; - - tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); - if (setup == tools::wallet2::BackgroundMiningNo) -@@ -582,6 +633,7 @@ namespace tools - bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.count < 1 || req.count > 64) { -@@ -619,6 +671,7 @@ namespace tools - bool wallet_rpc_server::on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_subaddress_label(req.index, req.label); -@@ -681,6 +734,7 @@ namespace tools - bool wallet_rpc_server::on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->add_subaddress_account(req.label); -@@ -698,6 +752,7 @@ namespace tools - bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_subaddress_label({req.account_index, 0}, req.label); -@@ -713,6 +768,7 @@ namespace tools - bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - const std::pair, std::vector> account_tags = m_wallet->get_account_tags(); - for (const std::pair& p : account_tags.first) - { -@@ -732,6 +788,7 @@ namespace tools - bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_account_tag(req.accounts, req.tag); -@@ -747,6 +804,7 @@ namespace tools - bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_account_tag(req.accounts, ""); -@@ -762,6 +820,7 @@ namespace tools - bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_account_tag_description(req.tag, req.description); -@@ -792,6 +851,7 @@ namespace tools - bool wallet_rpc_server::on_freeze(const wallet_rpc::COMMAND_RPC_FREEZE::request& req, wallet_rpc::COMMAND_RPC_FREEZE::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.key_image.empty()) -@@ -820,6 +880,7 @@ namespace tools - bool wallet_rpc_server::on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.key_image.empty()) -@@ -848,6 +909,7 @@ namespace tools - bool wallet_rpc_server::on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.key_image.empty()) -@@ -875,6 +937,8 @@ namespace tools - //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::validate_transfer(const std::list& destinations, const std::string& payment_id, std::vector& dsts, std::vector& extra, bool at_least_one_destination, epee::json_rpc::error& er) - { -+ CHECK_IF_BACKGROUND_SYNCING(); -+ - crypto::hash8 integrated_payment_id = crypto::null_hash8; - std::string extra_nonce; - for (auto it = destinations.begin(); it != destinations.end(); it++) -@@ -1204,6 +1268,7 @@ namespace tools - } - - CHECK_MULTISIG_ENABLED(); -+ CHECK_IF_BACKGROUND_SYNCING(); - - cryptonote::blobdata blob; - if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) -@@ -1285,6 +1350,7 @@ namespace tools - er.message = "command not supported by watch-only wallet"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - if(req.unsigned_txset.empty() && req.multisig_txset.empty()) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; -@@ -1554,6 +1620,7 @@ namespace tools - } - - CHECK_MULTISIG_ENABLED(); -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -@@ -2115,6 +2182,7 @@ namespace tools - er.message = "The wallet is watch-only. Cannot retrieve seed."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - if (!m_wallet->is_deterministic()) - { - er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC; -@@ -2143,6 +2211,7 @@ namespace tools - er.message = "The wallet is watch-only. Cannot retrieve spend key."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key); - res.key = std::string(key.data(), key.size()); - } -@@ -2164,6 +2233,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -@@ -2177,6 +2247,79 @@ namespace tools - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ -+ bool wallet_rpc_server::on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) -+ { -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ const tools::wallet2::BackgroundSyncType background_sync_type = tools::wallet2::background_sync_type_from_str(req.background_sync_type); -+ boost::optional background_cache_password = boost::none; -+ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) -+ background_cache_password = boost::optional(req.background_cache_password); -+ m_wallet->setup_background_sync(background_sync_type, req.wallet_password, background_cache_password); -+ } -+ catch (...) -+ { -+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); -+ return false; -+ } -+ return true; -+ } -+ //------------------------------------------------------------------------------------------------------------------------------ -+ bool wallet_rpc_server::on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) -+ { -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ m_wallet->start_background_sync(); -+ } -+ catch (...) -+ { -+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); -+ return false; -+ } -+ return true; -+ } -+ //------------------------------------------------------------------------------------------------------------------------------ -+ bool wallet_rpc_server::on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) -+ { -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ crypto::secret_key spend_secret_key = crypto::null_skey; -+ -+ // Load the spend key from seed if seed is provided -+ if (!req.seed.empty()) -+ { -+ crypto::secret_key recovery_key; -+ std::string language; -+ -+ if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, language)) -+ { -+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; -+ er.message = "Electrum-style word list failed verification"; -+ return false; -+ } -+ -+ if (!req.seed_offset.empty()) -+ recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset); -+ -+ // generate spend key -+ cryptonote::account_base account; -+ account.generate(recovery_key, true, false); -+ spend_secret_key = account.get_keys().m_spend_secret_key; -+ } -+ -+ m_wallet->stop_background_sync(req.wallet_password, spend_secret_key); -+ } -+ catch (...) -+ { -+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); -+ return false; -+ } -+ return true; -+ } -+ //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -@@ -2186,6 +2329,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - tools::wallet2::message_signature_type_t signature_type = tools::wallet2::sign_with_spend_key; - if (req.signature_type == "spend" || req.signature_type == "") -@@ -2278,6 +2422,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - if (req.txids.size() != req.notes.size()) - { -@@ -2350,6 +2495,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - m_wallet->set_attribute(req.key, req.value); - -@@ -2377,6 +2523,7 @@ namespace tools - bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(req.txid, txid)) -@@ -2468,6 +2615,7 @@ namespace tools - bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(req.txid, txid)) -@@ -2584,6 +2732,7 @@ namespace tools - bool wallet_rpc_server::on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - - boost::optional> account_minreserve; - if (!req.all) -@@ -2826,6 +2975,7 @@ namespace tools - er.message = "command not supported by HW wallet"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -@@ -2855,6 +3005,7 @@ namespace tools - er.message = "command not supported by HW wallet"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - cryptonote::blobdata blob; - if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob)) -@@ -2880,6 +3031,7 @@ namespace tools - bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - std::pair>> ski = m_wallet->export_key_images(req.all); -@@ -2916,6 +3068,7 @@ namespace tools - er.message = "This command requires a trusted daemon."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - std::vector> ski; -@@ -2984,6 +3137,7 @@ namespace tools - bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - const auto ab = m_wallet->get_address_book(); - if (req.entries.empty()) - { -@@ -3029,6 +3183,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - cryptonote::address_parse_info info; - er.message = ""; -@@ -3071,6 +3226,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - const auto ab = m_wallet->get_address_book(); - if (req.index >= ab.size()) -@@ -3133,6 +3289,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - const auto ab = m_wallet->get_address_book(); - if (req.index >= ab.size()) -@@ -3203,6 +3360,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - std::unordered_set txids; - std::list::const_iterator i = req.txids.begin(); -@@ -3242,6 +3400,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->rescan_spent(); -@@ -3506,6 +3665,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - if (m_wallet->verify_password(req.old_password)) - { - try -@@ -4033,6 +4193,7 @@ namespace tools - er.message = "wallet is watch-only and cannot be made multisig"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - res.multisig_info = m_wallet->get_multisig_first_kex_msg(); - return true; -@@ -4060,6 +4221,7 @@ namespace tools - er.message = "wallet is watch-only and cannot be made multisig"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h -index 3308d1751..c2329aafe 100644 ---- a/src/wallet/wallet_rpc_server.h -+++ b/src/wallet/wallet_rpc_server.h -@@ -160,6 +160,9 @@ namespace tools - MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES) - MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT) - MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) -+ MAP_JON_RPC_WE("setup_background_sync", on_setup_background_sync, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC) -+ MAP_JON_RPC_WE("start_background_sync", on_start_background_sync, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC) -+ MAP_JON_RPC_WE("stop_background_sync", on_stop_background_sync, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC) - END_JSON_RPC_MAP() - END_URI_MAP2() - -@@ -251,6 +254,9 @@ namespace tools - bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); - bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); - bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -+ bool on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -+ bool on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -+ bool on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); - - //json rpc v2 - bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h -index b6098d95c..a44b56ed6 100644 ---- a/src/wallet/wallet_rpc_server_commands_defs.h -+++ b/src/wallet/wallet_rpc_server_commands_defs.h -@@ -2696,5 +2696,69 @@ namespace wallet_rpc - typedef epee::misc_utils::struct_init response; - }; - -+ struct COMMAND_RPC_SETUP_BACKGROUND_SYNC -+ { -+ struct request_t -+ { -+ std::string background_sync_type; -+ std::string wallet_password; -+ std::string background_cache_password; -+ -+ BEGIN_KV_SERIALIZE_MAP() -+ KV_SERIALIZE(background_sync_type) -+ KV_SERIALIZE(wallet_password) -+ KV_SERIALIZE_OPT(background_cache_password, (std::string)"") -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init request; -+ -+ struct response_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init response; -+ }; -+ -+ struct COMMAND_RPC_START_BACKGROUND_SYNC -+ { -+ struct request_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init request; -+ -+ struct response_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init response; -+ }; -+ -+ struct COMMAND_RPC_STOP_BACKGROUND_SYNC -+ { -+ struct request_t -+ { -+ std::string wallet_password; -+ std::string seed; -+ std::string seed_offset; -+ -+ BEGIN_KV_SERIALIZE_MAP() -+ KV_SERIALIZE(wallet_password) -+ KV_SERIALIZE_OPT(seed, (std::string)"") -+ KV_SERIALIZE_OPT(seed_offset, (std::string)"") -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init request; -+ -+ struct response_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init response; -+ }; - } - } -diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h -index 541d29f86..6e88f6967 100644 ---- a/src/wallet/wallet_rpc_server_error_codes.h -+++ b/src/wallet/wallet_rpc_server_error_codes.h -@@ -81,3 +81,5 @@ - #define WALLET_RPC_ERROR_CODE_DISABLED -48 - #define WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED -49 - #define WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME -50 -+#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET -51 -+#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING -52 -\ No newline at end of file -diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py -index 4063911f4..60eb09a10 100755 ---- a/tests/functional_tests/transfer.py -+++ b/tests/functional_tests/transfer.py -@@ -30,6 +30,7 @@ - - from __future__ import print_function - import json -+import util_resources - import pprint - from deepdiff import DeepDiff - pp = pprint.PrettyPrinter(indent=2) -@@ -46,6 +47,17 @@ seeds = [ - 'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid', - ] - -+def diff_transfers(actual_transfers, expected_transfers, ignore_order = True): -+ # The payments containers aren't ordered; re-scanning can lead to diff orders -+ diff = DeepDiff(actual_transfers, expected_transfers, ignore_order = ignore_order) -+ if diff != {}: -+ pp.pprint(diff) -+ assert diff == {} -+ -+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) -+ - class TransferTest(): - def run_test(self): - self.reset() -@@ -63,6 +75,8 @@ class TransferTest(): - self.check_is_key_image_spent() - self.check_scan_tx() - self.check_subtract_fee_from_outputs() -+ self.check_background_sync() -+ self.check_background_sync_reorg_recovery() - - def reset(self): - print('Resetting blockchain') -@@ -840,12 +854,6 @@ class TransferTest(): - - print('Testing scan_tx') - -- def diff_transfers(actual_transfers, expected_transfers): -- diff = DeepDiff(actual_transfers, expected_transfers) -- if diff != {}: -- pp.pprint(diff) -- assert diff == {} -- - # set up sender_wallet - sender_wallet = self.wallet[0] - try: sender_wallet.close_wallet() -@@ -1127,5 +1135,385 @@ class TransferTest(): - except AssertionError: - pass - -+ def check_background_sync(self): -+ daemon = Daemon() -+ -+ print('Testing background sync') -+ -+ # Some helper functions -+ def stop_with_wrong_inputs(wallet, wallet_password, seed = ''): -+ invalid = False -+ try: wallet.stop_background_sync(wallet_password = wallet_password, seed = seed) -+ except: invalid = True -+ assert invalid -+ -+ def open_with_wrong_password(wallet, filename, password): -+ invalid_password = False -+ try: wallet.open_wallet(filename, password = password) -+ 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) -+ assert wallet.get_balance().balance == expected_balance -+ -+ # Set up sender_wallet. Prepare to sweep single output to receiver. -+ # 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] -+ 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] -+ assert len(unlocked) > 0 -+ ki = unlocked[0].key_image -+ amount = unlocked[0].amount -+ spent_txid = unlocked[0].tx_hash -+ sender_wallet.refresh() -+ res = sender_wallet.get_transfers() -+ out_len = 0 if 'out' not in res else len(res.out) -+ sender_starting_balance = sender_wallet.get_balance().balance -+ -+ # Background sync type options -+ reuse_password = sender_wallet.background_sync_options.reuse_password -+ custom_password = sender_wallet.background_sync_options.custom_password -+ -+ # set up receiver_wallet -+ receiver_wallet = self.wallet[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']) -+ receiver_starting_balance = receiver_wallet.get_balance().balance -+ -+ # transfer from sender_wallet to receiver_wallet -+ dst = '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW' -+ res = sender_wallet.sweep_single(dst, key_image = ki) -+ assert len(res.tx_hash) == 32*2 -+ txid = res.tx_hash -+ assert res.fee > 0 -+ fee = res.fee -+ assert res.amount == amount - fee -+ -+ expected_sender_balance = sender_starting_balance - amount -+ expected_receiver_balance = receiver_starting_balance + (amount - fee) -+ -+ print('Checking background sync on outgoing wallet') -+ sender_wallet.setup_background_sync(background_sync_type = reuse_password) -+ sender_wallet.start_background_sync() -+ # Mine block to an uninvolved wallet -+ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) -+ # sender should still be able to scan the transfer normally because we -+ # spent an output that had a known key image -+ sender_wallet.refresh() -+ transfers = sender_wallet.get_transfers() -+ assert 'pending' not in transfers or len(transfers.pending) == 0 -+ assert 'pool' not in transfers or len (transfers.pool) == 0 -+ assert len(transfers.out) == out_len + 1 -+ tx = [x for x in transfers.out if x.txid == txid] -+ assert len(tx) == 1 -+ tx = tx[0] -+ assert tx.amount == amount - fee -+ assert tx.fee == fee -+ assert len(tx.destinations) == 1 -+ assert tx.destinations[0].amount == amount - fee -+ assert tx.destinations[0].address == dst -+ incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all') -+ assert len([x for x in incoming_transfers.transfers if x.tx_hash == spent_txid and x.key_image == ki and x.spent]) == 1 -+ assert sender_wallet.get_balance().balance == expected_sender_balance -+ -+ # Restore and check background syncing outgoing wallet -+ restore_wallet(sender_wallet, seeds[0]) -+ sender_wallet.setup_background_sync(background_sync_type = reuse_password) -+ sender_wallet.start_background_sync() -+ sender_wallet.refresh() -+ for i, out_tx in enumerate(transfers.out): -+ if 'destinations' in out_tx: -+ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore -+ # sender's balance should be higher because can't detect spends while -+ # background sync enabled, only receives -+ background_bal = sender_wallet.get_balance().balance -+ assert background_bal > expected_sender_balance -+ background_transfers = sender_wallet.get_transfers() -+ assert 'out' not in background_transfers or len(background_transfers.out) == 0 -+ assert 'in' in background_transfers and len(background_transfers['in']) > 0 -+ background_incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all') -+ assert len(background_incoming_transfers) == len(incoming_transfers) -+ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0 -+ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == spent_txid]) == 1 -+ -+ # Try to stop background sync with the wrong seed -+ stop_with_wrong_inputs(sender_wallet, wallet_password = '', seed = seeds[1]) -+ -+ # Stop background sync and check transfers update correctly -+ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0]) -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ -+ # 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], '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() -+ sender_wallet.refresh() -+ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) -+ stop_with_wrong_inputs(sender_wallet, 'wrong_password') -+ sender_wallet.stop_background_sync(wallet_password = 'test_password') -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ -+ # Close wallet while background syncing, then reopen -+ for background_sync_type in [reuse_password, custom_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() -+ sender_wallet.refresh() -+ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) -+ sender_wallet.close_wallet() -+ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password') -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ # It should reopen with spend key loaded and correctly scan all transfers -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ -+ # Close wallet while syncing normally, then reopen -+ for background_sync_type in [reuse_password, custom_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() -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ sender_wallet.close_wallet() -+ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password') -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ -+ # 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], '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) -+ assert util_resources.file_exists('test1.background') -+ assert util_resources.file_exists('test1.background.keys') -+ sender_wallet.close_wallet() -+ open_with_wrong_password(sender_wallet, 'test1.background', 'test_password') -+ sender_wallet.open_wallet('test1.background', password = background_cache_password) -+ sender_wallet.refresh() -+ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) -+ sender_wallet.close_wallet() -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ 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], '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) -+ sender_wallet.close_wallet() -+ sender_wallet.open_wallet('test1.background', password = 'background_password') -+ 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], 'test1', 'test_password') -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ same_password = False -+ try: sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = 'test_password') -+ except: same_password = True -+ assert same_password -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ -+ # Turn off background sync -+ for background_sync_type in [reuse_password, custom_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: -+ assert util_resources.file_exists('test1.background') -+ assert util_resources.file_exists('test1.background.keys') -+ sender_wallet.close_wallet() -+ assert util_resources.file_exists('test1.background') -+ assert util_resources.file_exists('test1.background.keys') -+ else: -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ sender_wallet.close_wallet() -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ sender_wallet.setup_background_sync(background_sync_type = sender_wallet.background_sync_options.off, wallet_password = 'test_password') -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ sender_wallet.close_wallet() -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ -+ # Sanity check against outgoing wallet restored at 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) -+ -+ print('Checking background sync on incoming wallet') -+ receiver_wallet.setup_background_sync(background_sync_type = reuse_password) -+ receiver_wallet.start_background_sync() -+ receiver_wallet.refresh() -+ transfers = receiver_wallet.get_transfers() -+ assert 'pending' not in transfers or len(transfers.pending) == 0 -+ assert 'pool' not in transfers or len (transfers.pool) == 0 -+ assert len(transfers['in']) == in_len + 1 -+ tx = [x for x in transfers['in'] if x.txid == txid] -+ assert len(tx) == 1 -+ tx = tx[0] -+ assert tx.amount == amount - fee -+ assert tx.fee == fee -+ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') -+ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image == '' and not x.spent]) == 1 -+ assert receiver_wallet.get_balance().balance == expected_receiver_balance -+ -+ # Restore and check background syncing incoming wallet -+ restore_wallet(receiver_wallet, seeds[1]) -+ receiver_wallet.setup_background_sync(background_sync_type = reuse_password) -+ receiver_wallet.start_background_sync() -+ receiver_wallet.refresh() -+ if 'out' in transfers: -+ for i, out_tx in enumerate(transfers.out): -+ if 'destinations' in out_tx: -+ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore -+ background_bal = receiver_wallet.get_balance().balance -+ assert background_bal >= expected_receiver_balance -+ background_transfers = receiver_wallet.get_transfers() -+ assert 'out' not in background_transfers or len(background_transfers.out) == 0 -+ assert 'in' in background_transfers and len(background_transfers['in']) > 0 -+ background_incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') -+ assert len(background_incoming_transfers) == len(incoming_transfers) -+ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0 -+ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == txid]) == 1 -+ -+ # Stop background sync and check transfers update correctly -+ receiver_wallet.stop_background_sync(wallet_password = '', seed = seeds[1]) -+ diff_transfers(receiver_wallet.get_transfers(), transfers) -+ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') -+ assert len(background_incoming_transfers) == len(incoming_transfers) -+ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image != '' and not x.spent]) == 1 -+ 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], '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() -+ assert_correct_transfers(receiver_wallet, background_transfers, background_incoming_transfers, background_bal) -+ stop_with_wrong_inputs(receiver_wallet, 'wrong_password') -+ receiver_wallet.stop_background_sync(wallet_password = 'test_password') -+ 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], '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() -+ diff_transfers(receiver_wallet.get_transfers(), background_transfers) -+ diff_incoming_transfers(receiver_wallet.incoming_transfers(transfer_type = 'all'), background_incoming_transfers) -+ assert receiver_wallet.get_balance().balance == background_bal -+ receiver_wallet.close_wallet() -+ receiver_wallet.open_wallet('test2', password = 'test_password') -+ # It should reopen with spend key loaded and correctly scan all transfers -+ assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance) -+ -+ # Sanity check against incoming wallet restored at 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) -+ -+ # Clean up -+ util_resources.remove_wallet_files('test1') -+ util_resources.remove_wallet_files('test2') -+ for i in range(2): -+ self.wallet[i].close_wallet() -+ self.wallet[i].restore_deterministic_wallet(seed = seeds[i]) -+ -+ def check_background_sync_reorg_recovery(self): -+ daemon = Daemon() -+ -+ print('Testing background sync reorg recovery') -+ -+ # Disconnect daemon from peers -+ daemon.out_peers(0) -+ -+ # Background sync type options -+ sender_wallet = self.wallet[0] -+ reuse_password = sender_wallet.background_sync_options.reuse_password -+ custom_password = sender_wallet.background_sync_options.custom_password -+ -+ for background_sync_type in [reuse_password, custom_password]: -+ # Set up wallet saved to disk -+ sender_wallet.close_wallet() -+ util_resources.remove_wallet_files('test1') -+ sender_wallet.restore_deterministic_wallet(seed = seeds[0], filename = 'test1', password = '') -+ sender_wallet.auto_refresh(enable = False) -+ sender_wallet.refresh() -+ sender_starting_balance = sender_wallet.get_balance().balance -+ -+ # Send tx and mine a block -+ amount = 1000000000000 -+ assert sender_starting_balance > amount -+ dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': amount} -+ res = sender_wallet.transfer([dst]) -+ assert len(res.tx_hash) == 32*2 -+ txid = res.tx_hash -+ -+ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) -+ -+ # Make sure the wallet can see the tx -+ sender_wallet.refresh() -+ transfers = sender_wallet.get_transfers() -+ assert 'pool' not in transfers or len (transfers.pool) == 0 -+ tx = [x for x in transfers.out if x.txid == txid] -+ assert len(tx) == 1 -+ tx = tx[0] -+ assert sender_wallet.get_balance().balance < (sender_starting_balance - amount) -+ -+ # Pop the block while background syncing -+ 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 = '', background_cache_password = background_cache_password) -+ sender_wallet.start_background_sync() -+ daemon.pop_blocks(1) -+ daemon.flush_txpool() -+ -+ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) -+ -+ # Make sure the wallet can no longer see the tx -+ sender_wallet.refresh() -+ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0]) -+ transfers = sender_wallet.get_transfers() -+ no_tx = [x for x in transfers.out if x.txid == txid] -+ assert len(no_tx) == 0 -+ assert sender_wallet.get_balance().balance == sender_starting_balance -+ -+ # Clean up -+ daemon.out_peers(12) -+ util_resources.remove_wallet_files('test1') -+ self.wallet[0].close_wallet() -+ self.wallet[0].restore_deterministic_wallet(seed = seeds[0]) -+ - if __name__ == '__main__': - TransferTest().run_test() -diff --git a/tests/functional_tests/util_resources.py b/tests/functional_tests/util_resources.py -index e030312da..3ca6fdb86 100755 ---- a/tests/functional_tests/util_resources.py -+++ b/tests/functional_tests/util_resources.py -@@ -37,6 +37,8 @@ - from __future__ import print_function - import subprocess - import psutil -+import os -+import errno - - def available_ram_gb(): - ram_bytes = psutil.virtual_memory().available -@@ -51,3 +53,26 @@ def get_time_pi_seconds(cores, app_dir='.'): - miliseconds = int(decoded) - - return miliseconds / 1000.0 -+ -+def remove_file(name): -+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -+ assert WALLET_DIRECTORY != '' -+ try: -+ os.unlink(WALLET_DIRECTORY + '/' + name) -+ except OSError as e: -+ if e.errno != errno.ENOENT: -+ raise -+ -+def get_file_path(name): -+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -+ assert WALLET_DIRECTORY != '' -+ return WALLET_DIRECTORY + '/' + name -+ -+def remove_wallet_files(name): -+ for suffix in ['', '.keys', '.background', '.background.keys', '.address.txt']: -+ remove_file(name + suffix) -+ -+def file_exists(name): -+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -+ assert WALLET_DIRECTORY != '' -+ return os.path.isfile(WALLET_DIRECTORY + '/' + name) -diff --git a/tests/functional_tests/wallet.py b/tests/functional_tests/wallet.py -index 1ad05c98f..8182cecb2 100755 ---- a/tests/functional_tests/wallet.py -+++ b/tests/functional_tests/wallet.py -@@ -34,8 +34,7 @@ - - from __future__ import print_function - import sys --import os --import errno -+import util_resources - - from framework.wallet import Wallet - from framework.daemon import Daemon -@@ -54,24 +53,6 @@ class WalletTest(): - self.change_password() - self.store() - -- def remove_file(self, name): -- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -- assert WALLET_DIRECTORY != '' -- try: -- os.unlink(WALLET_DIRECTORY + '/' + name) -- except OSError as e: -- if e.errno != errno.ENOENT: -- raise -- -- def remove_wallet_files(self, name): -- for suffix in ['', '.keys']: -- self.remove_file(name + suffix) -- -- def file_exists(self, name): -- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -- assert WALLET_DIRECTORY != '' -- return os.path.isfile(WALLET_DIRECTORY + '/' + name) -- - def reset(self): - print('Resetting blockchain') - daemon = Daemon() -@@ -333,7 +314,7 @@ class WalletTest(): - try: wallet.close_wallet() - except: pass - -- self.remove_wallet_files('test1') -+ util_resources.remove_wallet_files('test1') - - seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' - res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') -@@ -359,7 +340,7 @@ class WalletTest(): - - wallet.close_wallet() - -- self.remove_wallet_files('test1') -+ util_resources.remove_wallet_files('test1') - - def store(self): - print('Testing store') -@@ -369,22 +350,26 @@ class WalletTest(): - try: wallet.close_wallet() - except: pass - -- self.remove_wallet_files('test1') -+ util_resources.remove_wallet_files('test1') - - seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' - res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') - assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' - assert res.seed == seed - -- self.remove_file('test1') -- assert self.file_exists('test1.keys') -- assert not self.file_exists('test1') -+ util_resources.remove_file('test1') -+ assert util_resources.file_exists('test1.keys') -+ assert not util_resources.file_exists('test1') - wallet.store() -- assert self.file_exists('test1.keys') -- assert self.file_exists('test1') -+ assert util_resources.file_exists('test1.keys') -+ assert util_resources.file_exists('test1') - - wallet.close_wallet() -- self.remove_wallet_files('test1') -+ -+ wallet.open_wallet(filename = 'test1', password = '') -+ wallet.close_wallet() -+ -+ util_resources.remove_wallet_files('test1') - - - if __name__ == '__main__': -diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp -index ef6964f9e..25121a02e 100644 ---- a/tests/unit_tests/wipeable_string.cpp -+++ b/tests/unit_tests/wipeable_string.cpp -@@ -211,3 +211,15 @@ TEST(wipeable_string, to_hex) - ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"", 0)) == epee::wipeable_string("")); - ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"abc", 3)) == epee::wipeable_string("616263")); - } -+ -+TEST(wipeable_string, to_string) -+{ -+ // Converting a wipeable_string to a string defeats the purpose of wipeable_string, -+ // but nice to know this works -+ std::string str; -+ { -+ epee::wipeable_string wipeable_str("foo"); -+ str = std::string(wipeable_str.data(), wipeable_str.size()); -+ } -+ ASSERT_TRUE(str == std::string("foo")); -+} -diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py -index 1e10e1f86..bff33a561 100644 ---- a/utils/python-rpc/framework/wallet.py -+++ b/utils/python-rpc/framework/wallet.py -@@ -1138,3 +1138,45 @@ class Wallet(object): - 'id': '0' - } - return self.rpc.send_json_rpc_request(frozen) -+ -+ class BackgroundSyncOptions(object): -+ def __init__(self): -+ self.off = 'off' -+ self.reuse_password = 'reuse-wallet-password' -+ self.custom_password = 'custom-background-password' -+ background_sync_options = BackgroundSyncOptions() -+ -+ def setup_background_sync(self, background_sync_type = background_sync_options.off, wallet_password = '', background_cache_password = ''): -+ setup_background_sync = { -+ 'method': 'setup_background_sync', -+ 'jsonrpc': '2.0', -+ 'params' : { -+ 'background_sync_type': background_sync_type, -+ 'wallet_password': wallet_password, -+ 'background_cache_password': background_cache_password, -+ }, -+ 'id': '0' -+ } -+ return self.rpc.send_json_rpc_request(setup_background_sync) -+ -+ def start_background_sync(self): -+ start_background_sync = { -+ 'method': 'start_background_sync', -+ 'jsonrpc': '2.0', -+ 'params' : {}, -+ 'id': '0' -+ } -+ return self.rpc.send_json_rpc_request(start_background_sync) -+ -+ def stop_background_sync(self, wallet_password = '', seed = '', seed_offset = ''): -+ stop_background_sync = { -+ 'method': 'stop_background_sync', -+ 'jsonrpc': '2.0', -+ 'params' : { -+ 'wallet_password': wallet_password, -+ 'seed': seed, -+ 'seed_offset': seed_offset, -+ }, -+ 'id': '0' -+ } -+ return self.rpc.send_json_rpc_request(stop_background_sync) --- -2.39.2 - diff --git a/patches/monero/0003-airgap.patch b/patches/monero/0003-airgap.patch deleted file mode 100644 index eebe097..0000000 --- a/patches/monero/0003-airgap.patch +++ /dev/null @@ -1,195 +0,0 @@ -From 5385d085c547b675adfccb64314d1c6f7bf2d508 Mon Sep 17 00:00:00 2001 -From: tobtoht -Date: Tue, 12 Mar 2024 10:09:50 +0100 -Subject: [PATCH 03/16] airgap - ---- - src/wallet/api/wallet.cpp | 23 ++++++++++++++++++ - src/wallet/api/wallet.h | 2 ++ - src/wallet/api/wallet2_api.h | 3 +++ - src/wallet/wallet2.cpp | 45 +++++++++++++++++++++++++++++++----- - src/wallet/wallet2.h | 1 + - 5 files changed, 68 insertions(+), 6 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 7d430b655..837b98e6b 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -1129,6 +1129,24 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const - return m_wallet->unlocked_balance(accountIndex, false); - } - -+uint64_t WalletImpl::viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const -+{ -+ clearStatus(); -+ -+ std::vector kis; -+ for (const auto &key_image : key_images) { -+ crypto::key_image ki; -+ if (!epee::string_tools::hex_to_pod(key_image, ki)) -+ { -+ setStatusError(tr("failed to parse key image")); -+ return 0; -+ } -+ kis.push_back(ki); -+ } -+ -+ return m_wallet->view_only_balance(accountIndex, kis); -+} -+ - uint64_t WalletImpl::blockChainHeight() const - { - if(m_wallet->light_wallet()) { -@@ -1291,6 +1309,11 @@ bool WalletImpl::submitTransaction(const string &fileName) { - return true; - } - -+bool WalletImpl::hasUnknownKeyImages() const -+{ -+ return m_wallet->has_unknown_key_images(); -+} -+ - bool WalletImpl::exportKeyImages(const string &filename, bool all) - { - if (m_wallet->watch_only()) -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 9acd2871f..05d065c5c 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -122,6 +122,7 @@ public: - bool setProxy(const std::string &address) override; - uint64_t balance(uint32_t accountIndex = 0) const override; - uint64_t unlockedBalance(uint32_t accountIndex = 0) const override; -+ uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const override; - uint64_t blockChainHeight() const override; - uint64_t approximateBlockChainHeight() const override; - uint64_t estimateBlockChainHeight() const override; -@@ -175,6 +176,7 @@ public: - virtual PendingTransaction * createSweepUnmixableTransaction() override; - bool submitTransaction(const std::string &fileName) override; - virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; -+ bool hasUnknownKeyImages() const override; - bool exportKeyImages(const std::string &filename, bool all = false) override; - bool importKeyImages(const std::string &filename) override; - bool exportOutputs(const std::string &filename, bool all = false) override; -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 4268b656e..4edaefefd 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -626,6 +626,7 @@ struct Wallet - result += unlockedBalance(i); - return result; - } -+ virtual uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images = {}) const = 0; - - /** - * @brief watchOnly - checks if wallet is watch only -@@ -910,6 +911,8 @@ struct Wallet - virtual uint64_t estimateTransactionFee(const std::vector> &destinations, - PendingTransaction::Priority priority) const = 0; - -+ virtual bool hasUnknownKeyImages() const = 0; -+ - /*! - * \brief exportKeyImages - exports key images to file - * \param filename -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index f2381740a..41cf1fd41 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -949,6 +949,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) - return idx + extra; - } - -+bool is_preferred_input(const std::vector& 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); -@@ -7038,6 +7048,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * - return amount; - } - //---------------------------------------------------------------------------------------------------- -+uint64_t wallet2::view_only_balance(uint32_t index_major, const std::vector& selected_inputs) -+{ -+ uint64_t amount = 0; -+ for (const auto &td : m_transfers) { -+ if (is_preferred_input(selected_inputs, td.m_key_image) && -+ !is_spent(td, false) && -+ !td.m_frozen && -+ !td.m_key_image_partial && -+ td.m_key_image_known && -+ td.is_rct() && -+ is_transfer_unlocked(td) && -+ td.m_subaddr_index.major == index_major) -+ { -+ amount += td.m_amount; -+ } -+ } -+ return amount; -+} -+//---------------------------------------------------------------------------------------------------- - std::map wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const - { - std::map amount_per_subaddr; -@@ -7889,9 +7918,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector additional_derivations; - -- // compute public keys from out secret keys -- crypto::public_key tx_pub_key; -- crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key); -+ crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - std::vector additional_tx_pub_keys; - for (const crypto::secret_key &skey: txs[n].additional_tx_keys) - { -@@ -11287,7 +11314,7 @@ std::vector wallet2::create_transactions_2(std::vector m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) - { -@@ -11337,9 +11364,15 @@ std::vector wallet2::create_transactions_2(std::vector(); -+ // use tobotoht's code path on view-only wallet, otherwise default to upstream -+ bool throwOnNoEnotes = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; - -+ if (throwOnNoEnotes) { -+ THROW_WALLET_EXCEPTION_IF(unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty(), error::wallet_internal_error, "No enotes available to spend") -+ } else { -+ if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty()) -+ return std::vector(); -+ } - // if empty, put dummy entry so that the front can be referenced later in the loop - if (unused_dust_indices_per_subaddr.empty()) - unused_dust_indices_per_subaddr.push_back({}); -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 56cc118f4..b9aa7a00d 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1172,6 +1172,7 @@ private: - // locked & unlocked balance of given or current subaddress account - uint64_t balance(uint32_t subaddr_index_major, bool strict) const; - uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL); -+ uint64_t view_only_balance(uint32_t index_major, const std::vector& selected_inputs = {}); - // locked & unlocked balance per subaddress of given or current subaddress account - std::map balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; - std::map>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict); --- -2.39.2 - diff --git a/patches/monero/0003-store-crash-fix.patch b/patches/monero/0003-store-crash-fix.patch new file mode 100644 index 0000000..225f0ab --- /dev/null +++ b/patches/monero/0003-store-crash-fix.patch @@ -0,0 +1,208 @@ +From b49bc52f3d3d1751dd65d4694e4f74b84b23f583 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Sat, 11 May 2024 16:25:10 +0200 +Subject: [PATCH 03/14] store crash fix + +Monero wallet crashes (sometimes) when it is syncing, +while the proper solution (that can be seen in feather) +is to not store wallet while it is being synced, this is not +acceptable for mobile wallets where OS can just come +and kill the wallet because it felt like it. + +This patch depends on the background-sync patch, but +to use it as a standalone fix grabbing the definition for the +LOCK_REFRESH macro should be enough. + +tobtoht suggested: +_say you want to store every 15 minutes during background sync. you stop the refresh every 15 minutes. then do something like this in the callback:_ + +``` +// Make sure this doesn't run in the refresh thread +onRefreshed() { + if (hasItBeen15MinutesSinceWeStored()) { + store(); + } + + if (shouldWeContinueRefreshing()) { + startRefresh(); + } +} +``` + +which works for crashes after the wallet is initially synced +but doesn't solve the issue for wallet that are syncing (it +would just wait for it to finish before actually storing). + +Also imo store() functin should store the wallet, no matter +the current state. +--- + src/wallet/api/wallet.cpp | 25 ++++++++++++------------- + src/wallet/api/wallet.h | 1 - + src/wallet/wallet2.cpp | 11 ++++++++++- + src/wallet/wallet2.h | 3 +++ + 4 files changed, 25 insertions(+), 15 deletions(-) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index e9f76f4..0d85cf3 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -55,8 +55,8 @@ using namespace cryptonote; + #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" + + #define LOCK_REFRESH() \ +- bool refresh_enabled = m_refreshEnabled; \ +- m_refreshEnabled = false; \ ++ bool refresh_enabled = m_wallet->get_refresh_enabled(); \ ++ m_wallet->set_refresh_enabled(false); \ + m_wallet->stop(); \ + m_refreshCV.notify_one(); \ + boost::mutex::scoped_lock lock(m_refreshMutex); \ +@@ -466,7 +466,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) + m_wallet2Callback.reset(new Wallet2CallbackImpl(this)); + m_wallet->callback(m_wallet2Callback.get()); + m_refreshThreadDone = false; +- m_refreshEnabled = false; ++ m_wallet->set_refresh_enabled(false); + m_addressBook.reset(new AddressBookImpl(this)); + m_subaddress.reset(new SubaddressImpl(this)); + m_subaddressAccount.reset(new SubaddressAccountImpl(this)); +@@ -962,6 +962,7 @@ void WalletImpl::stop() + bool WalletImpl::store(const std::string &path) + { + clearStatus(); ++ LOCK_REFRESH(); + try { + if (path.empty()) { + m_wallet->store(); +@@ -2448,10 +2449,10 @@ void WalletImpl::refreshThreadFunc() + } + + LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired..."); +- LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled); ++ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_wallet->get_refresh_enabled()); + LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status()); + LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan); +- if (m_refreshEnabled) { ++ if (m_wallet->get_refresh_enabled()) { + LOG_PRINT_L3(__FUNCTION__ << ": refreshing..."); + doRefresh(); + } +@@ -2481,12 +2482,12 @@ void WalletImpl::doRefresh() + } + m_wallet->find_and_save_rings(false); + } else { +- LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); ++ LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); + } + } catch (const std::exception &e) { + setStatusError(e.what()); + break; +- }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested ++ }while(m_wallet->get_refresh_enabled() && !rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested + + if (m_wallet2Callback->getListener()) { + m_wallet2Callback->getListener()->refreshed(); +@@ -2496,9 +2497,9 @@ void WalletImpl::doRefresh() + + void WalletImpl::startRefresh() + { +- if (!m_refreshEnabled) { ++ if (!m_wallet->get_refresh_enabled()) { + LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed..."); +- m_refreshEnabled = true; ++ m_wallet->set_refresh_enabled(true); + m_refreshCV.notify_one(); + } + } +@@ -2508,7 +2509,7 @@ void WalletImpl::startRefresh() + void WalletImpl::stopRefresh() + { + if (!m_refreshThreadDone) { +- m_refreshEnabled = false; ++ m_wallet->set_refresh_enabled(false); + m_refreshThreadDone = true; + m_refreshCV.notify_one(); + m_refreshThread.join(); +@@ -2519,9 +2520,7 @@ void WalletImpl::pauseRefresh() + { + LOG_PRINT_L2(__FUNCTION__ << ": refresh paused..."); + // TODO synchronize access +- if (!m_refreshThreadDone) { +- m_refreshEnabled = false; +- } ++ m_wallet->set_refresh_enabled(false); + } + + +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 1f199a7..ac7ce2f 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -273,7 +273,6 @@ private: + std::unique_ptr m_subaddressAccount; + + // multi-threaded refresh stuff +- std::atomic m_refreshEnabled; + std::atomic m_refreshThreadDone; + std::atomic m_refreshIntervalMillis; + std::atomic m_refreshShouldRescan; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 8146014..7913d55 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -1192,6 +1192,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std + m_upper_transaction_weight_limit(0), + m_run(true), + m_callback(0), ++ m_refreshEnabled(false), + m_trusted_daemon(false), + m_nettype(nettype), + m_multisig_rounds_passed(0), +@@ -1404,6 +1405,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optionalset_proxy(address); +@@ -4096,7 +4105,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo + // infer when we get an incoming output + + bool first = true, last = false; +- while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks) ++ while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks && m_refreshEnabled) + { + uint64_t next_blocks_start_height; + std::vector next_blocks; +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index b1dc4f7..a050286 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1071,6 +1071,8 @@ private: + boost::optional daemon_login = boost::none, bool trusted_daemon = true, + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); + bool set_proxy(const std::string &address); ++ bool get_refresh_enabled(); ++ void set_refresh_enabled(bool val); + + void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } + +@@ -1981,6 +1983,7 @@ private: + + boost::recursive_mutex m_daemon_rpc_mutex; + ++ bool m_refreshEnabled; + bool m_trusted_daemon; + i_wallet2_callback* m_callback; + hw::device::device_type m_key_device_type; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0004-coin-control.patch b/patches/monero/0004-coin-control.patch deleted file mode 100644 index 1700a61..0000000 --- a/patches/monero/0004-coin-control.patch +++ /dev/null @@ -1,902 +0,0 @@ -From d6d52b6156f1e83a69474a871043d414e4488f62 Mon Sep 17 00:00:00 2001 -From: tobtoht -Date: Tue, 12 Mar 2024 11:07:57 +0100 -Subject: [PATCH 04/16] coin control - ---- - src/wallet/api/CMakeLists.txt | 8 +- - src/wallet/api/coins.cpp | 185 ++++++++++++++++++++++++++++++++++ - src/wallet/api/coins.h | 40 ++++++++ - src/wallet/api/coins_info.cpp | 122 ++++++++++++++++++++++ - src/wallet/api/coins_info.h | 71 +++++++++++++ - src/wallet/api/wallet.cpp | 26 ++++- - src/wallet/api/wallet.h | 10 +- - src/wallet/api/wallet2_api.h | 52 +++++++++- - src/wallet/wallet2.cpp | 46 ++++++++- - src/wallet/wallet2.h | 11 +- - 10 files changed, 555 insertions(+), 16 deletions(-) - create mode 100644 src/wallet/api/coins.cpp - create mode 100644 src/wallet/api/coins.h - create mode 100644 src/wallet/api/coins_info.cpp - create mode 100644 src/wallet/api/coins_info.h - -diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt -index af7948d8a..bb740e2ac 100644 ---- a/src/wallet/api/CMakeLists.txt -+++ b/src/wallet/api/CMakeLists.txt -@@ -40,7 +40,9 @@ set(wallet_api_sources - address_book.cpp - subaddress.cpp - subaddress_account.cpp -- unsigned_transaction.cpp) -+ unsigned_transaction.cpp -+ coins.cpp -+ coins_info.cpp) - - set(wallet_api_headers - wallet2_api.h) -@@ -55,7 +57,9 @@ set(wallet_api_private_headers - address_book.h - subaddress.h - subaddress_account.h -- unsigned_transaction.h) -+ unsigned_transaction.h -+ coins.h -+ coins_info.h) - - monero_private_headers(wallet_api - ${wallet_api_private_headers}) -diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp -new file mode 100644 -index 000000000..2321c638d ---- /dev/null -+++ b/src/wallet/api/coins.cpp -@@ -0,0 +1,185 @@ -+#include "coins.h" -+#include "coins_info.h" -+#include "wallet.h" -+#include "crypto/hash.h" -+#include "wallet/wallet2.h" -+#include "common_defines.h" -+ -+#include -+#include -+ -+using namespace epee; -+ -+namespace Monero { -+ -+Coins::~Coins() = default; -+ -+CoinsImpl::CoinsImpl(WalletImpl *wallet) -+ : m_wallet(wallet) {} -+ -+CoinsImpl::~CoinsImpl() -+{ -+ for (auto t : m_rows) -+ delete t; -+} -+ -+int CoinsImpl::count() const -+{ -+ boost::shared_lock lock(m_rowsMutex); -+ int result = m_rows.size(); -+ return result; -+} -+ -+CoinsInfo *CoinsImpl::coin(int index) const -+{ -+ boost::shared_lock lock(m_rowsMutex); -+ // sanity check -+ if (index < 0) -+ return nullptr; -+ auto index_ = static_cast(index); -+ return index_ < m_rows.size() ? m_rows[index_] : nullptr; -+} -+ -+std::vector CoinsImpl::getAll() const -+{ -+ boost::shared_lock lock(m_rowsMutex); -+ return m_rows; -+} -+ -+ -+void CoinsImpl::refresh() -+{ -+ LOG_PRINT_L2("Refreshing coins"); -+ -+ boost::unique_lock lock(m_rowsMutex); -+ boost::shared_lock transfers_lock(m_wallet->m_wallet->m_transfers_mutex); -+ -+ // delete old outputs; -+ for (auto t : m_rows) -+ delete t; -+ m_rows.clear(); -+ -+ for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i) -+ { -+ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i); -+ -+ auto ci = new CoinsInfoImpl(); -+ ci->m_blockHeight = td.m_block_height; -+ ci->m_hash = string_tools::pod_to_hex(td.m_txid); -+ ci->m_internalOutputIndex = td.m_internal_output_index; -+ ci->m_globalOutputIndex = td.m_global_output_index; -+ ci->m_spent = td.m_spent; -+ ci->m_frozen = td.m_frozen; -+ ci->m_spentHeight = td.m_spent_height; -+ ci->m_amount = td.m_amount; -+ ci->m_rct = td.m_rct; -+ ci->m_keyImageKnown = td.m_key_image_known; -+ ci->m_pkIndex = td.m_pk_index; -+ ci->m_subaddrIndex = td.m_subaddr_index.minor; -+ ci->m_subaddrAccount = td.m_subaddr_index.major; -+ ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(td.m_subaddr_index); // todo: this is expensive, cache maybe? -+ ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index); -+ ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image); -+ ci->m_unlockTime = td.m_tx.unlock_time; -+ ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td); -+ ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key()); -+ ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen); -+ ci->m_description = m_wallet->m_wallet->get_tx_note(td.m_txid); -+ -+ m_rows.push_back(ci); -+ } -+} -+ -+void CoinsImpl::setFrozen(std::string public_key) -+{ -+ crypto::public_key pk; -+ if (!epee::string_tools::hex_to_pod(public_key, pk)) -+ { -+ LOG_ERROR("Invalid public key: " << public_key); -+ return; -+ } -+ -+ try -+ { -+ m_wallet->m_wallet->freeze(pk); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("setFrozen: " << e.what()); -+ } -+} -+ -+void CoinsImpl::setFrozen(int index) -+{ -+ try -+ { -+ m_wallet->m_wallet->freeze(index); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("setLabel: " << e.what()); -+ } -+} -+ -+void CoinsImpl::thaw(std::string public_key) -+{ -+ crypto::public_key pk; -+ if (!epee::string_tools::hex_to_pod(public_key, pk)) -+ { -+ LOG_ERROR("Invalid public key: " << public_key); -+ return; -+ } -+ -+ try -+ { -+ m_wallet->m_wallet->thaw(pk); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("thaw: " << e.what()); -+ } -+} -+ -+void CoinsImpl::thaw(int index) -+{ -+ try -+ { -+ m_wallet->m_wallet->thaw(index); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("thaw: " << e.what()); -+ } -+} -+ -+bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) { -+ return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight); -+} -+ -+void CoinsImpl::setDescription(const std::string &public_key, const std::string &description) -+{ -+ crypto::public_key pk; -+ if (!epee::string_tools::hex_to_pod(public_key, pk)) -+ { -+ LOG_ERROR("Invalid public key: " << public_key); -+ return; -+ } -+ -+ try -+ { -+ const size_t index = m_wallet->m_wallet->get_transfer_details(pk); -+ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index); -+ m_wallet->m_wallet->set_tx_note(td.m_txid, description); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("setDescription: " << e.what()); -+ } -+} -+ -+} // namespace -diff --git a/src/wallet/api/coins.h b/src/wallet/api/coins.h -new file mode 100644 -index 000000000..b7a0a8642 ---- /dev/null -+++ b/src/wallet/api/coins.h -@@ -0,0 +1,40 @@ -+#ifndef FEATHER_COINS_H -+#define FEATHER_COINS_H -+ -+#include "wallet/api/wallet2_api.h" -+#include "wallet/wallet2.h" -+ -+namespace Monero { -+ -+class WalletImpl; -+ -+class CoinsImpl : public Coins -+{ -+public: -+ explicit CoinsImpl(WalletImpl * wallet); -+ ~CoinsImpl() override; -+ int count() const override; -+ CoinsInfo * coin(int index) const override; -+ std::vector getAll() const override; -+ void refresh() override; -+ -+ void setFrozen(std::string public_key) override; -+ void setFrozen(int index) override; -+ void thaw(std::string public_key) override; -+ void thaw(int index) override; -+ -+ bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override; -+ -+ void setDescription(const std::string &public_key, const std::string &description) override; -+ -+private: -+ WalletImpl *m_wallet; -+ std::vector m_rows; -+ mutable boost::shared_mutex m_rowsMutex; -+}; -+ -+} -+ -+namespace Bitmonero = Monero; -+ -+#endif //FEATHER_COINS_H -diff --git a/src/wallet/api/coins_info.cpp b/src/wallet/api/coins_info.cpp -new file mode 100644 -index 000000000..5f2c4e1e4 ---- /dev/null -+++ b/src/wallet/api/coins_info.cpp -@@ -0,0 +1,122 @@ -+#include "coins_info.h" -+ -+using namespace std; -+ -+namespace Monero { -+ -+CoinsInfo::~CoinsInfo() = default; -+ -+CoinsInfoImpl::CoinsInfoImpl() -+ : m_blockHeight(0) -+ , m_internalOutputIndex(0) -+ , m_globalOutputIndex(0) -+ , m_spent(false) -+ , m_frozen(false) -+ , m_spentHeight(0) -+ , m_amount(0) -+ , m_rct(false) -+ , m_keyImageKnown(false) -+ , m_pkIndex(0) -+ , m_subaddrAccount(0) -+ , m_subaddrIndex(0) -+ , m_unlockTime(0) -+ , m_unlocked(false) -+{ -+ -+} -+ -+CoinsInfoImpl::~CoinsInfoImpl() = default; -+ -+uint64_t CoinsInfoImpl::blockHeight() const -+{ -+ return m_blockHeight; -+} -+ -+string CoinsInfoImpl::hash() const -+{ -+ return m_hash; -+} -+ -+size_t CoinsInfoImpl::internalOutputIndex() const { -+ return m_internalOutputIndex; -+} -+ -+uint64_t CoinsInfoImpl::globalOutputIndex() const -+{ -+ return m_globalOutputIndex; -+} -+ -+bool CoinsInfoImpl::spent() const -+{ -+ return m_spent; -+} -+ -+bool CoinsInfoImpl::frozen() const -+{ -+ return m_frozen; -+} -+ -+uint64_t CoinsInfoImpl::spentHeight() const -+{ -+ return m_spentHeight; -+} -+ -+uint64_t CoinsInfoImpl::amount() const -+{ -+ return m_amount; -+} -+ -+bool CoinsInfoImpl::rct() const { -+ return m_rct; -+} -+ -+bool CoinsInfoImpl::keyImageKnown() const { -+ return m_keyImageKnown; -+} -+ -+size_t CoinsInfoImpl::pkIndex() const { -+ return m_pkIndex; -+} -+ -+uint32_t CoinsInfoImpl::subaddrIndex() const { -+ return m_subaddrIndex; -+} -+ -+uint32_t CoinsInfoImpl::subaddrAccount() const { -+ return m_subaddrAccount; -+} -+ -+string CoinsInfoImpl::address() const { -+ return m_address; -+} -+ -+string CoinsInfoImpl::addressLabel() const { -+ return m_addressLabel; -+} -+ -+string CoinsInfoImpl::keyImage() const { -+ return m_keyImage; -+} -+ -+uint64_t CoinsInfoImpl::unlockTime() const { -+ return m_unlockTime; -+} -+ -+bool CoinsInfoImpl::unlocked() const { -+ return m_unlocked; -+} -+ -+string CoinsInfoImpl::pubKey() const { -+ return m_pubKey; -+} -+ -+bool CoinsInfoImpl::coinbase() const { -+ return m_coinbase; -+} -+ -+string CoinsInfoImpl::description() const { -+ return m_description; -+} -+} // namespace -+ -+namespace Bitmonero = Monero; -diff --git a/src/wallet/api/coins_info.h b/src/wallet/api/coins_info.h -new file mode 100644 -index 000000000..c43e45abd ---- /dev/null -+++ b/src/wallet/api/coins_info.h -@@ -0,0 +1,71 @@ -+#ifndef FEATHER_COINS_INFO_H -+#define FEATHER_COINS_INFO_H -+ -+#include "wallet/api/wallet2_api.h" -+#include -+#include -+ -+namespace Monero { -+ -+class CoinsImpl; -+ -+class CoinsInfoImpl : public CoinsInfo -+{ -+public: -+ CoinsInfoImpl(); -+ ~CoinsInfoImpl(); -+ -+ virtual uint64_t blockHeight() const override; -+ virtual std::string hash() const override; -+ virtual size_t internalOutputIndex() const override; -+ virtual uint64_t globalOutputIndex() const override; -+ virtual bool spent() const override; -+ virtual bool frozen() const override; -+ virtual uint64_t spentHeight() const override; -+ virtual uint64_t amount() const override; -+ virtual bool rct() const override; -+ virtual bool keyImageKnown() const override; -+ virtual size_t pkIndex() const override; -+ virtual uint32_t subaddrIndex() const override; -+ virtual uint32_t subaddrAccount() const override; -+ virtual std::string address() const override; -+ virtual std::string addressLabel() const override; -+ virtual std::string keyImage() const override; -+ virtual uint64_t unlockTime() const override; -+ virtual bool unlocked() const override; -+ virtual std::string pubKey() const override; -+ virtual bool coinbase() const override; -+ virtual std::string description() const override; -+ -+private: -+ uint64_t m_blockHeight; -+ std::string m_hash; -+ size_t m_internalOutputIndex; -+ uint64_t m_globalOutputIndex; -+ bool m_spent; -+ bool m_frozen; -+ uint64_t m_spentHeight; -+ uint64_t m_amount; -+ bool m_rct; -+ bool m_keyImageKnown; -+ size_t m_pkIndex; -+ uint32_t m_subaddrIndex; -+ uint32_t m_subaddrAccount; -+ std::string m_address; -+ std::string m_addressLabel; -+ std::string m_keyImage; -+ uint64_t m_unlockTime; -+ bool m_unlocked; -+ std::string m_pubKey; -+ bool m_coinbase; -+ std::string m_description; -+ -+ friend class CoinsImpl; -+ -+}; -+ -+} // namespace -+ -+namespace Bitmonero = Monero; -+ -+#endif //FEATHER_COINS_INFO_H -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 837b98e6b..2106dbd07 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -35,6 +35,7 @@ - #include "transaction_history.h" - #include "address_book.h" - #include "subaddress.h" -+#include "coins.h" - #include "subaddress_account.h" - #include "common_defines.h" - #include "common/util.h" -@@ -469,6 +470,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) - m_refreshEnabled = false; - m_addressBook.reset(new AddressBookImpl(this)); - m_subaddress.reset(new SubaddressImpl(this)); -+ m_coins.reset(new CoinsImpl(this)); - m_subaddressAccount.reset(new SubaddressAccountImpl(this)); - - -@@ -1752,7 +1754,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat - // - unconfirmed_transfer_details; - // - confirmed_transfer_details) - --PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) -+PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) - - { - clearStatus(); -@@ -1821,6 +1823,19 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector 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; - } -@@ -1920,10 +1935,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector amount, uint32_t mixin_count, -- PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) -+ PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) - - { -- return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices); -+ return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices, preferred_inputs); - } - - PendingTransaction *WalletImpl::createSweepUnmixableTransaction() -@@ -2048,6 +2063,11 @@ AddressBook *WalletImpl::addressBook() - return m_addressBook.get(); - } - -+Coins *WalletImpl::coins() -+{ -+ return m_coins.get(); -+} -+ - Subaddress *WalletImpl::subaddress() - { - return m_subaddress.get(); -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 05d065c5c..4a16ca028 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -46,6 +46,7 @@ class PendingTransactionImpl; - class UnsignedTransactionImpl; - class AddressBookImpl; - class SubaddressImpl; -+class CoinsImpl; - class SubaddressAccountImpl; - struct Wallet2CallbackImpl; - -@@ -167,12 +168,14 @@ public: - optional> amount, uint32_t mixin_count, - PendingTransaction::Priority priority = PendingTransaction::Priority_Low, - uint32_t subaddr_account = 0, -- std::set subaddr_indices = {}) override; -+ std::set subaddr_indices = {}, -+ const std::set &preferred_inputs = {}) override; - PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, - optional amount, uint32_t mixin_count, - PendingTransaction::Priority priority = PendingTransaction::Priority_Low, - uint32_t subaddr_account = 0, -- std::set subaddr_indices = {}) override; -+ std::set subaddr_indices = {}, -+ const std::set &preferred_inputs = {}) override; - virtual PendingTransaction * createSweepUnmixableTransaction() override; - bool submitTransaction(const std::string &fileName) override; - virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; -@@ -195,6 +198,7 @@ public: - PendingTransaction::Priority priority) const override; - virtual TransactionHistory * history() override; - virtual AddressBook * addressBook() override; -+ virtual Coins * coins() override; - virtual Subaddress * subaddress() override; - virtual SubaddressAccount * subaddressAccount() override; - virtual void setListener(WalletListener * l) override; -@@ -266,6 +270,7 @@ private: - friend class TransactionHistoryImpl; - friend struct Wallet2CallbackImpl; - friend class AddressBookImpl; -+ friend class CoinsImpl; - friend class SubaddressImpl; - friend class SubaddressAccountImpl; - -@@ -282,6 +287,7 @@ private: - std::unique_ptr m_wallet2Callback; - std::unique_ptr m_addressBook; - std::unique_ptr m_subaddress; -+ std::unique_ptr m_coins; - std::unique_ptr m_subaddressAccount; - - // multi-threaded refresh stuff -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 4edaefefd..8a5c4135e 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -261,6 +261,51 @@ struct AddressBook - virtual int lookupPaymentID(const std::string &payment_id) const = 0; - }; - -+/** -+ * @brief The CoinsInfo - interface for displaying coins information -+ */ -+struct CoinsInfo -+{ -+ virtual ~CoinsInfo() = 0; -+ -+ virtual uint64_t blockHeight() const = 0; -+ virtual std::string hash() const = 0; -+ virtual size_t internalOutputIndex() const = 0; -+ virtual uint64_t globalOutputIndex() const = 0; -+ virtual bool spent() const = 0; -+ virtual bool frozen() const = 0; -+ virtual uint64_t spentHeight() const = 0; -+ virtual uint64_t amount() const = 0; -+ virtual bool rct() const = 0; -+ virtual bool keyImageKnown() const = 0; -+ virtual size_t pkIndex() const = 0; -+ virtual uint32_t subaddrIndex() const = 0; -+ virtual uint32_t subaddrAccount() const = 0; -+ virtual std::string address() const = 0; -+ virtual std::string addressLabel() const = 0; -+ virtual std::string keyImage() const = 0; -+ virtual uint64_t unlockTime() const = 0; -+ virtual bool unlocked() const = 0; -+ virtual std::string pubKey() const = 0; -+ virtual bool coinbase() const = 0; -+ virtual std::string description() const = 0; -+}; -+ -+struct Coins -+{ -+ virtual ~Coins() = 0; -+ virtual int count() const = 0; -+ virtual CoinsInfo * coin(int index) const = 0; -+ virtual std::vector getAll() const = 0; -+ virtual void refresh() = 0; -+ virtual void setFrozen(std::string public_key) = 0; -+ virtual void setFrozen(int index) = 0; -+ virtual void thaw(std::string public_key) = 0; -+ virtual void thaw(int index) = 0; -+ virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; -+ virtual void setDescription(const std::string &public_key, const std::string &description) = 0; -+}; -+ - struct SubaddressRow { - public: - SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): -@@ -854,7 +899,8 @@ struct Wallet - optional> amount, uint32_t mixin_count, - PendingTransaction::Priority = PendingTransaction::Priority_Low, - uint32_t subaddr_account = 0, -- std::set subaddr_indices = {}) = 0; -+ std::set subaddr_indices = {}, -+ const std::set &preferred_inputs = {}) = 0; - - /*! - * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored -@@ -873,7 +919,8 @@ struct Wallet - optional amount, uint32_t mixin_count, - PendingTransaction::Priority = PendingTransaction::Priority_Low, - uint32_t subaddr_account = 0, -- std::set subaddr_indices = {}) = 0; -+ std::set subaddr_indices = {}, -+ const std::set &preferred_inputs = {}) = 0; - - /*! - * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. -@@ -987,6 +1034,7 @@ struct Wallet - - virtual TransactionHistory * history() = 0; - virtual AddressBook * addressBook() = 0; -+ virtual Coins * coins() = 0; - virtual Subaddress * subaddress() = 0; - virtual SubaddressAccount * subaddressAccount() = 0; - virtual void setListener(WalletListener *) = 0; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 41cf1fd41..61601f70c 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -2083,12 +2083,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)); -@@ -2099,6 +2108,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) -@@ -2510,6 +2531,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 lock(m_transfers_mutex); - m_transfers.push_back(transfer_details{}); - transfer_details& td = m_transfers.back(); - td.m_block_height = height; -@@ -2613,6 +2635,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - uint64_t extra_amount = amount - burnt; - if (!pool) - { -+ boost::unique_lock lock(m_transfers_mutex); - transfer_details &td = m_transfers[kit->second]; - td.m_block_height = height; - td.m_internal_output_index = o; -@@ -10487,7 +10510,7 @@ void wallet2::transfer_selected_rct(std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices) -+std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& preferred_input_list) - { - std::vector picks; - float current_output_relatdness = 1.0f; -@@ -10498,6 +10521,9 @@ std::vector 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) -@@ -10518,6 +10544,9 @@ std::vector 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) -@@ -10529,6 +10558,9 @@ std::vector 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) << "]"); -@@ -11101,7 +11133,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::create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs) -+std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& 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(); -@@ -11309,6 +11341,9 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector &ptx_vector, c - return true; - } - --std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices) -+std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list) - { - std::vector unused_transfers_indices; - std::vector unused_dust_indices; -@@ -11908,6 +11943,9 @@ std::vector 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)); -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index b9aa7a00d..67ed81383 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1207,8 +1207,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 &ptx, std::function accept_func = NULL); - bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); -- std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose -- std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices); -+ std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose -+ std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}); - std::vector 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& extra); - std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector& extra); - bool sanity_check(const std::vector &ptx_vector, const std::vector& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; -@@ -1560,6 +1560,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); -@@ -1789,7 +1790,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; -@@ -1830,6 +1833,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. -@@ -1901,7 +1906,7 @@ private: - std::vector 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 pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices); -+ std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& 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; --- -2.39.2 - diff --git a/patches/monero/0004-uint64_t-missing-definition-fix.patch b/patches/monero/0004-uint64_t-missing-definition-fix.patch new file mode 100644 index 0000000..129886b --- /dev/null +++ b/patches/monero/0004-uint64_t-missing-definition-fix.patch @@ -0,0 +1,25 @@ +From 99e6af859234d8b37866e46d95d77e20687767de Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 2 Sep 2024 16:40:31 +0200 +Subject: [PATCH 04/14] uint64_t missing definition fix + +--- + contrib/epee/include/net/http_base.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h +index 4af4da7..ae4c0d0 100644 +--- a/contrib/epee/include/net/http_base.h ++++ b/contrib/epee/include/net/http_base.h +@@ -28,7 +28,7 @@ + + #pragma once + #include "memwipe.h" +- ++#include + #include + + #include +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0005-fix-build-issues.patch b/patches/monero/0005-fix-build-issues.patch deleted file mode 100644 index 985df3e..0000000 --- a/patches/monero/0005-fix-build-issues.patch +++ /dev/null @@ -1,137 +0,0 @@ -From 0f1d5e1296dc1e8c9ee323fa7bdf706ff76df2a7 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 12 Mar 2024 17:59:13 +0100 -Subject: [PATCH 05/16] fix build issues - ---- - contrib/depends/hosts/linux.mk | 8 +++---- - contrib/depends/packages/android_ndk.mk | 2 ++ - contrib/depends/packages/packages.mk | 2 +- - contrib/depends/packages/polyseed.mk | 23 +++++++++++++++++++ - contrib/depends/packages/sodium.mk | 2 +- - .../patches/polyseed/force-static-mingw.patch | 23 +++++++++++++++++++ - 6 files changed, 54 insertions(+), 6 deletions(-) - create mode 100644 contrib/depends/packages/polyseed.mk - create mode 100644 contrib/depends/patches/polyseed/force-static-mingw.patch - -diff --git a/contrib/depends/hosts/linux.mk b/contrib/depends/hosts/linux.mk -index 912fdb03c..b79799f30 100644 ---- a/contrib/depends/hosts/linux.mk -+++ b/contrib/depends/hosts/linux.mk -@@ -11,15 +11,15 @@ linux_debug_CXXFLAGS=$(linux_debug_CFLAGS) - linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC - - ifeq (86,$(findstring 86,$(build_arch))) --i686_linux_CC=gcc -m32 --i686_linux_CXX=g++ -m32 -+i686_linux_CC=i686-linux-gnu-gcc -+i686_linux_CXX=i686-linux-gnu-g++ - i686_linux_AR=ar - i686_linux_RANLIB=ranlib - i686_linux_NM=nm - i686_linux_STRIP=strip - --x86_64_linux_CC=gcc -m64 --x86_64_linux_CXX=g++ -m64 -+x86_64_linux_CC=x86_64-linux-gnu-gcc -+x86_64_linux_CXX=x86_64-linux-gnu-g++ - x86_64_linux_AR=ar - x86_64_linux_RANLIB=ranlib - x86_64_linux_NM=nm -diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk -index 9b8a5332f..5deff76c7 100644 ---- a/contrib/depends/packages/android_ndk.mk -+++ b/contrib/depends/packages/android_ndk.mk -@@ -7,6 +7,8 @@ $(package)_sha256_hash=5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d - define $(package)_set_vars - $(package)_config_opts_arm=--arch arm - $(package)_config_opts_aarch64=--arch arm64 -+$(package)_config_opts_x86_64=--arch x86_64 -+$(package)_config_opts_i686=--arch x86 - endef - - define $(package)_extract_cmds -diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk -index d2d1eca85..8783d4955 100644 ---- a/contrib/depends/packages/packages.mk -+++ b/contrib/depends/packages/packages.mk -@@ -1,4 +1,4 @@ --packages:=boost openssl zeromq libiconv expat unbound -+packages:=boost openssl zeromq libiconv expat unbound polyseed - - # ccache is useless in gitian builds - ifneq ($(GITIAN),1) -diff --git a/contrib/depends/packages/polyseed.mk b/contrib/depends/packages/polyseed.mk -new file mode 100644 -index 000000000..2ddeac621 ---- /dev/null -+++ b/contrib/depends/packages/polyseed.mk -@@ -0,0 +1,23 @@ -+package=polyseed -+$(package)_version=2.0.0 -+$(package)_download_path=https://github.com/tevador/$(package)/archive/refs/tags/ -+$(package)_download_file=v$($(package)_version).tar.gz -+$(package)_file_name=$(package)-$($(package)_version).tar.gz -+$(package)_sha256_hash=f36282fcbcd68d32461b8230c89e1a40661bd46b91109681cec637433004135a -+$(package)_patches=force-static-mingw.patch -+ -+define $(package)_preprocess_cmds -+ patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch -+endef -+ -+define $(package)_config_cmds -+ cmake -DCMAKE_INSTALL_PREFIX=$(host_prefix) -DCMAKE_C_COMPILER=$($(package)_cc) . -+endef -+ -+define $(package)_build_cmds -+ $(MAKE) -+endef -+ -+define $(package)_stage_cmds -+ $(MAKE) DESTDIR=$($(package)_staging_dir) install -+endef -diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk -index 87b34599e..68a5b48ba 100644 ---- a/contrib/depends/packages/sodium.mk -+++ b/contrib/depends/packages/sodium.mk -@@ -6,7 +6,7 @@ $(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e - $(package)_patches=disable-glibc-getrandom-getentropy.patch fix-whitespace.patch - - define $(package)_set_vars --$(package)_config_opts=--enable-static --disable-shared -+$(package)_config_opts=--enable-static --disable-shared --with-pic - $(package)_config_opts+=--prefix=$(host_prefix) - endef - -diff --git a/contrib/depends/patches/polyseed/force-static-mingw.patch b/contrib/depends/patches/polyseed/force-static-mingw.patch -new file mode 100644 -index 000000000..f05cb2b6a ---- /dev/null -+++ b/contrib/depends/patches/polyseed/force-static-mingw.patch -@@ -0,0 +1,23 @@ -+--- a/include/polyseed.h -++++ b/include/polyseed.h -+@@ -93,13 +93,13 @@ Shared/static library definitions -+ - define POLYSEED_STATIC when linking to the static library -+ */ -+ #if defined(_WIN32) || defined(__CYGWIN__) -+- #ifdef POLYSEED_SHARED -+- #define POLYSEED_API __declspec(dllexport) -+- #elif !defined(POLYSEED_STATIC) -+- #define POLYSEED_API __declspec(dllimport) -+- #else -+- #define POLYSEED_API -+- #endif -++// #ifdef POLYSEED_SHARED -++// #define POLYSEED_API __declspec(dllexport) -++// #elif !defined(POLYSEED_STATIC) -++// #define POLYSEED_API __declspec(dllimport) -++// #else -++ #define POLYSEED_API -++// #endif -+ #define POLYSEED_PRIVATE -+ #else -+ #ifdef POLYSEED_SHARED --- -2.39.2 - diff --git a/patches/monero/0005-use-proper-error-handling-in-get_seed.patch b/patches/monero/0005-use-proper-error-handling-in-get_seed.patch new file mode 100644 index 0000000..3562b7b --- /dev/null +++ b/patches/monero/0005-use-proper-error-handling-in-get_seed.patch @@ -0,0 +1,71 @@ +From 6be87b2c75ea663e205f468c986809755839afa3 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 24 Jun 2024 10:49:12 +0200 +Subject: [PATCH 05/14] use proper error handling in get_seed + +--- + src/wallet/api/wallet.cpp | 17 ++++++++++++----- + src/wallet/wallet2.cpp | 5 ++++- + 2 files changed, 16 insertions(+), 6 deletions(-) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 0d85cf3..dfbf4fd 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -826,12 +826,19 @@ bool WalletImpl::close(bool store) + + std::string WalletImpl::seed(const std::string& seed_offset) const + { +- if (checkBackgroundSync("cannot get seed")) ++ if (checkBackgroundSync("cannot get seed")) { ++ setStatusError("cannot get seed"); + return std::string(); +- epee::wipeable_string seed; +- if (m_wallet) +- m_wallet->get_seed(seed, seed_offset); +- return std::string(seed.data(), seed.size()); // TODO ++ } ++ try { ++ epee::wipeable_string seed; ++ if (m_wallet) ++ m_wallet->get_seed(seed, seed_offset); ++ return std::string(seed.data(), seed.size()); // TODO ++ } catch (const std::exception &e) { ++ setStatusError(e.what()); ++ return std::string(); ++ } + } + + std::string WalletImpl::getSeedLanguage() const +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 7913d55..debc0c2 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -1440,11 +1440,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab + bool keys_deterministic = is_deterministic(); + if (!keys_deterministic) + { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "This is not a deterministic wallet"); + std::cout << "This is not a deterministic wallet" << std::endl; + return false; + } + if (seed_language.empty()) + { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "seed_language not set"); + std::cout << "seed_language not set" << std::endl; + return false; + } +@@ -1454,8 +1456,9 @@ 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)) + { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Failed to create seed from key for language: "+seed_language+", falling back to English."); + std::cout << "Failed to create seed from key for language: " << seed_language << std::endl; +- return false; ++ crypto::ElectrumWords::bytes_to_words(key, electrum_words, "English"); + } + + return true; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0006-UR-functions.patch b/patches/monero/0006-UR-functions.patch new file mode 100644 index 0000000..c25b2d0 --- /dev/null +++ b/patches/monero/0006-UR-functions.patch @@ -0,0 +1,1010 @@ +From 3138500cb65a6d72726d611d16cbd4b28d01fbe9 Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Tue, 12 Mar 2024 10:09:50 +0100 +Subject: [PATCH 06/14] UR functions + +This commit adds UR functions for UR tasks, +I believe that the right place to get +UR strings is the wallet code itself, +especially because it allows us to +skip the part when we have to store +things to file to encode them later. +Now we are fully in memory + +Things broken in the commit +- ledger support. + AUTO_LOCK_CMD macro causes compile time + issues with this patch. I don't know why + just yet, this is a issue that I'll fix + later. However (considering the purpose + of this patch) it is not a dealbreaker. +--- + .gitmodules | 4 + + CMakeLists.txt | 4 +- + external/CMakeLists.txt | 1 + + external/bc-ur | 1 + + src/device/device_ledger.cpp | 5 +- + src/wallet/CMakeLists.txt | 1 + + src/wallet/api/pending_transaction.cpp | 33 +++ + src/wallet/api/pending_transaction.h | 1 + + src/wallet/api/unsigned_transaction.cpp | 42 ++++ + src/wallet/api/unsigned_transaction.h | 1 + + src/wallet/api/wallet.cpp | 309 +++++++++++++++++++++++- + src/wallet/api/wallet.h | 8 + + src/wallet/api/wallet2_api.h | 22 +- + src/wallet/wallet2.cpp | 141 +++++++---- + src/wallet/wallet2.h | 3 + + 15 files changed, 519 insertions(+), 57 deletions(-) + create mode 160000 external/bc-ur + +diff --git a/.gitmodules b/.gitmodules +index ffb73fe..72af74d 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -15,3 +15,7 @@ + path = external/supercop + url = https://github.com/monero-project/supercop + branch = monero ++[submodule "external/bc-ur"] ++ path = external/bc-ur ++ url = https://github.com/MrCyjaneK/bc-ur ++ branch = misc +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 8fb03ba..63ee825 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -96,7 +96,8 @@ enable_language(C ASM) + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED ON) + set(CMAKE_C_EXTENSIONS OFF) +-set(CMAKE_CXX_STANDARD 14) ++set(CMAKE_CXX_STANDARD 17) ++add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_FEATURES) # boost: no template named 'unary_function' in namespace 'std'; did you mean '__unary_function'? + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + +@@ -364,6 +365,7 @@ if(NOT MANUAL_SUBMODULES) + endfunction () + + message(STATUS "Checking submodules") ++# check_submodule(external/bc-ur) + check_submodule(external/miniupnp) + check_submodule(external/rapidjson) + check_submodule(external/trezor-common) +diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt +index 5b7f69a..f9ed6a6 100644 +--- a/external/CMakeLists.txt ++++ b/external/CMakeLists.txt +@@ -69,4 +69,5 @@ endif() + add_subdirectory(db_drivers) + add_subdirectory(easylogging++) + add_subdirectory(qrcodegen) ++add_subdirectory(bc-ur) + add_subdirectory(randomx EXCLUDE_FROM_ALL) +diff --git a/external/bc-ur b/external/bc-ur +new file mode 160000 +index 0000000..d82e7c7 +--- /dev/null ++++ b/external/bc-ur +@@ -0,0 +1 @@ ++Subproject commit d82e7c753e710b8000706dc3383b498438795208 +diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp +index 5d0afe1..bb5b6f4 100644 +--- a/src/device/device_ledger.cpp ++++ b/src/device/device_ledger.cpp +@@ -313,12 +313,13 @@ namespace hw { + + /* ======================================================================= */ + /* LOCKER */ +- /* ======================================================================= */ ++ /* ======================================================================= */ + + //automatic lock one more level on device ensuring the current thread is allowed to use it ++ #pragma message ("Warning AUTO_LOCK_CMD is intentionally left broken. This is yet to be fixed.") + #define AUTO_LOCK_CMD() \ + /* lock both mutexes without deadlock*/ \ +- boost::lock(device_locker, command_locker); \ ++ /* boost::lock(device_locker, command_locker); */ \ + /* make sure both already-locked mutexes are unlocked at the end of scope */ \ + boost::lock_guard lock1(device_locker, boost::adopt_lock); \ + boost::lock_guard lock2(command_locker, boost::adopt_lock) +diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt +index 6095f99..b163212 100644 +--- a/src/wallet/CMakeLists.txt ++++ b/src/wallet/CMakeLists.txt +@@ -50,6 +50,7 @@ monero_add_library(wallet + target_link_libraries(wallet + PUBLIC + rpc_base ++ bc-ur + multisig + common + cryptonote_core +diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp +index 70a7027..9c3c26e 100644 +--- a/src/wallet/api/pending_transaction.cpp ++++ b/src/wallet/api/pending_transaction.cpp +@@ -42,6 +42,8 @@ + #include + #include + ++#include "bc-ur/src/bc-ur.hpp" ++ + using namespace std; + + namespace Monero { +@@ -162,6 +164,37 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) + return m_status == Status_Ok; + } + ++std::string PendingTransactionImpl::commitUR(int max_fragment_length) { ++ ++ LOG_PRINT_L3("m_pending_tx size: " << m_pending_tx.size()); ++ ++ try { ++ std::string ptx = m_wallet.m_wallet->dump_tx_to_str(m_pending_tx); ++ m_status = Status_Ok; ++ auto urMessage = ur::string_to_bytes(ptx); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ std::string type; ++ if (m_wallet.watchOnly()) { ++ type = "xmr-txunsigned"; ++ } else { ++ type = "xmr-txsigned"; ++ } ++ ur::UR urData = ur::UR(type, cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } catch (const std::exception &e) { ++ m_errorString = string(tr("Unknown exception: ")) + e.what(); ++ m_status = Status_Error; ++ return ""; ++ } ++} ++ ++ + uint64_t PendingTransactionImpl::amount() const + { + uint64_t result = 0; +diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h +index 0a9779c..403bfe2 100644 +--- a/src/wallet/api/pending_transaction.h ++++ b/src/wallet/api/pending_transaction.h +@@ -46,6 +46,7 @@ public: + int status() const override; + std::string errorString() const override; + bool commit(const std::string &filename = "", bool overwrite = false) override; ++ std::string commitUR(int max_fragment_length = 130) override; + uint64_t amount() const override; + uint64_t dust() const override; + uint64_t fee() const override; +diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp +index 6165a22..fd03e95 100644 +--- a/src/wallet/api/unsigned_transaction.cpp ++++ b/src/wallet/api/unsigned_transaction.cpp +@@ -40,6 +40,8 @@ + #include + #include + ++#include "bc-ur/src/bc-ur.hpp" ++ + using namespace std; + + namespace Monero { +@@ -96,6 +98,46 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName) + return true; + } + ++std::string UnsignedTransactionImpl::signUR(int max_fragment_length) ++{ ++ if(m_wallet.watchOnly()) ++ { ++ m_errorString = tr("This is a watch only wallet"); ++ m_status = Status_Error; ++ return ""; ++ } ++ std::vector ptx; ++ try ++ { ++ tools::wallet2::signed_tx_set signed_txes; ++ std::string signedTx = m_wallet.m_wallet->sign_tx_dump_to_str(m_unsigned_tx_set, ptx, signed_txes); ++ if (signedTx.empty()) ++ { ++ m_errorString = tr("Failed to sign transaction"); ++ m_status = Status_Error; ++ return ""; ++ } ++ auto urMessage = ur::string_to_bytes(signedTx); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ std::string type = "xmr-txsigned"; ++ ur::UR urData = ur::UR(type, cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } ++ catch (const std::exception &e) ++ { ++ m_errorString = string(tr("Failed to sign transaction")) + e.what(); ++ m_status = Status_Error; ++ return ""; ++ } ++ return ""; ++} ++ + //---------------------------------------------------------------------------------------------------- + bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) + { +diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h +index 30065a7..a94b23f 100644 +--- a/src/wallet/api/unsigned_transaction.h ++++ b/src/wallet/api/unsigned_transaction.h +@@ -53,6 +53,7 @@ public: + uint64_t txCount() const override; + // sign txs and save to file + bool sign(const std::string &signedFileName) override; ++ std::string signUR(int max_fragment_length = 130) override; + std::string confirmationMessage() const override {return m_confirmationMessage;} + uint64_t minMixinCount() const override; + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index dfbf4fd..4fb5f75 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -47,6 +47,7 @@ + + #include + #include ++#include "bc-ur/src/bc-ur.hpp" + + using namespace std; + using namespace cryptonote; +@@ -1066,6 +1067,24 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const + return m_wallet->unlocked_balance(accountIndex, false); + } + ++uint64_t WalletImpl::viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const ++{ ++ clearStatus(); ++ ++ std::vector kis; ++ for (const auto &key_image : key_images) { ++ crypto::key_image ki; ++ if (!epee::string_tools::hex_to_pod(key_image, ki)) ++ { ++ setStatusError(tr("failed to parse key image")); ++ return 0; ++ } ++ kis.push_back(ki); ++ } ++ ++ return m_wallet->view_only_balance(accountIndex, kis); ++} ++ + uint64_t WalletImpl::blockChainHeight() const + { + if(m_wallet->light_wallet()) { +@@ -1208,6 +1227,61 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file + return transaction; + } + ++ ++UnsignedTransaction *WalletImpl::loadUnsignedTxUR(const std::string &input) { ++ clearStatus(); ++ UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); ++ auto decoder = ur::URDecoder(); ++ ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ transaction->m_status = UnsignedTransaction::Status::Status_Error; ++ transaction->m_errorString = errorString(); ++ return transaction; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ transaction->m_status = UnsignedTransaction::Status::Status_Error; ++ transaction->m_errorString = errorString(); ++ return transaction; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ ++ if (checkBackgroundSync("cannot load tx") || !m_wallet->parse_unsigned_tx_from_str(data, transaction->m_unsigned_tx_set)){ ++ setStatusError(tr("Failed to load unsigned transactions")); ++ transaction->m_status = UnsignedTransaction::Status::Status_Error; ++ transaction->m_errorString = errorString(); ++ ++ return transaction; ++ } ++ ++ // Check tx data and construct confirmation message ++ std::string extra_message; ++ if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty()) ++ extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str(); ++ transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message); ++ setStatus(transaction->status(), transaction->errorString()); ++ ++ return transaction; ++} ++ + bool WalletImpl::submitTransaction(const string &fileName) { + clearStatus(); + if (checkBackgroundSync("cannot submit tx")) +@@ -1219,7 +1293,7 @@ bool WalletImpl::submitTransaction(const string &fileName) { + setStatus(Status_Ok, tr("Failed to load transaction from file")); + return false; + } +- ++ + if(!transaction->commit()) { + setStatusError(transaction->m_errorString); + return false; +@@ -1228,6 +1302,61 @@ bool WalletImpl::submitTransaction(const string &fileName) { + return true; + } + ++ ++bool WalletImpl::submitTransactionUR(const string &input) { ++ clearStatus(); ++ auto decoder = ur::URDecoder(); ++ ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ return false; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ return false; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ if (checkBackgroundSync("cannot submit tx")) ++ return false; ++ std::unique_ptr transaction(new PendingTransactionImpl(*this)); ++ ++ bool r = m_wallet->parse_tx_from_str(data, transaction->m_pending_tx, NULL); ++ if (!r) { ++ setStatus(Status_Ok, tr("Failed to load transaction from file")); ++ return false; ++ } ++ ++ if(!transaction->commit()) { ++ setStatusError(transaction->m_errorString); ++ return false; ++ } ++ ++ return true; ++} ++ ++ ++bool WalletImpl::hasUnknownKeyImages() const ++{ ++ return m_wallet->has_unknown_key_images(); ++} ++ + bool WalletImpl::exportKeyImages(const string &filename, bool all) + { + if (m_wallet->watch_only()) +@@ -1255,6 +1384,39 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) + return true; + } + ++std::string WalletImpl::exportKeyImagesUR(size_t max_fragment_length, bool all) ++{ ++ if (m_wallet->watch_only()) ++ { ++ setStatusError(tr("Wallet is view only")); ++ return ""; ++ } ++ if (checkBackgroundSync("cannot export key images")) ++ return ""; ++ ++ try ++ { ++ std::string keyImages = m_wallet->export_key_images_str(all); ++ auto urMessage = ur::string_to_bytes(keyImages); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ ur::UR urData = ur::UR("xmr-keyimage", cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error exporting key images: " << e.what()); ++ setStatusError(e.what()); ++ return ""; ++ } ++ return ""; ++} ++ + bool WalletImpl::importKeyImages(const string &filename) + { + if (checkBackgroundSync("cannot import key images")) +@@ -1280,6 +1442,62 @@ bool WalletImpl::importKeyImages(const string &filename) + return true; + } + ++ ++bool WalletImpl::importKeyImagesUR(const string &input) ++{ ++ if (checkBackgroundSync("cannot import key images")) ++ return false; ++ if (!trustedDaemon()) { ++ setStatusError(tr("Key images can only be imported with a trusted daemon")); ++ return false; ++ } ++ try ++ { ++ auto decoder = ur::URDecoder(); ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ return false; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ return false; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ ++ uint64_t spent = 0, unspent = 0; ++ ++ uint64_t height = m_wallet->import_key_images_str(data, spent, unspent); ++ LOG_PRINT_L2("Signed key images imported to height " << height << ", " ++ << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error exporting key images: " << e.what()); ++ setStatusError(string(tr("Failed to import key images: ")) + e.what()); ++ return false; ++ } ++ ++ return true; ++} ++ ++ + bool WalletImpl::exportOutputs(const string &filename, bool all) + { + if (checkBackgroundSync("cannot export outputs")) +@@ -1312,6 +1530,40 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) + return true; + } + ++std::string WalletImpl::exportOutputsUR(size_t max_fragment_length, bool all) ++{ ++ ++ if (checkBackgroundSync("cannot export outputs")) ++ return ""; ++ if (m_wallet->key_on_device()) ++ { ++ setStatusError(string(tr("Not supported on HW wallets."))); ++ return ""; ++ } ++ ++ try ++ { ++ std::string data = m_wallet->export_outputs_to_str(all); ++ auto urMessage = ur::string_to_bytes(data); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ ur::UR urData = ur::UR("xmr-output", cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error exporting outputs: " << e.what()); ++ setStatusError(string(tr("Error exporting outputs: ")) + e.what()); ++ return ""; ++ } ++} ++ ++ + bool WalletImpl::importOutputs(const string &filename) + { + if (checkBackgroundSync("cannot import outputs")) +@@ -1346,6 +1598,61 @@ bool WalletImpl::importOutputs(const string &filename) + return true; + } + ++ ++bool WalletImpl::importOutputsUR(const string &input) ++{ ++ if (checkBackgroundSync("cannot import outputs")) ++ return false; ++ if (m_wallet->key_on_device()) ++ { ++ setStatusError(string(tr("Not supported on HW wallets."))); ++ return false; ++ } ++ ++ try ++ { ++ auto decoder = ur::URDecoder(); ++ ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ return false; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ return false; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ size_t n_outputs = m_wallet->import_outputs_from_str(std::string(data)); ++ LOG_PRINT_L2(std::to_string(n_outputs) << " outputs imported"); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Failed to import outputs: " << e.what()); ++ setStatusError(string(tr("Failed to import outputs: ")) + e.what()); ++ return false; ++ } ++ ++ return true; ++} ++ ++ + bool WalletImpl::scanTransactions(const std::vector &txids) + { + if (checkBackgroundSync("cannot scan transactions")) +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index ac7ce2f..edf8bb8 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -112,6 +112,7 @@ public: + bool setProxy(const std::string &address) override; + uint64_t balance(uint32_t accountIndex = 0) const override; + uint64_t unlockedBalance(uint32_t accountIndex = 0) const override; ++ uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const override; + uint64_t blockChainHeight() const override; + uint64_t approximateBlockChainHeight() const override; + uint64_t estimateBlockChainHeight() const override; +@@ -164,11 +165,18 @@ public: + std::set subaddr_indices = {}) override; + virtual PendingTransaction * createSweepUnmixableTransaction() override; + bool submitTransaction(const std::string &fileName) override; ++ bool submitTransactionUR(const std::string &input) override; + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; ++ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) override; ++ bool hasUnknownKeyImages() const override; + bool exportKeyImages(const std::string &filename, bool all = false) override; ++ std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) override; + bool importKeyImages(const std::string &filename) override; ++ bool importKeyImagesUR(const std::string &input) override; + bool exportOutputs(const std::string &filename, bool all = false) override; ++ std::string exportOutputsUR(size_t max_fragment_length, bool all) override; + bool importOutputs(const std::string &filename) override; ++ bool importOutputsUR(const std::string &filename) override; + bool scanTransactions(const std::vector &txids) override; + + bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index e349df1..764adbf 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -91,6 +91,7 @@ struct PendingTransaction + virtual std::string errorString() const = 0; + // commit transaction or save to file if filename is provided. + virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0; ++ virtual std::string commitUR(int max_fragment_length = 130) = 0; + virtual uint64_t amount() const = 0; + virtual uint64_t dust() const = 0; + virtual uint64_t fee() const = 0; +@@ -160,7 +161,8 @@ struct UnsignedTransaction + * @param signedFileName + * return - true on success + */ +- virtual bool sign(const std::string &signedFileName) = 0; ++ virtual bool sign(const std::string &signedFileName) = 0; ++ virtual std::string signUR(int max_fragment_length = 130) = 0; + }; + + /** +@@ -626,6 +628,7 @@ struct Wallet + result += unlockedBalance(i); + return result; + } ++ virtual uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images = {}) const = 0; + + /** + * @brief watchOnly - checks if wallet is watch only +@@ -884,13 +887,15 @@ struct Wallet + * after object returned + */ + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; +- +- /*! ++ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) = 0; ++ ++ /*! + * \brief submitTransaction - submits transaction in signed tx file + * \return - true on success + */ + virtual bool submitTransaction(const std::string &fileName) = 0; +- ++ virtual bool submitTransactionUR(const std::string &input) = 0; ++ + + /*! + * \brief disposeTransaction - destroys transaction object +@@ -906,6 +911,8 @@ struct Wallet + virtual uint64_t estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const = 0; + ++ virtual bool hasUnknownKeyImages() const = 0; ++ + /*! + * \brief exportKeyImages - exports key images to file + * \param filename +@@ -913,20 +920,22 @@ struct Wallet + * \return - true on success + */ + virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0; +- ++ virtual std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) = 0; + /*! + * \brief importKeyImages - imports key images from file + * \param filename + * \return - true on success + */ + virtual bool importKeyImages(const std::string &filename) = 0; ++ virtual bool importKeyImagesUR(const std::string &input) = 0; + + /*! +- * \brief importOutputs - exports outputs to file ++ * \brief exportOutputs - exports outputs to file + * \param filename + * \return - true on success + */ + virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; ++ virtual std::string exportOutputsUR(size_t max_fragment_length, bool all = false) = 0; + + /*! + * \brief importOutputs - imports outputs from file +@@ -934,6 +943,7 @@ struct Wallet + * \return - true on success + */ + virtual bool importOutputs(const std::string &filename) = 0; ++ virtual bool importOutputsUR(const std::string &filename) = 0; + + /*! + * \brief scanTransactions - scan a list of transaction ids, this operation may reveal the txids to the remote node and affect your privacy +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index debc0c2..dfa3213 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -948,6 +948,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) + return idx + extra; + } + ++bool is_preferred_input(const std::vector& 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); +@@ -6986,6 +6996,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * + return amount; + } + //---------------------------------------------------------------------------------------------------- ++uint64_t wallet2::view_only_balance(uint32_t index_major, const std::vector& selected_inputs) ++{ ++ uint64_t amount = 0; ++ for (const auto &td : m_transfers) { ++ if (is_preferred_input(selected_inputs, td.m_key_image) && ++ !is_spent(td, false) && ++ !td.m_frozen && ++ !td.m_key_image_partial && ++ td.m_key_image_known && ++ td.is_rct() && ++ is_transfer_unlocked(td) && ++ td.m_subaddr_index.major == index_major) ++ { ++ amount += td.m_amount; ++ } ++ } ++ return amount; ++} ++//---------------------------------------------------------------------------------------------------- + std::map wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const + { + std::map amount_per_subaddr; +@@ -7837,9 +7866,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector additional_derivations; + +- // compute public keys from out secret keys +- crypto::public_key tx_pub_key; +- crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key); ++ crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); + std::vector additional_tx_pub_keys; + for (const crypto::secret_key &skey: txs[n].additional_tx_keys) + { +@@ -11235,7 +11262,7 @@ std::vector wallet2::create_transactions_2(std::vector m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) + { +@@ -11285,9 +11312,15 @@ std::vector wallet2::create_transactions_2(std::vector(); ++ // use tobotoht's code path on view-only wallet, otherwise default to upstream ++ bool throwOnNoEnotes = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; + ++ if (throwOnNoEnotes) { ++ THROW_WALLET_EXCEPTION_IF(unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty(), error::wallet_internal_error, "No enotes available to spend") ++ } else { ++ if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty()) ++ return std::vector(); ++ } + // if empty, put dummy entry so that the front can be referenced later in the loop + if (unused_dust_indices_per_subaddr.empty()) + unused_dust_indices_per_subaddr.push_back({}); +@@ -13923,33 +13956,40 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle + + bool wallet2::export_key_images(const std::string &filename, bool all) const + { +- PERF_TIMER(export_key_images); +- std::pair>> ski = export_key_images(all); +- std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); +- const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; +- const uint32_t offset = ski.first; ++ std::string data = export_key_images_str(all); ++ return save_to_file(filename, data); ++} + +- std::string data; +- data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); +- data.resize(4); +- data[0] = offset & 0xff; +- data[1] = (offset >> 8) & 0xff; +- data[2] = (offset >> 16) & 0xff; +- data[3] = (offset >> 24) & 0xff; +- data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); +- data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); +- for (const auto &i: ski.second) +- { +- data += std::string((const char *)&i.first, sizeof(crypto::key_image)); +- data += std::string((const char *)&i.second, sizeof(crypto::signature)); +- } ++std::string wallet2::export_key_images_str(bool all) const ++{ ++ PERF_TIMER(export_key_images); ++ std::pair>> ski = export_key_images(all); ++ std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); ++ const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; ++ const uint32_t offset = ski.first; + +- // encrypt data, keep magic plaintext +- PERF_TIMER(export_key_images_encrypt); +- std::string ciphertext = encrypt_with_view_secret_key(data); +- return save_to_file(filename, magic + ciphertext); ++ std::string data; ++ data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); ++ data.resize(4); ++ data[0] = offset & 0xff; ++ data[1] = (offset >> 8) & 0xff; ++ data[2] = (offset >> 16) & 0xff; ++ data[3] = (offset >> 24) & 0xff; ++ data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); ++ data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); ++ for (const auto &i: ski.second) ++ { ++ data += std::string((const char *)&i.first, sizeof(crypto::key_image)); ++ data += std::string((const char *)&i.second, sizeof(crypto::signature)); ++ } ++ ++ // encrypt data, keep magic plaintext ++ PERF_TIMER(export_key_images_encrypt); ++ std::string ciphertext = encrypt_with_view_secret_key(data); ++ return magic + ciphertext; + } + ++ + //---------------------------------------------------------------------------------------------------- + std::pair>> wallet2::export_key_images(bool all) const + { +@@ -14004,53 +14044,60 @@ std::pair> + return std::make_pair(offset, ski); + } + +-uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) ++uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) { ++ std::string data; ++ ++ bool r = load_from_file(filename, data); ++ ++ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); ++ ++ return import_key_images_str(data, spent, unspent); ++} ++ ++uint64_t wallet2::import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent) + { + PERF_TIMER(import_key_images_fsu); +- std::string data; +- bool r = load_from_file(filename, data); +- +- THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); ++ std::string data_local = data; + + const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC); + if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen)) + { +- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic")); + } + + try + { + PERF_TIMER(import_key_images_decrypt); +- data = decrypt_with_view_secret_key(std::string(data, magiclen)); ++ data_local = decrypt_with_view_secret_key(std::string(data, magiclen)); + } + catch (const std::exception &e) + { +- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what()); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + ": " + e.what()); + } + + const size_t headerlen = 4 + 2 * sizeof(crypto::public_key); +- THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename); +- const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24); +- const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4]; +- const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)]; ++ THROW_WALLET_EXCEPTION_IF(data_local.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ")); ++ const uint32_t offset = (uint8_t)data_local[0] | (((uint8_t)data_local[1]) << 8) | (((uint8_t)data_local[2]) << 16) | (((uint8_t)data_local[3]) << 24); ++ const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data_local[4]; ++ const crypto::public_key &public_view_key = *(const crypto::public_key*)&data_local[4 + sizeof(crypto::public_key)]; + const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; + if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) + { +- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account"); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + " are for a different account"); + } + THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); + + const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); +- THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size, +- error::wallet_internal_error, std::string("Bad data size from file ") + filename); +- size_t nki = (data.size() - headerlen) / record_size; ++ THROW_WALLET_EXCEPTION_IF((data_local.size() - headerlen) % record_size, ++ error::wallet_internal_error, std::string("Bad data size from file ")); ++ size_t nki = (data_local.size() - headerlen) / record_size; + + std::vector> ski; + ski.reserve(nki); + for (size_t n = 0; n < nki; ++n) + { +- crypto::key_image key_image = *reinterpret_cast(&data[headerlen + n * record_size]); +- crypto::signature signature = *reinterpret_cast(&data[headerlen + n * record_size + sizeof(crypto::key_image)]); ++ crypto::key_image key_image = *reinterpret_cast(&data_local[headerlen + n * record_size]); ++ crypto::signature signature = *reinterpret_cast(&data_local[headerlen + n * record_size + sizeof(crypto::key_image)]); + + ski.push_back(std::make_pair(key_image, signature)); + } +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index a050286..1d7e430 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1150,6 +1150,7 @@ private: + // locked & unlocked balance of given or current subaddress account + uint64_t balance(uint32_t subaddr_index_major, bool strict) const; + uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL); ++ uint64_t view_only_balance(uint32_t index_major, const std::vector& selected_inputs = {}); + // locked & unlocked balance per subaddress of given or current subaddress account + std::map balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; + std::map>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict); +@@ -1624,9 +1625,11 @@ private: + std::tuple> export_blockchain() const; + void import_blockchain(const std::tuple> &bc); + bool export_key_images(const std::string &filename, bool all = false) const; ++ std::string export_key_images_str(bool all) const; + std::pair>> export_key_images(bool all = false) const; + uint64_t import_key_images(const std::vector> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true); + uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); ++ uint64_t import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent); + bool import_key_images(std::vector key_images, size_t offset=0, boost::optional> selected_transfers=boost::none); + bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); + crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0006-macos-build-fix.patch b/patches/monero/0006-macos-build-fix.patch deleted file mode 100644 index f7c48b9..0000000 --- a/patches/monero/0006-macos-build-fix.patch +++ /dev/null @@ -1,114 +0,0 @@ -From 3343e3fbbd05546b3858c98afe3bad4673f250c8 Mon Sep 17 00:00:00 2001 -From: Your Name -Date: Thu, 28 Mar 2024 02:03:08 +0100 -Subject: [PATCH 06/16] macos build fix - ---- - contrib/depends/hosts/darwin.mk | 2 + - contrib/depends/packages/polyseed.mk | 13 +++-- - .../polyseed/0001-disable-soname.patch | 48 +++++++++++++++++++ - 3 files changed, 59 insertions(+), 4 deletions(-) - create mode 100644 contrib/depends/patches/polyseed/0001-disable-soname.patch - -diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk -index 79d449054..cbe795081 100644 ---- a/contrib/depends/hosts/darwin.mk -+++ b/contrib/depends/hosts/darwin.mk -@@ -8,6 +8,8 @@ endif - darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -B$(host_prefix)/native/bin/$(host)- - darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)- - -+darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib -+ - darwin_CFLAGS=-pipe - darwin_CXXFLAGS=$(darwin_CFLAGS) - darwin_ARFLAGS=cr -diff --git a/contrib/depends/packages/polyseed.mk b/contrib/depends/packages/polyseed.mk -index 2ddeac621..0071b20f3 100644 ---- a/contrib/depends/packages/polyseed.mk -+++ b/contrib/depends/packages/polyseed.mk -@@ -4,18 +4,23 @@ $(package)_download_path=https://github.com/tevador/$(package)/archive/refs/tags - $(package)_download_file=v$($(package)_version).tar.gz - $(package)_file_name=$(package)-$($(package)_version).tar.gz - $(package)_sha256_hash=f36282fcbcd68d32461b8230c89e1a40661bd46b91109681cec637433004135a --$(package)_patches=force-static-mingw.patch -+$(package)_patches=force-static-mingw.patch 0001-disable-soname.patch - - define $(package)_preprocess_cmds -- patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch -+ patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch &&\ -+ patch -p1 < $($(package)_patch_dir)/0001-disable-soname.patch - endef - - define $(package)_config_cmds -- cmake -DCMAKE_INSTALL_PREFIX=$(host_prefix) -DCMAKE_C_COMPILER=$($(package)_cc) . -+ CC="$($(package)_cc)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" . -+endef -+ -+define $(package)_set_vars -+ $(package)_build_opts=CC="$($(package)_cc)" - endef - - define $(package)_build_cmds -- $(MAKE) -+ CC="$($(package)_cc)" $(MAKE) - endef - - define $(package)_stage_cmds -diff --git a/contrib/depends/patches/polyseed/0001-disable-soname.patch b/contrib/depends/patches/polyseed/0001-disable-soname.patch -new file mode 100644 -index 000000000..bd97dd394 ---- /dev/null -+++ b/contrib/depends/patches/polyseed/0001-disable-soname.patch -@@ -0,0 +1,48 @@ -+From aabafcfc0572651436d024a635483c49042fad7f Mon Sep 17 00:00:00 2001 -+From: Czarek Nakamoto -+Date: Thu, 28 Mar 2024 00:32:51 +0100 -+Subject: [PATCH] disable soname -+ -+--- -+ CMakeLists.txt | 16 +++++++++------- -+ 1 file changed, 9 insertions(+), 7 deletions(-) -+ -+diff --git a/CMakeLists.txt b/CMakeLists.txt -+index 8a8e7c2..5301353 100644 -+--- a/CMakeLists.txt -++++ b/CMakeLists.txt -+@@ -36,6 +36,7 @@ include_directories(polyseed -+ target_compile_definitions(polyseed PRIVATE POLYSEED_SHARED) -+ set_target_properties(polyseed PROPERTIES VERSION 2.0.0 -+ SOVERSION 2 -++ NO_SONAME 1 -+ C_STANDARD 11 -+ C_STANDARD_REQUIRED ON) -+ -+@@ -45,16 +46,17 @@ include_directories(polyseed_static -+ include/) -+ target_compile_definitions(polyseed_static PRIVATE POLYSEED_STATIC) -+ set_target_properties(polyseed_static PROPERTIES OUTPUT_NAME polyseed -++ NO_SONAME 1 -+ C_STANDARD 11 -+ C_STANDARD_REQUIRED ON) -+ -+-add_executable(polyseed-tests -+- tests/tests.c) -+-include_directories(polyseed-tests -+- include/) -+-target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) -+-target_link_libraries(polyseed-tests -+- PRIVATE polyseed_static) -++# add_executable(polyseed-tests -++# tests/tests.c) -++# include_directories(polyseed-tests -++# include/) -++# target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) -++# target_link_libraries(polyseed-tests -++# PRIVATE polyseed_static) -+ -+ include(GNUInstallDirs) -+ install(TARGETS polyseed polyseed_static -+-- -+2.39.2 --- -2.39.2 - diff --git a/patches/monero/0007-add-dummy-device-for-ledger.patch b/patches/monero/0007-add-dummy-device-for-ledger.patch new file mode 100644 index 0000000..b9e1bea --- /dev/null +++ b/patches/monero/0007-add-dummy-device-for-ledger.patch @@ -0,0 +1,604 @@ +From bf8c0d01e60dade9fbdd77c4ce825e6b37d3c6c3 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Wed, 26 Jun 2024 15:04:38 +0200 +Subject: [PATCH 07/14] add dummy device for ledger + +--- + CMakeLists.txt | 6 +- + src/device/CMakeLists.txt | 6 +- + src/device/device.cpp | 10 ++- + src/device/device.hpp | 12 +-- + src/device/device_io_dummy.cpp | 133 ++++++++++++++++++++++++++++++ + src/device/device_io_dummy.hpp | 74 +++++++++++++++++ + src/device/device_ledger.cpp | 6 +- + src/device/device_ledger.hpp | 7 +- + src/wallet/api/wallet.cpp | 94 +++++++++++++++++++++ + src/wallet/api/wallet.h | 18 ++++ + src/wallet/api/wallet2_api.h | 12 +++ + src/wallet/api/wallet_manager.cpp | 12 ++- + 12 files changed, 365 insertions(+), 25 deletions(-) + create mode 100644 src/device/device_io_dummy.cpp + create mode 100644 src/device/device_io_dummy.hpp + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 63ee825..43ef6cd 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -692,8 +692,12 @@ include_directories(${LMDB_INCLUDE}) + include_directories(${LIBUNWIND_INCLUDE}) + link_directories(${LIBUNWIND_LIBRARY_DIRS}) + ++if (HIDAPI_DUMMY) ++ add_definitions(-DHIDAPI_DUMMY) ++endif() ++ + # Final setup for hid +-if (HIDAPI_FOUND) ++if (HIDAPI_FOUND) + message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}") + add_definitions(-DHAVE_HIDAPI) + include_directories(${HIDAPI_INCLUDE_DIR}) +diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt +index e4f1159..14d398f 100644 +--- a/src/device/CMakeLists.txt ++++ b/src/device/CMakeLists.txt +@@ -29,10 +29,11 @@ + set(device_sources + device.cpp + device_default.cpp ++ device_io_dummy.cpp + log.cpp + ) + +-if(HIDAPI_FOUND) ++if(HIDAPI_FOUND OR HIDAPI_DUMMY) + set(device_sources + ${device_sources} + device_ledger.cpp +@@ -45,10 +46,11 @@ set(device_headers + device_io.hpp + device_default.hpp + device_cold.hpp ++ device_io_dummy.hpp + log.hpp + ) + +-if(HIDAPI_FOUND) ++if(HIDAPI_FOUND OR HIDAPI_DUMMY) + set(device_headers + ${device_headers} + device_ledger.hpp +diff --git a/src/device/device.cpp b/src/device/device.cpp +index e6cd358..777584c 100644 +--- a/src/device/device.cpp ++++ b/src/device/device.cpp +@@ -29,7 +29,7 @@ + + #include "device.hpp" + #include "device_default.hpp" +-#ifdef WITH_DEVICE_LEDGER ++#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) + #include "device_ledger.hpp" + #endif + #include "misc_log_ex.h" +@@ -57,7 +57,7 @@ namespace hw { + + device_registry::device_registry(){ + hw::core::register_all(registry); +- #ifdef WITH_DEVICE_LEDGER ++ #if defined(WITH_DEVICE_LEDGER) && !defined(HIDAPI_DUMMY) + hw::ledger::register_all(registry); + #endif + atexit(clear_device_registry); +@@ -83,11 +83,13 @@ namespace hw { + + auto device = registry.find(device_descriptor_lookup); + if (device == registry.end()) { +- MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: "); ++ std::stringstream ss("Device not found in registry: '" + device_descriptor + "'. Known devices: "); ++ MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: \n"); + for( const auto& sm_pair : registry ) { ++ ss << "\n- " + sm_pair.first; + MERROR(" - " << sm_pair.first); + } +- throw std::runtime_error("device not found: " + device_descriptor); ++ throw std::runtime_error("device not found: " + device_descriptor + "\nlalala\n" + ss.str()); + } + return *device->second; + } +diff --git a/src/device/device.hpp b/src/device/device.hpp +index 392703a..ffd4197 100644 +--- a/src/device/device.hpp ++++ b/src/device/device.hpp +@@ -34,17 +34,7 @@ + #include "ringct/rctTypes.h" + #include "cryptonote_config.h" + +- +-#ifndef USE_DEVICE_LEDGER +-#define USE_DEVICE_LEDGER 1 +-#endif +- +-#if !defined(HAVE_HIDAPI) +-#undef USE_DEVICE_LEDGER +-#define USE_DEVICE_LEDGER 0 +-#endif +- +-#if USE_DEVICE_LEDGER ++#if defined(HAVE_HIDAPI) || defined(HIDAPI_DUMMY) + #define WITH_DEVICE_LEDGER + #endif + +diff --git a/src/device/device_io_dummy.cpp b/src/device/device_io_dummy.cpp +new file mode 100644 +index 0000000..edb4bee +--- /dev/null ++++ b/src/device/device_io_dummy.cpp +@@ -0,0 +1,133 @@ ++// Copyright (c) 2017-2022, The Monero Project ++// ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without modification, are ++// permitted provided that the following conditions are met: ++// ++// 1. Redistributions of source code must retain the above copyright notice, this list of ++// conditions and the following disclaimer. ++// ++// 2. Redistributions in binary form must reproduce the above copyright notice, this list ++// of conditions and the following disclaimer in the documentation and/or other ++// materials provided with the distribution. ++// ++// 3. Neither the name of the copyright holder nor the names of its contributors may be ++// used to endorse or promote products derived from this software without specific ++// prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY ++// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ++// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ++// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF ++// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++// ++ ++// device_io_dummy ++// Main goal of device_io_dummy is to emulate a hw::io::device_io without the need to actually ++// connect a device. ++// Many operating systems do not support giving raw USB access to a process (android), or don't ++// support that at all (hi iOS), therefore other means of connection can be used, either USB ++// abstraction provided by the OS (monerujo), or BLE (also monerujo). ++// Monerujo implementation is written in Java, which makes it a nice fit for iOS, but makes the ++// code extremely unportable, so for this reason the code in here is written in CPP. ++// Data transport is made available in wallet2_api.h, so wallet developers can easily plug their ++// own USB/BLE/other transport layer. ++ ++#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) ++#include ++#include "log.hpp" ++#include "device_io_dummy.hpp" ++#include "device_ledger.hpp" ++ ++ ++bool hw::io::device_io_dummy::stateIsConnected = false; ++unsigned char* hw::io::device_io_dummy::sendToDevice = {}; ++size_t hw::io::device_io_dummy::sendToDeviceLength = 0; ++unsigned char* hw::io::device_io_dummy::receivedFromDevice = {}; ++size_t hw::io::device_io_dummy::receivedFromDeviceLength = 0; ++bool hw::io::device_io_dummy::waitsForDeviceSend = false; ++bool hw::io::device_io_dummy::waitsForDeviceReceive = false; ++ ++namespace hw { ++ namespace io { ++ ++#undef MONERO_DEFAULT_LOG_CATEGORY ++#define MONERO_DEFAULT_LOG_CATEGORY "device.io_dummy" ++ device_io_dummy::device_io_dummy(int a, int b, int c, int d) { ++ MDEBUG("device_io_dummy(a: " << a << ", b: " << b << ", c: " << c << ", d: " << d <<")"); ++ } ++ ++ void device_io_dummy::init() { ++ MDEBUG("init()"); ++ } ++ ++ void device_io_dummy::connect(void *params) { ++ MDEBUG("connect(" << params << ")"); ++ stateIsConnected = true; ++ } ++ ++ void device_io_dummy::connect(const std::vector& known_devices) { ++ MDEBUG("connect(["); ++ for (const auto &item: known_devices) { ++ MDEBUG("{ interface_number: " << item.interface_number); ++ MDEBUG(" pid : " << item.pid); ++ MDEBUG(" usage_page : " << item.usage_page); ++ MDEBUG(" vid : " << item.vid << " },"); ++ } ++ MDEBUG("])"); ++ stateIsConnected = true; ++ } ++ ++ bool device_io_dummy::connected() const { ++ MDEBUG("connected()"); ++ return stateIsConnected; ++ } ++ ++ int device_io_dummy::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) { ++ MDEBUG("exchange(): locking mutex"); ++ boost::unique_lock lock(mutex); ++ sendToDevice = command; ++ sendToDeviceLength = cmd_len; ++ waitsForDeviceSend = true; ++ waitsForDeviceReceive = true; ++ MDEBUG("exchange(): waitsForDeviceSend"); ++ // NOTE: waitsForDeviceSend should be changed by external code ++ while (waitsForDeviceSend) { ++ usleep(1000); ++ MDEBUG("exchange(): waitsForDeviceSend (still)"); ++ } ++ ++ MDEBUG("exchange(): waitsForDeviceReceive"); ++ while (waitsForDeviceReceive) { ++ usleep(1000); ++ MDEBUG("exchange(): waitsForDeviceReceive (still)"); ++ } ++ ++ if (receivedFromDeviceLength > max_resp_len) { ++ MDEBUG("exchange(): receivedFromDeviceLength ("<& known_devices); ++ void disconnect(); ++ bool connected() const; ++ ++ int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input); ++ }; ++ }; ++}; ++ ++#endif // HAVE_HIDAPI +diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp +index bb5b6f4..046201a 100644 +--- a/src/device/device_ledger.cpp ++++ b/src/device/device_ledger.cpp +@@ -41,7 +41,7 @@ namespace hw { + + namespace ledger { + +- #ifdef WITH_DEVICE_LEDGER ++ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) + + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "device.ledger" +@@ -299,7 +299,7 @@ namespace hw { + + device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 2000) { + this->id = device_id++; +- this->reset_buffer(); ++ this->reset_buffer(); + this->mode = NONE; + this->has_view_key = false; + this->tx_in_progress = false; +@@ -533,7 +533,9 @@ namespace hw { + + bool device_ledger::connect(void) { + this->disconnect(); ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) + hw_device.connect(known_devices); ++ #endif + this->reset(); + #ifdef DEBUG_HWDEVICE + cryptonote::account_public_address pubkey; +diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp +index 03058c4..39454ca 100644 +--- a/src/device/device_ledger.hpp ++++ b/src/device/device_ledger.hpp +@@ -35,6 +35,7 @@ + #include "device.hpp" + #include "log.hpp" + #include "device_io_hid.hpp" ++#include "device_io_dummy.hpp" + #include + #include + +@@ -56,7 +57,7 @@ namespace hw { + + void register_all(std::map> ®istry); + +- #ifdef WITH_DEVICE_LEDGER ++ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) + + // Origin: https://github.com/LedgerHQ/ledger-app-monero/blob/master/src/monero_types.h + #define SW_OK 0x9000 +@@ -148,7 +149,11 @@ namespace hw { + mutable boost::mutex command_locker; + + //IO ++#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) ++ hw::io::device_io_dummy hw_device; ++#else + hw::io::device_io_hid hw_device; ++#endif + unsigned int length_send; + unsigned char buffer_send[BUFFER_SEND_SIZE]; + unsigned int length_recv; +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 4fb5f75..c73f38e 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -48,6 +48,9 @@ + #include + #include + #include "bc-ur/src/bc-ur.hpp" ++#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) ++#include "device/device_io_dummy.hpp" ++#endif + + using namespace std; + using namespace cryptonote; +@@ -3178,4 +3181,95 @@ uint64_t WalletImpl::getBytesSent() + return m_wallet->get_bytes_sent(); + } + ++ ++// HIDAPI_DUMMY ++bool WalletImpl::getStateIsConnected() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return false; ++ #else ++ return hw::io::device_io_dummy::stateIsConnected; ++ #endif ++} ++ ++unsigned char* WalletImpl::getSendToDevice() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return {}; ++ #else ++ return hw::io::device_io_dummy::sendToDevice; ++ #endif ++} ++ ++size_t WalletImpl::getSendToDeviceLength() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return -1; ++ #else ++ return hw::io::device_io_dummy::sendToDeviceLength; ++ #endif ++} ++ ++unsigned char* WalletImpl::getReceivedFromDevice() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return {}; ++ #else ++ return hw::io::device_io_dummy::receivedFromDevice; ++ #endif ++} ++ ++size_t WalletImpl::getReceivedFromDeviceLength() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return -1; ++ #else ++ return hw::io::device_io_dummy::receivedFromDeviceLength; ++ #endif ++} ++ ++bool WalletImpl::getWaitsForDeviceSend() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return false; ++ #else ++ return hw::io::device_io_dummy::receivedFromDeviceLength; ++ #endif ++} ++ ++bool WalletImpl::getWaitsForDeviceReceive() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return false; ++ #else ++ return hw::io::device_io_dummy::waitsForDeviceReceive; ++ #endif ++} ++ ++void WalletImpl::setDeviceReceivedData(unsigned char* data, size_t len) { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return; ++ #else ++ hw::io::device_io_dummy::receivedFromDevice = static_cast(malloc(len)); ++ hw::io::device_io_dummy::receivedFromDeviceLength = len; ++ memset(hw::io::device_io_dummy::receivedFromDevice, 0, len); ++ memcpy(hw::io::device_io_dummy::receivedFromDevice, data, len); ++ hw::io::device_io_dummy::waitsForDeviceReceive = false; ++ #endif ++} ++ ++void WalletImpl::setDeviceSendData(unsigned char* data, size_t len) { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return; ++ #else ++ hw::io::device_io_dummy::sendToDevice = static_cast(malloc(len)); ++ hw::io::device_io_dummy::sendToDeviceLength = len; ++ memset(hw::io::device_io_dummy::sendToDevice, 0, len); ++ memcpy(hw::io::device_io_dummy::sendToDevice, data, len); ++ hw::io::device_io_dummy::waitsForDeviceSend = false; ++ #endif ++} ++ + } // namespace +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index edf8bb8..4e9c21e 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -301,6 +301,24 @@ private: + // cache connection status to avoid unnecessary RPC calls + mutable std::atomic m_is_connected; + boost::optional m_daemon_login{}; ++ ++ bool getStateIsConnected(); ++ ++ unsigned char *getSendToDevice(); ++ ++ size_t getSendToDeviceLength(); ++ ++ unsigned char *getReceivedFromDevice(); ++ ++ size_t getReceivedFromDeviceLength(); ++ ++ bool getWaitsForDeviceSend(); ++ ++ bool getWaitsForDeviceReceive(); ++ ++ void setDeviceReceivedData(unsigned char *data, size_t len); ++ ++ void setDeviceSendData(unsigned char *data, size_t len); + }; + + +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 764adbf..53ec4ab 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -1150,6 +1150,18 @@ struct Wallet + + //! get bytes sent + virtual uint64_t getBytesSent() = 0; ++ ++ // HIDAPI_DUMMY ++ virtual bool getStateIsConnected() = 0; ++ virtual unsigned char* getSendToDevice() = 0; ++ virtual size_t getSendToDeviceLength() = 0; ++ virtual unsigned char* getReceivedFromDevice() = 0; ++ virtual size_t getReceivedFromDeviceLength() = 0; ++ virtual bool getWaitsForDeviceSend() = 0; ++ virtual bool getWaitsForDeviceReceive() = 0; ++ ++ virtual void setDeviceReceivedData(unsigned char* data, size_t len) = 0; ++ virtual void setDeviceSendData(unsigned char* data, size_t len) = 0; + }; + + /** +diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp +index e81b8f8..277be6a 100644 +--- a/src/wallet/api/wallet_manager.cpp ++++ b/src/wallet/api/wallet_manager.cpp +@@ -188,10 +188,14 @@ bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, + + bool WalletManagerImpl::queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds) const + { +- hw::device::device_type type; +- bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds); +- device_type = static_cast(type); +- return r; ++ try { ++ hw::device::device_type type; ++ bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds); ++ device_type = static_cast(type); ++ return r; ++ } catch (...) { ++ return false; ++ } + } + + std::vector WalletManagerImpl::findWallets(const std::string &path) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0007-fix-make-debug-test-target.patch b/patches/monero/0007-fix-make-debug-test-target.patch deleted file mode 100644 index f39c49e..0000000 --- a/patches/monero/0007-fix-make-debug-test-target.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 8945e9865ef831f85ef58ba3d269f4b17d0270b5 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 2 Apr 2024 01:13:12 +0200 -Subject: [PATCH 07/16] fix `make debug-test` target - ---- - src/simplewallet/simplewallet.cpp | 2 +- - src/wallet/wallet2.h | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp -index f8ed6659c..0aa76b668 100644 ---- a/src/simplewallet/simplewallet.cpp -+++ b/src/simplewallet/simplewallet.cpp -@@ -6929,7 +6929,7 @@ bool simple_wallet::transfer_main(const std::vector &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()) - { -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 67ed81383..fdc1a6212 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1207,7 +1207,7 @@ 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 &ptx, std::function accept_func = NULL); - bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); -- std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose -+ std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose - std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}); - std::vector 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& extra); - std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector& extra); --- -2.39.2 - diff --git a/patches/monero/0008-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/monero/0008-fix-missing-___clear_cache-when-targetting-iOS.patch deleted file mode 100644 index 5b46d7c..0000000 --- a/patches/monero/0008-fix-missing-___clear_cache-when-targetting-iOS.patch +++ /dev/null @@ -1,33 +0,0 @@ -From c7f8510fb4bfb54707053603f5cbcaa8c0bf72d2 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 2 Apr 2024 16:51:56 +0200 -Subject: [PATCH 08/16] fix missing ___clear_cache when targetting iOS - ---- - .gitmodules | 2 +- - external/randomx | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/.gitmodules b/.gitmodules -index 73a23fb35..7ea87a009 100644 ---- a/.gitmodules -+++ b/.gitmodules -@@ -9,7 +9,7 @@ - url = https://github.com/trezor/trezor-common.git - [submodule "external/randomx"] - path = external/randomx -- url = https://github.com/tevador/RandomX -+ url = https://github.com/MrCyjaneK/RandomX - [submodule "external/utf8proc"] - path = external/utf8proc - url = https://github.com/JuliaStrings/utf8proc.git -diff --git a/external/randomx b/external/randomx -index 102f8acf9..ce72c9bb9 160000 ---- a/external/randomx -+++ b/external/randomx -@@ -1 +1 @@ --Subproject commit 102f8acf90a7649ada410de5499a7ec62e49e1da -+Subproject commit ce72c9bb9cb799e0d9171094b9abb009e04c5bfc --- -2.39.2 - diff --git a/patches/monero/0008-polyseed.patch b/patches/monero/0008-polyseed.patch new file mode 100644 index 0000000..9289fc0 --- /dev/null +++ b/patches/monero/0008-polyseed.patch @@ -0,0 +1,1286 @@ +From ed2ce176ea5f1bbe81069a3df4601a141b533b95 Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Tue, 12 Mar 2024 09:42:37 +0100 +Subject: [PATCH 08/14] polyseed + +Co-authored-by: Czarek Nakamoto +--- + .gitmodules | 6 + + CMakeLists.txt | 4 +- + contrib/epee/include/wipeable_string.h | 7 + + contrib/epee/src/wipeable_string.cpp | 10 ++ + external/CMakeLists.txt | 2 + + external/polyseed | 1 + + external/utf8proc | 1 + + src/CMakeLists.txt | 1 + + src/cryptonote_basic/CMakeLists.txt | 1 + + src/cryptonote_basic/account.cpp | 23 +++- + src/cryptonote_basic/account.h | 6 + + src/cryptonote_config.h | 2 + + src/polyseed/CMakeLists.txt | 25 ++++ + src/polyseed/pbkdf2.c | 85 ++++++++++++ + src/polyseed/pbkdf2.h | 46 +++++++ + src/polyseed/polyseed.cpp | 182 +++++++++++++++++++++++++ + src/polyseed/polyseed.hpp | 167 +++++++++++++++++++++++ + src/wallet/api/wallet.cpp | 70 ++++++++++ + src/wallet/api/wallet.h | 10 ++ + src/wallet/api/wallet2_api.h | 25 ++++ + src/wallet/api/wallet_manager.cpp | 9 ++ + src/wallet/api/wallet_manager.h | 10 ++ + src/wallet/wallet2.cpp | 100 ++++++++++++-- + src/wallet/wallet2.h | 30 +++- + 24 files changed, 805 insertions(+), 18 deletions(-) + create mode 160000 external/polyseed + create mode 160000 external/utf8proc + create mode 100644 src/polyseed/CMakeLists.txt + create mode 100644 src/polyseed/pbkdf2.c + create mode 100644 src/polyseed/pbkdf2.h + create mode 100644 src/polyseed/polyseed.cpp + create mode 100644 src/polyseed/polyseed.hpp + +diff --git a/.gitmodules b/.gitmodules +index 72af74d..b838e84 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -11,6 +11,12 @@ + path = external/randomx + url = https://github.com/MrCyjaneK/RandomX + branch = cyjan-fix-ios ++[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 +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 43ef6cd..e7fa90a 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -371,6 +371,8 @@ if(NOT MANUAL_SUBMODULES) + check_submodule(external/trezor-common) + check_submodule(external/randomx) + check_submodule(external/supercop) ++ check_submodule(external/polyseed) ++ check_submodule(external/utf8proc) + endif() + endif() + +@@ -460,7 +462,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) +diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h +index 65977cd..594e15d 100644 +--- a/contrib/epee/include/wipeable_string.h ++++ b/contrib/epee/include/wipeable_string.h +@@ -34,6 +34,7 @@ + #include + #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); +diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp +index b016f2f..f2f365b 100644 +--- a/contrib/epee/src/wipeable_string.cpp ++++ b/contrib/epee/src/wipeable_string.cpp +@@ -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]; ++} ++ + } +diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt +index f9ed6a6..8fcf792 100644 +--- a/external/CMakeLists.txt ++++ b/external/CMakeLists.txt +@@ -69,5 +69,7 @@ endif() + add_subdirectory(db_drivers) + add_subdirectory(easylogging++) + add_subdirectory(qrcodegen) ++add_subdirectory(polyseed EXCLUDE_FROM_ALL) ++add_subdirectory(utf8proc EXCLUDE_FROM_ALL) + add_subdirectory(bc-ur) + add_subdirectory(randomx EXCLUDE_FROM_ALL) +diff --git a/external/polyseed b/external/polyseed +new file mode 160000 +index 0000000..bd79f50 +--- /dev/null ++++ b/external/polyseed +@@ -0,0 +1 @@ ++Subproject commit bd79f5014c331273357277ed8a3d756fb61b9fa1 +diff --git a/external/utf8proc b/external/utf8proc +new file mode 160000 +index 0000000..3de4596 +--- /dev/null ++++ b/external/utf8proc +@@ -0,0 +1 @@ ++Subproject commit 3de4596fbe28956855df2ecb3c11c0bbc3535838 +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 3335d3c..06b708c 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -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) +diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt +index 1414be1..414936a 100644 +--- a/src/cryptonote_basic/CMakeLists.txt ++++ b/src/cryptonote_basic/CMakeLists.txt +@@ -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} +diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp +index 4e87d44..2d556f2 100644 +--- a/src/cryptonote_basic/account.cpp ++++ b/src/cryptonote_basic/account.cpp +@@ -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 &multisig_keys) + { + m_keys.m_account_address.m_spend_public_key = spend_public_key; +diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h +index 93d1d28..1f76feb 100644 +--- a/src/cryptonote_basic/account.h ++++ b/src/cryptonote_basic/account.h +@@ -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 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 &multisig_keys); + const account_keys& get_keys() const; + std::string get_public_address_str(network_type nettype) const; +diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h +index f9e6a6c..3af3a63 100644 +--- a/src/cryptonote_config.h ++++ b/src/cryptonote_config.h +@@ -207,6 +207,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 +diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt +new file mode 100644 +index 0000000..cca4eb7 +--- /dev/null ++++ b/src/polyseed/CMakeLists.txt +@@ -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} ++) +diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c +new file mode 100644 +index 0000000..1c45f47 +--- /dev/null ++++ b/src/polyseed/pbkdf2.c +@@ -0,0 +1,85 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// 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 ++ ++#include ++#include ++ ++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); ++} +\ No newline at end of file +diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h +new file mode 100644 +index 0000000..f6253b9 +--- /dev/null ++++ b/src/polyseed/pbkdf2.h +@@ -0,0 +1,46 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// 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 ++#include ++ ++#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 +\ No newline at end of file +diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp +new file mode 100644 +index 0000000..231a48a +--- /dev/null ++++ b/src/polyseed/polyseed.cpp +@@ -0,0 +1,182 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// 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 ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++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(str), 0, buffer, POLYSEED_STR_SIZE, options); ++ if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) { ++ throw std::runtime_error("Unicode normalization failed"); ++ } ++ ++ result = utf8proc_reencode(buffer, result, options); ++ if (result < 0 || result > POLYSEED_STR_SIZE) { ++ throw std::runtime_error("Unicode normalization failed"); ++ } ++ ++ strcpy(norm, reinterpret_cast(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)); ++ } ++ ++ 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 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& 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 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); ++ } ++} +diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp +new file mode 100644 +index 0000000..2c8c777 +--- /dev/null ++++ b/src/polyseed/polyseed.hpp +@@ -0,0 +1,167 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// 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 ++#include ++#include ++#include ++#include ++#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& 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 ++ 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 +\ No newline at end of file +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index c73f38e..67ac90a 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -728,6 +728,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(m_wallet->get_device_type()); +@@ -845,6 +867,54 @@ std::string WalletImpl::seed(const std::string& seed_offset) const + } + } + ++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> Wallet::getPolyseedLanguages() ++ { ++ std::vector> languages; ++ ++ auto langs = polyseed::get_langs(); ++ for (const auto &lang : langs) { ++ languages.emplace_back(std::pair(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(); +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 4e9c21e..32e1228 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -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 *) {} +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 53ec4ab..be1c370 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -709,6 +709,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> getPolyseedLanguages(); ++ + /** + * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) + */ +@@ -1320,6 +1324,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 +diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp +index 277be6a..da2056d 100644 +--- a/src/wallet/api/wallet_manager.cpp ++++ b/src/wallet/api/wallet_manager.cpp +@@ -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(wallet); +diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h +index a223e1d..28fcd36 100644 +--- a/src/wallet/api/wallet_manager.h ++++ b/src/wallet/api/wallet_manager.h +@@ -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; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index dfa3213..fa346a9 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -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" + { +@@ -1278,7 +1279,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())); + } +@@ -1474,6 +1476,20 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab + 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; ++} ++//---------------------------------------------------------------------------------------------------- + bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase) const + { + bool ready; +@@ -4789,6 +4805,9 @@ boost::optional 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(m_custom_background_key.get().data()), m_custom_background_key.get().size()); +@@ -5028,6 +5047,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + m_enable_multisig = false; + m_allow_mismatched_daemon_version = false; + m_custom_background_key = boost::none; ++ m_polyseed = false; + } + else if(json.IsObject()) + { +@@ -5268,6 +5288,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; +@@ -5587,6 +5610,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. +@@ -5714,7 +5779,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 +@@ -5738,7 +5803,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) +@@ -13635,7 +13700,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 + { + // time of v2 fork + const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? 1520937818 : 1458748658; +@@ -13644,7 +13709,7 @@ uint64_t wallet2::get_approximate_blockchain_height() const + // avg seconds per block + const int seconds_per_block = DIFFICULTY_TARGET_V2; + // Calculated blockchain height +- uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block; ++ uint64_t approx_blockchain_height = fork_block + ((t > 0 ? t : time(NULL)) - fork_time)/seconds_per_block; + // testnet and stagenet got some huge rollbacks, so the estimation is way off + static const uint64_t approximate_rolled_back_blocks = m_nettype == TESTNET ? 342100 : m_nettype == STAGENET ? 60000 : 30000; + if ((m_nettype == TESTNET || m_nettype == STAGENET) && approx_blockchain_height > approximate_rolled_back_blocks) +@@ -15783,15 +15848,6 @@ 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) + { +- uint32_t version; +- if (!check_connection(&version)) +- { +- throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); +- } +- if (version < MAKE_CORE_RPC_VERSION(1, 6)) +- { +- 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; +@@ -15800,7 +15856,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui + { + 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)) ++ { ++ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); ++ } ++ if (version < MAKE_CORE_RPC_VERSION(1, 6)) ++ { ++ throw std::runtime_error("this function requires RPC version 1.6 or higher"); ++ } ++ + std::string err; + uint64_t height_min = 0; + uint64_t height_max = get_daemon_blockchain_height(err) - 1; +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index 1d7e430..91cf2a3 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -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" +@@ -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 +@@ -1088,6 +1103,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. + */ +@@ -1555,8 +1579,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 select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); + std::vector select_available_outputs(const std::function &f); + std::vector select_available_unmixable_outputs(); +@@ -1650,6 +1674,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 &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(); + +@@ -1995,6 +2020,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 m_multisig_signers; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0009-Add-recoverDeterministicWalletFromSpendKey.patch b/patches/monero/0009-Add-recoverDeterministicWalletFromSpendKey.patch deleted file mode 100644 index 0ef9900..0000000 --- a/patches/monero/0009-Add-recoverDeterministicWalletFromSpendKey.patch +++ /dev/null @@ -1,153 +0,0 @@ -From ff4d7f9500e6aca57a78d94599a70b7823ea6ecd Mon Sep 17 00:00:00 2001 -From: Konstantin Ullrich -Date: Wed, 11 Oct 2023 16:47:59 +0200 -Subject: [PATCH 09/16] Add recoverDeterministicWalletFromSpendKey - -This function is used by Cake Wallet to enable polyseed (dart implementation) -support. - -Sourced from the following commit: -https://github.com/cake-tech/monero/commit/cb6fb5ab218878702ed151c0e3d5d68eb2732788 - -Co-authored-by: Godwin Asuquo ---- - src/wallet/api/wallet.cpp | 29 +++++++++++++++++++++++++++++ - src/wallet/api/wallet.h | 4 ++++ - src/wallet/api/wallet2_api.h | 19 +++++++++++++++++++ - src/wallet/api/wallet_manager.cpp | 16 ++++++++++++++++ - src/wallet/api/wallet_manager.h | 7 +++++++ - 5 files changed, 75 insertions(+) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 2106dbd07..ec4ae51ff 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -820,6 +820,35 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c - return status() == Status_Ok; - } - -+bool WalletImpl::recoverDeterministicWalletFromSpendKey(const std::string &path, const std::string &password, const std::string &language, const std::string &spendkey_string) -+{ -+ clearStatus(); -+ m_errorString.clear(); -+ -+ m_recoveringFromSeed = true; -+ m_recoveringFromDevice = false; -+ -+ // parse spend key -+ crypto::secret_key spendkey; -+ if (!spendkey_string.empty()) { -+ cryptonote::blobdata spendkey_data; -+ if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) -+ { -+ setStatusError(tr("failed to parse secret spend key")); -+ return false; -+ } -+ spendkey = *reinterpret_cast(spendkey_data.data()); -+ } -+ -+ try { -+ m_wallet->generate(path, password, spendkey, true, false); -+ setSeedLanguage(language); -+ } catch (const std::exception &e) { -+ setStatusCritical(e.what()); -+ } -+ return status() == Status_Ok; -+} -+ - bool WalletImpl::close(bool store) - { - -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 4a16ca028..d0f443abc 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -77,6 +77,10 @@ public: - const std::string &address_string, - const std::string &viewkey_string, - const std::string &spendkey_string = ""); -+ bool recoverDeterministicWalletFromSpendKey(const std::string &path, -+ const std::string &password, -+ const std::string &language, -+ const std::string &spendkey_string); - bool recoverFromDevice(const std::string &path, - const std::string &password, - const std::string &device_name); -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 8a5c4135e..a585c8212 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -1302,6 +1302,25 @@ struct WalletManager - return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); - } - -+ /*! -+ * \brief recover deterministic wallet from spend key. -+ * \param path Name of wallet file to be created -+ * \param password Password of wallet file -+ * \param language language -+ * \param nettype Network type -+ * \param restoreHeight restore from start height -+ * \param spendKeyString spend key -+ * \param kdf_rounds Number of rounds for key derivation function -+ * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) -+ */ -+ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, -+ const std::string &password, -+ const std::string &language, -+ NetworkType nettype, -+ uint64_t restoreHeight, -+ const std::string &spendKeyString, -+ uint64_t kdf_rounds = 1) = 0; -+ - /*! - * \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead - * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted -diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp -index c79fe25d6..f88bd9e64 100644 ---- a/src/wallet/api/wallet_manager.cpp -+++ b/src/wallet/api/wallet_manager.cpp -@@ -127,6 +127,22 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, - return wallet; - } - -+Wallet *WalletManagerImpl::createDeterministicWalletFromSpendKey(const std::string &path, -+ const std::string &password, -+ const std::string &language, -+ NetworkType nettype, -+ uint64_t restoreHeight, -+ const std::string &spendkey_string, -+ uint64_t kdf_rounds) -+{ -+ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); -+ if(restoreHeight > 0){ -+ wallet->setRefreshFromBlockHeight(restoreHeight); -+ } -+ wallet->recoverDeterministicWalletFromSpendKey(path, password, language, spendkey_string); -+ return wallet; -+} -+ - Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, - const std::string &password, - NetworkType nettype, -diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h -index 28fcd36c9..be3ff8184 100644 ---- a/src/wallet/api/wallet_manager.h -+++ b/src/wallet/api/wallet_manager.h -@@ -67,6 +67,13 @@ public: - const std::string &addressString, - const std::string &viewKeyString, - const std::string &spendKeyString = "") override; -+ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, -+ const std::string &password, -+ const std::string &language, -+ NetworkType nettype, -+ uint64_t restoreHeight, -+ const std::string &spendkey_string, -+ uint64_t kdf_rounds) override; - virtual Wallet * createWalletFromDevice(const std::string &path, - const std::string &password, - NetworkType nettype, --- -2.39.2 - diff --git a/patches/monero/0009-coin-control.patch b/patches/monero/0009-coin-control.patch new file mode 100644 index 0000000..1aac12a --- /dev/null +++ b/patches/monero/0009-coin-control.patch @@ -0,0 +1,979 @@ +From 4d897d9ee1d24710500f4d58e9ccd79fb48cf1d2 Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Tue, 12 Mar 2024 11:07:57 +0100 +Subject: [PATCH 09/14] coin control + +--- + src/simplewallet/simplewallet.cpp | 2 +- + src/wallet/api/CMakeLists.txt | 8 +- + src/wallet/api/coins.cpp | 186 ++++++++++++++++++++++++++++++ + src/wallet/api/coins.h | 40 +++++++ + src/wallet/api/coins_info.cpp | 122 ++++++++++++++++++++ + src/wallet/api/coins_info.h | 71 ++++++++++++ + src/wallet/api/wallet.cpp | 64 +++++++++- + src/wallet/api/wallet.h | 10 +- + src/wallet/api/wallet2_api.h | 52 ++++++++- + src/wallet/wallet2.cpp | 46 +++++++- + src/wallet/wallet2.h | 11 +- + 11 files changed, 593 insertions(+), 19 deletions(-) + create mode 100644 src/wallet/api/coins.cpp + create mode 100644 src/wallet/api/coins.h + create mode 100644 src/wallet/api/coins_info.cpp + create mode 100644 src/wallet/api/coins_info.h + +diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp +index 2c51337..645bd37 100644 +--- a/src/simplewallet/simplewallet.cpp ++++ b/src/simplewallet/simplewallet.cpp +@@ -6930,7 +6930,7 @@ bool simple_wallet::transfer_main(const std::vector &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()) + { +diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt +index af7948d..bb740e2 100644 +--- a/src/wallet/api/CMakeLists.txt ++++ b/src/wallet/api/CMakeLists.txt +@@ -40,7 +40,9 @@ set(wallet_api_sources + address_book.cpp + subaddress.cpp + subaddress_account.cpp +- unsigned_transaction.cpp) ++ unsigned_transaction.cpp ++ coins.cpp ++ coins_info.cpp) + + set(wallet_api_headers + wallet2_api.h) +@@ -55,7 +57,9 @@ set(wallet_api_private_headers + address_book.h + subaddress.h + subaddress_account.h +- unsigned_transaction.h) ++ unsigned_transaction.h ++ coins.h ++ coins_info.h) + + monero_private_headers(wallet_api + ${wallet_api_private_headers}) +diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp +new file mode 100644 +index 0000000..ef12141 +--- /dev/null ++++ b/src/wallet/api/coins.cpp +@@ -0,0 +1,186 @@ ++#include "coins.h" ++#include "coins_info.h" ++#include "wallet.h" ++#include "crypto/hash.h" ++#include "wallet/wallet2.h" ++#include "common_defines.h" ++ ++#include ++#include ++ ++using namespace epee; ++ ++namespace Monero { ++ ++Coins::~Coins() = default; ++ ++CoinsImpl::CoinsImpl(WalletImpl *wallet) ++ : m_wallet(wallet) {} ++ ++CoinsImpl::~CoinsImpl() ++{ ++ for (auto t : m_rows) ++ delete t; ++} ++ ++int CoinsImpl::count() const ++{ ++ boost::shared_lock lock(m_rowsMutex); ++ int result = m_rows.size(); ++ return result; ++} ++ ++CoinsInfo *CoinsImpl::coin(int index) const ++{ ++ boost::shared_lock lock(m_rowsMutex); ++ // sanity check ++ if (index < 0) ++ return nullptr; ++ auto index_ = static_cast(index); ++ return index_ < m_rows.size() ? m_rows[index_] : nullptr; ++} ++ ++std::vector CoinsImpl::getAll() const ++{ ++ boost::shared_lock lock(m_rowsMutex); ++ return m_rows; ++} ++ ++ ++void CoinsImpl::refresh() ++{ ++ LOG_PRINT_L2("Refreshing coins"); ++ ++ boost::unique_lock lock(m_rowsMutex); ++ boost::shared_lock transfers_lock(m_wallet->m_wallet->m_transfers_mutex); ++ ++ // delete old outputs; ++ for (auto t : m_rows) ++ delete t; ++ m_rows.clear(); ++ ++ for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i) ++ { ++ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i); ++ ++ auto ci = new CoinsInfoImpl(); ++ ci->m_blockHeight = td.m_block_height; ++ ci->m_hash = string_tools::pod_to_hex(td.m_txid); ++ ci->m_internalOutputIndex = td.m_internal_output_index; ++ ci->m_globalOutputIndex = td.m_global_output_index; ++ ci->m_spent = td.m_spent; ++ ci->m_frozen = td.m_frozen; ++ ci->m_spentHeight = td.m_spent_height; ++ ci->m_amount = td.m_amount; ++ ci->m_rct = td.m_rct; ++ ci->m_keyImageKnown = td.m_key_image_known; ++ ci->m_pkIndex = td.m_pk_index; ++ ci->m_subaddrIndex = td.m_subaddr_index.minor; ++ ci->m_subaddrAccount = td.m_subaddr_index.major; ++ ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(td.m_subaddr_index); // todo: this is expensive, cache maybe? ++ ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index); ++ ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image); ++ ci->m_unlockTime = td.m_tx.unlock_time; ++ ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td); ++ ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key()); ++ ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen); ++ ci->m_description = m_wallet->m_wallet->get_tx_note(td.m_txid); ++ ++ m_rows.push_back(ci); ++ } ++} ++ ++void CoinsImpl::setFrozen(std::string public_key) ++{ ++ crypto::public_key pk; ++ if (!epee::string_tools::hex_to_pod(public_key, pk)) ++ { ++ LOG_ERROR("Invalid public key: " << public_key); ++ return; ++ } ++ ++ try ++ { ++ m_wallet->m_wallet->freeze(pk); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("setFrozen: " << e.what()); ++ } ++} ++ ++void CoinsImpl::setFrozen(int index) ++{ ++ try ++ { ++ LOG_ERROR("Freezing coin: " << index); ++ m_wallet->m_wallet->freeze(index); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("setLabel: " << e.what()); ++ } ++} ++ ++void CoinsImpl::thaw(std::string public_key) ++{ ++ crypto::public_key pk; ++ if (!epee::string_tools::hex_to_pod(public_key, pk)) ++ { ++ LOG_ERROR("Invalid public key: " << public_key); ++ return; ++ } ++ ++ try ++ { ++ m_wallet->m_wallet->thaw(pk); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("thaw: " << e.what()); ++ } ++} ++ ++void CoinsImpl::thaw(int index) ++{ ++ try ++ { ++ m_wallet->m_wallet->thaw(index); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("thaw: " << e.what()); ++ } ++} ++ ++bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) { ++ return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight); ++} ++ ++void CoinsImpl::setDescription(const std::string &public_key, const std::string &description) ++{ ++ crypto::public_key pk; ++ if (!epee::string_tools::hex_to_pod(public_key, pk)) ++ { ++ LOG_ERROR("Invalid public key: " << public_key); ++ return; ++ } ++ ++ try ++ { ++ const size_t index = m_wallet->m_wallet->get_transfer_details(pk); ++ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index); ++ m_wallet->m_wallet->set_tx_note(td.m_txid, description); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("setDescription: " << e.what()); ++ } ++} ++ ++} // namespace +diff --git a/src/wallet/api/coins.h b/src/wallet/api/coins.h +new file mode 100644 +index 0000000..b7a0a86 +--- /dev/null ++++ b/src/wallet/api/coins.h +@@ -0,0 +1,40 @@ ++#ifndef FEATHER_COINS_H ++#define FEATHER_COINS_H ++ ++#include "wallet/api/wallet2_api.h" ++#include "wallet/wallet2.h" ++ ++namespace Monero { ++ ++class WalletImpl; ++ ++class CoinsImpl : public Coins ++{ ++public: ++ explicit CoinsImpl(WalletImpl * wallet); ++ ~CoinsImpl() override; ++ int count() const override; ++ CoinsInfo * coin(int index) const override; ++ std::vector getAll() const override; ++ void refresh() override; ++ ++ void setFrozen(std::string public_key) override; ++ void setFrozen(int index) override; ++ void thaw(std::string public_key) override; ++ void thaw(int index) override; ++ ++ bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override; ++ ++ void setDescription(const std::string &public_key, const std::string &description) override; ++ ++private: ++ WalletImpl *m_wallet; ++ std::vector m_rows; ++ mutable boost::shared_mutex m_rowsMutex; ++}; ++ ++} ++ ++namespace Bitmonero = Monero; ++ ++#endif //FEATHER_COINS_H +diff --git a/src/wallet/api/coins_info.cpp b/src/wallet/api/coins_info.cpp +new file mode 100644 +index 0000000..5f2c4e1 +--- /dev/null ++++ b/src/wallet/api/coins_info.cpp +@@ -0,0 +1,122 @@ ++#include "coins_info.h" ++ ++using namespace std; ++ ++namespace Monero { ++ ++CoinsInfo::~CoinsInfo() = default; ++ ++CoinsInfoImpl::CoinsInfoImpl() ++ : m_blockHeight(0) ++ , m_internalOutputIndex(0) ++ , m_globalOutputIndex(0) ++ , m_spent(false) ++ , m_frozen(false) ++ , m_spentHeight(0) ++ , m_amount(0) ++ , m_rct(false) ++ , m_keyImageKnown(false) ++ , m_pkIndex(0) ++ , m_subaddrAccount(0) ++ , m_subaddrIndex(0) ++ , m_unlockTime(0) ++ , m_unlocked(false) ++{ ++ ++} ++ ++CoinsInfoImpl::~CoinsInfoImpl() = default; ++ ++uint64_t CoinsInfoImpl::blockHeight() const ++{ ++ return m_blockHeight; ++} ++ ++string CoinsInfoImpl::hash() const ++{ ++ return m_hash; ++} ++ ++size_t CoinsInfoImpl::internalOutputIndex() const { ++ return m_internalOutputIndex; ++} ++ ++uint64_t CoinsInfoImpl::globalOutputIndex() const ++{ ++ return m_globalOutputIndex; ++} ++ ++bool CoinsInfoImpl::spent() const ++{ ++ return m_spent; ++} ++ ++bool CoinsInfoImpl::frozen() const ++{ ++ return m_frozen; ++} ++ ++uint64_t CoinsInfoImpl::spentHeight() const ++{ ++ return m_spentHeight; ++} ++ ++uint64_t CoinsInfoImpl::amount() const ++{ ++ return m_amount; ++} ++ ++bool CoinsInfoImpl::rct() const { ++ return m_rct; ++} ++ ++bool CoinsInfoImpl::keyImageKnown() const { ++ return m_keyImageKnown; ++} ++ ++size_t CoinsInfoImpl::pkIndex() const { ++ return m_pkIndex; ++} ++ ++uint32_t CoinsInfoImpl::subaddrIndex() const { ++ return m_subaddrIndex; ++} ++ ++uint32_t CoinsInfoImpl::subaddrAccount() const { ++ return m_subaddrAccount; ++} ++ ++string CoinsInfoImpl::address() const { ++ return m_address; ++} ++ ++string CoinsInfoImpl::addressLabel() const { ++ return m_addressLabel; ++} ++ ++string CoinsInfoImpl::keyImage() const { ++ return m_keyImage; ++} ++ ++uint64_t CoinsInfoImpl::unlockTime() const { ++ return m_unlockTime; ++} ++ ++bool CoinsInfoImpl::unlocked() const { ++ return m_unlocked; ++} ++ ++string CoinsInfoImpl::pubKey() const { ++ return m_pubKey; ++} ++ ++bool CoinsInfoImpl::coinbase() const { ++ return m_coinbase; ++} ++ ++string CoinsInfoImpl::description() const { ++ return m_description; ++} ++} // namespace ++ ++namespace Bitmonero = Monero; +diff --git a/src/wallet/api/coins_info.h b/src/wallet/api/coins_info.h +new file mode 100644 +index 0000000..c43e45a +--- /dev/null ++++ b/src/wallet/api/coins_info.h +@@ -0,0 +1,71 @@ ++#ifndef FEATHER_COINS_INFO_H ++#define FEATHER_COINS_INFO_H ++ ++#include "wallet/api/wallet2_api.h" ++#include ++#include ++ ++namespace Monero { ++ ++class CoinsImpl; ++ ++class CoinsInfoImpl : public CoinsInfo ++{ ++public: ++ CoinsInfoImpl(); ++ ~CoinsInfoImpl(); ++ ++ virtual uint64_t blockHeight() const override; ++ virtual std::string hash() const override; ++ virtual size_t internalOutputIndex() const override; ++ virtual uint64_t globalOutputIndex() const override; ++ virtual bool spent() const override; ++ virtual bool frozen() const override; ++ virtual uint64_t spentHeight() const override; ++ virtual uint64_t amount() const override; ++ virtual bool rct() const override; ++ virtual bool keyImageKnown() const override; ++ virtual size_t pkIndex() const override; ++ virtual uint32_t subaddrIndex() const override; ++ virtual uint32_t subaddrAccount() const override; ++ virtual std::string address() const override; ++ virtual std::string addressLabel() const override; ++ virtual std::string keyImage() const override; ++ virtual uint64_t unlockTime() const override; ++ virtual bool unlocked() const override; ++ virtual std::string pubKey() const override; ++ virtual bool coinbase() const override; ++ virtual std::string description() const override; ++ ++private: ++ uint64_t m_blockHeight; ++ std::string m_hash; ++ size_t m_internalOutputIndex; ++ uint64_t m_globalOutputIndex; ++ bool m_spent; ++ bool m_frozen; ++ uint64_t m_spentHeight; ++ uint64_t m_amount; ++ bool m_rct; ++ bool m_keyImageKnown; ++ size_t m_pkIndex; ++ uint32_t m_subaddrIndex; ++ uint32_t m_subaddrAccount; ++ std::string m_address; ++ std::string m_addressLabel; ++ std::string m_keyImage; ++ uint64_t m_unlockTime; ++ bool m_unlocked; ++ std::string m_pubKey; ++ bool m_coinbase; ++ std::string m_description; ++ ++ friend class CoinsImpl; ++ ++}; ++ ++} // namespace ++ ++namespace Bitmonero = Monero; ++ ++#endif //FEATHER_COINS_INFO_H +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 67ac90a..6bb3a21 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -35,6 +35,7 @@ + #include "transaction_history.h" + #include "address_book.h" + #include "subaddress.h" ++#include "coins.h" + #include "subaddress_account.h" + #include "common_defines.h" + #include "common/util.h" +@@ -473,6 +474,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) + m_wallet->set_refresh_enabled(false); + m_addressBook.reset(new AddressBookImpl(this)); + m_subaddress.reset(new SubaddressImpl(this)); ++ m_coins.reset(new CoinsImpl(this)); + m_subaddressAccount.reset(new SubaddressAccountImpl(this)); + + +@@ -2046,7 +2048,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat + // - unconfirmed_transfer_details; + // - confirmed_transfer_details) + +-PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) ++PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) + + { + clearStatus(); +@@ -2084,6 +2086,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectornettype(), dst_addr[i])) { + // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982 +@@ -2105,6 +2108,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorunlocked_balance(subaddr_account, true); ++ // if (maxAllowedSpend < amountSum) { ++ // error = true; ++ // setStatusError(tr("Amount you are trying to spend is larger than unlocked amount")); ++ // break; ++ // } ++ std::vector preferred_input_list; ++ if (!preferred_inputs.empty()) { ++ LOG_ERROR("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; ++ } ++ if (m_wallet->frozen(keyImage)) { ++ error = true; ++ setStatusError(tr("refusing to spend frozen coin")); ++ break; ++ } ++ ++ preferred_input_list.push_back(keyImage); ++ } ++ } else { ++ LOG_ERROR("not empty"); ++ ++ boost::shared_lock transfers_lock(m_wallet->m_transfers_mutex); ++ 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); ++ LOG_ERROR("COIN: " << i << ": " << td.amount() << "; "<frozen(td)); ++ if (td.m_spent) continue; ++ LOG_ERROR("is frozen"); ++ if (!td.m_frozen) { ++ LOG_ERROR("isn't:"); ++ LOG_ERROR("hash: " << td.m_key_image << "; " << td.amount()); ++ preferred_input_list.push_back(td.m_key_image); ++ } ++ } ++ } ++ for (const auto &de : preferred_input_list) { ++ LOG_ERROR("preferred input: " << de); ++ } + if (error) { + break; + } +@@ -2129,11 +2178,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorm_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); + +@@ -2214,10 +2263,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector amount, uint32_t mixin_count, +- PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) ++ PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) + + { +- return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices); ++ return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices, preferred_inputs); + } + + PendingTransaction *WalletImpl::createSweepUnmixableTransaction() +@@ -2342,6 +2391,11 @@ AddressBook *WalletImpl::addressBook() + return m_addressBook.get(); + } + ++Coins *WalletImpl::coins() ++{ ++ return m_coins.get(); ++} ++ + Subaddress *WalletImpl::subaddress() + { + return m_subaddress.get(); +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 32e1228..a82f270 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -46,6 +46,7 @@ class PendingTransactionImpl; + class UnsignedTransactionImpl; + class AddressBookImpl; + class SubaddressImpl; ++class CoinsImpl; + class SubaddressAccountImpl; + struct Wallet2CallbackImpl; + +@@ -167,12 +168,14 @@ public: + optional> amount, uint32_t mixin_count, + PendingTransaction::Priority priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) override; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) override; + PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, + optional amount, uint32_t mixin_count, + PendingTransaction::Priority priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) override; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) override; + virtual PendingTransaction * createSweepUnmixableTransaction() override; + bool submitTransaction(const std::string &fileName) override; + bool submitTransactionUR(const std::string &input) override; +@@ -201,6 +204,7 @@ public: + PendingTransaction::Priority priority) const override; + virtual TransactionHistory * history() override; + virtual AddressBook * addressBook() override; ++ virtual Coins * coins() override; + virtual Subaddress * subaddress() override; + virtual SubaddressAccount * subaddressAccount() override; + virtual void setListener(WalletListener * l) override; +@@ -272,6 +276,7 @@ private: + friend class TransactionHistoryImpl; + friend struct Wallet2CallbackImpl; + friend class AddressBookImpl; ++ friend class CoinsImpl; + friend class SubaddressImpl; + friend class SubaddressAccountImpl; + +@@ -288,6 +293,7 @@ private: + std::unique_ptr m_wallet2Callback; + std::unique_ptr m_addressBook; + std::unique_ptr m_subaddress; ++ std::unique_ptr m_coins; + std::unique_ptr m_subaddressAccount; + + // multi-threaded refresh stuff +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index be1c370..013b5bc 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -263,6 +263,51 @@ struct AddressBook + virtual int lookupPaymentID(const std::string &payment_id) const = 0; + }; + ++/** ++ * @brief The CoinsInfo - interface for displaying coins information ++ */ ++struct CoinsInfo ++{ ++ virtual ~CoinsInfo() = 0; ++ ++ virtual uint64_t blockHeight() const = 0; ++ virtual std::string hash() const = 0; ++ virtual size_t internalOutputIndex() const = 0; ++ virtual uint64_t globalOutputIndex() const = 0; ++ virtual bool spent() const = 0; ++ virtual bool frozen() const = 0; ++ virtual uint64_t spentHeight() const = 0; ++ virtual uint64_t amount() const = 0; ++ virtual bool rct() const = 0; ++ virtual bool keyImageKnown() const = 0; ++ virtual size_t pkIndex() const = 0; ++ virtual uint32_t subaddrIndex() const = 0; ++ virtual uint32_t subaddrAccount() const = 0; ++ virtual std::string address() const = 0; ++ virtual std::string addressLabel() const = 0; ++ virtual std::string keyImage() const = 0; ++ virtual uint64_t unlockTime() const = 0; ++ virtual bool unlocked() const = 0; ++ virtual std::string pubKey() const = 0; ++ virtual bool coinbase() const = 0; ++ virtual std::string description() const = 0; ++}; ++ ++struct Coins ++{ ++ virtual ~Coins() = 0; ++ virtual int count() const = 0; ++ virtual CoinsInfo * coin(int index) const = 0; ++ virtual std::vector getAll() const = 0; ++ virtual void refresh() = 0; ++ virtual void setFrozen(std::string public_key) = 0; ++ virtual void setFrozen(int index) = 0; ++ virtual void thaw(std::string public_key) = 0; ++ virtual void thaw(int index) = 0; ++ virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; ++ virtual void setDescription(const std::string &public_key, const std::string &description) = 0; ++}; ++ + struct SubaddressRow { + public: + SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): +@@ -856,7 +901,8 @@ struct Wallet + optional> amount, uint32_t mixin_count, + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) = 0; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) = 0; + + /*! + * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored +@@ -875,7 +921,8 @@ struct Wallet + optional amount, uint32_t mixin_count, + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) = 0; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) = 0; + + /*! + * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. +@@ -994,6 +1041,7 @@ struct Wallet + + virtual TransactionHistory * history() = 0; + virtual AddressBook * addressBook() = 0; ++ virtual Coins * coins() = 0; + virtual Subaddress * subaddress() = 0; + virtual SubaddressAccount * subaddressAccount() = 0; + virtual void setListener(WalletListener *) = 0; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index fa346a9..d060bf9 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -2094,12 +2094,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)); +@@ -2110,6 +2119,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) +@@ -2521,6 +2542,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 lock(m_transfers_mutex); + m_transfers.push_back(transfer_details{}); + transfer_details& td = m_transfers.back(); + td.m_block_height = height; +@@ -2624,6 +2646,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + uint64_t extra_amount = amount - burnt; + if (!pool) + { ++ boost::unique_lock lock(m_transfers_mutex); + transfer_details &td = m_transfers[kit->second]; + td.m_block_height = height; + td.m_internal_output_index = o; +@@ -10500,7 +10523,7 @@ void wallet2::transfer_selected_rct(std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices) ++std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& preferred_input_list) + { + std::vector picks; + float current_output_relatdness = 1.0f; +@@ -10511,6 +10534,9 @@ std::vector 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) +@@ -10531,6 +10557,9 @@ std::vector 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) +@@ -10542,6 +10571,9 @@ std::vector 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) << "]"); +@@ -11114,7 +11146,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::create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs) ++std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& 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(); +@@ -11322,6 +11354,9 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector &ptx_vector, c + return true; + } + +-std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices) ++std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list) + { + std::vector unused_transfers_indices; + std::vector unused_dust_indices; +@@ -11921,6 +11956,9 @@ std::vector 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)); +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index 91cf2a3..bc16d52 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1209,8 +1209,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 &ptx, std::function accept_func = NULL); + bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); +- std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose +- std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices); ++ std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose ++ std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}); + std::vector 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& extra); + std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector& extra); + bool sanity_check(const std::vector &ptx_vector, const std::vector& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; +@@ -1562,6 +1562,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); +@@ -1793,7 +1794,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; +@@ -1834,6 +1837,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. +@@ -1905,7 +1910,7 @@ private: + std::vector 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 pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices); ++ std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& 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; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0010-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch b/patches/monero/0010-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch index e317627..6917daa 100644 --- a/patches/monero/0010-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch +++ b/patches/monero/0010-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch @@ -1,7 +1,7 @@ -From 3cc8b1f822f642b574fe38ff27627b362002eab7 Mon Sep 17 00:00:00 2001 +From 42aca87b69ecdc9fb24d14dd947f8bbffc7f3500 Mon Sep 17 00:00:00 2001 From: M Date: Fri, 21 Apr 2023 15:43:47 -0400 -Subject: [PATCH 10/16] Add hex encoding and tx key getter for +Subject: [PATCH 10/14] Add hex encoding and tx key getter for PendingTransction in wallet api. --- @@ -11,10 +11,10 @@ Subject: [PATCH 10/16] Add hex encoding and tx key getter for 3 files changed, 20 insertions(+) diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp -index 70a702796..be20b478c 100644 +index 9c3c26e..1f714d2 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp -@@ -78,6 +78,22 @@ std::vector PendingTransactionImpl::txid() const +@@ -80,6 +80,22 @@ std::vector PendingTransactionImpl::txid() const return txid; } @@ -38,10 +38,10 @@ index 70a702796..be20b478c 100644 { diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h -index 0a9779c07..2fbaa83d9 100644 +index 403bfe2..0cc6c58 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h -@@ -58,6 +58,8 @@ public: +@@ -59,6 +59,8 @@ public: std::string multisigSignData() override; void signMultisigTx() override; std::vector signersKeys() const override; @@ -51,10 +51,10 @@ index 0a9779c07..2fbaa83d9 100644 private: friend class WalletImpl; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index a585c8212..1c3a11c39 100644 +index 013b5bc..f421fdc 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h -@@ -126,6 +126,8 @@ struct PendingTransaction +@@ -127,6 +127,8 @@ struct PendingTransaction * @return vector of base58-encoded signers' public keys */ virtual std::vector signersKeys() const = 0; @@ -64,5 +64,5 @@ index a585c8212..1c3a11c39 100644 /** -- -2.39.2 +2.39.5 (Apple Git-154) diff --git a/patches/monero/0011-Add-recoverDeterministicWalletFromSpendKey.patch b/patches/monero/0011-Add-recoverDeterministicWalletFromSpendKey.patch new file mode 100644 index 0000000..12d82ff --- /dev/null +++ b/patches/monero/0011-Add-recoverDeterministicWalletFromSpendKey.patch @@ -0,0 +1,153 @@ +From ccddf3be96fd08d1eccbb58a7e8f8c98d07b07f2 Mon Sep 17 00:00:00 2001 +From: Konstantin Ullrich +Date: Wed, 11 Oct 2023 16:47:59 +0200 +Subject: [PATCH 11/14] Add recoverDeterministicWalletFromSpendKey + +This function is used by Cake Wallet to enable polyseed (dart implementation) +support. + +Sourced from the following commit: +https://github.com/cake-tech/monero/commit/cb6fb5ab218878702ed151c0e3d5d68eb2732788 + +Co-authored-by: Godwin Asuquo +--- + src/wallet/api/wallet.cpp | 29 +++++++++++++++++++++++++++++ + src/wallet/api/wallet.h | 4 ++++ + src/wallet/api/wallet2_api.h | 19 +++++++++++++++++++ + src/wallet/api/wallet_manager.cpp | 16 ++++++++++++++++ + src/wallet/api/wallet_manager.h | 7 +++++++ + 5 files changed, 75 insertions(+) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 6bb3a21..5734aff 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -824,6 +824,35 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c + return status() == Status_Ok; + } + ++bool WalletImpl::recoverDeterministicWalletFromSpendKey(const std::string &path, const std::string &password, const std::string &language, const std::string &spendkey_string) ++{ ++ clearStatus(); ++ m_errorString.clear(); ++ ++ m_recoveringFromSeed = true; ++ m_recoveringFromDevice = false; ++ ++ // parse spend key ++ crypto::secret_key spendkey; ++ if (!spendkey_string.empty()) { ++ cryptonote::blobdata spendkey_data; ++ if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) ++ { ++ setStatusError(tr("failed to parse secret spend key")); ++ return false; ++ } ++ spendkey = *reinterpret_cast(spendkey_data.data()); ++ } ++ ++ try { ++ m_wallet->generate(path, password, spendkey, true, false); ++ setSeedLanguage(language); ++ } catch (const std::exception &e) { ++ setStatusCritical(e.what()); ++ } ++ return status() == Status_Ok; ++} ++ + bool WalletImpl::close(bool store) + { + +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index a82f270..9e1fbb4 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -77,6 +77,10 @@ public: + const std::string &address_string, + const std::string &viewkey_string, + const std::string &spendkey_string = ""); ++ bool recoverDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ const std::string &spendkey_string); + bool recoverFromDevice(const std::string &path, + const std::string &password, + const std::string &device_name); +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index f421fdc..c8d6bb1 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -1323,6 +1323,25 @@ struct WalletManager + return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); + } + ++ /*! ++ * \brief recover deterministic wallet from spend key. ++ * \param path Name of wallet file to be created ++ * \param password Password of wallet file ++ * \param language language ++ * \param nettype Network type ++ * \param restoreHeight restore from start height ++ * \param spendKeyString spend key ++ * \param kdf_rounds Number of rounds for key derivation function ++ * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) ++ */ ++ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ NetworkType nettype, ++ uint64_t restoreHeight, ++ const std::string &spendKeyString, ++ uint64_t kdf_rounds = 1) = 0; ++ + /*! + * \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead + * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted +diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp +index da2056d..c200f52 100644 +--- a/src/wallet/api/wallet_manager.cpp ++++ b/src/wallet/api/wallet_manager.cpp +@@ -127,6 +127,22 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, + return wallet; + } + ++Wallet *WalletManagerImpl::createDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ NetworkType nettype, ++ uint64_t restoreHeight, ++ const std::string &spendkey_string, ++ uint64_t kdf_rounds) ++{ ++ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); ++ if(restoreHeight > 0){ ++ wallet->setRefreshFromBlockHeight(restoreHeight); ++ } ++ wallet->recoverDeterministicWalletFromSpendKey(path, password, language, spendkey_string); ++ return wallet; ++} ++ + Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, +diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h +index 28fcd36..be3ff81 100644 +--- a/src/wallet/api/wallet_manager.h ++++ b/src/wallet/api/wallet_manager.h +@@ -67,6 +67,13 @@ public: + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = "") override; ++ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ NetworkType nettype, ++ uint64_t restoreHeight, ++ const std::string &spendkey_string, ++ uint64_t kdf_rounds) override; + virtual Wallet * createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0011-store-crash-fix.patch b/patches/monero/0011-store-crash-fix.patch deleted file mode 100644 index 1ddae67..0000000 --- a/patches/monero/0011-store-crash-fix.patch +++ /dev/null @@ -1,210 +0,0 @@ -From 238c847c153c74953b094d83bfe181a596771d37 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Sat, 11 May 2024 16:25:10 +0200 -Subject: [PATCH 11/16] store crash fix - -Monero wallet crashes (sometimes) when it is syncing, -while the proper solution (that can be seen in feather) -is to not store wallet while it is being synced, this is not -acceptable for mobile wallets where OS can just come -and kill the wallet because it felt like it. - -This patch depends on the background-sync patch, but -to use it as a standalone fix grabbing the definition for the -LOCK_REFRESH macro should be enough. - -tobtoht suggested: -_say you want to store every 15 minutes during background sync. you stop the refresh every 15 minutes. then do something like this in the callback:_ - -``` -// Make sure this doesn't run in the refresh thread -onRefreshed() { - if (hasItBeen15MinutesSinceWeStored()) { - store(); - } - - if (shouldWeContinueRefreshing()) { - startRefresh(); - } -} -``` - -which works for crashes after the wallet is initially synced -but doesn't solve the issue for wallet that are syncing (it -would just wait for it to finish before actually storing). - -Also imo store() functin should store the wallet, no matter -the current state. ---- - src/wallet/api/wallet.cpp | 25 ++++++++++++------------- - src/wallet/api/wallet.h | 1 - - src/wallet/wallet2.cpp | 12 +++++++++++- - src/wallet/wallet2.h | 3 +++ - 4 files changed, 26 insertions(+), 15 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index ec4ae51ff..306c9b8ae 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -56,8 +56,8 @@ using namespace cryptonote; - #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" - - #define LOCK_REFRESH() \ -- bool refresh_enabled = m_refreshEnabled; \ -- m_refreshEnabled = false; \ -+ bool refresh_enabled = m_wallet->get_refresh_enabled(); \ -+ m_wallet->set_refresh_enabled(false); \ - m_wallet->stop(); \ - m_refreshCV.notify_one(); \ - boost::mutex::scoped_lock lock(m_refreshMutex); \ -@@ -467,7 +467,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) - m_wallet2Callback.reset(new Wallet2CallbackImpl(this)); - m_wallet->callback(m_wallet2Callback.get()); - m_refreshThreadDone = false; -- m_refreshEnabled = false; -+ m_wallet->set_refresh_enabled(false); - m_addressBook.reset(new AddressBookImpl(this)); - m_subaddress.reset(new SubaddressImpl(this)); - m_coins.reset(new CoinsImpl(this)); -@@ -1064,6 +1064,7 @@ void WalletImpl::stop() - bool WalletImpl::store(const std::string &path) - { - clearStatus(); -+ LOCK_REFRESH(); - try { - if (path.empty()) { - m_wallet->store(); -@@ -2591,10 +2592,10 @@ void WalletImpl::refreshThreadFunc() - } - - LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired..."); -- LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled); -+ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_wallet->get_refresh_enabled()); - LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status()); - LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan); -- if (m_refreshEnabled) { -+ if (m_wallet->get_refresh_enabled()) { - LOG_PRINT_L3(__FUNCTION__ << ": refreshing..."); - doRefresh(); - } -@@ -2624,12 +2625,12 @@ void WalletImpl::doRefresh() - } - m_wallet->find_and_save_rings(false); - } else { -- LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); -+ LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); - } - } catch (const std::exception &e) { - setStatusError(e.what()); - break; -- }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested -+ }while(m_wallet->get_refresh_enabled() && !rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested - - if (m_wallet2Callback->getListener()) { - m_wallet2Callback->getListener()->refreshed(); -@@ -2639,9 +2640,9 @@ void WalletImpl::doRefresh() - - void WalletImpl::startRefresh() - { -- if (!m_refreshEnabled) { -+ if (!m_wallet->get_refresh_enabled()) { - LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed..."); -- m_refreshEnabled = true; -+ m_wallet->set_refresh_enabled(true); - m_refreshCV.notify_one(); - } - } -@@ -2651,7 +2652,7 @@ void WalletImpl::startRefresh() - void WalletImpl::stopRefresh() - { - if (!m_refreshThreadDone) { -- m_refreshEnabled = false; -+ m_wallet->set_refresh_enabled(false); - m_refreshThreadDone = true; - m_refreshCV.notify_one(); - m_refreshThread.join(); -@@ -2662,9 +2663,7 @@ void WalletImpl::pauseRefresh() - { - LOG_PRINT_L2(__FUNCTION__ << ": refresh paused..."); - // TODO synchronize access -- if (!m_refreshThreadDone) { -- m_refreshEnabled = false; -- } -+ m_wallet->set_refresh_enabled(false); - } - - -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index d0f443abc..2ad2b62a4 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -295,7 +295,6 @@ private: - std::unique_ptr m_subaddressAccount; - - // multi-threaded refresh stuff -- std::atomic m_refreshEnabled; - std::atomic m_refreshThreadDone; - std::atomic m_refreshIntervalMillis; - std::atomic m_refreshShouldRescan; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 61601f70c..4de226a4a 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -1203,6 +1203,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - m_upper_transaction_weight_limit(0), - m_run(true), - m_callback(0), -+ m_refreshEnabled(false), - m_trusted_daemon(false), - m_nettype(nettype), - m_multisig_rounds_passed(0), -@@ -1416,6 +1417,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optionalset_proxy(address); -@@ -4146,8 +4155,9 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo - // infer when we get an incoming output - - bool first = true, last = false; -- while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks) -+ while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks && m_refreshEnabled) - { -+ LOG_ERROR("SYNCING"); - uint64_t next_blocks_start_height; - std::vector next_blocks; - std::vector next_parsed_blocks; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index fdc1a6212..3ce710433 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1086,6 +1086,8 @@ private: - boost::optional daemon_login = boost::none, bool trusted_daemon = true, - epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); - bool set_proxy(const std::string &address); -+ bool get_refresh_enabled(); -+ void set_refresh_enabled(bool val); - - void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } - -@@ -2012,6 +2014,7 @@ private: - - boost::recursive_mutex m_daemon_rpc_mutex; - -+ bool m_refreshEnabled; - bool m_trusted_daemon; - i_wallet2_callback* m_callback; - hw::device::device_type m_key_device_type; --- -2.39.2 - diff --git a/patches/monero/0012-WIP-UR-functions.patch b/patches/monero/0012-WIP-UR-functions.patch deleted file mode 100644 index 564be97..0000000 --- a/patches/monero/0012-WIP-UR-functions.patch +++ /dev/null @@ -1,893 +0,0 @@ -From 1b938a3f98468de3fa06b21a458104cf32831586 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Thu, 16 May 2024 17:28:59 +0200 -Subject: [PATCH 12/16] WIP: UR functions - -This commit adds UR functions for UR tasks, -I believe that the right place to get -UR strings is the wallet code itself, -especially because it allows us to -skip the part when we have to store -things to file to encode them later. -Now we are fully in memory - -Things broken in the commit -- ledger support. - AUTO_LOCK_CMD macro causes compile time - issues with this patch. I don't know why - just yet, this is a issue that I'll fix - later. However (considering the purpose - of this patch) it is not a dealbreaker. ---- - .gitmodules | 3 + - CMakeLists.txt | 4 +- - contrib/depends/hosts/darwin.mk | 2 +- - contrib/depends/toolchain.cmake.in | 2 +- - external/CMakeLists.txt | 1 + - external/bc-ur | 1 + - src/device/device_ledger.cpp | 5 +- - src/wallet/CMakeLists.txt | 1 + - src/wallet/api/pending_transaction.cpp | 33 +++ - src/wallet/api/pending_transaction.h | 1 + - src/wallet/api/unsigned_transaction.cpp | 42 ++++ - src/wallet/api/unsigned_transaction.h | 1 + - src/wallet/api/wallet.cpp | 286 +++++++++++++++++++++++- - src/wallet/api/wallet.h | 6 + - src/wallet/api/wallet2_api.h | 19 +- - src/wallet/wallet2.cpp | 96 ++++---- - src/wallet/wallet2.h | 2 + - 17 files changed, 452 insertions(+), 53 deletions(-) - create mode 160000 external/bc-ur - -diff --git a/.gitmodules b/.gitmodules -index 7ea87a009..a7e1d2cd0 100644 ---- a/.gitmodules -+++ b/.gitmodules -@@ -20,3 +20,6 @@ - path = external/supercop - url = https://github.com/monero-project/supercop - branch = monero -+[submodule "external/bc-ur"] -+ path = external/bc-ur -+ url = https://github.com/MrCyjaneK/bc-ur -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 63b8c5079..6028c0961 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -96,7 +96,8 @@ enable_language(C ASM) - set(CMAKE_C_STANDARD 11) - set(CMAKE_C_STANDARD_REQUIRED ON) - set(CMAKE_C_EXTENSIONS OFF) --set(CMAKE_CXX_STANDARD 14) -+set(CMAKE_CXX_STANDARD 17) -+add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_FEATURES) # boost: no template named 'unary_function' in namespace 'std'; did you mean '__unary_function'? - set(CMAKE_CXX_STANDARD_REQUIRED ON) - set(CMAKE_CXX_EXTENSIONS OFF) - -@@ -364,6 +365,7 @@ if(NOT MANUAL_SUBMODULES) - endfunction () - - message(STATUS "Checking submodules") -+# check_submodule(external/bc-ur) - check_submodule(external/miniupnp) - check_submodule(external/rapidjson) - check_submodule(external/trezor-common) -diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk -index cbe795081..b14ee5c5b 100644 ---- a/contrib/depends/hosts/darwin.mk -+++ b/contrib/depends/hosts/darwin.mk -@@ -1,4 +1,4 @@ --OSX_MIN_VERSION=10.8 -+OSX_MIN_VERSION=10.14 - LD64_VERSION=609 - ifeq (aarch64, $(host_arch)) - CC_target=arm64-apple-$(host_os) -diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in -index f118c754e..f26655d68 100644 ---- a/contrib/depends/toolchain.cmake.in -+++ b/contrib/depends/toolchain.cmake.in -@@ -94,7 +94,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - SET(BREW OFF) - SET(PORT OFF) - SET(CMAKE_OSX_SYSROOT "@prefix@/native/SDK/") -- SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.08") -+ SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") - SET(CMAKE_CXX_STANDARD 14) - SET(LLVM_ENABLE_PIC OFF) - SET(LLVM_ENABLE_PIE OFF) -diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 1b9761d70..0df9f9116 100644 ---- a/external/CMakeLists.txt -+++ b/external/CMakeLists.txt -@@ -69,6 +69,7 @@ endif() - add_subdirectory(db_drivers) - add_subdirectory(easylogging++) - add_subdirectory(qrcodegen) -+add_subdirectory(bc-ur) - add_subdirectory(randomx EXCLUDE_FROM_ALL) - add_subdirectory(polyseed EXCLUDE_FROM_ALL) - add_subdirectory(utf8proc EXCLUDE_FROM_ALL) -\ No newline at end of file -diff --git a/external/bc-ur b/external/bc-ur -new file mode 160000 -index 000000000..d82e7c753 ---- /dev/null -+++ b/external/bc-ur -@@ -0,0 +1 @@ -+Subproject commit d82e7c753e710b8000706dc3383b498438795208 -diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp -index a4b5f3ef0..90675df11 100644 ---- a/src/device/device_ledger.cpp -+++ b/src/device/device_ledger.cpp -@@ -313,12 +313,13 @@ namespace hw { - - /* ======================================================================= */ - /* LOCKER */ -- /* ======================================================================= */ -+ /* ======================================================================= */ - - //automatic lock one more level on device ensuring the current thread is allowed to use it -+ #pragma message ("Warning AUTO_LOCK_CMD is intentionally left broken. This is yet to be fixed.") - #define AUTO_LOCK_CMD() \ - /* lock both mutexes without deadlock*/ \ -- boost::lock(device_locker, command_locker); \ -+ /* boost::lock(device_locker, command_locker); */ \ - /* make sure both already-locked mutexes are unlocked at the end of scope */ \ - boost::lock_guard lock1(device_locker, boost::adopt_lock); \ - boost::lock_guard lock2(command_locker, boost::adopt_lock) -diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt -index 6095f99d5..b163212b7 100644 ---- a/src/wallet/CMakeLists.txt -+++ b/src/wallet/CMakeLists.txt -@@ -50,6 +50,7 @@ monero_add_library(wallet - target_link_libraries(wallet - PUBLIC - rpc_base -+ bc-ur - multisig - common - cryptonote_core -diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp -index be20b478c..1f714d229 100644 ---- a/src/wallet/api/pending_transaction.cpp -+++ b/src/wallet/api/pending_transaction.cpp -@@ -42,6 +42,8 @@ - #include - #include - -+#include "bc-ur/src/bc-ur.hpp" -+ - using namespace std; - - namespace Monero { -@@ -178,6 +180,37 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) - return m_status == Status_Ok; - } - -+std::string PendingTransactionImpl::commitUR(int max_fragment_length) { -+ -+ LOG_PRINT_L3("m_pending_tx size: " << m_pending_tx.size()); -+ -+ try { -+ std::string ptx = m_wallet.m_wallet->dump_tx_to_str(m_pending_tx); -+ m_status = Status_Ok; -+ auto urMessage = ur::string_to_bytes(ptx); -+ ur::ByteVector cbor; -+ ur::CborLite::encodeBytes(cbor, urMessage); -+ std::string type; -+ if (m_wallet.watchOnly()) { -+ type = "xmr-txunsigned"; -+ } else { -+ type = "xmr-txsigned"; -+ } -+ ur::UR urData = ur::UR(type, cbor); -+ auto encoder = ur::UREncoder(urData, max_fragment_length); -+ std::string output; -+ for(size_t i = 0; i < encoder.seq_len(); i++) { -+ output.append("\n"+encoder.next_part()); -+ } -+ return output; -+ } catch (const std::exception &e) { -+ m_errorString = string(tr("Unknown exception: ")) + e.what(); -+ m_status = Status_Error; -+ return ""; -+ } -+} -+ -+ - uint64_t PendingTransactionImpl::amount() const - { - uint64_t result = 0; -diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h -index 2fbaa83d9..0cc6c58e9 100644 ---- a/src/wallet/api/pending_transaction.h -+++ b/src/wallet/api/pending_transaction.h -@@ -46,6 +46,7 @@ public: - int status() const override; - std::string errorString() const override; - bool commit(const std::string &filename = "", bool overwrite = false) override; -+ std::string commitUR(int max_fragment_length = 130) override; - uint64_t amount() const override; - uint64_t dust() const override; - uint64_t fee() const override; -diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp -index 6165a2240..fd03e959d 100644 ---- a/src/wallet/api/unsigned_transaction.cpp -+++ b/src/wallet/api/unsigned_transaction.cpp -@@ -40,6 +40,8 @@ - #include - #include - -+#include "bc-ur/src/bc-ur.hpp" -+ - using namespace std; - - namespace Monero { -@@ -96,6 +98,46 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName) - return true; - } - -+std::string UnsignedTransactionImpl::signUR(int max_fragment_length) -+{ -+ if(m_wallet.watchOnly()) -+ { -+ m_errorString = tr("This is a watch only wallet"); -+ m_status = Status_Error; -+ return ""; -+ } -+ std::vector ptx; -+ try -+ { -+ tools::wallet2::signed_tx_set signed_txes; -+ std::string signedTx = m_wallet.m_wallet->sign_tx_dump_to_str(m_unsigned_tx_set, ptx, signed_txes); -+ if (signedTx.empty()) -+ { -+ m_errorString = tr("Failed to sign transaction"); -+ m_status = Status_Error; -+ return ""; -+ } -+ auto urMessage = ur::string_to_bytes(signedTx); -+ ur::ByteVector cbor; -+ ur::CborLite::encodeBytes(cbor, urMessage); -+ std::string type = "xmr-txsigned"; -+ ur::UR urData = ur::UR(type, cbor); -+ auto encoder = ur::UREncoder(urData, max_fragment_length); -+ std::string output; -+ for(size_t i = 0; i < encoder.seq_len(); i++) { -+ output.append("\n"+encoder.next_part()); -+ } -+ return output; -+ } -+ catch (const std::exception &e) -+ { -+ m_errorString = string(tr("Failed to sign transaction")) + e.what(); -+ m_status = Status_Error; -+ return ""; -+ } -+ return ""; -+} -+ - //---------------------------------------------------------------------------------------------------- - bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) - { -diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h -index 30065a7fa..a94b23f75 100644 ---- a/src/wallet/api/unsigned_transaction.h -+++ b/src/wallet/api/unsigned_transaction.h -@@ -53,6 +53,7 @@ public: - uint64_t txCount() const override; - // sign txs and save to file - bool sign(const std::string &signedFileName) override; -+ std::string signUR(int max_fragment_length = 130) override; - std::string confirmationMessage() const override {return m_confirmationMessage;} - uint64_t minMixinCount() const override; - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 306c9b8ae..5ca190c7d 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -48,6 +48,7 @@ - - #include - #include -+#include "bc-ur/src/bc-ur.hpp" - - using namespace std; - using namespace cryptonote; -@@ -1321,6 +1322,61 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file - return transaction; - } - -+ -+UnsignedTransaction *WalletImpl::loadUnsignedTxUR(const std::string &input) { -+ clearStatus(); -+ UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); -+ auto decoder = ur::URDecoder(); -+ -+ std::string delimiter = "\n"; -+ std::string inp = input; -+ size_t pos = 0; -+ std::string token; -+ while ((pos = inp.find(delimiter)) != std::string::npos) { -+ token = inp.substr(0, pos); -+ decoder.receive_part(token); -+ inp.erase(0, pos + delimiter.length()); -+ } -+ decoder.receive_part(inp); -+ -+ if (decoder.is_failure()) { -+ setStatusError(decoder.result_error().what()); -+ transaction->m_status = UnsignedTransaction::Status::Status_Error; -+ transaction->m_errorString = errorString(); -+ return transaction; -+ } -+ -+ if (!decoder.is_complete()) { -+ setStatusError("file ended but ur didn't complete"); -+ transaction->m_status = UnsignedTransaction::Status::Status_Error; -+ transaction->m_errorString = errorString(); -+ return transaction; -+ } -+ -+ std::string data; -+ auto cbor = decoder.result_ur().cbor(); -+ auto i = cbor.begin(); -+ auto end = cbor.end(); -+ ur::CborLite::decodeBytes(i, end, data); -+ -+ if (checkBackgroundSync("cannot load tx") || !m_wallet->parse_unsigned_tx_from_str(data, transaction->m_unsigned_tx_set)){ -+ setStatusError(tr("Failed to load unsigned transactions")); -+ transaction->m_status = UnsignedTransaction::Status::Status_Error; -+ transaction->m_errorString = errorString(); -+ -+ return transaction; -+ } -+ -+ // Check tx data and construct confirmation message -+ std::string extra_message; -+ if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty()) -+ extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str(); -+ transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message); -+ setStatus(transaction->status(), transaction->errorString()); -+ -+ return transaction; -+} -+ - bool WalletImpl::submitTransaction(const string &fileName) { - clearStatus(); - if (checkBackgroundSync("cannot submit tx")) -@@ -1332,7 +1388,7 @@ bool WalletImpl::submitTransaction(const string &fileName) { - setStatus(Status_Ok, tr("Failed to load transaction from file")); - return false; - } -- -+ - if(!transaction->commit()) { - setStatusError(transaction->m_errorString); - return false; -@@ -1341,6 +1397,56 @@ bool WalletImpl::submitTransaction(const string &fileName) { - return true; - } - -+ -+bool WalletImpl::submitTransactionUR(const string &input) { -+ clearStatus(); -+ auto decoder = ur::URDecoder(); -+ -+ std::string delimiter = "\n"; -+ std::string inp = input; -+ size_t pos = 0; -+ std::string token; -+ while ((pos = inp.find(delimiter)) != std::string::npos) { -+ token = inp.substr(0, pos); -+ decoder.receive_part(token); -+ inp.erase(0, pos + delimiter.length()); -+ } -+ decoder.receive_part(inp); -+ -+ if (decoder.is_failure()) { -+ setStatusError(decoder.result_error().what()); -+ return false; -+ } -+ -+ if (!decoder.is_complete()) { -+ setStatusError("file ended but ur didn't complete"); -+ return false; -+ } -+ -+ std::string data; -+ auto cbor = decoder.result_ur().cbor(); -+ auto i = cbor.begin(); -+ auto end = cbor.end(); -+ ur::CborLite::decodeBytes(i, end, data); -+ if (checkBackgroundSync("cannot submit tx")) -+ return false; -+ std::unique_ptr transaction(new PendingTransactionImpl(*this)); -+ -+ bool r = m_wallet->parse_tx_from_str(data, transaction->m_pending_tx, NULL); -+ if (!r) { -+ setStatus(Status_Ok, tr("Failed to load transaction from file")); -+ return false; -+ } -+ -+ if(!transaction->commit()) { -+ setStatusError(transaction->m_errorString); -+ return false; -+ } -+ -+ return true; -+} -+ -+ - bool WalletImpl::hasUnknownKeyImages() const - { - return m_wallet->has_unknown_key_images(); -@@ -1373,6 +1479,39 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) - return true; - } - -+std::string WalletImpl::exportKeyImagesUR(size_t max_fragment_length, bool all) -+{ -+ if (m_wallet->watch_only()) -+ { -+ setStatusError(tr("Wallet is view only")); -+ return ""; -+ } -+ if (checkBackgroundSync("cannot export key images")) -+ return ""; -+ -+ try -+ { -+ std::string keyImages = m_wallet->export_key_images_str(all); -+ auto urMessage = ur::string_to_bytes(keyImages); -+ ur::ByteVector cbor; -+ ur::CborLite::encodeBytes(cbor, urMessage); -+ ur::UR urData = ur::UR("xmr-keyimage", cbor); -+ auto encoder = ur::UREncoder(urData, max_fragment_length); -+ std::string output; -+ for(size_t i = 0; i < encoder.seq_len(); i++) { -+ output.append("\n"+encoder.next_part()); -+ } -+ return output; -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Error exporting key images: " << e.what()); -+ setStatusError(e.what()); -+ return ""; -+ } -+ return ""; -+} -+ - bool WalletImpl::importKeyImages(const string &filename) - { - if (checkBackgroundSync("cannot import key images")) -@@ -1398,6 +1537,62 @@ bool WalletImpl::importKeyImages(const string &filename) - return true; - } - -+ -+bool WalletImpl::importKeyImagesUR(const string &input) -+{ -+ if (checkBackgroundSync("cannot import key images")) -+ return false; -+ if (!trustedDaemon()) { -+ setStatusError(tr("Key images can only be imported with a trusted daemon")); -+ return false; -+ } -+ try -+ { -+ auto decoder = ur::URDecoder(); -+ std::string delimiter = "\n"; -+ std::string inp = input; -+ size_t pos = 0; -+ std::string token; -+ while ((pos = inp.find(delimiter)) != std::string::npos) { -+ token = inp.substr(0, pos); -+ decoder.receive_part(token); -+ inp.erase(0, pos + delimiter.length()); -+ } -+ decoder.receive_part(inp); -+ -+ if (decoder.is_failure()) { -+ setStatusError(decoder.result_error().what()); -+ return false; -+ } -+ -+ if (!decoder.is_complete()) { -+ setStatusError("file ended but ur didn't complete"); -+ return false; -+ } -+ -+ std::string data; -+ auto cbor = decoder.result_ur().cbor(); -+ auto i = cbor.begin(); -+ auto end = cbor.end(); -+ ur::CborLite::decodeBytes(i, end, data); -+ -+ uint64_t spent = 0, unspent = 0; -+ -+ uint64_t height = m_wallet->import_key_images_str(data, spent, unspent); -+ LOG_PRINT_L2("Signed key images imported to height " << height << ", " -+ << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Error exporting key images: " << e.what()); -+ setStatusError(string(tr("Failed to import key images: ")) + e.what()); -+ return false; -+ } -+ -+ return true; -+} -+ -+ - bool WalletImpl::exportOutputs(const string &filename, bool all) - { - if (checkBackgroundSync("cannot export outputs")) -@@ -1430,6 +1625,40 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) - return true; - } - -+std::string WalletImpl::exportOutputsUR(size_t max_fragment_length, bool all) -+{ -+ -+ if (checkBackgroundSync("cannot export outputs")) -+ return ""; -+ if (m_wallet->key_on_device()) -+ { -+ setStatusError(string(tr("Not supported on HW wallets."))); -+ return ""; -+ } -+ -+ try -+ { -+ std::string data = m_wallet->export_outputs_to_str(all); -+ auto urMessage = ur::string_to_bytes(data); -+ ur::ByteVector cbor; -+ ur::CborLite::encodeBytes(cbor, urMessage); -+ ur::UR urData = ur::UR("xmr-output", cbor); -+ auto encoder = ur::UREncoder(urData, max_fragment_length); -+ std::string output; -+ for(size_t i = 0; i < encoder.seq_len(); i++) { -+ output.append("\n"+encoder.next_part()); -+ } -+ return output; -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Error exporting outputs: " << e.what()); -+ setStatusError(string(tr("Error exporting outputs: ")) + e.what()); -+ return ""; -+ } -+} -+ -+ - bool WalletImpl::importOutputs(const string &filename) - { - if (checkBackgroundSync("cannot import outputs")) -@@ -1464,6 +1693,61 @@ bool WalletImpl::importOutputs(const string &filename) - return true; - } - -+ -+bool WalletImpl::importOutputsUR(const string &input) -+{ -+ if (checkBackgroundSync("cannot import outputs")) -+ return false; -+ if (m_wallet->key_on_device()) -+ { -+ setStatusError(string(tr("Not supported on HW wallets."))); -+ return false; -+ } -+ -+ try -+ { -+ auto decoder = ur::URDecoder(); -+ -+ std::string delimiter = "\n"; -+ std::string inp = input; -+ size_t pos = 0; -+ std::string token; -+ while ((pos = inp.find(delimiter)) != std::string::npos) { -+ token = inp.substr(0, pos); -+ decoder.receive_part(token); -+ inp.erase(0, pos + delimiter.length()); -+ } -+ decoder.receive_part(inp); -+ -+ if (decoder.is_failure()) { -+ setStatusError(decoder.result_error().what()); -+ return false; -+ } -+ -+ if (!decoder.is_complete()) { -+ setStatusError("file ended but ur didn't complete"); -+ return false; -+ } -+ -+ std::string data; -+ auto cbor = decoder.result_ur().cbor(); -+ auto i = cbor.begin(); -+ auto end = cbor.end(); -+ ur::CborLite::decodeBytes(i, end, data); -+ size_t n_outputs = m_wallet->import_outputs_from_str(std::string(data)); -+ LOG_PRINT_L2(std::to_string(n_outputs) << " outputs imported"); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to import outputs: " << e.what()); -+ setStatusError(string(tr("Failed to import outputs: ")) + e.what()); -+ return false; -+ } -+ -+ return true; -+} -+ -+ - bool WalletImpl::scanTransactions(const std::vector &txids) - { - if (checkBackgroundSync("cannot scan transactions")) -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 2ad2b62a4..febc93119 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -182,12 +182,18 @@ public: - const std::set &preferred_inputs = {}) override; - virtual PendingTransaction * createSweepUnmixableTransaction() override; - bool submitTransaction(const std::string &fileName) override; -+ bool submitTransactionUR(const std::string &input) override; - virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; -+ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) override; - bool hasUnknownKeyImages() const override; - bool exportKeyImages(const std::string &filename, bool all = false) override; -+ std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) override; - bool importKeyImages(const std::string &filename) override; -+ bool importKeyImagesUR(const std::string &input) override; - bool exportOutputs(const std::string &filename, bool all = false) override; -+ std::string exportOutputsUR(size_t max_fragment_length, bool all) override; - bool importOutputs(const std::string &filename) override; -+ bool importOutputsUR(const std::string &filename) override; - bool scanTransactions(const std::vector &txids) override; - - bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 1c3a11c39..2bbb32c8b 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -91,6 +91,7 @@ struct PendingTransaction - virtual std::string errorString() const = 0; - // commit transaction or save to file if filename is provided. - virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0; -+ virtual std::string commitUR(int max_fragment_length = 130) = 0; - virtual uint64_t amount() const = 0; - virtual uint64_t dust() const = 0; - virtual uint64_t fee() const = 0; -@@ -162,7 +163,8 @@ struct UnsignedTransaction - * @param signedFileName - * return - true on success - */ -- virtual bool sign(const std::string &signedFileName) = 0; -+ virtual bool sign(const std::string &signedFileName) = 0; -+ virtual std::string signUR(int max_fragment_length = 130) = 0; - }; - - /** -@@ -938,13 +940,15 @@ struct Wallet - * after object returned - */ - virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; -- -- /*! -+ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) = 0; -+ -+ /*! - * \brief submitTransaction - submits transaction in signed tx file - * \return - true on success - */ - virtual bool submitTransaction(const std::string &fileName) = 0; -- -+ virtual bool submitTransactionUR(const std::string &input) = 0; -+ - - /*! - * \brief disposeTransaction - destroys transaction object -@@ -969,20 +973,22 @@ struct Wallet - * \return - true on success - */ - virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0; -- -+ virtual std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) = 0; - /*! - * \brief importKeyImages - imports key images from file - * \param filename - * \return - true on success - */ - virtual bool importKeyImages(const std::string &filename) = 0; -+ virtual bool importKeyImagesUR(const std::string &input) = 0; - - /*! -- * \brief importOutputs - exports outputs to file -+ * \brief exportOutputs - exports outputs to file - * \param filename - * \return - true on success - */ - virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; -+ virtual std::string exportOutputsUR(size_t max_fragment_length, bool all = false) = 0; - - /*! - * \brief importOutputs - imports outputs from file -@@ -990,6 +996,7 @@ struct Wallet - * \return - true on success - */ - virtual bool importOutputs(const std::string &filename) = 0; -+ virtual bool importOutputsUR(const std::string &filename) = 0; - - /*! - * \brief scanTransactions - scan a list of transaction ids, this operation may reveal the txids to the remote node and affect your privacy -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 4de226a4a..7d97e683b 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -14056,33 +14056,40 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle - - bool wallet2::export_key_images(const std::string &filename, bool all) const - { -- PERF_TIMER(export_key_images); -- std::pair>> ski = export_key_images(all); -- std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); -- const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; -- const uint32_t offset = ski.first; -+ std::string data = export_key_images_str(all); -+ return save_to_file(filename, data); -+} - -- std::string data; -- data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); -- data.resize(4); -- data[0] = offset & 0xff; -- data[1] = (offset >> 8) & 0xff; -- data[2] = (offset >> 16) & 0xff; -- data[3] = (offset >> 24) & 0xff; -- data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); -- data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); -- for (const auto &i: ski.second) -- { -- data += std::string((const char *)&i.first, sizeof(crypto::key_image)); -- data += std::string((const char *)&i.second, sizeof(crypto::signature)); -- } -+std::string wallet2::export_key_images_str(bool all) const -+{ -+ PERF_TIMER(export_key_images); -+ std::pair>> ski = export_key_images(all); -+ std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); -+ const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; -+ const uint32_t offset = ski.first; -+ -+ std::string data; -+ data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); -+ data.resize(4); -+ data[0] = offset & 0xff; -+ data[1] = (offset >> 8) & 0xff; -+ data[2] = (offset >> 16) & 0xff; -+ data[3] = (offset >> 24) & 0xff; -+ data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); -+ data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); -+ for (const auto &i: ski.second) -+ { -+ data += std::string((const char *)&i.first, sizeof(crypto::key_image)); -+ data += std::string((const char *)&i.second, sizeof(crypto::signature)); -+ } - -- // encrypt data, keep magic plaintext -- PERF_TIMER(export_key_images_encrypt); -- std::string ciphertext = encrypt_with_view_secret_key(data); -- return save_to_file(filename, magic + ciphertext); -+ // encrypt data, keep magic plaintext -+ PERF_TIMER(export_key_images_encrypt); -+ std::string ciphertext = encrypt_with_view_secret_key(data); -+ return magic + ciphertext; - } - -+ - //---------------------------------------------------------------------------------------------------- - std::pair>> wallet2::export_key_images(bool all) const - { -@@ -14137,53 +14144,60 @@ std::pair> - return std::make_pair(offset, ski); - } - --uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) -+uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) { -+ std::string data; -+ -+ bool r = load_from_file(filename, data); -+ -+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); -+ -+ return import_key_images_str(data, spent, unspent); -+} -+ -+uint64_t wallet2::import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent) - { - PERF_TIMER(import_key_images_fsu); -- std::string data; -- bool r = load_from_file(filename, data); -- -- THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); -+ std::string data_local = data; - - const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC); - if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen)) - { -- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename); -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic")); - } - - try - { - PERF_TIMER(import_key_images_decrypt); -- data = decrypt_with_view_secret_key(std::string(data, magiclen)); -+ data_local = decrypt_with_view_secret_key(std::string(data, magiclen)); - } - catch (const std::exception &e) - { -- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what()); -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + ": " + e.what()); - } - - const size_t headerlen = 4 + 2 * sizeof(crypto::public_key); -- THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename); -- const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24); -- const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4]; -- const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)]; -+ THROW_WALLET_EXCEPTION_IF(data_local.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ")); -+ const uint32_t offset = (uint8_t)data_local[0] | (((uint8_t)data_local[1]) << 8) | (((uint8_t)data_local[2]) << 16) | (((uint8_t)data_local[3]) << 24); -+ const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data_local[4]; -+ const crypto::public_key &public_view_key = *(const crypto::public_key*)&data_local[4 + sizeof(crypto::public_key)]; - const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; - if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) - { -- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account"); -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + " are for a different account"); - } - THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); - - const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); -- THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size, -- error::wallet_internal_error, std::string("Bad data size from file ") + filename); -- size_t nki = (data.size() - headerlen) / record_size; -+ THROW_WALLET_EXCEPTION_IF((data_local.size() - headerlen) % record_size, -+ error::wallet_internal_error, std::string("Bad data size from file ")); -+ size_t nki = (data_local.size() - headerlen) / record_size; - - std::vector> ski; - ski.reserve(nki); - for (size_t n = 0; n < nki; ++n) - { -- crypto::key_image key_image = *reinterpret_cast(&data[headerlen + n * record_size]); -- crypto::signature signature = *reinterpret_cast(&data[headerlen + n * record_size + sizeof(crypto::key_image)]); -+ crypto::key_image key_image = *reinterpret_cast(&data_local[headerlen + n * record_size]); -+ crypto::signature signature = *reinterpret_cast(&data_local[headerlen + n * record_size + sizeof(crypto::key_image)]); - - ski.push_back(std::make_pair(key_image, signature)); - } -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 3ce710433..9ff169a40 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1650,9 +1650,11 @@ private: - std::tuple> export_blockchain() const; - void import_blockchain(const std::tuple> &bc); - bool export_key_images(const std::string &filename, bool all = false) const; -+ std::string export_key_images_str(bool all) const; - std::pair>> export_key_images(bool all = false) const; - uint64_t import_key_images(const std::vector> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true); - uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); -+ uint64_t import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent); - bool import_key_images(std::vector key_images, size_t offset=0, boost::optional> selected_transfers=boost::none); - bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); - crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; --- -2.39.2 - diff --git a/patches/monero/0012-add-monero-submodule-support.patch b/patches/monero/0012-add-monero-submodule-support.patch new file mode 100644 index 0000000..d993a14 --- /dev/null +++ b/patches/monero/0012-add-monero-submodule-support.patch @@ -0,0 +1,65 @@ +From d8ab8f6fffd6235e935400f0fe750c1ba29b85e7 Mon Sep 17 00:00:00 2001 +From: cyan +Date: Thu, 7 Nov 2024 16:46:24 +0000 +Subject: [PATCH 12/14] add monero submodule support + +--- + CMakeLists.txt | 6 +++--- + cmake/CheckLinkerFlag.cmake | 2 +- + src/wallet/wallet_rpc_server.cpp | 2 +- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e7fa90a..b995a68 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -223,9 +223,9 @@ function(forbid_undefined_symbols) + cmake_minimum_required(VERSION 3.1) + project(test) + option(EXPECT_SUCCESS "" ON) +-file(WRITE "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }") ++file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }") + if (EXPECT_SUCCESS) +- file(APPEND "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ") ++ file(APPEND "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ") + endif() + add_library(l0 SHARED incorrect_source.cpp) + add_library(l1 MODULE incorrect_source.cpp) +@@ -390,7 +390,7 @@ else() + endif() + + list(INSERT CMAKE_MODULE_PATH 0 +- "${CMAKE_SOURCE_DIR}/cmake") ++ "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + + if (NOT DEFINED ENV{DEVELOPER_LOCAL_TOOLS}) + message(STATUS "Could not find DEVELOPER_LOCAL_TOOLS in env (not required)") +diff --git a/cmake/CheckLinkerFlag.cmake b/cmake/CheckLinkerFlag.cmake +index 7ecf5f6..89fb9d1 100644 +--- a/cmake/CheckLinkerFlag.cmake ++++ b/cmake/CheckLinkerFlag.cmake +@@ -6,7 +6,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE) + message(STATUS "Looking for ${flag} linker flag") + endif() + +- set(_cle_source ${CMAKE_SOURCE_DIR}/cmake/CheckLinkerFlag.c) ++ set(_cle_source ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckLinkerFlag.c) + + set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) + set(CMAKE_C_FLAGS "${flag}") +diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp +index d24b4c5..91f6925 100644 +--- a/src/wallet/wallet_rpc_server.cpp ++++ b/src/wallet/wallet_rpc_server.cpp +@@ -1163,7 +1163,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 ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs); ++ std::vector 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()) + { +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0013-android-ndk-version-bump.patch b/patches/monero/0013-android-ndk-version-bump.patch deleted file mode 100644 index 2b741a0..0000000 --- a/patches/monero/0013-android-ndk-version-bump.patch +++ /dev/null @@ -1,120 +0,0 @@ -From 1b978a697e1bdb3144bc08f998d125ac593fa971 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Thu, 23 May 2024 08:02:49 +0200 -Subject: [PATCH 13/16] android ndk version bump - ---- - contrib/depends/hosts/android.mk | 16 +++++++++++++--- - contrib/depends/packages/android_ndk.mk | 20 ++++++++++++++------ - contrib/depends/packages/boost.mk | 1 + - contrib/depends/toolchain.cmake.in | 11 ++++++----- - 4 files changed, 34 insertions(+), 14 deletions(-) - -diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk -index d6f8b99dd..e1c99aef3 100644 ---- a/contrib/depends/hosts/android.mk -+++ b/contrib/depends/hosts/android.mk -@@ -1,12 +1,22 @@ - ANDROID_API=21 -- -+host_toolchain=nonexistent - ifeq ($(host_arch),arm) --host_toolchain=arm-linux-androideabi- -+host_toolchain=armv7a-linux-androideabi${ANDROID_API}- -+endif -+ifeq ($(host_arch),x86_64) -+host_toolchain=x86_64-linux-android${ANDROID_API}- -+endif -+ifeq ($(host_arch),i686) -+host_toolchain=i686-linux-android${ANDROID_API}- -+endif -+ifeq ($(host_arch),aarch64) -+host_toolchain=aarch64-linux-android${ANDROID_API}- - endif - - android_CC=$(host_toolchain)clang - android_CXX=$(host_toolchain)clang++ --android_RANLIB=: -+android_RANLIB=llvm-ranlib -+android_AR=llvm-ar - - android_CFLAGS=-pipe - android_CXXFLAGS=$(android_CFLAGS) -diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk -index 5deff76c7..1ad250653 100644 ---- a/contrib/depends/packages/android_ndk.mk -+++ b/contrib/depends/packages/android_ndk.mk -@@ -1,8 +1,10 @@ - package=android_ndk --$(package)_version=17b -+$(package)_version=26d - $(package)_download_path=https://dl.google.com/android/repository/ --$(package)_file_name=android-ndk-r$($(package)_version)-linux-x86_64.zip --$(package)_sha256_hash=5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d48ccecd -+$(package)_file_name=android-ndk-r$($(package)_version)-linux.zip -+$(package)_sha256_hash=eefeafe7ccf177de7cc57158da585e7af119bb7504a63604ad719e4b2a328b54 -+ -+$(package)_version_apiversion=21 - - define $(package)_set_vars - $(package)_config_opts_arm=--arch arm -@@ -17,8 +19,14 @@ define $(package)_extract_cmds - endef - - define $(package)_stage_cmds -- android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api 21 \ -- --install-dir $(build_prefix) --stl=libc++ $($(package)_config_opts) &&\ -- mv $(build_prefix) $($(package)_staging_dir)/$(host_prefix) -+ mkdir -p $(build_prefix) &&\ -+ echo $(build_prefix)/toolchain && \ -+ android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api $($(package)_version_apiversion) \ -+ --install-dir $(build_prefix)/toolchain --stl=libc++ $($(package)_config_opts) &&\ -+ mv $(build_prefix)/toolchain $($(package)_staging_dir)/$(host_prefix)/native && \ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ar &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ranlib &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ar &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ranlib - endef - -diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk -index fd06c5393..c17e863cc 100644 ---- a/contrib/depends/packages/boost.mk -+++ b/contrib/depends/packages/boost.mk -@@ -25,6 +25,7 @@ $(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_android=-fPIC - $(package)_cxxflags_freebsd=-fPIC - endef - -diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in -index f26655d68..96a95ceb8 100644 ---- a/contrib/depends/toolchain.cmake.in -+++ b/contrib/depends/toolchain.cmake.in -@@ -104,16 +104,17 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") - SET(CMAKE_ANDROID_ARCH_ABI "armeabi-v7a") - SET(CMAKE_SYSTEM_PROCESSOR "armv7-a") - SET(CMAKE_ANDROID_ARM_MODE ON) -- SET(CMAKE_C_COMPILER_TARGET arm-linux-androideabi) -- SET(CMAKE_CXX_COMPILER_TARGET arm-linux-androideabi) -- SET(_CMAKE_TOOLCHAIN_PREFIX arm-linux-androideabi-) -+ SET(CMAKE_C_COMPILER_TARGET arm-linux-androideabi21) -+ SET(CMAKE_CXX_COMPILER_TARGET arm-linux-androideabi21) -+ SET(_CMAKE_TOOLCHAIN_PREFIX arm-linux-androideabi21-) - elseif(ARCHITECTURE STREQUAL "aarch64") - SET(CMAKE_ANDROID_ARCH_ABI "arm64-v8a") - SET(CMAKE_SYSTEM_PROCESSOR "aarch64") - endif() - SET(CMAKE_ANDROID_STANDALONE_TOOLCHAIN @prefix@/native) -- SET(CMAKE_C_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang") -- SET(CMAKE_CXX_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang++") -+ SET(_ANDROID_STANDALONE_TOOLCHAIN_API 21) -+ SET(CMAKE_C_COMPILER @CC@) -+ SET(CMAKE_CXX_COMPILER @CXX@) - else() - SET(CMAKE_C_COMPILER @CC@) - SET(CMAKE_CXX_COMPILER @CXX@) --- -2.39.2 - diff --git a/patches/monero/0013-fix-iOS-depends-build.patch b/patches/monero/0013-fix-iOS-depends-build.patch new file mode 100644 index 0000000..97c23c6 --- /dev/null +++ b/patches/monero/0013-fix-iOS-depends-build.patch @@ -0,0 +1,104 @@ +From dcb483e5cb97c4e3c500355d8225bd49d3e0ae7f Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Thu, 21 Nov 2024 06:05:03 -0500 +Subject: [PATCH 13/14] fix iOS depends build + +--- + CMakeLists.txt | 4 ---- + src/checkpoints/CMakeLists.txt | 6 +++++- + src/cryptonote_basic/CMakeLists.txt | 6 +++++- + src/cryptonote_basic/miner.cpp | 8 ++++---- + 4 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index b995a68..4e86328 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -39,10 +39,6 @@ include(CheckLibraryExists) + include(CheckFunctionExists) + include(FindPythonInterp) + +-if (IOS) +- INCLUDE(CmakeLists_IOS.txt) +-endif() +- + cmake_minimum_required(VERSION 3.5) + message(STATUS "CMake version ${CMAKE_VERSION}") + +diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt +index 665441f..841df32 100644 +--- a/src/checkpoints/CMakeLists.txt ++++ b/src/checkpoints/CMakeLists.txt +@@ -28,7 +28,11 @@ + + if(APPLE) + if(DEPENDS) +- list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") ++ if(${CMAKE_SYSTEM_NAME} STREQUAL "iOS") ++ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") ++ else() ++ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") ++ endif() + else() + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) +diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt +index 414936a..81c8176 100644 +--- a/src/cryptonote_basic/CMakeLists.txt ++++ b/src/cryptonote_basic/CMakeLists.txt +@@ -28,7 +28,11 @@ + + if(APPLE) + if(DEPENDS) +- list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") ++ if(${CMAKE_SYSTEM_NAME} STREQUAL "iOS") ++ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") ++ else() ++ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") ++ endif() + else() + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) +diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp +index 71b8f78..0f53f02 100644 +--- a/src/cryptonote_basic/miner.cpp ++++ b/src/cryptonote_basic/miner.cpp +@@ -45,7 +45,7 @@ + #include "boost/logic/tribool.hpp" + #include + +-#ifdef __APPLE__ ++#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE) + #include + #include + #include +@@ -883,7 +883,7 @@ namespace cryptonote + + return true; + +- #elif defined(__APPLE__) ++ #elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE) + + mach_msg_type_number_t count; + kern_return_t status; +@@ -949,7 +949,7 @@ namespace cryptonote + return true; + } + +- #elif (defined(__linux__) && defined(_SC_CLK_TCK)) || defined(__APPLE__) || defined(__FreeBSD__) ++ #elif (defined(__linux__) && defined(_SC_CLK_TCK)) || (defined(__APPLE__) && !defined(TARGET_OS_IPHONE)) || defined(__FreeBSD__) + + struct tms tms; + if ( times(&tms) != (clock_t)-1 ) +@@ -978,7 +978,7 @@ namespace cryptonote + return boost::logic::tribool(power_status.ACLineStatus != 1); + } + +- #elif defined(__APPLE__) ++ #elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE) + + #if TARGET_OS_MAC && (!defined(MAC_OS_X_VERSION_MIN_REQUIRED) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7) + return boost::logic::tribool(IOPSGetTimeRemainingEstimate() != kIOPSTimeRemainingUnlimited); +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0014-include-locale-only-when-targeting-WIN32.patch b/patches/monero/0014-include-locale-only-when-targeting-WIN32.patch new file mode 100644 index 0000000..4cd1d0c --- /dev/null +++ b/patches/monero/0014-include-locale-only-when-targeting-WIN32.patch @@ -0,0 +1,43 @@ +From dfe8fde34cd51f5d5ec95fb464737f6e447d41bb Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 18 Nov 2024 10:57:37 -0500 +Subject: [PATCH 14/14] include locale only when targeting WIN32 + +--- + CMakeLists.txt | 5 ++++- + src/wallet/api/wallet.cpp | 2 ++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 4e86328..809bce6 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1079,7 +1079,10 @@ if(STATIC) + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_STATIC_RUNTIME ON) + endif() +-find_package(Boost 1.58 QUIET REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options locale) ++if(WIN32) ++ set(BOOST_LOCALE locale) ++endif() ++find_package(Boost 1.58 QUIET REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options ${BOOST_LOCALE}) + add_definitions(-DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) + + set(CMAKE_FIND_LIBRARY_SUFFIXES ${OLD_LIB_SUFFIXES}) +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 5734aff..f4fb093 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -46,7 +46,9 @@ + #include + #include + ++#ifdef WIN32 + #include ++#endif + #include + #include "bc-ur/src/bc-ur.hpp" + #if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/monero/0014-rename-arm-armv7a.patch b/patches/monero/0014-rename-arm-armv7a.patch deleted file mode 100644 index 20e2822..0000000 --- a/patches/monero/0014-rename-arm-armv7a.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 2dfa0442a096f61fe16a5bc569a4d0ab9ddb7de5 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Wed, 12 Jun 2024 15:48:01 +0200 -Subject: [PATCH 14/16] rename arm -> armv7a - ---- - contrib/depends/hosts/android.mk | 2 +- - contrib/depends/packages/android_ndk.mk | 6 +++++- - contrib/depends/packages/openssl.mk | 2 +- - contrib/depends/toolchain.cmake.in | 8 ++++---- - 4 files changed, 11 insertions(+), 7 deletions(-) - -diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk -index e1c99aef3..827103c36 100644 ---- a/contrib/depends/hosts/android.mk -+++ b/contrib/depends/hosts/android.mk -@@ -1,6 +1,6 @@ - ANDROID_API=21 - host_toolchain=nonexistent --ifeq ($(host_arch),arm) -+ifeq ($(host_arch),armv7a) - host_toolchain=armv7a-linux-androideabi${ANDROID_API}- - endif - ifeq ($(host_arch),x86_64) -diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk -index 1ad250653..2c2914ec2 100644 ---- a/contrib/depends/packages/android_ndk.mk -+++ b/contrib/depends/packages/android_ndk.mk -@@ -7,7 +7,7 @@ $(package)_sha256_hash=eefeafe7ccf177de7cc57158da585e7af119bb7504a63604ad719e4b2 - $(package)_version_apiversion=21 - - define $(package)_set_vars --$(package)_config_opts_arm=--arch arm -+$(package)_config_opts_armv7a=--arch arm - $(package)_config_opts_aarch64=--arch arm64 - $(package)_config_opts_x86_64=--arch x86_64 - $(package)_config_opts_i686=--arch x86 -@@ -18,6 +18,8 @@ define $(package)_extract_cmds - unzip -q $($(1)_source_dir)/$($(package)_file_name) - endef - -+# arm-linux-androideabi-ar - openssl workaround -+ - define $(package)_stage_cmds - mkdir -p $(build_prefix) &&\ - echo $(build_prefix)/toolchain && \ -@@ -25,7 +27,9 @@ define $(package)_stage_cmds - --install-dir $(build_prefix)/toolchain --stl=libc++ $($(package)_config_opts) &&\ - mv $(build_prefix)/toolchain $($(package)_staging_dir)/$(host_prefix)/native && \ - cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ar &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ar &&\ - cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ranlib &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ranlib &&\ - cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ar &&\ - cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ranlib - endef -diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk -index a157762c7..2430f6495 100644 ---- a/contrib/depends/packages/openssl.mk -+++ b/contrib/depends/packages/openssl.mk -@@ -34,7 +34,7 @@ $(package)_config_opts_x86_64_linux=linux-x86_64 - $(package)_config_opts_i686_linux=linux-generic32 - $(package)_config_opts_arm_linux=linux-generic32 - $(package)_config_opts_aarch64_linux=linux-generic64 --$(package)_config_opts_arm_android=--static android-arm -+$(package)_config_opts_armv7a_android=--static android-arm - $(package)_config_opts_aarch64_android=--static android-arm64 - $(package)_config_opts_aarch64_darwin=darwin64-arm64-cc - $(package)_config_opts_riscv64_linux=linux-generic64 -diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in -index 96a95ceb8..48a6f947e 100644 ---- a/contrib/depends/toolchain.cmake.in -+++ b/contrib/depends/toolchain.cmake.in -@@ -100,13 +100,13 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - SET(LLVM_ENABLE_PIE OFF) - elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") - SET(ANDROID TRUE) -- if(ARCHITECTURE STREQUAL "arm") -+ if(ARCHITECTURE STREQUAL "armv7a") - SET(CMAKE_ANDROID_ARCH_ABI "armeabi-v7a") - SET(CMAKE_SYSTEM_PROCESSOR "armv7-a") - SET(CMAKE_ANDROID_ARM_MODE ON) -- SET(CMAKE_C_COMPILER_TARGET arm-linux-androideabi21) -- SET(CMAKE_CXX_COMPILER_TARGET arm-linux-androideabi21) -- SET(_CMAKE_TOOLCHAIN_PREFIX arm-linux-androideabi21-) -+ SET(CMAKE_C_COMPILER_TARGET armv7a-linux-androideabi21) -+ SET(CMAKE_CXX_COMPILER_TARGET armv7a-linux-androideabi21) -+ SET(_CMAKE_TOOLCHAIN_PREFIX armv7a-linux-androideabi21-) - elseif(ARCHITECTURE STREQUAL "aarch64") - SET(CMAKE_ANDROID_ARCH_ABI "arm64-v8a") - SET(CMAKE_SYSTEM_PROCESSOR "aarch64") --- -2.39.2 - diff --git a/patches/monero/0015-use-proper-error-handling-in-get_seed.patch b/patches/monero/0015-use-proper-error-handling-in-get_seed.patch deleted file mode 100644 index aef7dcf..0000000 --- a/patches/monero/0015-use-proper-error-handling-in-get_seed.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 6e7a9770e4f18f931f5caca7dec2a197e779afbc Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 24 Jun 2024 10:49:12 +0200 -Subject: [PATCH 15/16] use proper error handling in get_seed - ---- - src/wallet/api/wallet.cpp | 17 ++++++++++++----- - src/wallet/wallet2.cpp | 3 +++ - 2 files changed, 15 insertions(+), 5 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 5ca190c7d..a307d35a7 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -880,12 +880,19 @@ bool WalletImpl::close(bool store) - - std::string WalletImpl::seed(const std::string& seed_offset) const - { -- if (checkBackgroundSync("cannot get seed")) -+ if (checkBackgroundSync("cannot get seed")) { -+ setStatusError("cannot get seed"); - return std::string(); -- epee::wipeable_string seed; -- if (m_wallet) -- m_wallet->get_seed(seed, seed_offset); -- return std::string(seed.data(), seed.size()); // TODO -+ } -+ try { -+ epee::wipeable_string seed; -+ if (m_wallet) -+ m_wallet->get_seed(seed, seed_offset); -+ return std::string(seed.data(), seed.size()); // TODO -+ } catch (const std::exception &e) { -+ setStatusError(e.what()); -+ return std::string(); -+ } - } - - bool WalletImpl::getPolyseed(std::string &seed_words, std::string &passphrase) const -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 7d97e683b..8e44806fc 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -1452,11 +1452,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab - bool keys_deterministic = is_deterministic(); - if (!keys_deterministic) - { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "This is not a deterministic wallet"); - std::cout << "This is not a deterministic wallet" << std::endl; - return false; - } - if (seed_language.empty()) - { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "seed_language not set"); - std::cout << "seed_language not set" << std::endl; - return false; - } -@@ -1466,6 +1468,7 @@ 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)) - { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Failed to create seed from key for language: "+seed_language+", falling back to English."); - 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"); - } --- -2.39.2 - diff --git a/patches/monero/0016-add-dummy-device-for-ledger.patch b/patches/monero/0016-add-dummy-device-for-ledger.patch deleted file mode 100644 index dddb4b1..0000000 --- a/patches/monero/0016-add-dummy-device-for-ledger.patch +++ /dev/null @@ -1,580 +0,0 @@ -From 77f328ba6befb9b964f764a27f8a03b71bf9095d Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Wed, 26 Jun 2024 15:04:38 +0200 -Subject: [PATCH 16/16] add dummy device for ledger - ---- - CMakeLists.txt | 6 +- - src/device/CMakeLists.txt | 6 +- - src/device/device.cpp | 10 ++- - src/device/device.hpp | 12 +-- - src/device/device_io_dummy.cpp | 133 +++++++++++++++++++++++++++++++++ - src/device/device_io_dummy.hpp | 74 ++++++++++++++++++ - src/device/device_ledger.cpp | 6 +- - src/device/device_ledger.hpp | 7 +- - src/wallet/api/wallet.cpp | 94 +++++++++++++++++++++++ - src/wallet/api/wallet.h | 18 +++++ - src/wallet/api/wallet2_api.h | 12 +++ - 11 files changed, 357 insertions(+), 21 deletions(-) - create mode 100644 src/device/device_io_dummy.cpp - create mode 100644 src/device/device_io_dummy.hpp - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 6028c0961..e7fa90abb 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -694,8 +694,12 @@ include_directories(${LMDB_INCLUDE}) - include_directories(${LIBUNWIND_INCLUDE}) - link_directories(${LIBUNWIND_LIBRARY_DIRS}) - -+if (HIDAPI_DUMMY) -+ add_definitions(-DHIDAPI_DUMMY) -+endif() -+ - # Final setup for hid --if (HIDAPI_FOUND) -+if (HIDAPI_FOUND) - message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}") - add_definitions(-DHAVE_HIDAPI) - include_directories(${HIDAPI_INCLUDE_DIR}) -diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt -index e4f1159b5..14d398f87 100644 ---- a/src/device/CMakeLists.txt -+++ b/src/device/CMakeLists.txt -@@ -29,10 +29,11 @@ - set(device_sources - device.cpp - device_default.cpp -+ device_io_dummy.cpp - log.cpp - ) - --if(HIDAPI_FOUND) -+if(HIDAPI_FOUND OR HIDAPI_DUMMY) - set(device_sources - ${device_sources} - device_ledger.cpp -@@ -45,10 +46,11 @@ set(device_headers - device_io.hpp - device_default.hpp - device_cold.hpp -+ device_io_dummy.hpp - log.hpp - ) - --if(HIDAPI_FOUND) -+if(HIDAPI_FOUND OR HIDAPI_DUMMY) - set(device_headers - ${device_headers} - device_ledger.hpp -diff --git a/src/device/device.cpp b/src/device/device.cpp -index e6cd358b6..636929feb 100644 ---- a/src/device/device.cpp -+++ b/src/device/device.cpp -@@ -29,7 +29,7 @@ - - #include "device.hpp" - #include "device_default.hpp" --#ifdef WITH_DEVICE_LEDGER -+#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) - #include "device_ledger.hpp" - #endif - #include "misc_log_ex.h" -@@ -57,7 +57,7 @@ namespace hw { - - device_registry::device_registry(){ - hw::core::register_all(registry); -- #ifdef WITH_DEVICE_LEDGER -+ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) - hw::ledger::register_all(registry); - #endif - atexit(clear_device_registry); -@@ -83,11 +83,13 @@ namespace hw { - - auto device = registry.find(device_descriptor_lookup); - if (device == registry.end()) { -- MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: "); -+ std::stringstream ss("Device not found in registry: '" + device_descriptor + "'. Known devices: "); -+ MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: \n"); - for( const auto& sm_pair : registry ) { -+ ss << "\n- " + sm_pair.first; - MERROR(" - " << sm_pair.first); - } -- throw std::runtime_error("device not found: " + device_descriptor); -+ throw std::runtime_error("device not found: " + device_descriptor + "\nlalala\n" + ss.str()); - } - return *device->second; - } -diff --git a/src/device/device.hpp b/src/device/device.hpp -index 392703a24..ffd419779 100644 ---- a/src/device/device.hpp -+++ b/src/device/device.hpp -@@ -34,17 +34,7 @@ - #include "ringct/rctTypes.h" - #include "cryptonote_config.h" - -- --#ifndef USE_DEVICE_LEDGER --#define USE_DEVICE_LEDGER 1 --#endif -- --#if !defined(HAVE_HIDAPI) --#undef USE_DEVICE_LEDGER --#define USE_DEVICE_LEDGER 0 --#endif -- --#if USE_DEVICE_LEDGER -+#if defined(HAVE_HIDAPI) || defined(HIDAPI_DUMMY) - #define WITH_DEVICE_LEDGER - #endif - -diff --git a/src/device/device_io_dummy.cpp b/src/device/device_io_dummy.cpp -new file mode 100644 -index 000000000..fb082694e ---- /dev/null -+++ b/src/device/device_io_dummy.cpp -@@ -0,0 +1,133 @@ -+// Copyright (c) 2017-2022, The Monero Project -+// -+// All rights reserved. -+// -+// Redistribution and use in source and binary forms, with or without modification, are -+// permitted provided that the following conditions are met: -+// -+// 1. Redistributions of source code must retain the above copyright notice, this list of -+// conditions and the following disclaimer. -+// -+// 2. Redistributions in binary form must reproduce the above copyright notice, this list -+// of conditions and the following disclaimer in the documentation and/or other -+// materials provided with the distribution. -+// -+// 3. Neither the name of the copyright holder nor the names of its contributors may be -+// used to endorse or promote products derived from this software without specific -+// prior written permission. -+// -+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+// -+ -+// device_io_dummy -+// Main goal of device_io_dummy is to emulate a hw::io::device_io without the need to actually -+// connect a device. -+// Many operating systems do not support giving raw USB access to a process (android), or don't -+// support that at all (hi iOS), therefore other means of connection can be used, either USB -+// abstraction provided by the OS (monerujo), or BLE (also monerujo). -+// Monerujo implementation is written in Java, which makes it a nice fit for iOS, but makes the -+// code extremely unportable, so for this reason the code in here is written in CPP. -+// Data transport is made available in wallet2_api.h, so wallet developers can easily plug their -+// own USB/BLE/other transport layer. -+ -+#ifdef HIDAPI_DUMMY -+#include -+#include "log.hpp" -+#include "device_io_dummy.hpp" -+#include "device_ledger.hpp" -+ -+ -+bool hw::io::device_io_dummy::stateIsConnected = false; -+unsigned char* hw::io::device_io_dummy::sendToDevice = {}; -+size_t hw::io::device_io_dummy::sendToDeviceLength = 0; -+unsigned char* hw::io::device_io_dummy::receivedFromDevice = {}; -+size_t hw::io::device_io_dummy::receivedFromDeviceLength = 0; -+bool hw::io::device_io_dummy::waitsForDeviceSend = false; -+bool hw::io::device_io_dummy::waitsForDeviceReceive = false; -+ -+namespace hw { -+ namespace io { -+ -+#undef MONERO_DEFAULT_LOG_CATEGORY -+#define MONERO_DEFAULT_LOG_CATEGORY "device.io_dummy" -+ device_io_dummy::device_io_dummy(int a, int b, int c, int d) { -+ MDEBUG("device_io_dummy(a: " << a << ", b: " << b << ", c: " << c << ", d: " << d <<")"); -+ } -+ -+ void device_io_dummy::init() { -+ MDEBUG("init()"); -+ } -+ -+ void device_io_dummy::connect(void *params) { -+ MDEBUG("connect(" << params << ")"); -+ stateIsConnected = true; -+ } -+ -+ void device_io_dummy::connect(const std::vector& known_devices) { -+ MDEBUG("connect(["); -+ for (const auto &item: known_devices) { -+ MDEBUG("{ interface_number: " << item.interface_number); -+ MDEBUG(" pid : " << item.pid); -+ MDEBUG(" usage_page : " << item.usage_page); -+ MDEBUG(" vid : " << item.vid << " },"); -+ } -+ MDEBUG("])"); -+ stateIsConnected = true; -+ } -+ -+ bool device_io_dummy::connected() const { -+ MDEBUG("connected()"); -+ return stateIsConnected; -+ } -+ -+ int device_io_dummy::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) { -+ MDEBUG("exchange(): locking mutex"); -+ boost::unique_lock lock(mutex); -+ sendToDevice = command; -+ sendToDeviceLength = cmd_len; -+ waitsForDeviceSend = true; -+ waitsForDeviceReceive = true; -+ MDEBUG("exchange(): waitsForDeviceSend"); -+ // NOTE: waitsForDeviceSend should be changed by external code -+ while (waitsForDeviceSend) { -+ usleep(1000); -+ MDEBUG("exchange(): waitsForDeviceSend (still)"); -+ } -+ -+ MDEBUG("exchange(): waitsForDeviceReceive"); -+ while (waitsForDeviceReceive) { -+ usleep(1000); -+ MDEBUG("exchange(): waitsForDeviceReceive (still)"); -+ } -+ -+ if (receivedFromDeviceLength > max_resp_len) { -+ MDEBUG("exchange(): receivedFromDeviceLength ("<& known_devices); -+ void disconnect(); -+ bool connected() const; -+ -+ int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input); -+ }; -+ }; -+}; -+ -+#endif // HAVE_HIDAPI -diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp -index 90675df11..136c6094b 100644 ---- a/src/device/device_ledger.cpp -+++ b/src/device/device_ledger.cpp -@@ -41,7 +41,7 @@ namespace hw { - - namespace ledger { - -- #ifdef WITH_DEVICE_LEDGER -+ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) - - #undef MONERO_DEFAULT_LOG_CATEGORY - #define MONERO_DEFAULT_LOG_CATEGORY "device.ledger" -@@ -299,7 +299,7 @@ namespace hw { - - device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 2000) { - this->id = device_id++; -- this->reset_buffer(); -+ this->reset_buffer(); - this->mode = NONE; - this->has_view_key = false; - this->tx_in_progress = false; -@@ -532,7 +532,9 @@ namespace hw { - - bool device_ledger::connect(void) { - this->disconnect(); -+ #ifndef HIDAPI_DUMMY - hw_device.connect(known_devices); -+ #endif - this->reset(); - #ifdef DEBUG_HWDEVICE - cryptonote::account_public_address pubkey; -diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp -index 03058c4f1..506f27c4a 100644 ---- a/src/device/device_ledger.hpp -+++ b/src/device/device_ledger.hpp -@@ -35,6 +35,7 @@ - #include "device.hpp" - #include "log.hpp" - #include "device_io_hid.hpp" -+#include "device_io_dummy.hpp" - #include - #include - -@@ -56,7 +57,7 @@ namespace hw { - - void register_all(std::map> ®istry); - -- #ifdef WITH_DEVICE_LEDGER -+ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) - - // Origin: https://github.com/LedgerHQ/ledger-app-monero/blob/master/src/monero_types.h - #define SW_OK 0x9000 -@@ -148,7 +149,11 @@ namespace hw { - mutable boost::mutex command_locker; - - //IO -+#ifdef HIDAPI_DUMMY -+ hw::io::device_io_dummy hw_device; -+#else - hw::io::device_io_hid hw_device; -+#endif - unsigned int length_send; - unsigned char buffer_send[BUFFER_SEND_SIZE]; - unsigned int length_recv; -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 8bdd75a5a..09f91c5e2 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -49,6 +49,9 @@ - #include - #include - #include "bc-ur/src/bc-ur.hpp" -+#ifdef HIDAPI_DUMMY -+#include "device/device_io_dummy.hpp" -+#endif - - using namespace std; - using namespace cryptonote; -@@ -3299,4 +3302,95 @@ uint64_t WalletImpl::getBytesSent() - return m_wallet->get_bytes_sent(); - } - -+ -+// HIDAPI_DUMMY -+bool WalletImpl::getStateIsConnected() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return false; -+ #else -+ return hw::io::device_io_dummy::stateIsConnected; -+ #endif -+} -+ -+unsigned char* WalletImpl::getSendToDevice() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return {}; -+ #else -+ return hw::io::device_io_dummy::sendToDevice; -+ #endif -+} -+ -+size_t WalletImpl::getSendToDeviceLength() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return -1; -+ #else -+ return hw::io::device_io_dummy::sendToDeviceLength; -+ #endif -+} -+ -+unsigned char* WalletImpl::getReceivedFromDevice() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return {}; -+ #else -+ return hw::io::device_io_dummy::receivedFromDevice; -+ #endif -+} -+ -+size_t WalletImpl::getReceivedFromDeviceLength() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return -1; -+ #else -+ return hw::io::device_io_dummy::receivedFromDeviceLength; -+ #endif -+} -+ -+bool WalletImpl::getWaitsForDeviceSend() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return false; -+ #else -+ return hw::io::device_io_dummy::receivedFromDeviceLength; -+ #endif -+} -+ -+bool WalletImpl::getWaitsForDeviceReceive() { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return false; -+ #else -+ return hw::io::device_io_dummy::waitsForDeviceReceive; -+ #endif -+} -+ -+void WalletImpl::setDeviceReceivedData(unsigned char* data, size_t len) { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return; -+ #else -+ hw::io::device_io_dummy::receivedFromDevice = static_cast(malloc(len)); -+ hw::io::device_io_dummy::receivedFromDeviceLength = len; -+ memset(hw::io::device_io_dummy::receivedFromDevice, 0, len); -+ memcpy(hw::io::device_io_dummy::receivedFromDevice, data, len); -+ hw::io::device_io_dummy::waitsForDeviceReceive = false; -+ #endif -+} -+ -+void WalletImpl::setDeviceSendData(unsigned char* data, size_t len) { -+ #ifndef HIDAPI_DUMMY -+ setStatusError("MONERO compiled with -DHIDAPI_DUMMY"); -+ return; -+ #else -+ hw::io::device_io_dummy::sendToDevice = static_cast(malloc(len)); -+ hw::io::device_io_dummy::sendToDeviceLength = len; -+ memset(hw::io::device_io_dummy::sendToDevice, 0, len); -+ memcpy(hw::io::device_io_dummy::sendToDevice, data, len); -+ hw::io::device_io_dummy::waitsForDeviceSend = false; -+ #endif -+} -+ - } // namespace -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index febc93119..9e1fbb40b 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -321,6 +321,24 @@ private: - // cache connection status to avoid unnecessary RPC calls - mutable std::atomic m_is_connected; - boost::optional m_daemon_login{}; -+ -+ bool getStateIsConnected(); -+ -+ unsigned char *getSendToDevice(); -+ -+ size_t getSendToDeviceLength(); -+ -+ unsigned char *getReceivedFromDevice(); -+ -+ size_t getReceivedFromDeviceLength(); -+ -+ bool getWaitsForDeviceSend(); -+ -+ bool getWaitsForDeviceReceive(); -+ -+ void setDeviceReceivedData(unsigned char *data, size_t len); -+ -+ void setDeviceSendData(unsigned char *data, size_t len); - }; - - -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 2bbb32c8b..c8d6bb179 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -1204,6 +1204,18 @@ struct Wallet - - //! get bytes sent - virtual uint64_t getBytesSent() = 0; -+ -+ // HIDAPI_DUMMY -+ virtual bool getStateIsConnected() = 0; -+ virtual unsigned char* getSendToDevice() = 0; -+ virtual size_t getSendToDeviceLength() = 0; -+ virtual unsigned char* getReceivedFromDevice() = 0; -+ virtual size_t getReceivedFromDeviceLength() = 0; -+ virtual bool getWaitsForDeviceSend() = 0; -+ virtual bool getWaitsForDeviceReceive() = 0; -+ -+ virtual void setDeviceReceivedData(unsigned char* data, size_t len) = 0; -+ virtual void setDeviceSendData(unsigned char* data, size_t len) = 0; - }; - - /** --- -2.45.1.windows.1 - diff --git a/patches/monero/0016-uint64_t-missing-definition-fix.patch b/patches/monero/0016-uint64_t-missing-definition-fix.patch deleted file mode 100644 index e555829..0000000 --- a/patches/monero/0016-uint64_t-missing-definition-fix.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 9fe376e0024dfdbea47219477e797cb20c56305f Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 2 Sep 2024 16:40:31 +0200 -Subject: [PATCH] uint64_t missing definition fix - ---- - contrib/epee/include/net/http_base.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h -index 4af4da790..ae4c0d05e 100644 ---- a/contrib/epee/include/net/http_base.h -+++ b/contrib/epee/include/net/http_base.h -@@ -28,7 +28,7 @@ - - #pragma once - #include "memwipe.h" -- -+#include - #include - - #include --- -2.43.0 - diff --git a/patches/monero/0017-added-deps.patch b/patches/monero/0017-added-deps.patch deleted file mode 100644 index 4edacce..0000000 --- a/patches/monero/0017-added-deps.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 8f0b07a685b5971f4deb66bb8c53e6da4065370c Mon Sep 17 00:00:00 2001 -From: cyan -Date: Thu, 25 Jul 2024 20:06:29 +0000 -Subject: [PATCH] added deps - ---- - external/polyseed | 1 + - external/utf8proc | 1 + - 2 files changed, 2 insertions(+) - create mode 160000 external/polyseed - create mode 160000 external/utf8proc - -diff --git a/external/polyseed b/external/polyseed -new file mode 160000 -index 000000000..dfb05d8ed ---- /dev/null -+++ b/external/polyseed -@@ -0,0 +1 @@ -+Subproject commit dfb05d8edb682b0e8f743b1b70c9131712ff4157 -diff --git a/external/utf8proc b/external/utf8proc -new file mode 160000 -index 000000000..5568eff49 ---- /dev/null -+++ b/external/utf8proc -@@ -0,0 +1 @@ -+Subproject commit 5568eff49a6bf417b6fdef2808df9db8d3d68a76 --- -2.39.2 - diff --git a/patches/monero/0018-fix-coin-control-patch.patch b/patches/monero/0018-fix-coin-control-patch.patch deleted file mode 100644 index 001a1c0..0000000 --- a/patches/monero/0018-fix-coin-control-patch.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 3d99b4efb5a79d7c52d596c349de25d1ca34c264 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Fri, 27 Sep 2024 12:20:49 +0200 -Subject: [PATCH] fix coin control patch - ---- - src/wallet/api/wallet.cpp | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 3ead385ec..ec7d60ec0 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -2174,11 +2174,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorm_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); - --- -2.39.5 (Apple Git-154) - diff --git a/patches/monero/0019-fix-for-coin-control-patch.patch b/patches/monero/0019-fix-for-coin-control-patch.patch deleted file mode 100644 index b7ff7f9..0000000 --- a/patches/monero/0019-fix-for-coin-control-patch.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 8f93306ed526e0e573b33fc7dd40abbba7e7a00a Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 15 Oct 2024 18:00:05 +0200 -Subject: [PATCH] fix for coin control patch - ---- - src/wallet/api/coins.cpp | 1 + - src/wallet/api/wallet.cpp | 36 +++++++++++++++++++++++++++++++++++- - 2 files changed, 36 insertions(+), 1 deletion(-) - -diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp -index 2321c638d..ef12141cf 100644 ---- a/src/wallet/api/coins.cpp -+++ b/src/wallet/api/coins.cpp -@@ -114,6 +114,7 @@ void CoinsImpl::setFrozen(int index) - { - try - { -+ LOG_ERROR("Freezing coin: " << index); - m_wallet->m_wallet->freeze(index); - refresh(); - } -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index ec7d60ec0..db127dae4 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -2116,6 +2116,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectornettype(), dst_addr[i])) { - // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982 -@@ -2137,6 +2138,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorunlocked_balance(subaddr_account, true); -+ // if (maxAllowedSpend < amountSum) { -+ // error = true; -+ // setStatusError(tr("Amount you are trying to spend is larger than unlocked amount")); -+ // break; -+ // } - std::vector preferred_input_list; - if (!preferred_inputs.empty()) { -+ LOG_ERROR("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) { -+ if (!r) { - error = true; - setStatusError(tr("failed to parse key image")); - break; - } -+ if (m_wallet->frozen(keyImage)) { -+ error = true; -+ setStatusError(tr("refusing to spend frozen coin")); -+ break; -+ } -+ - preferred_input_list.push_back(keyImage); - } -+ } else { -+ LOG_ERROR("not empty"); -+ -+ boost::shared_lock transfers_lock(m_wallet->m_transfers_mutex); -+ 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); -+ LOG_ERROR("COIN: " << i << ": " << td.amount() << "; "<frozen(td)); -+ if (td.m_spent) continue; -+ LOG_ERROR("is frozen"); -+ if (!td.m_frozen) { -+ LOG_ERROR("isn't:"); -+ LOG_ERROR("hash: " << td.m_key_image << "; " << td.amount()); -+ preferred_input_list.push_back(td.m_key_image); -+ } -+ } -+ } -+ for (const auto &de : preferred_input_list) { -+ LOG_ERROR("preferred input: " << de); - } - if (error) { - break; --- -2.39.5 (Apple Git-154) - diff --git a/patches/monero/0020-catch-exception-in-queryWalletDevice.patch b/patches/monero/0020-catch-exception-in-queryWalletDevice.patch deleted file mode 100644 index 1fb2471..0000000 --- a/patches/monero/0020-catch-exception-in-queryWalletDevice.patch +++ /dev/null @@ -1,35 +0,0 @@ -From d0774a747e7520f6dae3cf90ecbb682395da8c9c Mon Sep 17 00:00:00 2001 -From: cyan -Date: Wed, 27 Nov 2024 23:28:32 +0000 -Subject: [PATCH] catch exception in queryWalletDevice - ---- - src/wallet/api/wallet_manager.cpp | 12 ++++++++---- - 1 file changed, 8 insertions(+), 4 deletions(-) - -diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp -index f88bd9e64..c200f52ae 100644 ---- a/src/wallet/api/wallet_manager.cpp -+++ b/src/wallet/api/wallet_manager.cpp -@@ -213,10 +213,14 @@ bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, - - bool WalletManagerImpl::queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds) const - { -- hw::device::device_type type; -- bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds); -- device_type = static_cast(type); -- return r; -+ try { -+ hw::device::device_type type; -+ bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds); -+ device_type = static_cast(type); -+ return r; -+ } catch (...) { -+ return false; -+ } - } - - std::vector WalletManagerImpl::findWallets(const std::string &path) --- -2.43.0 - diff --git a/patches/monero/README.md b/patches/monero/README.md deleted file mode 100644 index b840515..0000000 --- a/patches/monero/README.md +++ /dev/null @@ -1,43 +0,0 @@ - -> 0001 to 0004 were created most likely by feather dev, anonero monero repository got nuked and now says that I made the changes, so I am unable to credit the original authors, http://git.anonero5wmhraxqsvzq2ncgptq6gq45qoto6fnkfwughfl4gbt44swad.onion/ANONERO/monero/commits/branch/v0.18.3.3-anonero - - -# 0001-polyseed - -Polyseed support for wallets [planned in long distant future as a part of walet3 - not getting upstream, no PR available]. - -Note, only English support is available due to issues with normalization libraries. - -> tobtoht: You may also want to reconsider supporting languages other than English: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#wordlists (this is about BIP39, but because unicode normalization is such a PITA it will become true for Polyseed wallets as well) - -Considering the fact that even Feather Wallet doesn't support non-english seeds, it makes sense to go that way. - -At least until (if ever) feather wallet supports multi-language polyseed seeds I don't think it is a good idea to support them, especially because of possible issues when targetting different platforms. - -# 0002-background-sync - -Sourced from: https://github.com/monero-project/monero/pull/8617, no changes except for merge conflicts. - -# 0003-airgap - -Cool functions for offline transactions - -# 0004-coin-control - -Coin control patch, I was able to trace it's orgins back to wownero/monerujo. - -# 0005-fix-build - -Fix cross compilation for linux - -# 0006-macos-build-fix - -Fixes cross compilation for MacOS targets - -# 0007-fix-make-debug-test-target - -I had some debugging to do, I don't remember actually why I decided to run the tests, but since it is a fix I've decided to leave it in here just in case. - -# 0008-fix-missing-___clear_cache-when-targetting-iOS - -https://github.com/tevador/RandomX/pull/294 \ No newline at end of file diff --git a/patches/wownero/0001-Fix-wallet_api-compilation-target-https-git.wownero..patch b/patches/wownero/0001-Fix-wallet_api-compilation-target-https-git.wownero..patch deleted file mode 100644 index cf7a2cf..0000000 --- a/patches/wownero/0001-Fix-wallet_api-compilation-target-https-git.wownero..patch +++ /dev/null @@ -1,31 +0,0 @@ -From 1c32a5d6f62a550b9ee6635bca37f20a0471cc85 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 26 Mar 2024 06:55:38 +0100 -Subject: [PATCH 01/14] Fix wallet_api compilation target - https://git.wownero.com/wownero/wownero/issues/483 - ---- - src/wallet/api/wallet.cpp | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 823f122c2..d81ddec93 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -1773,11 +1773,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorm_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, - adjusted_priority, -- extra, subaddr_account, subaddr_indices, preferred_input_list); -+ extra, subaddr_account, subaddr_indices); - } else { - transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, 0 /* unlock_time */, - adjusted_priority, -- extra, subaddr_account, subaddr_indices, preferred_input_list); -+ extra, subaddr_account, subaddr_indices); - } - pendingTxPostProcess(transaction); - --- -2.45.2 - diff --git a/patches/wownero/0001-wallet-background-sync-with-just-the-view-key.patch b/patches/wownero/0001-wallet-background-sync-with-just-the-view-key.patch new file mode 100644 index 0000000..df672bd --- /dev/null +++ b/patches/wownero/0001-wallet-background-sync-with-just-the-view-key.patch @@ -0,0 +1,4318 @@ +From 82bdb3dcf75f9e6bd8c5e31a125b8acc154a0623 Mon Sep 17 00:00:00 2001 +From: j-berman +Date: Thu, 13 Oct 2022 18:33:33 -0700 +Subject: [PATCH 01/15] wallet: background sync with just the view key + +- When background syncing, the wallet wipes the spend key +from memory and processes all new transactions. The wallet saves +all receives, spends, and "plausible" spends of receives the +wallet does not know key images for. +- When background sync disabled, the wallet processes all +background synced txs and then clears the background sync cache. +- Adding "plausible" spends to the background sync cache ensures +that the wallet does not need to query the daemon to see if any +received outputs were spent while background sync was enabled. +This would harm privacy especially for users of 3rd party daemons. +- To enable the feature in the CLI wallet, the user can set +background-sync to reuse-wallet-password or +custom-background-password and the wallet automatically syncs in +the background when the wallet locks, then processes all +background synced txs when the wallet is unlocked. +- The custom-background-password option enables the user to +open a distinct background wallet that only has a view key saved +and can be opened/closed/synced separately from the main wallet. +When the main wallet opens, it processes the background wallet's +cache. +- To enable the feature in the RPC wallet, there is a new +`/setup_background_sync` endpoint. +- HW, multsig and view-only wallets cannot background sync. +--- + src/cryptonote_basic/account.cpp | 11 + + src/cryptonote_basic/account.h | 1 + + src/cryptonote_config.h | 2 + + src/simplewallet/simplewallet.cpp | 204 +++- + src/simplewallet/simplewallet.h | 1 + + src/wallet/api/wallet.cpp | 213 +++- + src/wallet/api/wallet.h | 12 + + src/wallet/api/wallet2_api.h | 42 + + src/wallet/wallet2.cpp | 1030 ++++++++++++++++-- + src/wallet/wallet2.h | 156 ++- + src/wallet/wallet_errors.h | 39 + + src/wallet/wallet_rpc_server.cpp | 162 +++ + src/wallet/wallet_rpc_server.h | 6 + + src/wallet/wallet_rpc_server_commands_defs.h | 64 ++ + src/wallet/wallet_rpc_server_error_codes.h | 2 + + tests/functional_tests/transfer.py | 400 ++++++- + tests/functional_tests/util_resources.py | 25 + + tests/functional_tests/wallet.py | 43 +- + tests/unit_tests/wipeable_string.cpp | 12 + + utils/python-rpc/framework/wallet.py | 42 + + 20 files changed, 2337 insertions(+), 130 deletions(-) + +diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp +index 2ac455fda..4e87d4477 100644 +--- a/src/cryptonote_basic/account.cpp ++++ b/src/cryptonote_basic/account.cpp +@@ -152,6 +152,17 @@ DISABLE_VS_WARNINGS(4244 4345) + m_keys.m_multisig_keys.clear(); + } + //----------------------------------------------------------------- ++ void account_base::set_spend_key(const crypto::secret_key& spend_secret_key) ++ { ++ // make sure derived spend public key matches saved public spend key ++ crypto::public_key spend_public_key; ++ crypto::secret_key_to_public_key(spend_secret_key, spend_public_key); ++ CHECK_AND_ASSERT_THROW_MES(m_keys.m_account_address.m_spend_public_key == spend_public_key, ++ "Unexpected derived public spend key"); ++ ++ m_keys.m_spend_secret_key = spend_secret_key; ++ } ++ //----------------------------------------------------------------- + crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) + { + crypto::secret_key first = generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, recovery_key, recover); +diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h +index 2ee9545d4..93d1d28f0 100644 +--- a/src/cryptonote_basic/account.h ++++ b/src/cryptonote_basic/account.h +@@ -95,6 +95,7 @@ namespace cryptonote + bool store(const std::string& file_path); + + void forget_spend_key(); ++ void set_spend_key(const crypto::secret_key& spend_secret_key); + const std::vector &get_multisig_keys() const { return m_keys.m_multisig_keys; } + + void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); } +diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h +index 8c0d3ce20..8b5091a46 100644 +--- a/src/cryptonote_config.h ++++ b/src/cryptonote_config.h +@@ -253,6 +253,8 @@ namespace config + const unsigned char HASH_KEY_ENCRYPTED_PAYMENT_ID = 0x8d; + const unsigned char HASH_KEY_WALLET = 0x8c; + const unsigned char HASH_KEY_WALLET_CACHE = 0x8d; ++ const unsigned char HASH_KEY_BACKGROUND_CACHE = 0x8e; ++ const unsigned char HASH_KEY_BACKGROUND_KEYS_FILE = 0x8f; + const unsigned char HASH_KEY_RPC_PAYMENT_NONCE = 0x58; + const unsigned char HASH_KEY_MEMORY = 'k'; + const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp +index 462c06f0e..8c5122097 100644 +--- a/src/simplewallet/simplewallet.cpp ++++ b/src/simplewallet/simplewallet.cpp +@@ -155,6 +155,17 @@ typedef cryptonote::simple_wallet sw; + } \ + } while(0) + ++#define CHECK_IF_BACKGROUND_SYNCING(msg) \ ++ do \ ++ { \ ++ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) \ ++ { \ ++ std::string type = m_wallet->is_background_wallet() ? "background wallet" : "background syncing wallet"; \ ++ fail_msg_writer() << boost::format(tr("%s %s")) % type % msg; \ ++ return false; \ ++ } \ ++ } while (0) ++ + static std::string get_human_readable_timespan(std::chrono::seconds seconds); + static std::string get_human_readable_timespan(uint64_t seconds); + +@@ -325,7 +336,7 @@ namespace + auto pwd_container = tools::password_container::prompt(verify, prompt); + if (!pwd_container) + { +- tools::fail_msg_writer() << sw::tr("failed to read wallet password"); ++ tools::fail_msg_writer() << sw::tr("failed to read password"); + } + return pwd_container; + } +@@ -335,6 +346,11 @@ namespace + return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify); + } + ++ boost::optional background_sync_cache_password_prompter(bool verify) ++ { ++ return password_prompter(verify ? sw::tr("Enter a custom password for the background sync cache") : sw::tr("Background sync cache password"), verify); ++ } ++ + inline std::string interpret_rpc_response(bool ok, const std::string& status) + { + std::string err; +@@ -452,6 +468,41 @@ namespace + return "invalid"; + } + ++ const struct ++ { ++ const char *name; ++ tools::wallet2::BackgroundSyncType background_sync_type; ++ } background_sync_type_names[] = ++ { ++ { "off", tools::wallet2::BackgroundSyncOff }, ++ { "reuse-wallet-password", tools::wallet2::BackgroundSyncReusePassword }, ++ { "custom-background-password", tools::wallet2::BackgroundSyncCustomPassword }, ++ }; ++ ++ bool parse_background_sync_type(const std::string &s, tools::wallet2::BackgroundSyncType &background_sync_type) ++ { ++ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) ++ { ++ if (s == background_sync_type_names[n].name) ++ { ++ background_sync_type = background_sync_type_names[n].background_sync_type; ++ return true; ++ } ++ } ++ fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse background sync type"); ++ return false; ++ } ++ ++ std::string get_background_sync_type_name(tools::wallet2::BackgroundSyncType type) ++ { ++ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) ++ { ++ if (type == background_sync_type_names[n].background_sync_type) ++ return background_sync_type_names[n].name; ++ } ++ return "invalid"; ++ } ++ + std::string get_version_string(uint32_t version) + { + return boost::lexical_cast(version >> 16) + "." + boost::lexical_cast(version & 0xffff); +@@ -805,6 +856,7 @@ bool simple_wallet::spendkey(const std::vector &args/* = std::vecto + fail_msg_writer() << tr("wallet is watch-only and has no spend key"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("has no spend key"); + // don't log + PAUSE_READLINE(); + if (m_wallet->key_on_device()) { +@@ -836,6 +888,7 @@ bool simple_wallet::print_seed(bool encrypted) + fail_msg_writer() << tr("wallet is watch-only and has no seed"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("has no seed"); + + multisig = m_wallet->multisig(&ready); + if (multisig) +@@ -913,6 +966,7 @@ bool simple_wallet::seed_set_language(const std::vector &args/* = s + fail_msg_writer() << tr("wallet is watch-only and has no seed"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("has no seed"); + + epee::wipeable_string password; + { +@@ -1059,6 +1113,7 @@ bool simple_wallet::prepare_multisig_main(const std::vector &args, + fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig"); + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot be made multisig"); + + if(m_wallet->get_num_transfer_details()) + { +@@ -2195,6 +2250,7 @@ bool simple_wallet::save_known_rings(const std::vector &args) + + bool simple_wallet::freeze_thaw(const std::vector &args, bool freeze) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot freeze/thaw"); + if (args.empty()) + { + fail_msg_writer() << boost::format(tr("usage: %s |")) % (freeze ? "freeze" : "thaw"); +@@ -2234,6 +2290,7 @@ bool simple_wallet::thaw(const std::vector &args) + + bool simple_wallet::frozen(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot see frozen key images"); + if (args.empty()) + { + size_t ntd = m_wallet->get_num_transfer_details(); +@@ -3005,6 +3062,57 @@ bool simple_wallet::set_track_uses(const std::vector &args/* = std: + return true; + } + ++bool simple_wallet::setup_background_sync(const std::vector &args/* = std::vector()*/) ++{ ++ if (m_wallet->multisig()) ++ { ++ fail_msg_writer() << tr("background sync not implemented for multisig wallet"); ++ return true; ++ } ++ if (m_wallet->watch_only()) ++ { ++ fail_msg_writer() << tr("background sync not implemented for watch only wallet"); ++ return true; ++ } ++ if (m_wallet->key_on_device()) ++ { ++ fail_msg_writer() << tr("command not supported by HW wallet"); ++ return true; ++ } ++ ++ tools::wallet2::BackgroundSyncType background_sync_type; ++ if (!parse_background_sync_type(args[1], background_sync_type)) ++ { ++ fail_msg_writer() << tr("invalid option"); ++ return true; ++ } ++ ++ const auto pwd_container = get_and_verify_password(); ++ if (!pwd_container) ++ return true; ++ ++ try ++ { ++ boost::optional background_cache_password = boost::none; ++ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) ++ { ++ const auto background_pwd_container = background_sync_cache_password_prompter(true); ++ if (!background_pwd_container) ++ return true; ++ background_cache_password = background_pwd_container->password(); ++ } ++ ++ LOCK_IDLE_SCOPE(); ++ m_wallet->setup_background_sync(background_sync_type, pwd_container->password(), background_cache_password); ++ } ++ catch (const std::exception &e) ++ { ++ fail_msg_writer() << tr("Error setting background sync type: ") << e.what(); ++ } ++ ++ return true; ++} ++ + bool simple_wallet::set_show_wallet_name_when_locked(const std::vector &args/* = std::vector()*/) + { + const auto pwd_container = get_and_verify_password(); +@@ -3237,6 +3345,7 @@ bool simple_wallet::apropos(const std::vector &args) + + bool simple_wallet::scan_tx(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot scan tx"); + if (args.empty()) + { + PRINT_USAGE(USAGE_SCAN_TX); +@@ -3459,6 +3568,8 @@ simple_wallet::simple_wallet() + " Ignore outputs of amount above this threshold when spending. Value 0 is translated to the maximum value (18 million) which disables this filter.\n " + "ignore-outputs-below \n " + " Ignore outputs of amount below this threshold when spending.\n " ++ "background-sync \n " ++ " Set this to enable scanning in the background with just the view key while the wallet is locked.\n " + "track-uses <1|0>\n " + " Whether to keep track of owned outputs uses.\n " + "setup-background-mining <1|0>\n " +@@ -3879,6 +3990,7 @@ bool simple_wallet::set_variable(const std::vector &args) + success_msg_writer() << "ignore-outputs-above = " << cryptonote::print_money(m_wallet->ignore_outputs_above()); + success_msg_writer() << "ignore-outputs-below = " << cryptonote::print_money(m_wallet->ignore_outputs_below()); + success_msg_writer() << "track-uses = " << m_wallet->track_uses(); ++ success_msg_writer() << "background-sync = " << get_background_sync_type_name(m_wallet->background_sync_type()); + success_msg_writer() << "setup-background-mining = " << setup_background_mining_string; + success_msg_writer() << "device-name = " << m_wallet->device_name(); + success_msg_writer() << "export-format = " << (m_wallet->export_format() == tools::wallet2::ExportFormat::Ascii ? "ascii" : "binary"); +@@ -3897,6 +4009,7 @@ bool simple_wallet::set_variable(const std::vector &args) + } + else + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot change wallet settings"); + + #define CHECK_SIMPLE_VARIABLE(name, f, help) do \ + if (args[0] == name) { \ +@@ -3950,6 +4063,7 @@ bool simple_wallet::set_variable(const std::vector &args) + CHECK_SIMPLE_VARIABLE("ignore-outputs-above", set_ignore_outputs_above, tr("amount")); + CHECK_SIMPLE_VARIABLE("ignore-outputs-below", set_ignore_outputs_below, tr("amount")); + CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1")); ++ CHECK_SIMPLE_VARIABLE("background-sync", setup_background_sync, tr("off (default); reuse-wallet-password (reuse the wallet password to encrypt the background cache); custom-background-password (use a custom background password to encrypt the background cache)")); + CHECK_SIMPLE_VARIABLE("show-wallet-name-when-locked", set_show_wallet_name_when_locked, tr("1 or 0")); + CHECK_SIMPLE_VARIABLE("inactivity-lock-timeout", set_inactivity_lock_timeout, tr("unsigned integer (seconds, 0 to disable)")); + CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no")); +@@ -4904,7 +5018,10 @@ std::string simple_wallet::get_mnemonic_language() + //---------------------------------------------------------------------------------------------------- + boost::optional simple_wallet::get_and_verify_password() const + { +- auto pwd_container = default_password_prompter(m_wallet_file.empty()); ++ const bool verify = m_wallet_file.empty(); ++ auto pwd_container = (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) ++ ? background_sync_cache_password_prompter(verify) ++ : default_password_prompter(verify); + if (!pwd_container) + return boost::none; + +@@ -5208,6 +5325,8 @@ boost::optional simple_wallet::open_wallet(const boost::p + prefix = tr("Opened watch-only wallet"); + else if (m_wallet->multisig(&ready, &threshold, &total)) + prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); ++ else if (m_wallet->is_background_wallet()) ++ prefix = tr("Opened background wallet"); + else + prefix = tr("Opened wallet"); + message_writer(console_color_white, true) << +@@ -5415,6 +5534,10 @@ void simple_wallet::stop_background_mining() + //---------------------------------------------------------------------------------------------------- + void simple_wallet::check_background_mining(const epee::wipeable_string &password) + { ++ // Background mining can be toggled from the main wallet ++ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) ++ return; ++ + tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); + if (setup == tools::wallet2::BackgroundMiningNo) + { +@@ -6287,6 +6410,7 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::rescan_spent(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot rescan spent"); + if (!m_wallet->is_trusted_daemon()) + { + fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); +@@ -6584,11 +6708,27 @@ void simple_wallet::check_for_inactivity_lock(bool user) + " B B " << std::endl << + "" << std::endl; + } ++ ++ bool started_background_sync = false; ++ if (!m_wallet->is_background_wallet() && ++ m_wallet->background_sync_type() != tools::wallet2::BackgroundSyncOff) ++ { ++ LOCK_IDLE_SCOPE(); ++ m_wallet->start_background_sync(); ++ started_background_sync = true; ++ } ++ + while (1) + { + const char *inactivity_msg = user ? "" : tr("Locked due to inactivity."); +- tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << tr("The wallet password is required to unlock the console."); ++ tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << ( ++ (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) ++ ? tr("The background password is required to unlock the console.") ++ : tr("The wallet password is required to unlock the console.") ++ ); + ++ if (m_wallet->is_background_syncing()) ++ tools::msg_writer() << tr("\nSyncing in the background while locked...") << std::endl; + const bool show_wallet_name = m_wallet->show_wallet_name_when_locked(); + if (show_wallet_name) + { +@@ -6600,8 +6740,16 @@ void simple_wallet::check_for_inactivity_lock(bool user) + } + try + { +- if (get_and_verify_password()) ++ const auto pwd_container = get_and_verify_password(); ++ if (pwd_container) ++ { ++ if (started_background_sync) ++ { ++ LOCK_IDLE_SCOPE(); ++ m_wallet->stop_background_sync(pwd_container->password()); ++ } + break; ++ } + } + catch (...) { /* do nothing, just let the loop loop */ } + } +@@ -6628,6 +6776,7 @@ bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector &args_, bool called_by_mms) + { + // "transfer [index=[,,...]] [] []
[]" ++ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); + if (!try_connect_to_daemon()) + return false; + +@@ -7056,6 +7205,7 @@ bool simple_wallet::transfer_main(const std::vector &args_, bool ca + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::transfer(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); + if (args_.size() < 1) + { + PRINT_USAGE(USAGE_TRANSFER); +@@ -7068,6 +7218,7 @@ bool simple_wallet::transfer(const std::vector &args_) + + bool simple_wallet::sweep_unmixable(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + if (!try_connect_to_daemon()) + return true; + +@@ -7175,6 +7326,7 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + auto print_usage = [this, account, below]() + { + if (below) +@@ -7456,6 +7608,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vect + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_single(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + if (!try_connect_to_daemon()) + return true; + +@@ -7694,12 +7847,14 @@ bool simple_wallet::sweep_single(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_all(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + sweep_main(m_current_subaddress_account, 0, args_); + return true; + } + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_account(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + auto local_args = args_; + if (local_args.empty()) + { +@@ -7720,6 +7875,7 @@ bool simple_wallet::sweep_account(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sweep_below(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); + uint64_t below = 0; + if (args_.size() < 1) + { +@@ -7738,6 +7894,7 @@ bool simple_wallet::sweep_below(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::donate(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot donate"); + std::vector local_args = args_; + if(local_args.empty() || local_args.size() > 5) + { +@@ -7799,6 +7956,7 @@ bool simple_wallet::donate(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot load tx"); + // gather info to ask the user + uint64_t amount = 0, amount_to_dests = 0, change = 0; + size_t min_ring_size = ~0; +@@ -7980,6 +8138,8 @@ bool simple_wallet::sign_transfer(const std::vector &args_) + return true; + } + ++ CHECK_IF_BACKGROUND_SYNCING("cannot sign transfer"); ++ + bool export_raw = false; + std::string unsigned_filename = "unsigned_wownero_tx"; + if (args_.size() > 2 || (args_.size() == 2 && args_[0] != "export_raw")) +@@ -8086,6 +8246,8 @@ std::string get_tx_key_stream(crypto::secret_key tx_key, std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get tx key"); ++ + std::vector local_args = args_; + + if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR) +@@ -8126,6 +8288,8 @@ bool simple_wallet::get_tx_key(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::set_tx_key(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot set tx key"); ++ + std::vector local_args = args_; + + if(local_args.size() != 2 && local_args.size() != 3) { +@@ -8202,6 +8366,8 @@ bool simple_wallet::set_tx_key(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_tx_proof(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get tx proof"); ++ + if (args.size() != 2 && args.size() != 3) + { + PRINT_USAGE(USAGE_GET_TX_PROOF); +@@ -8408,6 +8574,7 @@ bool simple_wallet::check_tx_proof(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_spend_proof(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get spend proof"); + if (m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command not supported by HW wallet"); +@@ -8492,6 +8659,7 @@ bool simple_wallet::check_spend_proof(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_reserve_proof(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get reserve proof"); + if (m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command not supported by HW wallet"); +@@ -9178,6 +9346,8 @@ bool simple_wallet::unspent_outputs(const std::vector &args_) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::rescan_blockchain(const std::vector &args_) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot rescan"); ++ + uint64_t start_height = 0; + ResetType reset_type = ResetSoft; + +@@ -9475,6 +9645,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + if (command == "new") + { + // create a new account and switch to it ++ CHECK_IF_BACKGROUND_SYNCING("cannot create new account"); + std::string label = boost::join(local_args, " "); + if (label.empty()) + label = tr("(Untitled account)"); +@@ -9505,6 +9676,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + else if (command == "label" && local_args.size() >= 1) + { + // set label of the specified account ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); + uint32_t index_major; + if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0])) + { +@@ -9526,6 +9698,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + } + else if (command == "tag" && local_args.size() >= 2) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); + const std::string tag = local_args[0]; + std::set account_indices; + for (size_t i = 1; i < local_args.size(); ++i) +@@ -9550,6 +9723,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + } + else if (command == "untag" && local_args.size() >= 1) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); + std::set account_indices; + for (size_t i = 0; i < local_args.size(); ++i) + { +@@ -9573,6 +9747,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector + } + else if (command == "tag_description" && local_args.size() >= 1) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); + const std::string tag = local_args[0]; + std::string description; + if (local_args.size() > 1) +@@ -9690,6 +9865,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: + } + else if (local_args[0] == "new") + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); + local_args.erase(local_args.begin()); + std::string label; + if (local_args.size() > 0) +@@ -9702,6 +9878,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: + } + else if (local_args[0] == "mnew") + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot add addresses"); + local_args.erase(local_args.begin()); + if (local_args.size() != 1) + { +@@ -9727,6 +9904,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: + } + else if (local_args[0] == "one-off") + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); + local_args.erase(local_args.begin()); + std::string label; + if (local_args.size() != 2) +@@ -9745,6 +9923,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: + } + else if (local_args.size() >= 2 && local_args[0] == "label") + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot modify address"); + if (!epee::string_tools::get_xtype_from_string(index, local_args[1])) + { + fail_msg_writer() << tr("failed to parse index: ") << local_args[1]; +@@ -9891,6 +10070,8 @@ bool simple_wallet::print_integrated_address(const std::vector &arg + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::address_book(const std::vector &args/* = std::vector()*/) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get address book"); ++ + if (args.size() == 0) + { + } +@@ -9951,6 +10132,8 @@ bool simple_wallet::address_book(const std::vector &args/* = std::v + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::set_tx_note(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot set tx note"); ++ + if (args.size() == 0) + { + PRINT_USAGE(USAGE_SET_TX_NOTE); +@@ -9979,6 +10162,8 @@ bool simple_wallet::set_tx_note(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_tx_note(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get tx note"); ++ + if (args.size() != 1) + { + PRINT_USAGE(USAGE_GET_TX_NOTE); +@@ -10004,6 +10189,8 @@ bool simple_wallet::get_tx_note(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::set_description(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot set description"); ++ + // 0 arguments allowed, for setting the description to empty string + + std::string description = ""; +@@ -10020,6 +10207,8 @@ bool simple_wallet::set_description(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::get_description(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot get description"); ++ + if (args.size() != 0) + { + PRINT_USAGE(USAGE_GET_DESCRIPTION); +@@ -10078,6 +10267,8 @@ bool simple_wallet::wallet_info(const std::vector &args) + type = tr("Watch only"); + else if (m_wallet->multisig(&ready, &threshold, &total)) + type = (boost::format(tr("%u/%u multisig%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); ++ else if (m_wallet->is_background_wallet()) ++ type = tr("Background wallet"); + else + type = tr("Normal"); + message_writer() << tr("Type: ") << type; +@@ -10089,6 +10280,7 @@ bool simple_wallet::wallet_info(const std::vector &args) + //---------------------------------------------------------------------------------------------------- + bool simple_wallet::sign(const std::vector &args) + { ++ CHECK_IF_BACKGROUND_SYNCING("cannot sign"); + if (m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command not supported by HW wallet"); +@@ -10196,6 +10388,7 @@ bool simple_wallet::export_key_images(const std::vector &args_) + fail_msg_writer() << tr("command not supported by HW wallet"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot export key images"); + auto args = args_; + + if (m_wallet->watch_only()) +@@ -10249,6 +10442,7 @@ bool simple_wallet::import_key_images(const std::vector &args) + fail_msg_writer() << tr("command not supported by HW wallet"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot import key images"); + if (!m_wallet->is_trusted_daemon()) + { + fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); +@@ -10357,6 +10551,7 @@ bool simple_wallet::export_outputs(const std::vector &args_) + fail_msg_writer() << tr("command not supported by HW wallet"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot export outputs"); + auto args = args_; + + bool all = false; +@@ -10406,6 +10601,7 @@ bool simple_wallet::import_outputs(const std::vector &args) + fail_msg_writer() << tr("command not supported by HW wallet"); + return true; + } ++ CHECK_IF_BACKGROUND_SYNCING("cannot import outputs"); + if (args.size() != 1) + { + PRINT_USAGE(USAGE_IMPORT_OUTPUTS); +diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h +index d641dca1b..b98a40713 100644 +--- a/src/simplewallet/simplewallet.h ++++ b/src/simplewallet/simplewallet.h +@@ -147,6 +147,7 @@ namespace cryptonote + bool set_ignore_outputs_above(const std::vector &args = std::vector()); + bool set_ignore_outputs_below(const std::vector &args = std::vector()); + bool set_track_uses(const std::vector &args = std::vector()); ++ bool setup_background_sync(const std::vector &args = std::vector()); + bool set_show_wallet_name_when_locked(const std::vector &args = std::vector()); + bool set_inactivity_lock_timeout(const std::vector &args = std::vector()); + bool set_setup_background_mining(const std::vector &args = std::vector()); +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index e81265ad3..e868fa039 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -54,6 +54,40 @@ using namespace cryptonote; + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" + ++#define LOCK_REFRESH() \ ++ bool refresh_enabled = m_refreshEnabled; \ ++ m_refreshEnabled = false; \ ++ m_wallet->stop(); \ ++ m_refreshCV.notify_one(); \ ++ boost::mutex::scoped_lock lock(m_refreshMutex); \ ++ boost::mutex::scoped_lock lock2(m_refreshMutex2); \ ++ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ ++ /* m_refreshMutex's still locked here */ \ ++ if (refresh_enabled) \ ++ startRefresh(); \ ++ }) ++ ++#define PRE_VALIDATE_BACKGROUND_SYNC() \ ++ do \ ++ { \ ++ clearStatus(); \ ++ if (m_wallet->key_on_device()) \ ++ { \ ++ setStatusError(tr("HW wallet cannot use background sync")); \ ++ return false; \ ++ } \ ++ if (m_wallet->watch_only()) \ ++ { \ ++ setStatusError(tr("View only wallet cannot use background sync")); \ ++ return false; \ ++ } \ ++ if (m_wallet->multisig()) \ ++ { \ ++ setStatusError(tr("Multisig wallet cannot use background sync")); \ ++ return false; \ ++ } \ ++ } while (0) ++ + namespace Monero { + + namespace { +@@ -792,6 +826,8 @@ bool WalletImpl::close(bool store) + + std::string WalletImpl::seed(const std::string& seed_offset) const + { ++ if (checkBackgroundSync("cannot get seed")) ++ return std::string(); + epee::wipeable_string seed; + if (m_wallet) + m_wallet->get_seed(seed, seed_offset); +@@ -805,6 +841,8 @@ std::string WalletImpl::getSeedLanguage() const + + void WalletImpl::setSeedLanguage(const std::string &arg) + { ++ if (checkBackgroundSync("cannot set seed language")) ++ return; + m_wallet->set_seed_language(arg); + } + +@@ -828,6 +866,8 @@ void WalletImpl::statusWithErrorString(int& status, std::string& errorString) co + + bool WalletImpl::setPassword(const std::string &password) + { ++ if (checkBackgroundSync("cannot change password")) ++ return false; + clearStatus(); + try { + m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password); +@@ -988,6 +1028,8 @@ bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_ + + void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height) + { ++ if (checkBackgroundSync("cannot change refresh height")) ++ return; + m_wallet->set_refresh_from_block_height(refresh_from_block_height); + } + +@@ -1105,6 +1147,8 @@ void WalletImpl::refreshAsync() + + bool WalletImpl::rescanBlockchain() + { ++ if (checkBackgroundSync("cannot rescan blockchain")) ++ return false; + clearStatus(); + m_refreshShouldRescan = true; + doRefresh(); +@@ -1113,6 +1157,8 @@ bool WalletImpl::rescanBlockchain() + + void WalletImpl::rescanBlockchainAsync() + { ++ if (checkBackgroundSync("cannot rescan blockchain")) ++ return; + m_refreshShouldRescan = true; + refreshAsync(); + } +@@ -1136,7 +1182,7 @@ int WalletImpl::autoRefreshInterval() const + UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_filename) { + clearStatus(); + UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); +- if (!m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ ++ if (checkBackgroundSync("cannot load tx") || !m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ + setStatusError(tr("Failed to load unsigned transactions")); + transaction->m_status = UnsignedTransaction::Status::Status_Error; + transaction->m_errorString = errorString(); +@@ -1156,6 +1202,8 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file + + bool WalletImpl::submitTransaction(const string &fileName) { + clearStatus(); ++ if (checkBackgroundSync("cannot submit tx")) ++ return false; + std::unique_ptr transaction(new PendingTransactionImpl(*this)); + + bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx); +@@ -1179,6 +1227,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) + setStatusError(tr("Wallet is view only")); + return false; + } ++ if (checkBackgroundSync("cannot export key images")) ++ return false; + + try + { +@@ -1199,6 +1249,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) + + bool WalletImpl::importKeyImages(const string &filename) + { ++ if (checkBackgroundSync("cannot import key images")) ++ return false; + if (!trustedDaemon()) { + setStatusError(tr("Key images can only be imported with a trusted daemon")); + return false; +@@ -1222,6 +1274,8 @@ bool WalletImpl::importKeyImages(const string &filename) + + bool WalletImpl::exportOutputs(const string &filename, bool all) + { ++ if (checkBackgroundSync("cannot export outputs")) ++ return false; + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets.")) + filename); +@@ -1252,6 +1306,8 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) + + bool WalletImpl::importOutputs(const string &filename) + { ++ if (checkBackgroundSync("cannot import outputs")) ++ return false; + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets.")) + filename); +@@ -1284,6 +1340,8 @@ bool WalletImpl::importOutputs(const string &filename) + + bool WalletImpl::scanTransactions(const std::vector &txids) + { ++ if (checkBackgroundSync("cannot scan transactions")) ++ return false; + if (txids.empty()) + { + setStatusError(string(tr("Failed to scan transactions: no transaction ids provided."))); +@@ -1322,8 +1380,86 @@ bool WalletImpl::scanTransactions(const std::vector &txids) + return true; + } + ++bool WalletImpl::setupBackgroundSync(const Wallet::BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) ++{ ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ ++ tools::wallet2::BackgroundSyncType bgs_type; ++ switch (background_sync_type) ++ { ++ case Wallet::BackgroundSync_Off: bgs_type = tools::wallet2::BackgroundSyncOff; break; ++ case Wallet::BackgroundSync_ReusePassword: bgs_type = tools::wallet2::BackgroundSyncReusePassword; break; ++ case Wallet::BackgroundSync_CustomPassword: bgs_type = tools::wallet2::BackgroundSyncCustomPassword; break; ++ default: setStatusError(tr("Unknown background sync type")); return false; ++ } ++ ++ boost::optional bgc_password = background_cache_password ++ ? boost::optional(*background_cache_password) ++ : boost::none; ++ ++ LOCK_REFRESH(); ++ m_wallet->setup_background_sync(bgs_type, wallet_password, bgc_password); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Failed to setup background sync: " << e.what()); ++ setStatusError(string(tr("Failed to setup background sync: ")) + e.what()); ++ return false; ++ } ++ return true; ++} ++ ++Wallet::BackgroundSyncType WalletImpl::getBackgroundSyncType() const ++{ ++ switch (m_wallet->background_sync_type()) ++ { ++ case tools::wallet2::BackgroundSyncOff: return Wallet::BackgroundSync_Off; ++ case tools::wallet2::BackgroundSyncReusePassword: return Wallet::BackgroundSync_ReusePassword; ++ case tools::wallet2::BackgroundSyncCustomPassword: return Wallet::BackgroundSync_CustomPassword; ++ default: setStatusError(tr("Unknown background sync type")); return Wallet::BackgroundSync_Off; ++ } ++} ++ ++bool WalletImpl::startBackgroundSync() ++{ ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ LOCK_REFRESH(); ++ m_wallet->start_background_sync(); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Failed to start background sync: " << e.what()); ++ setStatusError(string(tr("Failed to start background sync: ")) + e.what()); ++ return false; ++ } ++ return true; ++} ++ ++bool WalletImpl::stopBackgroundSync(const std::string &wallet_password) ++{ ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ LOCK_REFRESH(); ++ m_wallet->stop_background_sync(epee::wipeable_string(wallet_password)); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Failed to stop background sync: " << e.what()); ++ setStatusError(string(tr("Failed to stop background sync: ")) + e.what()); ++ return false; ++ } ++ return true; ++} ++ + void WalletImpl::addSubaddressAccount(const std::string& label) + { ++ if (checkBackgroundSync("cannot add account")) ++ return; + m_wallet->add_subaddress_account(label); + } + size_t WalletImpl::numSubaddressAccounts() const +@@ -1336,10 +1472,14 @@ size_t WalletImpl::numSubaddresses(uint32_t accountIndex) const + } + void WalletImpl::addSubaddress(uint32_t accountIndex, const std::string& label) + { ++ if (checkBackgroundSync("cannot add subbaddress")) ++ return; + m_wallet->add_subaddress(accountIndex, label); + } + std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const + { ++ if (checkBackgroundSync("cannot get subbaddress label")) ++ return ""; + try + { + return m_wallet->get_subaddress_label({accountIndex, addressIndex}); +@@ -1353,6 +1493,8 @@ std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addre + } + void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) + { ++ if (checkBackgroundSync("cannot set subbaddress label")) ++ return; + try + { + return m_wallet->set_subaddress_label({accountIndex, addressIndex}, label); +@@ -1366,12 +1508,16 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex + + MultisigState WalletImpl::multisig() const { + MultisigState state; ++ if (checkBackgroundSync("cannot use multisig")) ++ return state; + state.isMultisig = m_wallet->multisig(&state.isReady, &state.threshold, &state.total); + + return state; + } + + string WalletImpl::getMultisigInfo() const { ++ if (checkBackgroundSync("cannot use multisig")) ++ return string(); + try { + clearStatus(); + return m_wallet->get_multisig_first_kex_msg(); +@@ -1384,6 +1530,8 @@ string WalletImpl::getMultisigInfo() const { + } + + string WalletImpl::makeMultisig(const vector& info, const uint32_t threshold) { ++ if (checkBackgroundSync("cannot make multisig")) ++ return string(); + try { + clearStatus(); + +@@ -1524,6 +1672,9 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector extra; + std::string extra_nonce; + vector dsts; +@@ -1690,6 +1841,9 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() + PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); + + do { ++ if (checkBackgroundSync("cannot sweep")) ++ break; ++ + try { + transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions(); + pendingTxPostProcess(transaction); +@@ -1823,11 +1977,15 @@ uint32_t WalletImpl::defaultMixin() const + + void WalletImpl::setDefaultMixin(uint32_t arg) + { ++ if (checkBackgroundSync("cannot set default mixin")) ++ return; + m_wallet->default_mixin(arg); + } + + bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val) + { ++ if (checkBackgroundSync("cannot set cache attribute")) ++ return false; + m_wallet->set_attribute(key, val); + return true; + } +@@ -1841,6 +1999,8 @@ std::string WalletImpl::getCacheAttribute(const std::string &key) const + + bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) + { ++ if (checkBackgroundSync("cannot set user note")) ++ return false; + cryptonote::blobdata txid_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) + return false; +@@ -1852,6 +2012,8 @@ bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) + + std::string WalletImpl::getUserNote(const std::string &txid) const + { ++ if (checkBackgroundSync("cannot get user note")) ++ return ""; + cryptonote::blobdata txid_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) + return ""; +@@ -1862,6 +2024,9 @@ std::string WalletImpl::getUserNote(const std::string &txid) const + + std::string WalletImpl::getTxKey(const std::string &txid_str) const + { ++ if (checkBackgroundSync("cannot get tx key")) ++ return ""; ++ + crypto::hash txid; + if(!epee::string_tools::hex_to_pod(txid_str, txid)) + { +@@ -1946,6 +2111,9 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str, + + std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const + { ++ if (checkBackgroundSync("cannot get tx proof")) ++ return ""; ++ + crypto::hash txid; + if (!epee::string_tools::hex_to_pod(txid_str, txid)) + { +@@ -2002,6 +2170,9 @@ bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &ad + } + + std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::string &message) const { ++ if (checkBackgroundSync("cannot get spend proof")) ++ return ""; ++ + crypto::hash txid; + if(!epee::string_tools::hex_to_pod(txid_str, txid)) + { +@@ -2044,6 +2215,9 @@ bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string + } + + std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const { ++ if (checkBackgroundSync("cannot get reserve proof")) ++ return ""; ++ + try + { + clearStatus(); +@@ -2090,6 +2264,9 @@ bool WalletImpl::checkReserveProof(const std::string &address, const std::string + + std::string WalletImpl::signMessage(const std::string &message, const std::string &address) + { ++ if (checkBackgroundSync("cannot sign message")) ++ return ""; ++ + if (address.empty()) { + return m_wallet->sign(message, tools::wallet2::sign_with_spend_key); + } +@@ -2217,6 +2394,16 @@ bool WalletImpl::isDeterministic() const + return m_wallet->is_deterministic(); + } + ++bool WalletImpl::isBackgroundSyncing() const ++{ ++ return m_wallet->is_background_syncing(); ++} ++ ++bool WalletImpl::isBackgroundWallet() const ++{ ++ return m_wallet->is_background_wallet(); ++} ++ + void WalletImpl::clearStatus() const + { + boost::lock_guard l(m_statusMutex); +@@ -2285,9 +2472,7 @@ void WalletImpl::doRefresh() + if(rescan) + m_wallet->rescan_blockchain(false); + m_wallet->refresh(trustedDaemon()); +- if (!m_synchronized) { +- m_synchronized = true; +- } ++ m_synchronized = m_wallet->is_synced(); + // assuming if we have empty history, it wasn't initialized yet + // for further history changes client need to update history in + // "on_money_received" and "on_money_sent" callbacks +@@ -2391,6 +2576,24 @@ bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_a + return true; + } + ++bool WalletImpl::checkBackgroundSync(const std::string &message) const ++{ ++ clearStatus(); ++ if (m_wallet->is_background_wallet()) ++ { ++ LOG_ERROR("Background wallets " + message); ++ setStatusError(tr("Background wallets ") + message); ++ return true; ++ } ++ if (m_wallet->is_background_syncing()) ++ { ++ LOG_ERROR(message + " while background syncing"); ++ setStatusError(message + tr(" while background syncing. Stop background syncing first.")); ++ return true; ++ } ++ return false; ++} ++ + bool WalletImpl::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 &unknown_parameters, std::string &error) + { + return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); +@@ -2409,6 +2612,8 @@ std::string WalletImpl::getDefaultDataDir() const + bool WalletImpl::rescanSpent() + { + clearStatus(); ++ if (checkBackgroundSync("cannot rescan spent")) ++ return false; + if (!trustedDaemon()) { + setStatusError(tr("Rescan spent can only be used with a trusted daemon")); + return false; +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index ec2d7e9b3..1f199a72c 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -171,6 +171,13 @@ public: + bool importOutputs(const std::string &filename) override; + bool scanTransactions(const std::vector &txids) override; + ++ bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; ++ BackgroundSyncType getBackgroundSyncType() const override; ++ bool startBackgroundSync() override; ++ bool stopBackgroundSync(const std::string &wallet_password) override; ++ bool isBackgroundSyncing() const override; ++ bool isBackgroundWallet() const override; ++ + virtual void disposeTransaction(PendingTransaction * t) override; + virtual uint64_t estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const override; +@@ -239,6 +246,7 @@ private: + bool isNewWallet() const; + void pendingTxPostProcess(PendingTransactionImpl * pending); + bool doInit(const std::string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit = 0, bool ssl = false); ++ bool checkBackgroundSync(const std::string &message) const; + + private: + friend class PendingTransactionImpl; +@@ -253,6 +261,10 @@ private: + mutable boost::mutex m_statusMutex; + mutable int m_status; + mutable std::string m_errorString; ++ // TODO: harden password handling in the wallet API, see relevant discussion ++ // https://github.com/monero-project/monero-gui/issues/1537 ++ // https://github.com/feather-wallet/feather/issues/72#issuecomment-1405602142 ++ // https://github.com/monero-project/monero/pull/8619#issuecomment-1632951461 + std::string m_password; + std::unique_ptr m_history; + std::unique_ptr m_wallet2Callback; +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 71991df0d..e349df176 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -445,6 +445,12 @@ struct Wallet + ConnectionStatus_WrongVersion + }; + ++ enum BackgroundSyncType { ++ BackgroundSync_Off = 0, ++ BackgroundSync_ReusePassword = 1, ++ BackgroundSync_CustomPassword = 2 ++ }; ++ + virtual ~Wallet() = 0; + virtual std::string seed(const std::string& seed_offset = "") const = 0; + virtual std::string getSeedLanguage() const = 0; +@@ -936,6 +942,42 @@ struct Wallet + */ + virtual bool scanTransactions(const std::vector &txids) = 0; + ++ /*! ++ * \brief setupBackgroundSync - setup background sync mode with just a view key ++ * \param background_sync_type - the mode the wallet background syncs in ++ * \param wallet_password ++ * \param background_cache_password - custom password to encrypt background cache, only needed for custom password background sync type ++ * \return - true on success ++ */ ++ virtual bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) = 0; ++ ++ /*! ++ * \brief getBackgroundSyncType - get mode the wallet background syncs in ++ * \return - the type, or off if type is unknown ++ */ ++ virtual BackgroundSyncType getBackgroundSyncType() const = 0; ++ ++ /** ++ * @brief startBackgroundSync - sync the chain in the background with just view key ++ */ ++ virtual bool startBackgroundSync() = 0; ++ ++ /** ++ * @brief stopBackgroundSync - bring back spend key and process background synced txs ++ * \param wallet_password ++ */ ++ virtual bool stopBackgroundSync(const std::string &wallet_password) = 0; ++ ++ /** ++ * @brief isBackgroundSyncing - returns true if the wallet is background syncing ++ */ ++ virtual bool isBackgroundSyncing() const = 0; ++ ++ /** ++ * @brief isBackgroundWallet - returns true if the wallet is a background wallet ++ */ ++ virtual bool isBackgroundWallet() const = 0; ++ + virtual TransactionHistory * history() = 0; + virtual AddressBook * addressBook() = 0; + virtual Subaddress * subaddress() = 0; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 30b3502dc..8ce515c57 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -157,6 +157,8 @@ static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; + + static const std::string ASCII_OUTPUT_MAGIC = "WowneroAsciiDataV1"; + ++static const std::string BACKGROUND_WALLET_SUFFIX = ".background"; ++ + boost::mutex tools::wallet2::default_daemon_address_lock; + std::string tools::wallet2::default_daemon_address = ""; + +@@ -1009,14 +1011,14 @@ uint64_t num_priv_multisig_keys_post_setup(uint64_t threshold, uint64_t total) + * @param keys_data_key the chacha key that encrypts wallet keys files + * @return crypto::chacha_key the chacha key that encrypts the wallet cache files + */ +-crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key) ++crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key, const unsigned char domain_separator) + { + static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key"); + + crypto::chacha_key cache_key; + epee::mlocked> cache_key_data; + memcpy(cache_key_data.data(), &keys_data_key, HASH_SIZE); +- cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE; ++ cache_key_data[HASH_SIZE] = domain_separator; + cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&) cache_key); + + return cache_key; +@@ -1104,7 +1106,7 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional lock(lockers_lock); + if (lockers++ > 0) + locked = false; +- if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only()) ++ if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only() || w.is_background_syncing()) + { + locked = false; + return; +@@ -1221,6 +1223,11 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std + m_ignore_outputs_above(MONEY_SUPPLY), + m_ignore_outputs_below(0), + m_track_uses(false), ++ m_is_background_wallet(false), ++ m_background_sync_type(BackgroundSyncOff), ++ m_background_syncing(false), ++ m_processing_background_cache(false), ++ m_custom_background_key(boost::none), + m_show_wallet_name_when_locked(false), + m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT), + m_setup_background_mining(BackgroundMiningNo), +@@ -1860,6 +1867,9 @@ bool has_nonrequested_tx_at_height_or_above_requested(uint64_t height, const std + //---------------------------------------------------------------------------------------------------- + void wallet2::scan_tx(const std::unordered_set &txids) + { ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "cannot scan tx from background wallet"); ++ + // Get the transactions from daemon in batches sorted lowest height to highest + tx_entry_data txs_to_scan = get_tx_entries(txids); + if (txs_to_scan.tx_entries.empty()) +@@ -2169,11 +2179,11 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons + THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); + + // if keys are encrypted, ask for password +- if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k) ++ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k && !m_background_syncing) + { + static critical_section password_lock; + CRITICAL_REGION_LOCAL(password_lock); +- if (!m_encrypt_keys_after_refresh) ++ if (!m_encrypt_keys_after_refresh && !m_processing_background_cache) + { + boost::optional pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received"); + THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming wownero")); +@@ -2185,7 +2195,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons + crypto::public_key output_public_key; + THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key"); + +- if (m_multisig) ++ if (m_multisig || m_background_syncing/*no spend key*/) + { + tx_scan_info.in_ephemeral.pub = output_public_key; + tx_scan_info.in_ephemeral.sec = crypto::null_skey; +@@ -2442,6 +2452,22 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error, + "transactions outputs size=" + std::to_string(tx.vout.size()) + + " not match with daemon response size=" + std::to_string(o_indices.size())); ++ ++ // we're going to re-process this receive when background sync is disabled ++ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) ++ { ++ size_t bgs_idx = m_background_sync_data.txs.size(); ++ background_synced_tx_t bgs_tx = { ++ .index_in_background_sync_data = bgs_idx, ++ .tx = tx, ++ .output_indices = o_indices, ++ .height = height, ++ .block_timestamp = ts, ++ .double_spend_seen = double_spend_seen ++ }; ++ LOG_PRINT_L2("Adding received tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); ++ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); ++ } + } + + for(size_t o: outs) +@@ -2467,7 +2493,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + td.m_tx = (const cryptonote::transaction_prefix&)tx; + td.m_txid = txid; + td.m_key_image = tx_scan_info[o].ki; +- td.m_key_image_known = !m_watch_only && !m_multisig; ++ td.m_key_image_known = !m_watch_only && !m_multisig && !m_background_syncing; + if (!td.m_key_image_known) + { + // we might have cold signed, and have a mapping to key images +@@ -2657,10 +2683,25 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + 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); ++ ++ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) ++ { ++ size_t bgs_idx = m_background_sync_data.txs.size(); ++ background_synced_tx_t bgs_tx = { ++ .index_in_background_sync_data = bgs_idx, ++ .tx = tx, ++ .output_indices = o_indices, ++ .height = height, ++ .block_timestamp = ts, ++ .double_spend_seen = double_spend_seen ++ }; ++ LOG_PRINT_L2("Adding spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); ++ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); ++ } + } + } + +- if (!pool && m_track_uses) ++ if (!pool && (m_track_uses || (m_background_syncing && it == m_key_images.end()))) + { + PERF_TIMER(track_uses); + const uint64_t amount = in_to_key.amount; +@@ -2674,7 +2715,27 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + { + size_t idx = i->second; + THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Output tracker cache index out of range"); +- m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); ++ ++ if (m_track_uses) ++ m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); ++ ++ // We'll re-process all txs which *might* be spends when we disable ++ // background sync and retrieve the spend key. We don't know if an ++ // output is a spend in this tx if we don't know its key image. ++ if (m_background_syncing && !m_transfers[idx].m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) ++ { ++ size_t bgs_idx = m_background_sync_data.txs.size(); ++ background_synced_tx_t bgs_tx = { ++ .index_in_background_sync_data = bgs_idx, ++ .tx = tx, ++ .output_indices = o_indices, ++ .height = height, ++ .block_timestamp = ts, ++ .double_spend_seen = double_spend_seen ++ }; ++ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); ++ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); ++ } + } + } + } +@@ -2684,7 +2745,24 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + continue; + for (uint64_t offset: offsets) + if (offset == td.m_global_output_index) +- td.m_uses.push_back(std::make_pair(height, txid)); ++ { ++ if (m_track_uses) ++ td.m_uses.push_back(std::make_pair(height, txid)); ++ if (m_background_syncing && !td.m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) ++ { ++ size_t bgs_idx = m_background_sync_data.txs.size(); ++ background_synced_tx_t bgs_tx = { ++ .index_in_background_sync_data = bgs_idx, ++ .tx = tx, ++ .output_indices = o_indices, ++ .height = height, ++ .block_timestamp = ts, ++ .double_spend_seen = double_spend_seen ++ }; ++ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); ++ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); ++ } ++ } + } + } + } +@@ -3057,8 +3135,8 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh + req.start_height = start_height; + req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; + +- req.requested_info = first ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; +- if (try_incremental) ++ req.requested_info = (first && !m_background_syncing) ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; ++ if (try_incremental && !m_background_syncing) + req.pool_info_since = m_pool_info_query_time; + + { +@@ -3085,7 +3163,7 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh + << ", height " << blocks_start_height + blocks.size() << ", node height " << res.current_height + << ", pool info " << static_cast(res.pool_info_extent)); + +- if (first) ++ if (first && !m_background_syncing) + { + if (res.pool_info_extent != COMMAND_RPC_GET_BLOCKS_FAST::NONE) + { +@@ -3597,6 +3675,9 @@ void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash + // incremental update anymore, because with that we might miss some txs altogether. + void wallet2::update_pool_state(std::vector> &process_txs, bool refreshed, bool try_incremental) + { ++ process_txs.clear(); ++ if (m_background_syncing) ++ return; + bool updated = false; + if (m_pool_info_query_time != 0 && try_incremental) + { +@@ -4168,6 +4249,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo + } + + m_first_refresh_done = true; ++ if (m_background_syncing || m_is_background_wallet) ++ m_background_sync_data.first_refresh_done = true; + + LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all(false)) << ", unlocked: " << print_money(unlocked_balance_all(false))); + } +@@ -4253,6 +4336,14 @@ wallet2::detached_blockchain_data wallet2::detach_blockchain(uint64_t height, st + td.m_uses.pop_back(); + } + ++ for (auto it = m_background_sync_data.txs.begin(); it != m_background_sync_data.txs.end(); ) ++ { ++ if(height <= it->second.height) ++ it = m_background_sync_data.txs.erase(it); ++ else ++ ++it; ++ } ++ + if (output_tracker_cache) + output_tracker_cache->clear(); + +@@ -4327,8 +4418,12 @@ void wallet2::handle_reorg(uint64_t height, std::map m_blockchain.offset(), + error::wallet_internal_error, "Daemon claims reorg below last checkpoint"); ++ + detached_blockchain_data dbd = detach_blockchain(height, output_tracker_cache); + ++ if (m_background_syncing && height < m_background_sync_data.start_height) ++ m_background_sync_data.start_height = height; ++ + if (m_callback) + m_callback->on_reorg(height, dbd.detached_blockchain.size(), dbd.detached_tx_hashes.size()); + } +@@ -4338,6 +4433,7 @@ bool wallet2::deinit() + if(m_is_initialized) { + m_is_initialized = false; + unlock_keys_file(); ++ unlock_background_keys_file(); + m_account.deinit(); + } + return true; +@@ -4364,6 +4460,7 @@ bool wallet2::clear() + m_device_last_key_image_sync = 0; + m_pool_info_query_time = 0; + m_skip_to_height = 0; ++ m_background_sync_data = background_sync_data_t{}; + return true; + } + //---------------------------------------------------------------------------------------------------- +@@ -4382,13 +4479,30 @@ void wallet2::clear_soft(bool keep_key_images) + m_scanned_pool_txs[1].clear(); + m_pool_info_query_time = 0; + m_skip_to_height = 0; ++ m_background_sync_data = background_sync_data_t{}; + + cryptonote::block b; + generate_genesis(b); + m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); + } +- ++//---------------------------------------------------------------------------------------------------- ++void wallet2::clear_user_data() ++{ ++ for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) ++ i->second.m_dests.clear(); ++ for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) ++ i->second.m_dests.clear(); ++ for (auto i = m_transfers.begin(); i != m_transfers.end(); ++i) ++ i->m_frozen = false; ++ m_tx_keys.clear(); ++ m_tx_notes.clear(); ++ m_address_book.clear(); ++ m_subaddress_labels.clear(); ++ m_attributes.clear(); ++ m_account_tags = std::pair, std::vector>(); ++} ++//---------------------------------------------------------------------------------------------------- + /*! + * \brief Stores wallet information to wallet file. + * \param keys_file_name Name of wallet file +@@ -4400,16 +4514,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable + { + boost::optional keys_file_data = get_keys_file_data(password, watch_only); + CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); +- ++ return store_keys_file_data(keys_file_name, keys_file_data.get()); ++} ++//---------------------------------------------------------------------------------------------------- ++bool wallet2::store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only, bool background_keys_file) ++{ ++ boost::optional keys_file_data = get_keys_file_data(key, watch_only, background_keys_file); ++ CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); ++ return store_keys_file_data(keys_file_name, keys_file_data.get(), background_keys_file); ++} ++//---------------------------------------------------------------------------------------------------- ++bool wallet2::store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file) ++{ + std::string tmp_file_name = keys_file_name + ".new"; + std::string buf; +- bool r = ::serialization::dump_binary(keys_file_data.get(), buf); ++ bool r = ::serialization::dump_binary(keys_file_data, buf); + r = r && save_to_file(tmp_file_name, buf); + CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name); + +- unlock_keys_file(); ++ if (!background_keys_file) ++ unlock_keys_file(); ++ else ++ unlock_background_keys_file(); ++ + std::error_code e = tools::replace_file(tmp_file_name, keys_file_name); +- lock_keys_file(); ++ ++ if (!background_keys_file) ++ lock_keys_file(); ++ else ++ lock_background_keys_file(keys_file_name); + + if (e) { + boost::filesystem::remove(tmp_file_name); +@@ -4421,26 +4554,27 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable + } + //---------------------------------------------------------------------------------------------------- + boost::optional wallet2::get_keys_file_data(const epee::wipeable_string& password, bool watch_only) ++{ ++ crypto::chacha_key key; ++ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); ++ verify_password_with_cached_key(key); ++ return get_keys_file_data(key, watch_only); ++} ++//---------------------------------------------------------------------------------------------------- ++boost::optional wallet2::get_keys_file_data(const crypto::chacha_key& key, bool watch_only, bool background_keys_file) + { + epee::byte_slice account_data; + std::string multisig_signers; + std::string multisig_derivations; + cryptonote::account_base account = m_account; + +- crypto::chacha_key key; +- crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); +- +- // We use m_cache_key as a deterministic test to see if given key corresponds to original password +- const crypto::chacha_key cache_key = derive_cache_key(key); +- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); +- + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) + { + account.encrypt_viewkey(key); + account.decrypt_keys(key); + } + +- if (watch_only) ++ if (watch_only || background_keys_file) + account.forget_spend_key(); + + account.encrypt_keys(key); +@@ -4575,6 +4709,9 @@ boost::optional wallet2::get_keys_file_data(const epee: + value2.SetInt(m_track_uses ? 1 : 0); + json.AddMember("track_uses", value2, json.GetAllocator()); + ++ value2.SetInt(m_background_sync_type); ++ json.AddMember("background_sync_type", value2, json.GetAllocator()); ++ + value2.SetInt(m_show_wallet_name_when_locked ? 1 : 0); + json.AddMember("show_wallet_name_when_locked", value2, json.GetAllocator()); + +@@ -4632,6 +4769,12 @@ boost::optional wallet2::get_keys_file_data(const epee: + value2.SetInt(m_enable_multisig ? 1 : 0); + json.AddMember("enable_multisig", value2, json.GetAllocator()); + ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key) ++ { ++ value.SetString(reinterpret_cast(m_custom_background_key.get().data()), m_custom_background_key.get().size()); ++ json.AddMember("custom_background_key", value, json.GetAllocator()); ++ } ++ + // Serialize the JSON object + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); +@@ -4658,13 +4801,81 @@ void wallet2::setup_keys(const epee::wipeable_string &password) + m_account.decrypt_viewkey(key); + } + +- m_cache_key = derive_cache_key(key); ++ m_cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); + + get_ringdb_key(); + } + //---------------------------------------------------------------------------------------------------- ++void validate_background_cache_password_usage(const tools::wallet2::BackgroundSyncType background_sync_type, const boost::optional &background_cache_password, const bool multisig, const bool watch_only, const bool key_on_device) ++{ ++ THROW_WALLET_EXCEPTION_IF(multisig || watch_only || key_on_device, error::wallet_internal_error, multisig ++ ? "Background sync not implemented for multisig wallets" : watch_only ++ ? "Background sync not implemented for view only wallets" ++ : "Background sync not implemented for HW wallets"); ++ ++ switch (background_sync_type) ++ { ++ case tools::wallet2::BackgroundSyncOff: ++ { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "background sync is not enabled"); ++ break; ++ } ++ case tools::wallet2::BackgroundSyncReusePassword: ++ { ++ THROW_WALLET_EXCEPTION_IF(background_cache_password, error::wallet_internal_error, ++ "unexpected custom background cache password"); ++ break; ++ } ++ case tools::wallet2::BackgroundSyncCustomPassword: ++ { ++ THROW_WALLET_EXCEPTION_IF(!background_cache_password, error::wallet_internal_error, ++ "expected custom background cache password"); ++ break; ++ } ++ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); ++ } ++} ++//---------------------------------------------------------------------------------------------------- ++void get_custom_background_key(const epee::wipeable_string &password, crypto::chacha_key &custom_background_key, const uint64_t kdf_rounds) ++{ ++ crypto::chacha_key key; ++ crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds); ++ custom_background_key = derive_cache_key(key, config::HASH_KEY_BACKGROUND_KEYS_FILE); ++} ++//---------------------------------------------------------------------------------------------------- ++const crypto::chacha_key wallet2::get_cache_key() ++{ ++ if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing) ++ { ++ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); ++ // Domain separate keys used to encrypt background keys file and cache ++ return derive_cache_key(m_custom_background_key.get(), config::HASH_KEY_BACKGROUND_CACHE); ++ } ++ else ++ { ++ return m_cache_key; ++ } ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::verify_password_with_cached_key(const epee::wipeable_string &password) ++{ ++ crypto::chacha_key key; ++ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); ++ verify_password_with_cached_key(key); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::verify_password_with_cached_key(const crypto::chacha_key &key) ++{ ++ // We use m_cache_key as a deterministic test to see if given key corresponds to original password ++ const crypto::chacha_key cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); ++ THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); ++} ++//---------------------------------------------------------------------------------------------------- + void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password) + { ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "cannot change password from background wallet"); ++ + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) + decrypt_keys(original_password); + setup_keys(new_password); +@@ -4723,8 +4934,24 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + std::string account_data; + account_data.resize(keys_file_data.account_data.size()); + crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); +- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) ++ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); ++ if (try_v0_format) + crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); ++ ++ // Check if it's a background keys file if both of the above formats fail ++ { ++ m_is_background_wallet = false; ++ m_background_syncing = false; ++ cryptonote::account_base account_data_check; ++ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) ++ { ++ get_custom_background_key(password, key, m_kdf_rounds); ++ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); ++ m_is_background_wallet = !json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); ++ m_background_syncing = m_is_background_wallet; // start a background wallet background syncing ++ } ++ } ++ + // The contents should be JSON if the wallet follows the new format. + if (json.Parse(account_data.c_str()).HasParseError()) + { +@@ -4762,6 +4989,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + m_ignore_outputs_above = MONEY_SUPPLY; + m_ignore_outputs_below = 0; + m_track_uses = false; ++ m_background_sync_type = BackgroundSyncOff; + m_show_wallet_name_when_locked = false; + m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT; + m_setup_background_mining = BackgroundMiningNo; +@@ -4779,6 +5007,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 = true; ++ m_custom_background_key = boost::none; + } + else if(json.IsObject()) + { +@@ -5015,6 +5244,39 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + m_credits_target = field_credits_target; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false); + m_enable_multisig = field_enable_multisig; ++ ++ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff); ++ m_background_sync_type = field_background_sync_type; ++ ++ // Load encryption key used to encrypt background cache ++ crypto::chacha_key custom_background_key; ++ m_custom_background_key = boost::none; ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_is_background_wallet) ++ { ++ if (!json.HasMember("custom_background_key")) ++ { ++ LOG_ERROR("Field custom_background_key not found in JSON"); ++ return false; ++ } ++ else if (!json["custom_background_key"].IsString()) ++ { ++ LOG_ERROR("Field custom_background_key found in JSON, but not String"); ++ return false; ++ } ++ else if (json["custom_background_key"].GetStringLength() != sizeof(crypto::chacha_key)) ++ { ++ LOG_ERROR("Field custom_background_key found in JSON, but not correct length"); ++ return false; ++ } ++ const char *field_custom_background_key = json["custom_background_key"].GetString(); ++ memcpy(custom_background_key.data(), field_custom_background_key, sizeof(crypto::chacha_key)); ++ m_custom_background_key = boost::optional(custom_background_key); ++ LOG_PRINT_L1("Loaded custom background key derived from custom password"); ++ } ++ else if (json.HasMember("custom_background_key")) ++ { ++ LOG_ERROR("Unexpected field custom_background_key found in JSON"); ++ } + } + else + { +@@ -5078,12 +5340,17 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + const cryptonote::account_keys& keys = m_account.get_keys(); + hw::device &hwdev = m_account.get_device(); + r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); +- if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) ++ if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD && !m_is_background_wallet) + r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); + + if (r) +- setup_keys(password); ++ { ++ if (!m_is_background_wallet) ++ setup_keys(password); ++ else ++ m_custom_background_key = boost::optional(key); ++ } + + return true; + } +@@ -5098,11 +5365,12 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password + * + */ +-bool wallet2::verify_password(const epee::wipeable_string& password) ++bool wallet2::verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out) + { + // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). + unlock_keys_file(); +- bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds); ++ const bool no_spend_key = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; ++ bool r = verify_password(m_keys_file, password, no_spend_key, m_account.get_device(), m_kdf_rounds, spend_key_out); + lock_keys_file(); + return r; + } +@@ -5120,7 +5388,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) + * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password + * + */ +-bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) ++bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out) + { + rapidjson::Document json; + wallet2::keys_file_data keys_file_data; +@@ -5137,9 +5405,22 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip + std::string account_data; + account_data.resize(keys_file_data.account_data.size()); + crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); +- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) ++ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); ++ if (try_v0_format) + crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + ++ // Check if it's a background keys file if both of the above formats fail ++ { ++ cryptonote::account_base account_data_check; ++ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) ++ { ++ get_custom_background_key(password, key, kdf_rounds); ++ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); ++ const bool is_background_wallet = json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); ++ no_spend_key = no_spend_key || is_background_wallet; ++ } ++ } ++ + // The contents should be JSON if the wallet follows the new format. + if (json.Parse(account_data.c_str()).HasParseError()) + { +@@ -5164,6 +5445,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip + r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); + if(!no_spend_key) + r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); ++ spend_key_out = (!no_spend_key && r) ? keys.m_spend_secret_key : crypto::null_skey; + return r; + } + +@@ -5175,9 +5457,7 @@ void wallet2::encrypt_keys(const crypto::chacha_key &key) + + void wallet2::decrypt_keys(const crypto::chacha_key &key) + { +- // We use m_cache_key as a deterministic test to see if given key corresponds to original password +- const crypto::chacha_key cache_key = derive_cache_key(key); +- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); ++ verify_password_with_cached_key(key); + + m_account.encrypt_viewkey(key); + m_account.decrypt_keys(key); +@@ -5853,11 +6133,30 @@ void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_strin + { + if (wallet_name.empty()) + return; ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "cannot change wallet settings from background wallet"); + prepare_file_names(wallet_name); + boost::system::error_code ignored_ec; + THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file); + bool r = store_keys(m_keys_file, password, m_watch_only); + THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); ++ ++ // Update the background keys file when we rewrite the main wallet keys file ++ if (m_background_sync_type == BackgroundSyncCustomPassword && m_custom_background_key) ++ { ++ const std::string background_keys_filename = make_background_keys_file_name(wallet_name); ++ if (!lock_background_keys_file(background_keys_filename)) ++ { ++ LOG_ERROR("Background keys file " << background_keys_filename << " is opened by another wallet program and cannot be rewritten"); ++ return; // not fatal, background keys file will just have different wallet settings ++ } ++ store_background_keys(m_custom_background_key.get()); ++ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); ++ } ++ else if (m_background_sync_type == BackgroundSyncReusePassword) ++ { ++ reset_background_sync_data(m_background_sync_data); ++ } + } + /*! + * \brief Writes to a file named based on the normal wallet (doesn't generate key, assumes it's already there) +@@ -5891,6 +6190,16 @@ bool wallet2::wallet_valid_path_format(const std::string& file_path) + return !file_path.empty(); + } + //---------------------------------------------------------------------------------------------------- ++std::string wallet2::make_background_wallet_file_name(const std::string &wallet_file) ++{ ++ return wallet_file + BACKGROUND_WALLET_SUFFIX; ++} ++//---------------------------------------------------------------------------------------------------- ++std::string wallet2::make_background_keys_file_name(const std::string &wallet_file) ++{ ++ return make_background_wallet_file_name(wallet_file) + ".keys"; ++} ++//---------------------------------------------------------------------------------------------------- + bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) + { + cryptonote::blobdata payment_id_data; +@@ -6126,10 +6435,81 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass + THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, "failed to load keys from buffer"); + } + +- wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password); ++ wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_is_background_wallet, password); + + //keys loaded ok! + //try to load wallet cache. but even if we failed, it is not big problem ++ load_wallet_cache(use_fs, cache_buf); ++ ++ if (!m_persistent_rpc_client_id) ++ set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); ++ ++ // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. ++ // Here we erase these multisig keys if they're zero'd out to free up space. ++ for (auto &td : m_transfers) ++ { ++ auto mk_it = td.m_multisig_k.begin(); ++ while (mk_it != td.m_multisig_k.end()) ++ { ++ if (*mk_it == rct::zero()) ++ mk_it = td.m_multisig_k.erase(mk_it); ++ else ++ ++mk_it; ++ } ++ } ++ ++ cryptonote::block genesis; ++ generate_genesis(genesis); ++ crypto::hash genesis_hash = get_block_hash(genesis); ++ ++ if (m_blockchain.empty()) ++ { ++ m_blockchain.push_back(genesis_hash); ++ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); ++ } ++ else ++ { ++ check_genesis(genesis_hash); ++ } ++ ++ trim_hashchain(); ++ ++ if (get_num_subaddress_accounts() == 0) ++ add_subaddress_account(tr("Primary account")); ++ ++ try ++ { ++ find_and_save_rings(false); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to save rings, will try again next time"); ++ } ++ ++ try ++ { ++ if (use_fs) ++ m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to initialize MMS, it will be unusable"); ++ } ++ ++ try ++ { ++ if (use_fs) ++ process_background_cache_on_open(); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to process background cache on open: " << e.what()); ++ } ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::load_wallet_cache(const bool use_fs, const std::string& cache_buf) ++{ ++ boost::system::error_code e; + bool cache_missing = use_fs ? (!boost::filesystem::exists(m_wallet_file, e) || e) : cache_buf.empty(); + if (cache_missing) + { +@@ -6143,7 +6523,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass + bool r = true; + if (use_fs) + { +- load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); ++ r = load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); + THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); + } + +@@ -6156,7 +6536,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"'); + std::string cache_data; + cache_data.resize(cache_file_data.cache_data.size()); +- crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]); ++ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), get_cache_key(), cache_file_data.iv, &cache_data[0]); + + try { + bool loaded = false; +@@ -6246,60 +6626,76 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass + m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key, + error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); + } ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::process_background_cache_on_open() ++{ ++ if (m_wallet_file.empty()) ++ return; ++ if (m_background_syncing || m_is_background_wallet) ++ return; ++ if (m_background_sync_type == BackgroundSyncOff) ++ return; + +- if (!m_persistent_rpc_client_id) +- set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); ++ if (m_background_sync_type == BackgroundSyncReusePassword) ++ { ++ const background_sync_data_t background_sync_data = m_background_sync_data; ++ const hashchain blockchain = m_blockchain; ++ process_background_cache(background_sync_data, blockchain, m_last_block_reward); + +- // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. +- // Here we erase these multisig keys if they're zero'd out to free up space. +- for (auto &td : m_transfers) ++ // Reset the background cache after processing ++ reset_background_sync_data(m_background_sync_data); ++ } ++ else if (m_background_sync_type == BackgroundSyncCustomPassword) + { +- auto mk_it = td.m_multisig_k.begin(); +- while (mk_it != td.m_multisig_k.end()) ++ // If the background wallet files don't exist, recreate them ++ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); ++ const std::string background_wallet_file = make_background_wallet_file_name(m_wallet_file); ++ const bool background_keys_file_exists = boost::filesystem::exists(background_keys_file); ++ const bool background_wallet_exists = boost::filesystem::exists(background_wallet_file); ++ ++ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_keys_file), error::background_wallet_already_open, background_wallet_file); ++ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); ++ ++ if (!background_keys_file_exists) + { +- if (*mk_it == rct::zero()) +- mk_it = td.m_multisig_k.erase(mk_it); +- else +- ++mk_it; ++ MDEBUG("Background keys file not found, restoring"); ++ store_background_keys(m_custom_background_key.get()); + } +- } + +- cryptonote::block genesis; +- generate_genesis(genesis); +- crypto::hash genesis_hash = get_block_hash(genesis); ++ if (!background_wallet_exists) ++ { ++ MDEBUG("Background cache not found, restoring"); ++ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); ++ return; ++ } + +- if (m_blockchain.empty()) +- { +- m_blockchain.push_back(genesis_hash); +- m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); +- } +- else +- { +- check_genesis(genesis_hash); +- } ++ MDEBUG("Loading background cache"); + +- trim_hashchain(); ++ // Set up a minimal background wallet2 instance ++ std::unique_ptr background_w2(new wallet2(m_nettype)); ++ background_w2->m_is_background_wallet = true; ++ background_w2->m_background_syncing = true; ++ background_w2->m_background_sync_type = m_background_sync_type; ++ background_w2->m_custom_background_key = m_custom_background_key; + +- if (get_num_subaddress_accounts() == 0) +- add_subaddress_account(tr("Primary account")); ++ cryptonote::account_base account = m_account; ++ account.forget_spend_key(); ++ background_w2->m_account = account; + +- try +- { +- find_and_save_rings(false); +- } +- catch (const std::exception &e) +- { +- MERROR("Failed to save rings, will try again next time"); +- } +- +- try +- { +- if (use_fs) +- m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); ++ // Load background cache from file ++ background_w2->clear(); ++ background_w2->prepare_file_names(background_wallet_file); ++ background_w2->load_wallet_cache(true/*use_fs*/); ++ ++ process_background_cache(background_w2->m_background_sync_data, background_w2->m_blockchain, background_w2->m_last_block_reward); ++ ++ // Reset the background cache after processing ++ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); + } +- catch (const std::exception &e) ++ else + { +- MERROR("Failed to initialize MMS, it will be unusable"); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); + } + } + //---------------------------------------------------------------------------------------------------- +@@ -6381,6 +6777,8 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas + same_file = canonical_old_path == canonical_new_path; + } + ++ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet && !same_file, error::wallet_internal_error, ++ "Cannot save background wallet files to a different location"); + + if (!same_file) + { +@@ -6397,6 +6795,21 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas + } + } + } ++ else if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing && !m_is_background_wallet) ++ { ++ // We're background syncing, so store the wallet cache as a background cache ++ // keeping the background sync data ++ try ++ { ++ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); ++ store_background_cache(m_custom_background_key.get(), false/*do_reset_background_sync_data*/); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to store background cache while background syncing: " << e.what()); ++ } ++ return; ++ } + + // get wallet cache data + boost::optional cache_file_data = get_cache_file_data(); +@@ -6490,6 +6903,22 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas + // store should only exist if the MMS is really active + m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file); + } ++ ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_background_syncing && !m_is_background_wallet) ++ { ++ // Update the background wallet cache when we store the main wallet cache ++ // Note: if background syncing when this is called, it means the background ++ // wallet is open and was already stored above ++ try ++ { ++ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); ++ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); ++ } ++ catch (const std::exception &e) ++ { ++ MERROR("Failed to update background cache: " << e.what()); ++ } ++ } + } + //---------------------------------------------------------------------------------------------------- + boost::optional wallet2::get_cache_file_data() +@@ -6507,7 +6936,7 @@ boost::optional wallet2::get_cache_file_data() + std::string cipher; + cipher.resize(cache_file_data.get().cache_data.size()); + cache_file_data.get().iv = crypto::rand(); +- crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), m_cache_key, cache_file_data.get().iv, &cipher[0]); ++ crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), get_cache_key(), cache_file_data.get().iv, &cipher[0]); + cache_file_data.get().cache_data = cipher; + return cache_file_data; + } +@@ -8577,6 +9006,34 @@ bool wallet2::is_keys_file_locked() const + return m_keys_file_locker->locked(); + } + ++bool wallet2::lock_background_keys_file(const std::string &background_keys_file) ++{ ++ if (background_keys_file.empty() || !boost::filesystem::exists(background_keys_file)) ++ return true; ++ if (m_background_keys_file_locker && m_background_keys_file_locker->locked()) ++ return true; ++ m_background_keys_file_locker.reset(new tools::file_locker(background_keys_file)); ++ return m_background_keys_file_locker->locked(); ++} ++ ++bool wallet2::unlock_background_keys_file() ++{ ++ if (!m_background_keys_file_locker) ++ { ++ MDEBUG("background keys file locker is not set"); ++ return false; ++ } ++ m_background_keys_file_locker.reset(); ++ return true; ++} ++ ++bool wallet2::is_background_keys_file_locked() const ++{ ++ if (!m_background_keys_file_locker) ++ return false; ++ return m_background_keys_file_locker->locked(); ++} ++ + bool wallet2::tx_add_fake_output(std::vector> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set &valid_public_keys_cache) const + { + if (!unlocked) // don't add locked outs +@@ -13900,6 +14357,413 @@ bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool o + return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none); + } + ++/* ++ In background sync mode, we use just the view key when the wallet is scanning ++ to identify all txs where: ++ ++ 1. We received an output. ++ 2. We spent an output. ++ 3. We *may* have spent a received output but we didn't know for sure because ++ the spend key was not loaded while background sync was enabled. ++ ++ When the user is ready to use the spend key again, we call this function to ++ process all those background synced transactions with the spend key loaded, ++ so that we can properly generate key images for the transactions which we ++ we were not able to do so for while background sync was enabled. This allows ++ us to determine *all* receives and spends the user completed while the wallet ++ had background sync enabled. Once this function completes, we can continue ++ scanning from where the background sync left off. ++ ++ Txs of type 3 (txs which we *may* have spent received output(s)) are txs where ++ 1+ rings contain an output that the user received and the wallet does not know ++ the associated key image for that output. We don't know if the user spent in ++ this type of tx or not. This function will generate key images for all outputs ++ we don't know key images for, and then check if those outputs were spent in ++ the txs of type 3. ++ ++ By storing this type of "plausible spend tx" when scanning in background sync ++ mode, we avoid the need to query the daemon with key images when background ++ sync mode is disabled to see if those key images were spent. This would ++ reveal key images to 3rd party nodes for users who don't run their own. ++ Although this is not a perfect solution to avoid revealing key images to a 3rd ++ party node (since tx submission trivially reveals key images to a node), it's ++ probably better than revealing *unused* key images to a 3rd party node, which ++ would enable the 3rd party to deduce that a tx is spending an output at least ++ X old when the key image is included in the chain. ++*/ ++void wallet2::process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_synced_chain, uint64_t last_block_reward) ++{ ++ // We expect the spend key to be in a decrypted state while ++ // m_processing_background_cache is true ++ m_processing_background_cache = true; ++ auto done_processing = epee::misc_utils::create_scope_leave_handler([&, this]() { ++ m_processing_background_cache = false; ++ }); ++ ++ if (m_background_syncing || m_multisig || m_watch_only || key_on_device()) ++ return; ++ ++ if (!background_sync_data.first_refresh_done) ++ { ++ MDEBUG("Skipping processing background cache, background cache has not synced yet"); ++ return; ++ } ++ ++ // Skip processing if wallet cache is synced higher than background cache ++ const uint64_t current_height = m_blockchain.size(); ++ const uint64_t background_height = background_synced_chain.size(); ++ MDEBUG("Background cache height " << background_height << " , wallet height " << current_height); ++ if (current_height > background_height) ++ { ++ MWARNING("Skipping processing background cache, synced height is higher than background cache"); ++ return; ++ } ++ ++ if (m_refresh_from_block_height < background_sync_data.wallet_refresh_from_block_height || ++ m_subaddress_lookahead_major > background_sync_data.subaddress_lookahead_major || ++ m_subaddress_lookahead_minor > background_sync_data.subaddress_lookahead_minor || ++ m_refresh_type < background_sync_data.wallet_refresh_type) ++ { ++ MWARNING("Skipping processing background cache, background wallet sync settings did not match main wallet's"); ++ MDEBUG("Wallet settings: " << ++ ", m_refresh_from_block_height: " << m_refresh_from_block_height << " vs " << background_sync_data.wallet_refresh_from_block_height << ++ ", m_subaddress_lookahead_major: " << m_subaddress_lookahead_major << " vs " << background_sync_data.subaddress_lookahead_major << ++ ", m_subaddress_lookahead_minor: " << m_subaddress_lookahead_minor << " vs " << background_sync_data.subaddress_lookahead_minor << ++ ", m_refresh_type: " << m_refresh_type << " vs " << background_sync_data.wallet_refresh_type); ++ return; ++ } ++ ++ // Sort background synced txs in the order they appeared in the cache so that ++ // we process them in the order they appeared in the chain. Thus if tx2 spends ++ // from tx1, we will know because tx1 is processed before tx2. ++ std::vector> sorted_bgs_cache(background_sync_data.txs.begin(), background_sync_data.txs.end()); ++ std::sort(sorted_bgs_cache.begin(), sorted_bgs_cache.end(), ++ [](const std::pair& l, const std::pair& r) ++ { ++ uint64_t left_index = l.second.index_in_background_sync_data; ++ uint64_t right_index = r.second.index_in_background_sync_data; ++ THROW_WALLET_EXCEPTION_IF( ++ (left_index < right_index && l.second.height > r.second.height) || ++ (left_index > right_index && l.second.height < r.second.height), ++ error::wallet_internal_error, "Unexpected background sync data order"); ++ return left_index < right_index; ++ }); ++ ++ // All txs in the background cache should have height >= sync start height, ++ // but not fatal if not ++ if (!sorted_bgs_cache.empty() && sorted_bgs_cache[0].second.height < background_sync_data.start_height) ++ MWARNING("First tx in background cache has height (" << sorted_bgs_cache[0].second.height << ") lower than sync start height (" << background_sync_data.start_height << ")"); ++ ++ // We want to process all background synced txs in order to make sure ++ // the wallet state updates correctly. First we remove all txs from the wallet ++ // from before the background sync start height, then re-process them in ++ // chronological order. The background cache should contain a superset of ++ // *all* the wallet's txs from after the background sync start height. ++ MDEBUG("Processing " << background_sync_data.txs.size() << " background synced txs starting from height " << background_sync_data.start_height); ++ detached_blockchain_data dbd = detach_blockchain(background_sync_data.start_height); ++ ++ for (const auto &bgs_tx : sorted_bgs_cache) ++ { ++ MDEBUG("Processing background synced tx " << bgs_tx.first); ++ ++ process_new_transaction(bgs_tx.first, bgs_tx.second.tx, bgs_tx.second.output_indices, bgs_tx.second.height, 0, bgs_tx.second.block_timestamp, ++ cryptonote::is_coinbase(bgs_tx.second.tx), false/*pool*/, bgs_tx.second.double_spend_seen, {}, {}, true/*ignore_callbacks*/); ++ ++ // Re-set destination addresses if they were previously set ++ if (m_confirmed_txs.find(bgs_tx.first) != m_confirmed_txs.end() && ++ dbd.detached_confirmed_txs_dests.find(bgs_tx.first) != dbd.detached_confirmed_txs_dests.end()) ++ { ++ m_confirmed_txs[bgs_tx.first].m_dests = std::move(dbd.detached_confirmed_txs_dests[bgs_tx.first]); ++ } ++ } ++ ++ m_blockchain = background_synced_chain; ++ m_last_block_reward = last_block_reward; ++ ++ MDEBUG("Finished processing background sync data"); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::reset_background_sync_data(background_sync_data_t &background_sync_data) ++{ ++ background_sync_data.first_refresh_done = false; ++ background_sync_data.start_height = get_blockchain_current_height(); ++ background_sync_data.txs.clear(); ++ ++ background_sync_data.wallet_refresh_from_block_height = m_refresh_from_block_height; ++ background_sync_data.subaddress_lookahead_major = m_subaddress_lookahead_major; ++ background_sync_data.subaddress_lookahead_minor = m_subaddress_lookahead_minor; ++ background_sync_data.wallet_refresh_type = m_refresh_type; ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data) ++{ ++ MDEBUG("Storing background cache (do_reset_background_sync_data=" << do_reset_background_sync_data << ")"); ++ ++ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, ++ "Can only write a background cache when using a custom background password"); ++ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, ++ "No wallet file known, can't store background cache"); ++ ++ std::unique_ptr background_w2(new wallet2(m_nettype)); ++ background_w2->prepare_file_names(make_background_wallet_file_name(m_wallet_file)); ++ ++ // Make sure background wallet is opened by this wallet ++ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_w2->m_keys_file), ++ error::background_wallet_already_open, background_w2->m_wallet_file); ++ ++ // Load a background wallet2 instance using this wallet2 instance ++ std::string this_wallet2; ++ bool r = ::serialization::dump_binary(*this, this_wallet2); ++ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to serialize wallet cache"); ++ ++ background_w2->clear(); ++ r = ::serialization::parse_binary(this_wallet2, *background_w2); ++ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to deserialize wallet cache"); ++ ++ // Clear sensitive data from background cache not needed to sync ++ background_w2->clear_user_data(); ++ ++ background_w2->m_is_background_wallet = true; ++ if (do_reset_background_sync_data) ++ reset_background_sync_data(background_w2->m_background_sync_data); ++ else ++ background_w2->m_background_sync_data = m_background_sync_data; ++ background_w2->m_background_syncing = true; ++ ++ background_w2->m_custom_background_key = boost::optional(custom_background_key); ++ background_w2->m_background_sync_type = m_background_sync_type; ++ background_w2->store(); ++ ++ MDEBUG("Background cache stored (" << background_w2->m_transfers.size() << " transfers, " << background_w2->m_background_sync_data.txs.size() << " background synced txs)"); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::store_background_keys(const crypto::chacha_key &custom_background_key) ++{ ++ MDEBUG("Storing background keys"); ++ ++ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, ++ "No wallet file known, can't store background keys"); ++ ++ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); ++ bool r = store_keys(background_keys_file, custom_background_key, false/*watch_only*/, true/*background_keys_file*/); ++ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, background_keys_file); ++ THROW_WALLET_EXCEPTION_IF(!is_background_keys_file_locked(), error::wallet_internal_error, background_keys_file + "\" should be locked"); ++ ++ // GUI uses the address file to differentiate non-mainnet wallets in the UI ++ const std::string background_address_file = make_background_wallet_file_name(m_wallet_file) + ".address.txt"; ++ if (m_nettype != MAINNET && !boost::filesystem::exists(background_address_file)) ++ { ++ r = save_to_file(background_address_file, m_account.get_public_address_str(m_nettype), true); ++ if (!r) MERROR("String with address text not saved"); ++ } ++ ++ MDEBUG("Background keys stored"); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password) ++{ ++ MDEBUG("Storing background sync wallet"); ++ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, ++ "Can only write a background sync wallet when using a custom background password"); ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "Can't write background sync wallet from an existing background cache"); ++ THROW_WALLET_EXCEPTION_IF(wallet_password == background_cache_password, ++ error::background_custom_password_same_as_wallet_password); ++ ++ // Set the background encryption key ++ crypto::chacha_key custom_background_key; ++ get_custom_background_key(background_cache_password, custom_background_key, m_kdf_rounds); ++ ++ // Keep the background encryption key in memory so the main wallet can update ++ // the background cache when it stores the main wallet cache ++ m_custom_background_key = boost::optional(custom_background_key); ++ ++ if (m_wallet_file.empty() || m_keys_file.empty()) ++ return; ++ ++ // Save background keys file, then background cache, then update main wallet settings ++ store_background_keys(custom_background_key); ++ store_background_cache(custom_background_key, true/*do_reset_background_sync_data*/); ++ bool r = store_keys(m_keys_file, wallet_password, false/*watch_only*/); ++ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); ++ ++ MDEBUG("Background sync wallet saved successfully"); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password) ++{ ++ MDEBUG("Setting background sync to type " << background_sync_type); ++ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, ++ "Can't set background sync type from an existing background cache"); ++ verify_password_with_cached_key(wallet_password); ++ ++ if (background_sync_type != BackgroundSyncOff) ++ validate_background_cache_password_usage(background_sync_type, background_cache_password, m_multisig, m_watch_only, key_on_device()); ++ ++ THROW_WALLET_EXCEPTION_IF(background_sync_type == BackgroundSyncCustomPassword && wallet_password == background_cache_password, ++ error::background_custom_password_same_as_wallet_password); ++ ++ if (m_background_sync_type == background_sync_type && background_sync_type != BackgroundSyncCustomPassword) ++ return; // No need to make any changes ++ ++ if (!m_wallet_file.empty()) ++ { ++ // Delete existing background files if they already exist ++ const std::string old_background_wallet_file = make_background_wallet_file_name(m_wallet_file); ++ const std::string old_background_keys_file = make_background_keys_file_name(m_wallet_file); ++ const std::string old_background_address_file = old_background_wallet_file + ".address.txt"; ++ ++ // Make sure no other program is using the background wallet ++ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(old_background_keys_file), ++ error::background_wallet_already_open, old_background_wallet_file); ++ ++ if (boost::filesystem::exists(old_background_wallet_file)) ++ if (!boost::filesystem::remove(old_background_wallet_file)) ++ LOG_ERROR("Error deleting background wallet file: " << old_background_wallet_file); ++ ++ if (boost::filesystem::exists(old_background_keys_file)) ++ if (!boost::filesystem::remove(old_background_keys_file)) ++ LOG_ERROR("Error deleting background keys file: " << old_background_keys_file); ++ ++ if (boost::filesystem::exists(old_background_address_file)) ++ if (!boost::filesystem::remove(old_background_address_file)) ++ LOG_ERROR("Error deleting background address file: " << old_background_address_file); ++ } ++ ++ m_background_sync_type = background_sync_type; ++ m_custom_background_key = boost::none; ++ ++ // Write the new files ++ switch (background_sync_type) ++ { ++ case BackgroundSyncOff: ++ case BackgroundSyncReusePassword: rewrite(m_wallet_file, wallet_password); break; ++ case BackgroundSyncCustomPassword: write_background_sync_wallet(wallet_password, background_cache_password.get()); break; ++ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); ++ } ++ ++ MDEBUG("Done setting background sync type"); ++} ++//---------------------------------------------------------------------------------------------------- ++/* ++ When background syncing, the wallet scans using just the view key, without ++ keeping the spend key in decrypted state. When a user returns to the wallet ++ and decrypts the spend key, the wallet processes the background synced txs, ++ then the wallet picks up scanning normally right where the background sync ++ left off. ++*/ ++void wallet2::start_background_sync() ++{ ++ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, ++ "must setup background sync first before using background sync"); ++ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, ++ "Can't start background syncing from a background wallet (it is always background syncing)"); ++ ++ MDEBUG("Starting background sync"); ++ ++ if (m_background_syncing) ++ { ++ MDEBUG("Already background syncing"); ++ return; ++ } ++ ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) ++ { ++ // Save the current state of the wallet cache. Only necessary when using a ++ // custom background password which uses distinct background wallet to sync. ++ // When reusing wallet password to sync we reuse the main wallet cache. ++ store(); ++ ++ // Wipe user data from the background wallet cache not needed to sync. ++ // Only wipe user data from background cache if wallet cache is stored ++ // on disk; otherwise we could lose the data. ++ clear_user_data(); ++ ++ // Wipe m_cache_key since it can be used to decrypt main wallet cache ++ m_cache_key.scrub(); ++ } ++ ++ reset_background_sync_data(m_background_sync_data); ++ m_background_syncing = true; ++ ++ // Wipe the spend key from memory ++ m_account.forget_spend_key(); ++ ++ MDEBUG("Background sync started at height " << m_background_sync_data.start_height); ++} ++//---------------------------------------------------------------------------------------------------- ++void wallet2::stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key) ++{ ++ MDEBUG("Stopping background sync"); ++ ++ // Verify provided password and spend secret key. If no spend secret key is ++ // provided, recover it from the wallet keys file ++ crypto::secret_key recovered_spend_key = crypto::null_skey; ++ if (!m_wallet_file.empty()) ++ { ++ THROW_WALLET_EXCEPTION_IF(!verify_password(wallet_password, recovered_spend_key), error::invalid_password); ++ } ++ else ++ { ++ verify_password_with_cached_key(wallet_password); ++ } ++ ++ if (spend_secret_key != crypto::null_skey) ++ { ++ THROW_WALLET_EXCEPTION_IF(!m_wallet_file.empty() && spend_secret_key != recovered_spend_key, ++ error::invalid_spend_key); ++ MDEBUG("Setting spend secret key with the provided key"); ++ recovered_spend_key = spend_secret_key; ++ } ++ ++ // Verify private spend key derives to wallet's public spend key ++ const auto verify_spend_key = [this](crypto::secret_key &recovered_spend_key) -> bool ++ { ++ crypto::public_key spend_public_key; ++ return recovered_spend_key != crypto::null_skey && ++ crypto::secret_key_to_public_key(recovered_spend_key, spend_public_key) && ++ m_account.get_keys().m_account_address.m_spend_public_key == spend_public_key; ++ }; ++ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); ++ ++ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, ++ "must setup background sync first before using background sync"); ++ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, ++ "Can't stop background syncing from a background wallet"); ++ ++ if (!m_background_syncing) ++ return; ++ ++ // Copy background cache, we're about to overwrite it ++ const background_sync_data_t background_sync_data = m_background_sync_data; ++ const hashchain background_synced_chain = m_blockchain; ++ const uint64_t last_block_reward = m_last_block_reward; ++ ++ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) ++ { ++ // Reload the wallet from disk ++ load(m_wallet_file, wallet_password); ++ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); ++ } ++ m_background_syncing = false; ++ ++ // Set the plaintext spend key ++ m_account.set_spend_key(recovered_spend_key); ++ ++ // Encrypt the spend key when done if needed ++ epee::misc_utils::auto_scope_leave_caller keys_reencryptor; ++ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) ++ keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]{encrypt_keys(wallet_password);}); ++ ++ // Now we can use the decrypted spend key to process background cache ++ process_background_cache(background_sync_data, background_synced_chain, last_block_reward); ++ ++ // Reset the background cache after processing ++ reset_background_sync_data(m_background_sync_data); ++ ++ MDEBUG("Background sync stopped"); ++} ++//---------------------------------------------------------------------------------------------------- + wallet2::payment_container wallet2::export_payments() const + { + payment_container payments; +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index 75137237b..295976b53 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -256,6 +256,20 @@ private: + BackgroundMiningNo = 2, + }; + ++ enum BackgroundSyncType { ++ BackgroundSyncOff = 0, ++ BackgroundSyncReusePassword = 1, ++ BackgroundSyncCustomPassword = 2, ++ }; ++ ++ static BackgroundSyncType background_sync_type_from_str(const std::string &background_sync_type_str) ++ { ++ if (background_sync_type_str == "off") return BackgroundSyncOff; ++ if (background_sync_type_str == "reuse-wallet-password") return BackgroundSyncReusePassword; ++ if (background_sync_type_str == "custom-background-password") return BackgroundSyncCustomPassword; ++ throw std::logic_error("Unknown background sync type"); ++ }; ++ + enum ExportFormat { + Binary = 0, + Ascii, +@@ -282,7 +296,12 @@ private: + //! Just parses variables. + static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter); + +- static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); ++ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) ++ { ++ crypto::secret_key spend_key = crypto::null_skey; ++ return verify_password(keys_file_name, password, no_spend_key, hwdev, kdf_rounds, spend_key); ++ }; ++ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out); + static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1); + + wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false, std::unique_ptr http_client_factory = std::unique_ptr(new net::http::client_factory())); +@@ -792,6 +811,54 @@ private: + END_SERIALIZE() + }; + ++ struct background_synced_tx_t ++ { ++ uint64_t index_in_background_sync_data; ++ cryptonote::transaction tx; ++ std::vector output_indices; ++ uint64_t height; ++ uint64_t block_timestamp; ++ bool double_spend_seen; ++ ++ BEGIN_SERIALIZE_OBJECT() ++ VERSION_FIELD(0) ++ VARINT_FIELD(index_in_background_sync_data) ++ ++ // prune tx; don't need to keep signature data ++ if (!tx.serialize_base(ar)) ++ return false; ++ ++ FIELD(output_indices) ++ VARINT_FIELD(height) ++ VARINT_FIELD(block_timestamp) ++ FIELD(double_spend_seen) ++ END_SERIALIZE() ++ }; ++ ++ struct background_sync_data_t ++ { ++ bool first_refresh_done = false; ++ uint64_t start_height = 0; ++ serializable_unordered_map txs; ++ ++ // Relevant wallet settings ++ uint64_t wallet_refresh_from_block_height; ++ size_t subaddress_lookahead_major; ++ size_t subaddress_lookahead_minor; ++ RefreshType wallet_refresh_type; ++ ++ BEGIN_SERIALIZE_OBJECT() ++ VERSION_FIELD(0) ++ FIELD(first_refresh_done) ++ FIELD(start_height) ++ FIELD(txs) ++ FIELD(wallet_refresh_from_block_height) ++ VARINT_FIELD(subaddress_lookahead_major) ++ VARINT_FIELD(subaddress_lookahead_minor) ++ VARINT_FIELD(wallet_refresh_type) ++ END_SERIALIZE() ++ }; ++ + typedef std::tuple get_outs_entry; + + struct parsed_block +@@ -974,7 +1041,8 @@ private: + /*! + * \brief verifies given password is correct for default wallet keys file + */ +- bool verify_password(const epee::wipeable_string& password); ++ bool verify_password(const epee::wipeable_string& password) {crypto::secret_key key = crypto::null_skey; return verify_password(password, key);}; ++ bool verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out); + cryptonote::account_base& get_account(){return m_account;} + const cryptonote::account_base& get_account()const{return m_account;} + +@@ -1069,6 +1137,7 @@ private: + cryptonote::network_type nettype() const { return m_nettype; } + bool watch_only() const { return m_watch_only; } + bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; ++ bool is_background_wallet() const { return m_is_background_wallet; } + bool has_multisig_partial_key_images() const; + bool has_unknown_key_images() const; + bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string()) const; +@@ -1276,11 +1345,17 @@ private: + return; + } + a & m_has_ever_refreshed_from_node; ++ if(ver < 31) ++ { ++ m_background_sync_data = background_sync_data_t{}; ++ return; ++ } ++ a & m_background_sync_data; + } + + BEGIN_SERIALIZE_OBJECT() + MAGIC_FIELD("wownero wallet cache") +- VERSION_FIELD(1) ++ VERSION_FIELD(2) + FIELD(m_blockchain) + FIELD(m_transfers) + FIELD(m_account_public_address) +@@ -1312,6 +1387,12 @@ private: + return true; + } + FIELD(m_has_ever_refreshed_from_node) ++ if (version < 2) ++ { ++ m_background_sync_data = background_sync_data_t{}; ++ return true; ++ } ++ FIELD(m_background_sync_data) + END_SERIALIZE() + + /*! +@@ -1327,6 +1408,8 @@ private: + * \return Whether path is valid format + */ + static bool wallet_valid_path_format(const std::string& file_path); ++ static std::string make_background_wallet_file_name(const std::string &wallet_file); ++ static std::string make_background_keys_file_name(const std::string &wallet_file); + static bool parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); + static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id); + static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); +@@ -1375,6 +1458,9 @@ private: + void ignore_outputs_below(uint64_t value) { m_ignore_outputs_below = value; } + bool track_uses() const { return m_track_uses; } + void track_uses(bool value) { m_track_uses = value; } ++ BackgroundSyncType background_sync_type() const { return m_background_sync_type; } ++ void setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password); ++ bool is_background_syncing() const { return m_background_syncing; } + bool show_wallet_name_when_locked() const { return m_show_wallet_name_when_locked; } + void show_wallet_name_when_locked(bool value) { m_show_wallet_name_when_locked = value; } + BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; } +@@ -1689,6 +1775,9 @@ private: + uint64_t get_bytes_sent() const; + uint64_t get_bytes_received() const; + ++ void start_background_sync(); ++ void stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key = crypto::null_skey); ++ + // MMS ------------------------------------------------------------------------------------------------- + mms::message_store& get_message_store() { return m_message_store; }; + const mms::message_store& get_message_store() const { return m_message_store; }; +@@ -1724,6 +1813,9 @@ private: + * \return Whether it was successful. + */ + bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false); ++ bool store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); ++ boost::optional get_keys_file_data(const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); ++ bool store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file = false); + /*! + * \brief Load wallet keys information from wallet file. + * \param keys_file_name Name of wallet file +@@ -1737,6 +1829,7 @@ private: + */ + bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password); + bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional& keys_to_encrypt); ++ 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 &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, 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, size_t tx_cache_data_offset, std::map, size_t> *output_tracker_cache = NULL); +@@ -1745,6 +1838,15 @@ private: + void get_short_chain_history(std::list& ids, uint64_t granularity = 1) const; + bool clear(); + void clear_soft(bool keep_key_images=false); ++ /* ++ * clear_user_data clears data created by the user, which is mostly data ++ * that a view key cannot identify on chain. This function was initially ++ * added to ensure that a "background" wallet (a wallet that syncs with just ++ * a view key hot in memory) does not have any sensitive data loaded that it ++ * does not need in order to sync. Future devs should take care to ensure ++ * that this function deletes data that is not useful for background syncing ++ */ ++ void clear_user_data(); + void pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, uint64_t ¤t_height); + void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); + void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); +@@ -1796,10 +1898,23 @@ private: + bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector &outs); + crypto::chacha_key get_ringdb_key(); + void setup_keys(const epee::wipeable_string &password); ++ const crypto::chacha_key get_cache_key(); ++ void verify_password_with_cached_key(const epee::wipeable_string &password); ++ void verify_password_with_cached_key(const crypto::chacha_key &key); + size_t get_transfer_details(const crypto::key_image &ki) const; + tx_entry_data get_tx_entries(const std::unordered_set &txids); + void sort_scan_tx_entries(std::vector &unsorted_tx_entries); + void process_scan_txs(const tx_entry_data &txs_to_scan, const tx_entry_data &txs_to_reprocess, const std::unordered_set &tx_hashes_to_reprocess, detached_blockchain_data &dbd); ++ void write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password); ++ void process_background_cache_on_open(); ++ void process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_chain, uint64_t last_block_reward); ++ void reset_background_sync_data(background_sync_data_t &background_sync_data); ++ void store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data = true); ++ void store_background_keys(const crypto::chacha_key &custom_background_key); ++ ++ bool lock_background_keys_file(const std::string &background_keys_file); ++ bool unlock_background_keys_file(); ++ bool is_background_keys_file_locked() const; + + void register_devices(); + hw::device& lookup_device(const std::string & device_descriptor); +@@ -1914,6 +2029,8 @@ private: + uint64_t m_ignore_outputs_above; + uint64_t m_ignore_outputs_below; + bool m_track_uses; ++ bool m_is_background_wallet; ++ BackgroundSyncType m_background_sync_type; + bool m_show_wallet_name_when_locked; + uint32_t m_inactivity_lock_timeout; + BackgroundMiningSetupType m_setup_background_mining; +@@ -1959,6 +2076,7 @@ private: + + uint64_t m_last_block_reward; + std::unique_ptr m_keys_file_locker; ++ std::unique_ptr m_background_keys_file_locker; + + mms::message_store m_message_store; + bool m_original_keys_available; +@@ -1966,6 +2084,7 @@ private: + crypto::secret_key m_original_view_secret_key; + + crypto::chacha_key m_cache_key; ++ boost::optional m_custom_background_key = boost::none; + std::shared_ptr m_encrypt_keys_after_refresh; + + bool m_unattended; +@@ -1981,9 +2100,13 @@ private: + + static boost::mutex default_daemon_address_lock; + static std::string default_daemon_address; ++ ++ bool m_background_syncing; ++ bool m_processing_background_cache; ++ background_sync_data_t m_background_sync_data; + }; + } +-BOOST_CLASS_VERSION(tools::wallet2, 30) ++BOOST_CLASS_VERSION(tools::wallet2, 31) + BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) + BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) + BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) +@@ -1999,6 +2122,8 @@ BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1) + BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4) + BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) + BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 1) ++BOOST_CLASS_VERSION(tools::wallet2::background_synced_tx_t, 0) ++BOOST_CLASS_VERSION(tools::wallet2::background_sync_data_t, 0) + + namespace boost + { +@@ -2497,6 +2622,29 @@ namespace boost + return; + a & x.multisig_sigs; + } ++ ++ template ++ inline void serialize(Archive& a, tools::wallet2::background_synced_tx_t &x, const boost::serialization::version_type ver) ++ { ++ a & x.index_in_background_sync_data; ++ a & x.tx; ++ a & x.output_indices; ++ a & x.height; ++ a & x.block_timestamp; ++ a & x.double_spend_seen; ++ } ++ ++ template ++ inline void serialize(Archive& a, tools::wallet2::background_sync_data_t &x, const boost::serialization::version_type ver) ++ { ++ a & x.first_refresh_done; ++ a & x.start_height; ++ a & x.txs.parent(); ++ a & x.wallet_refresh_from_block_height; ++ a & x.subaddress_lookahead_major; ++ a & x.subaddress_lookahead_minor; ++ a & x.wallet_refresh_type; ++ } + } + } + +diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h +index c077313d4..c54cd3499 100644 +--- a/src/wallet/wallet_errors.h ++++ b/src/wallet/wallet_errors.h +@@ -63,6 +63,7 @@ namespace tools + // invalid_password + // invalid_priority + // invalid_multisig_seed ++ // invalid_spend_key + // refresh_error * + // acc_outs_lookup_error + // block_parse_error +@@ -97,6 +98,9 @@ namespace tools + // wallet_files_doesnt_correspond + // scan_tx_error * + // wont_reprocess_recent_txs_via_untrusted_daemon ++ // background_sync_error * ++ // background_wallet_already_open ++ // background_custom_password_same_as_wallet_password + // + // * - class with protected ctor + +@@ -304,6 +308,16 @@ namespace tools + std::string to_string() const { return wallet_logic_error::to_string(); } + }; + ++ struct invalid_spend_key : public wallet_logic_error ++ { ++ explicit invalid_spend_key(std::string&& loc) ++ : wallet_logic_error(std::move(loc), "invalid spend key") ++ { ++ } ++ ++ std::string to_string() const { return wallet_logic_error::to_string(); } ++ }; ++ + //---------------------------------------------------------------------------------------------------- + struct invalid_pregenerated_random : public wallet_logic_error + { +@@ -947,6 +961,31 @@ namespace tools + } + }; + //---------------------------------------------------------------------------------------------------- ++ struct background_sync_error : public wallet_logic_error ++ { ++ protected: ++ explicit background_sync_error(std::string&& loc, const std::string& message) ++ : wallet_logic_error(std::move(loc), message) ++ { ++ } ++ }; ++ //---------------------------------------------------------------------------------------------------- ++ struct background_wallet_already_open : public background_sync_error ++ { ++ explicit background_wallet_already_open(std::string&& loc, const std::string& background_wallet_file) ++ : background_sync_error(std::move(loc), "background wallet " + background_wallet_file + " is already opened by another wallet program") ++ { ++ } ++ }; ++ //---------------------------------------------------------------------------------------------------- ++ struct background_custom_password_same_as_wallet_password : public background_sync_error ++ { ++ explicit background_custom_password_same_as_wallet_password(std::string&& loc) ++ : background_sync_error(std::move(loc), "custom background password must be different than wallet password") ++ { ++ } ++ }; ++ //---------------------------------------------------------------------------------------------------- + + #if !defined(_MSC_VER) + +diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp +index 6fc996299..3cc6b278f 100644 +--- a/src/wallet/wallet_rpc_server.cpp ++++ b/src/wallet/wallet_rpc_server.cpp +@@ -73,6 +73,54 @@ using namespace epee; + } \ + } while(0) + ++#define CHECK_IF_BACKGROUND_SYNCING() \ ++ do \ ++ { \ ++ if (!m_wallet) { return not_open(er); } \ ++ if (m_wallet->is_background_wallet()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET; \ ++ er.message = "This command is disabled for background wallets."; \ ++ return false; \ ++ } \ ++ if (m_wallet->is_background_syncing()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING; \ ++ er.message = "This command is disabled while background syncing. Stop background syncing to use this command."; \ ++ return false; \ ++ } \ ++ } while(0) ++ ++#define PRE_VALIDATE_BACKGROUND_SYNC() \ ++ do \ ++ { \ ++ if (!m_wallet) { return not_open(er); } \ ++ if (m_restricted) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_DENIED; \ ++ er.message = "Command unavailable in restricted mode."; \ ++ return false; \ ++ } \ ++ if (m_wallet->key_on_device()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ ++ er.message = "Command not supported by HW wallet"; \ ++ return false; \ ++ } \ ++ if (m_wallet->multisig()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ ++ er.message = "Multisig wallet cannot enable background sync"; \ ++ return false; \ ++ } \ ++ if (m_wallet->watch_only()) \ ++ { \ ++ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; \ ++ er.message = "Watch-only wallet cannot enable background sync"; \ ++ return false; \ ++ } \ ++ } while (0) ++ + namespace + { + const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; +@@ -291,6 +339,9 @@ namespace tools + { + if (!m_wallet) + return; ++ // Background mining can be toggled from the main wallet ++ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) ++ return; + + tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); + if (setup == tools::wallet2::BackgroundMiningNo) +@@ -581,6 +632,7 @@ namespace tools + bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + if (req.count < 1 || req.count > 64) { +@@ -618,6 +670,7 @@ namespace tools + bool wallet_rpc_server::on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_subaddress_label(req.index, req.label); +@@ -680,6 +733,7 @@ namespace tools + bool wallet_rpc_server::on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->add_subaddress_account(req.label); +@@ -697,6 +751,7 @@ namespace tools + bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_subaddress_label({req.account_index, 0}, req.label); +@@ -712,6 +767,7 @@ namespace tools + bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + const std::pair, std::vector> account_tags = m_wallet->get_account_tags(); + for (const std::pair& p : account_tags.first) + { +@@ -731,6 +787,7 @@ namespace tools + bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_account_tag(req.accounts, req.tag); +@@ -746,6 +803,7 @@ namespace tools + bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_account_tag(req.accounts, ""); +@@ -761,6 +819,7 @@ namespace tools + bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->set_account_tag_description(req.tag, req.description); +@@ -791,6 +850,7 @@ namespace tools + bool wallet_rpc_server::on_freeze(const wallet_rpc::COMMAND_RPC_FREEZE::request& req, wallet_rpc::COMMAND_RPC_FREEZE::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + if (req.key_image.empty()) +@@ -819,6 +879,7 @@ namespace tools + bool wallet_rpc_server::on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + if (req.key_image.empty()) +@@ -847,6 +908,7 @@ namespace tools + bool wallet_rpc_server::on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + if (req.key_image.empty()) +@@ -874,6 +936,8 @@ namespace tools + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::validate_transfer(const std::list& destinations, const std::string& payment_id, std::vector& dsts, std::vector& extra, bool at_least_one_destination, epee::json_rpc::error& er) + { ++ CHECK_IF_BACKGROUND_SYNCING(); ++ + crypto::hash8 integrated_payment_id = crypto::null_hash8; + std::string extra_nonce; + for (auto it = destinations.begin(); it != destinations.end(); it++) +@@ -1203,6 +1267,7 @@ namespace tools + } + + CHECK_MULTISIG_ENABLED(); ++ CHECK_IF_BACKGROUND_SYNCING(); + + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) +@@ -1284,6 +1349,7 @@ namespace tools + er.message = "command not supported by watch-only wallet"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + if(req.unsigned_txset.empty() && req.multisig_txset.empty()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; +@@ -1553,6 +1619,7 @@ namespace tools + } + + CHECK_MULTISIG_ENABLED(); ++ CHECK_IF_BACKGROUND_SYNCING(); + + try + { +@@ -2114,6 +2181,7 @@ namespace tools + er.message = "The wallet is watch-only. Cannot retrieve seed."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + if (!m_wallet->is_deterministic()) + { + er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC; +@@ -2142,6 +2210,7 @@ namespace tools + er.message = "The wallet is watch-only. Cannot retrieve spend key."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key); + res.key = std::string(key.data(), key.size()); + } +@@ -2163,6 +2232,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + try + { +@@ -2176,6 +2246,79 @@ namespace tools + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ ++ bool wallet_rpc_server::on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) ++ { ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ const tools::wallet2::BackgroundSyncType background_sync_type = tools::wallet2::background_sync_type_from_str(req.background_sync_type); ++ boost::optional background_cache_password = boost::none; ++ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) ++ background_cache_password = boost::optional(req.background_cache_password); ++ m_wallet->setup_background_sync(background_sync_type, req.wallet_password, background_cache_password); ++ } ++ catch (...) ++ { ++ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); ++ return false; ++ } ++ return true; ++ } ++ //------------------------------------------------------------------------------------------------------------------------------ ++ bool wallet_rpc_server::on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) ++ { ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ m_wallet->start_background_sync(); ++ } ++ catch (...) ++ { ++ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); ++ return false; ++ } ++ return true; ++ } ++ //------------------------------------------------------------------------------------------------------------------------------ ++ bool wallet_rpc_server::on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) ++ { ++ try ++ { ++ PRE_VALIDATE_BACKGROUND_SYNC(); ++ crypto::secret_key spend_secret_key = crypto::null_skey; ++ ++ // Load the spend key from seed if seed is provided ++ if (!req.seed.empty()) ++ { ++ crypto::secret_key recovery_key; ++ std::string language; ++ ++ if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, language)) ++ { ++ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; ++ er.message = "Electrum-style word list failed verification"; ++ return false; ++ } ++ ++ if (!req.seed_offset.empty()) ++ recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset); ++ ++ // generate spend key ++ cryptonote::account_base account; ++ account.generate(recovery_key, true, false); ++ spend_secret_key = account.get_keys().m_spend_secret_key; ++ } ++ ++ m_wallet->stop_background_sync(req.wallet_password, spend_secret_key); ++ } ++ catch (...) ++ { ++ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); ++ return false; ++ } ++ return true; ++ } ++ //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); +@@ -2185,6 +2328,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + tools::wallet2::message_signature_type_t signature_type = tools::wallet2::sign_with_spend_key; + if (req.signature_type == "spend" || req.signature_type == "") +@@ -2277,6 +2421,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + if (req.txids.size() != req.notes.size()) + { +@@ -2349,6 +2494,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + m_wallet->set_attribute(req.key, req.value); + +@@ -2376,6 +2522,7 @@ namespace tools + bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + + crypto::hash txid; + if (!epee::string_tools::hex_to_pod(req.txid, txid)) +@@ -2467,6 +2614,7 @@ namespace tools + bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + + crypto::hash txid; + if (!epee::string_tools::hex_to_pod(req.txid, txid)) +@@ -2583,6 +2731,7 @@ namespace tools + bool wallet_rpc_server::on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + + boost::optional> account_minreserve; + if (!req.all) +@@ -2825,6 +2974,7 @@ namespace tools + er.message = "command not supported by HW wallet"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + try + { +@@ -2854,6 +3004,7 @@ namespace tools + er.message = "command not supported by HW wallet"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + cryptonote::blobdata blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob)) +@@ -2879,6 +3030,7 @@ namespace tools + bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + std::pair>> ski = m_wallet->export_key_images(req.all); +@@ -2915,6 +3067,7 @@ namespace tools + er.message = "This command requires a trusted daemon."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + std::vector> ski; +@@ -2983,6 +3136,7 @@ namespace tools + bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx) + { + if (!m_wallet) return not_open(er); ++ CHECK_IF_BACKGROUND_SYNCING(); + const auto ab = m_wallet->get_address_book(); + if (req.entries.empty()) + { +@@ -3028,6 +3182,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + cryptonote::address_parse_info info; + er.message = ""; +@@ -3070,6 +3225,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + const auto ab = m_wallet->get_address_book(); + if (req.index >= ab.size()) +@@ -3132,6 +3288,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + const auto ab = m_wallet->get_address_book(); + if (req.index >= ab.size()) +@@ -3202,6 +3359,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + std::unordered_set txids; + std::list::const_iterator i = req.txids.begin(); +@@ -3241,6 +3399,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + try + { + m_wallet->rescan_spent(); +@@ -3505,6 +3664,7 @@ namespace tools + er.message = "Command unavailable in restricted mode."; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + if (m_wallet->verify_password(req.old_password)) + { + try +@@ -4032,6 +4192,7 @@ namespace tools + er.message = "wallet is watch-only and cannot be made multisig"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + res.multisig_info = m_wallet->get_multisig_first_kex_msg(); + return true; +@@ -4059,6 +4220,7 @@ namespace tools + er.message = "wallet is watch-only and cannot be made multisig"; + return false; + } ++ CHECK_IF_BACKGROUND_SYNCING(); + + try + { +diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h +index 3308d1751..c2329aafe 100644 +--- a/src/wallet/wallet_rpc_server.h ++++ b/src/wallet/wallet_rpc_server.h +@@ -160,6 +160,9 @@ namespace tools + MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES) + MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT) + MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) ++ MAP_JON_RPC_WE("setup_background_sync", on_setup_background_sync, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC) ++ MAP_JON_RPC_WE("start_background_sync", on_start_background_sync, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC) ++ MAP_JON_RPC_WE("stop_background_sync", on_stop_background_sync, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC) + END_JSON_RPC_MAP() + END_URI_MAP2() + +@@ -251,6 +254,9 @@ namespace tools + bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); ++ bool on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); ++ bool on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); ++ bool on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + + //json rpc v2 + bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); +diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h +index b6098d95c..a44b56ed6 100644 +--- a/src/wallet/wallet_rpc_server_commands_defs.h ++++ b/src/wallet/wallet_rpc_server_commands_defs.h +@@ -2696,5 +2696,69 @@ namespace wallet_rpc + typedef epee::misc_utils::struct_init response; + }; + ++ struct COMMAND_RPC_SETUP_BACKGROUND_SYNC ++ { ++ struct request_t ++ { ++ std::string background_sync_type; ++ std::string wallet_password; ++ std::string background_cache_password; ++ ++ BEGIN_KV_SERIALIZE_MAP() ++ KV_SERIALIZE(background_sync_type) ++ KV_SERIALIZE(wallet_password) ++ KV_SERIALIZE_OPT(background_cache_password, (std::string)"") ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init request; ++ ++ struct response_t ++ { ++ BEGIN_KV_SERIALIZE_MAP() ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init response; ++ }; ++ ++ struct COMMAND_RPC_START_BACKGROUND_SYNC ++ { ++ struct request_t ++ { ++ BEGIN_KV_SERIALIZE_MAP() ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init request; ++ ++ struct response_t ++ { ++ BEGIN_KV_SERIALIZE_MAP() ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init response; ++ }; ++ ++ struct COMMAND_RPC_STOP_BACKGROUND_SYNC ++ { ++ struct request_t ++ { ++ std::string wallet_password; ++ std::string seed; ++ std::string seed_offset; ++ ++ BEGIN_KV_SERIALIZE_MAP() ++ KV_SERIALIZE(wallet_password) ++ KV_SERIALIZE_OPT(seed, (std::string)"") ++ KV_SERIALIZE_OPT(seed_offset, (std::string)"") ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init request; ++ ++ struct response_t ++ { ++ BEGIN_KV_SERIALIZE_MAP() ++ END_KV_SERIALIZE_MAP() ++ }; ++ typedef epee::misc_utils::struct_init response; ++ }; + } + } +diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h +index 541d29f86..4756c191c 100644 +--- a/src/wallet/wallet_rpc_server_error_codes.h ++++ b/src/wallet/wallet_rpc_server_error_codes.h +@@ -81,3 +81,5 @@ + #define WALLET_RPC_ERROR_CODE_DISABLED -48 + #define WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED -49 + #define WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME -50 ++#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET -51 ++#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING -52 +diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py +index 4063911f4..60eb09a10 100755 +--- a/tests/functional_tests/transfer.py ++++ b/tests/functional_tests/transfer.py +@@ -30,6 +30,7 @@ + + from __future__ import print_function + import json ++import util_resources + import pprint + from deepdiff import DeepDiff + pp = pprint.PrettyPrinter(indent=2) +@@ -46,6 +47,17 @@ seeds = [ + 'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid', + ] + ++def diff_transfers(actual_transfers, expected_transfers, ignore_order = True): ++ # The payments containers aren't ordered; re-scanning can lead to diff orders ++ diff = DeepDiff(actual_transfers, expected_transfers, ignore_order = ignore_order) ++ if diff != {}: ++ pp.pprint(diff) ++ assert diff == {} ++ ++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) ++ + class TransferTest(): + def run_test(self): + self.reset() +@@ -63,6 +75,8 @@ class TransferTest(): + self.check_is_key_image_spent() + self.check_scan_tx() + self.check_subtract_fee_from_outputs() ++ self.check_background_sync() ++ self.check_background_sync_reorg_recovery() + + def reset(self): + print('Resetting blockchain') +@@ -840,12 +854,6 @@ class TransferTest(): + + print('Testing scan_tx') + +- def diff_transfers(actual_transfers, expected_transfers): +- diff = DeepDiff(actual_transfers, expected_transfers) +- if diff != {}: +- pp.pprint(diff) +- assert diff == {} +- + # set up sender_wallet + sender_wallet = self.wallet[0] + try: sender_wallet.close_wallet() +@@ -1127,5 +1135,385 @@ class TransferTest(): + except AssertionError: + pass + ++ def check_background_sync(self): ++ daemon = Daemon() ++ ++ print('Testing background sync') ++ ++ # Some helper functions ++ def stop_with_wrong_inputs(wallet, wallet_password, seed = ''): ++ invalid = False ++ try: wallet.stop_background_sync(wallet_password = wallet_password, seed = seed) ++ except: invalid = True ++ assert invalid ++ ++ def open_with_wrong_password(wallet, filename, password): ++ invalid_password = False ++ try: wallet.open_wallet(filename, password = password) ++ 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) ++ assert wallet.get_balance().balance == expected_balance ++ ++ # Set up sender_wallet. Prepare to sweep single output to receiver. ++ # 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] ++ 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] ++ assert len(unlocked) > 0 ++ ki = unlocked[0].key_image ++ amount = unlocked[0].amount ++ spent_txid = unlocked[0].tx_hash ++ sender_wallet.refresh() ++ res = sender_wallet.get_transfers() ++ out_len = 0 if 'out' not in res else len(res.out) ++ sender_starting_balance = sender_wallet.get_balance().balance ++ ++ # Background sync type options ++ reuse_password = sender_wallet.background_sync_options.reuse_password ++ custom_password = sender_wallet.background_sync_options.custom_password ++ ++ # set up receiver_wallet ++ receiver_wallet = self.wallet[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']) ++ receiver_starting_balance = receiver_wallet.get_balance().balance ++ ++ # transfer from sender_wallet to receiver_wallet ++ dst = '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW' ++ res = sender_wallet.sweep_single(dst, key_image = ki) ++ assert len(res.tx_hash) == 32*2 ++ txid = res.tx_hash ++ assert res.fee > 0 ++ fee = res.fee ++ assert res.amount == amount - fee ++ ++ expected_sender_balance = sender_starting_balance - amount ++ expected_receiver_balance = receiver_starting_balance + (amount - fee) ++ ++ print('Checking background sync on outgoing wallet') ++ sender_wallet.setup_background_sync(background_sync_type = reuse_password) ++ sender_wallet.start_background_sync() ++ # Mine block to an uninvolved wallet ++ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) ++ # sender should still be able to scan the transfer normally because we ++ # spent an output that had a known key image ++ sender_wallet.refresh() ++ transfers = sender_wallet.get_transfers() ++ assert 'pending' not in transfers or len(transfers.pending) == 0 ++ assert 'pool' not in transfers or len (transfers.pool) == 0 ++ assert len(transfers.out) == out_len + 1 ++ tx = [x for x in transfers.out if x.txid == txid] ++ assert len(tx) == 1 ++ tx = tx[0] ++ assert tx.amount == amount - fee ++ assert tx.fee == fee ++ assert len(tx.destinations) == 1 ++ assert tx.destinations[0].amount == amount - fee ++ assert tx.destinations[0].address == dst ++ incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all') ++ assert len([x for x in incoming_transfers.transfers if x.tx_hash == spent_txid and x.key_image == ki and x.spent]) == 1 ++ assert sender_wallet.get_balance().balance == expected_sender_balance ++ ++ # Restore and check background syncing outgoing wallet ++ restore_wallet(sender_wallet, seeds[0]) ++ sender_wallet.setup_background_sync(background_sync_type = reuse_password) ++ sender_wallet.start_background_sync() ++ sender_wallet.refresh() ++ for i, out_tx in enumerate(transfers.out): ++ if 'destinations' in out_tx: ++ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore ++ # sender's balance should be higher because can't detect spends while ++ # background sync enabled, only receives ++ background_bal = sender_wallet.get_balance().balance ++ assert background_bal > expected_sender_balance ++ background_transfers = sender_wallet.get_transfers() ++ assert 'out' not in background_transfers or len(background_transfers.out) == 0 ++ assert 'in' in background_transfers and len(background_transfers['in']) > 0 ++ background_incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all') ++ assert len(background_incoming_transfers) == len(incoming_transfers) ++ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0 ++ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == spent_txid]) == 1 ++ ++ # Try to stop background sync with the wrong seed ++ stop_with_wrong_inputs(sender_wallet, wallet_password = '', seed = seeds[1]) ++ ++ # Stop background sync and check transfers update correctly ++ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0]) ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ ++ # 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], '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() ++ sender_wallet.refresh() ++ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) ++ stop_with_wrong_inputs(sender_wallet, 'wrong_password') ++ sender_wallet.stop_background_sync(wallet_password = 'test_password') ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ ++ # Close wallet while background syncing, then reopen ++ for background_sync_type in [reuse_password, custom_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() ++ sender_wallet.refresh() ++ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) ++ sender_wallet.close_wallet() ++ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password') ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ # It should reopen with spend key loaded and correctly scan all transfers ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ ++ # Close wallet while syncing normally, then reopen ++ for background_sync_type in [reuse_password, custom_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() ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ sender_wallet.close_wallet() ++ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password') ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) ++ ++ # 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], '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) ++ assert util_resources.file_exists('test1.background') ++ assert util_resources.file_exists('test1.background.keys') ++ sender_wallet.close_wallet() ++ open_with_wrong_password(sender_wallet, 'test1.background', 'test_password') ++ sender_wallet.open_wallet('test1.background', password = background_cache_password) ++ sender_wallet.refresh() ++ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) ++ sender_wallet.close_wallet() ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ 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], '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) ++ sender_wallet.close_wallet() ++ sender_wallet.open_wallet('test1.background', password = 'background_password') ++ 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], 'test1', 'test_password') ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ same_password = False ++ try: sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = 'test_password') ++ except: same_password = True ++ assert same_password ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ ++ # Turn off background sync ++ for background_sync_type in [reuse_password, custom_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: ++ assert util_resources.file_exists('test1.background') ++ assert util_resources.file_exists('test1.background.keys') ++ sender_wallet.close_wallet() ++ assert util_resources.file_exists('test1.background') ++ assert util_resources.file_exists('test1.background.keys') ++ else: ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ sender_wallet.close_wallet() ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ sender_wallet.setup_background_sync(background_sync_type = sender_wallet.background_sync_options.off, wallet_password = 'test_password') ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ sender_wallet.close_wallet() ++ assert not util_resources.file_exists('test1.background') ++ assert not util_resources.file_exists('test1.background.keys') ++ sender_wallet.open_wallet('test1', password = 'test_password') ++ ++ # Sanity check against outgoing wallet restored at 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) ++ ++ print('Checking background sync on incoming wallet') ++ receiver_wallet.setup_background_sync(background_sync_type = reuse_password) ++ receiver_wallet.start_background_sync() ++ receiver_wallet.refresh() ++ transfers = receiver_wallet.get_transfers() ++ assert 'pending' not in transfers or len(transfers.pending) == 0 ++ assert 'pool' not in transfers or len (transfers.pool) == 0 ++ assert len(transfers['in']) == in_len + 1 ++ tx = [x for x in transfers['in'] if x.txid == txid] ++ assert len(tx) == 1 ++ tx = tx[0] ++ assert tx.amount == amount - fee ++ assert tx.fee == fee ++ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') ++ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image == '' and not x.spent]) == 1 ++ assert receiver_wallet.get_balance().balance == expected_receiver_balance ++ ++ # Restore and check background syncing incoming wallet ++ restore_wallet(receiver_wallet, seeds[1]) ++ receiver_wallet.setup_background_sync(background_sync_type = reuse_password) ++ receiver_wallet.start_background_sync() ++ receiver_wallet.refresh() ++ if 'out' in transfers: ++ for i, out_tx in enumerate(transfers.out): ++ if 'destinations' in out_tx: ++ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore ++ background_bal = receiver_wallet.get_balance().balance ++ assert background_bal >= expected_receiver_balance ++ background_transfers = receiver_wallet.get_transfers() ++ assert 'out' not in background_transfers or len(background_transfers.out) == 0 ++ assert 'in' in background_transfers and len(background_transfers['in']) > 0 ++ background_incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') ++ assert len(background_incoming_transfers) == len(incoming_transfers) ++ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0 ++ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == txid]) == 1 ++ ++ # Stop background sync and check transfers update correctly ++ receiver_wallet.stop_background_sync(wallet_password = '', seed = seeds[1]) ++ diff_transfers(receiver_wallet.get_transfers(), transfers) ++ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') ++ assert len(background_incoming_transfers) == len(incoming_transfers) ++ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image != '' and not x.spent]) == 1 ++ 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], '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() ++ assert_correct_transfers(receiver_wallet, background_transfers, background_incoming_transfers, background_bal) ++ stop_with_wrong_inputs(receiver_wallet, 'wrong_password') ++ receiver_wallet.stop_background_sync(wallet_password = 'test_password') ++ 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], '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() ++ diff_transfers(receiver_wallet.get_transfers(), background_transfers) ++ diff_incoming_transfers(receiver_wallet.incoming_transfers(transfer_type = 'all'), background_incoming_transfers) ++ assert receiver_wallet.get_balance().balance == background_bal ++ receiver_wallet.close_wallet() ++ receiver_wallet.open_wallet('test2', password = 'test_password') ++ # It should reopen with spend key loaded and correctly scan all transfers ++ assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance) ++ ++ # Sanity check against incoming wallet restored at 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) ++ ++ # Clean up ++ util_resources.remove_wallet_files('test1') ++ util_resources.remove_wallet_files('test2') ++ for i in range(2): ++ self.wallet[i].close_wallet() ++ self.wallet[i].restore_deterministic_wallet(seed = seeds[i]) ++ ++ def check_background_sync_reorg_recovery(self): ++ daemon = Daemon() ++ ++ print('Testing background sync reorg recovery') ++ ++ # Disconnect daemon from peers ++ daemon.out_peers(0) ++ ++ # Background sync type options ++ sender_wallet = self.wallet[0] ++ reuse_password = sender_wallet.background_sync_options.reuse_password ++ custom_password = sender_wallet.background_sync_options.custom_password ++ ++ for background_sync_type in [reuse_password, custom_password]: ++ # Set up wallet saved to disk ++ sender_wallet.close_wallet() ++ util_resources.remove_wallet_files('test1') ++ sender_wallet.restore_deterministic_wallet(seed = seeds[0], filename = 'test1', password = '') ++ sender_wallet.auto_refresh(enable = False) ++ sender_wallet.refresh() ++ sender_starting_balance = sender_wallet.get_balance().balance ++ ++ # Send tx and mine a block ++ amount = 1000000000000 ++ assert sender_starting_balance > amount ++ dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': amount} ++ res = sender_wallet.transfer([dst]) ++ assert len(res.tx_hash) == 32*2 ++ txid = res.tx_hash ++ ++ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) ++ ++ # Make sure the wallet can see the tx ++ sender_wallet.refresh() ++ transfers = sender_wallet.get_transfers() ++ assert 'pool' not in transfers or len (transfers.pool) == 0 ++ tx = [x for x in transfers.out if x.txid == txid] ++ assert len(tx) == 1 ++ tx = tx[0] ++ assert sender_wallet.get_balance().balance < (sender_starting_balance - amount) ++ ++ # Pop the block while background syncing ++ 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 = '', background_cache_password = background_cache_password) ++ sender_wallet.start_background_sync() ++ daemon.pop_blocks(1) ++ daemon.flush_txpool() ++ ++ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) ++ ++ # Make sure the wallet can no longer see the tx ++ sender_wallet.refresh() ++ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0]) ++ transfers = sender_wallet.get_transfers() ++ no_tx = [x for x in transfers.out if x.txid == txid] ++ assert len(no_tx) == 0 ++ assert sender_wallet.get_balance().balance == sender_starting_balance ++ ++ # Clean up ++ daemon.out_peers(12) ++ util_resources.remove_wallet_files('test1') ++ self.wallet[0].close_wallet() ++ self.wallet[0].restore_deterministic_wallet(seed = seeds[0]) ++ + if __name__ == '__main__': + TransferTest().run_test() +diff --git a/tests/functional_tests/util_resources.py b/tests/functional_tests/util_resources.py +index e030312da..3ca6fdb86 100755 +--- a/tests/functional_tests/util_resources.py ++++ b/tests/functional_tests/util_resources.py +@@ -37,6 +37,8 @@ + from __future__ import print_function + import subprocess + import psutil ++import os ++import errno + + def available_ram_gb(): + ram_bytes = psutil.virtual_memory().available +@@ -51,3 +53,26 @@ def get_time_pi_seconds(cores, app_dir='.'): + miliseconds = int(decoded) + + return miliseconds / 1000.0 ++ ++def remove_file(name): ++ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] ++ assert WALLET_DIRECTORY != '' ++ try: ++ os.unlink(WALLET_DIRECTORY + '/' + name) ++ except OSError as e: ++ if e.errno != errno.ENOENT: ++ raise ++ ++def get_file_path(name): ++ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] ++ assert WALLET_DIRECTORY != '' ++ return WALLET_DIRECTORY + '/' + name ++ ++def remove_wallet_files(name): ++ for suffix in ['', '.keys', '.background', '.background.keys', '.address.txt']: ++ remove_file(name + suffix) ++ ++def file_exists(name): ++ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] ++ assert WALLET_DIRECTORY != '' ++ return os.path.isfile(WALLET_DIRECTORY + '/' + name) +diff --git a/tests/functional_tests/wallet.py b/tests/functional_tests/wallet.py +index 1ad05c98f..8182cecb2 100755 +--- a/tests/functional_tests/wallet.py ++++ b/tests/functional_tests/wallet.py +@@ -34,8 +34,7 @@ + + from __future__ import print_function + import sys +-import os +-import errno ++import util_resources + + from framework.wallet import Wallet + from framework.daemon import Daemon +@@ -54,24 +53,6 @@ class WalletTest(): + self.change_password() + self.store() + +- def remove_file(self, name): +- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] +- assert WALLET_DIRECTORY != '' +- try: +- os.unlink(WALLET_DIRECTORY + '/' + name) +- except OSError as e: +- if e.errno != errno.ENOENT: +- raise +- +- def remove_wallet_files(self, name): +- for suffix in ['', '.keys']: +- self.remove_file(name + suffix) +- +- def file_exists(self, name): +- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] +- assert WALLET_DIRECTORY != '' +- return os.path.isfile(WALLET_DIRECTORY + '/' + name) +- + def reset(self): + print('Resetting blockchain') + daemon = Daemon() +@@ -333,7 +314,7 @@ class WalletTest(): + try: wallet.close_wallet() + except: pass + +- self.remove_wallet_files('test1') ++ util_resources.remove_wallet_files('test1') + + seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' + res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') +@@ -359,7 +340,7 @@ class WalletTest(): + + wallet.close_wallet() + +- self.remove_wallet_files('test1') ++ util_resources.remove_wallet_files('test1') + + def store(self): + print('Testing store') +@@ -369,22 +350,26 @@ class WalletTest(): + try: wallet.close_wallet() + except: pass + +- self.remove_wallet_files('test1') ++ util_resources.remove_wallet_files('test1') + + seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' + res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') + assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' + assert res.seed == seed + +- self.remove_file('test1') +- assert self.file_exists('test1.keys') +- assert not self.file_exists('test1') ++ util_resources.remove_file('test1') ++ assert util_resources.file_exists('test1.keys') ++ assert not util_resources.file_exists('test1') + wallet.store() +- assert self.file_exists('test1.keys') +- assert self.file_exists('test1') ++ assert util_resources.file_exists('test1.keys') ++ assert util_resources.file_exists('test1') + + wallet.close_wallet() +- self.remove_wallet_files('test1') ++ ++ wallet.open_wallet(filename = 'test1', password = '') ++ wallet.close_wallet() ++ ++ util_resources.remove_wallet_files('test1') + + + if __name__ == '__main__': +diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp +index ef6964f9e..25121a02e 100644 +--- a/tests/unit_tests/wipeable_string.cpp ++++ b/tests/unit_tests/wipeable_string.cpp +@@ -211,3 +211,15 @@ TEST(wipeable_string, to_hex) + ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"", 0)) == epee::wipeable_string("")); + ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"abc", 3)) == epee::wipeable_string("616263")); + } ++ ++TEST(wipeable_string, to_string) ++{ ++ // Converting a wipeable_string to a string defeats the purpose of wipeable_string, ++ // but nice to know this works ++ std::string str; ++ { ++ epee::wipeable_string wipeable_str("foo"); ++ str = std::string(wipeable_str.data(), wipeable_str.size()); ++ } ++ ASSERT_TRUE(str == std::string("foo")); ++} +diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py +index 1e10e1f86..bff33a561 100644 +--- a/utils/python-rpc/framework/wallet.py ++++ b/utils/python-rpc/framework/wallet.py +@@ -1138,3 +1138,45 @@ class Wallet(object): + 'id': '0' + } + return self.rpc.send_json_rpc_request(frozen) ++ ++ class BackgroundSyncOptions(object): ++ def __init__(self): ++ self.off = 'off' ++ self.reuse_password = 'reuse-wallet-password' ++ self.custom_password = 'custom-background-password' ++ background_sync_options = BackgroundSyncOptions() ++ ++ def setup_background_sync(self, background_sync_type = background_sync_options.off, wallet_password = '', background_cache_password = ''): ++ setup_background_sync = { ++ 'method': 'setup_background_sync', ++ 'jsonrpc': '2.0', ++ 'params' : { ++ 'background_sync_type': background_sync_type, ++ 'wallet_password': wallet_password, ++ 'background_cache_password': background_cache_password, ++ }, ++ 'id': '0' ++ } ++ return self.rpc.send_json_rpc_request(setup_background_sync) ++ ++ def start_background_sync(self): ++ start_background_sync = { ++ 'method': 'start_background_sync', ++ 'jsonrpc': '2.0', ++ 'params' : {}, ++ 'id': '0' ++ } ++ return self.rpc.send_json_rpc_request(start_background_sync) ++ ++ def stop_background_sync(self, wallet_password = '', seed = '', seed_offset = ''): ++ stop_background_sync = { ++ 'method': 'stop_background_sync', ++ 'jsonrpc': '2.0', ++ 'params' : { ++ 'wallet_password': wallet_password, ++ 'seed': seed, ++ 'seed_offset': seed_offset, ++ }, ++ 'id': '0' ++ } ++ return self.rpc.send_json_rpc_request(stop_background_sync) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/wownero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch new file mode 100644 index 0000000..d922a40 --- /dev/null +++ b/patches/wownero/0002-fix-missing-___clear_cache-when-targetting-iOS.patch @@ -0,0 +1,19 @@ +From 21e95275c2e40454d9cce05c1139d8139af16587 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Wed, 23 Oct 2024 15:18:21 +0200 +Subject: [PATCH 02/15] fix missing ___clear_cache when targetting iOS + +--- + external/randomwow | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/external/randomwow b/external/randomwow +index 27b099b6d..c00685dad 160000 +--- a/external/randomwow ++++ b/external/randomwow +@@ -1 +1 @@ +-Subproject commit 27b099b6dd6fef6e17f58c6dfe00009e9c5df587 ++Subproject commit c00685dad575137caf4a9100ba08e77be376fa98 +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0002-polyseed.patch b/patches/wownero/0002-polyseed.patch deleted file mode 100644 index 302d392..0000000 --- a/patches/wownero/0002-polyseed.patch +++ /dev/null @@ -1,1299 +0,0 @@ -From 8729f36a109b33a080f5ee117a9d2a53fb55a47a Mon Sep 17 00:00:00 2001 -From: tobtoht -Date: Tue, 12 Mar 2024 09:42:37 +0100 -Subject: [PATCH 02/14] polyseed - -Co-authored-by: Czarek Nakamoto ---- - .github/workflows/build.yml | 4 +- - .gitmodules | 6 + - CMakeLists.txt | 4 +- - contrib/epee/include/wipeable_string.h | 7 + - contrib/epee/src/wipeable_string.cpp | 10 ++ - external/CMakeLists.txt | 2 + - external/polyseed | 1 + - external/utf8proc | 1 + - src/CMakeLists.txt | 1 + - src/cryptonote_basic/CMakeLists.txt | 1 + - src/cryptonote_basic/account.cpp | 23 +++- - src/cryptonote_basic/account.h | 6 + - src/cryptonote_config.h | 2 + - src/polyseed/CMakeLists.txt | 25 ++++ - src/polyseed/pbkdf2.c | 85 ++++++++++++ - src/polyseed/pbkdf2.h | 46 +++++++ - src/polyseed/polyseed.cpp | 182 +++++++++++++++++++++++++ - src/polyseed/polyseed.hpp | 167 +++++++++++++++++++++++ - src/wallet/api/wallet.cpp | 71 ++++++++++ - src/wallet/api/wallet.h | 10 ++ - src/wallet/api/wallet2_api.h | 25 ++++ - src/wallet/api/wallet_manager.cpp | 9 ++ - src/wallet/api/wallet_manager.h | 10 ++ - src/wallet/wallet2.cpp | 102 ++++++++++++-- - src/wallet/wallet2.h | 30 +++- - 25 files changed, 809 insertions(+), 21 deletions(-) - create mode 160000 external/polyseed - create mode 160000 external/utf8proc - create mode 100644 src/polyseed/CMakeLists.txt - create mode 100644 src/polyseed/pbkdf2.c - create mode 100644 src/polyseed/pbkdf2.h - create mode 100644 src/polyseed/polyseed.cpp - create mode 100644 src/polyseed/polyseed.hpp - -diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml -index 4c1e381c0..70bea03b3 100644 ---- a/.github/workflows/build.yml -+++ b/.github/workflows/build.yml -@@ -124,8 +124,8 @@ jobs: - - name: build - run: | - ${{env.CCACHE_SETTINGS}} -- cmake . -- make wallet_api -j3 -+ cmake -S . -B build -+ cmake --build build wallet_api -j3 - - test-ubuntu: - needs: build-ubuntu -diff --git a/.gitmodules b/.gitmodules -index e40b7b4c7..bd30f0444 100644 ---- a/.gitmodules -+++ b/.gitmodules -@@ -8,6 +8,12 @@ - active = false - 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 -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 20829bc30..2dd427d3d 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -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) -diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h -index 65977cd97..594e15de4 100644 ---- a/contrib/epee/include/wipeable_string.h -+++ b/contrib/epee/include/wipeable_string.h -@@ -34,6 +34,7 @@ - #include - #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); -diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp -index b016f2f48..f2f365b1b 100644 ---- a/contrib/epee/src/wipeable_string.cpp -+++ b/contrib/epee/src/wipeable_string.cpp -@@ -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]; -+} -+ - } -diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt -index 29aed0cc6..04910a4fc 100644 ---- a/external/CMakeLists.txt -+++ b/external/CMakeLists.txt -@@ -70,3 +70,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) -diff --git a/external/polyseed b/external/polyseed -new file mode 160000 -index 000000000..9d4f1a032 ---- /dev/null -+++ b/external/polyseed -@@ -0,0 +1 @@ -+Subproject commit 9d4f1a032585656e1a642ee70cdf929001badba6 -diff --git a/external/utf8proc b/external/utf8proc -new file mode 160000 -index 000000000..1cb28a66c ---- /dev/null -+++ b/external/utf8proc -@@ -0,0 +1 @@ -+Subproject commit 1cb28a66ca79a0845e99433fd1056257456cef8b -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index 9216bcaa5..c043ba150 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -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) -diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt -index 1414be1b2..414936a05 100644 ---- a/src/cryptonote_basic/CMakeLists.txt -+++ b/src/cryptonote_basic/CMakeLists.txt -@@ -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} -diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 2ac455fda..4931c3740 100644 ---- a/src/cryptonote_basic/account.cpp -+++ b/src/cryptonote_basic/account.cpp -@@ -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(); - } - //----------------------------------------------------------------- - crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) -@@ -244,6 +250,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 &multisig_keys) - { - m_keys.m_account_address.m_spend_public_key = spend_public_key; -diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h -index 2ee9545d4..0099ebfe7 100644 ---- a/src/cryptonote_basic/account.h -+++ b/src/cryptonote_basic/account.h -@@ -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 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 &multisig_keys); - const account_keys& get_keys() const; - std::string get_public_address_str(network_type nettype) const; -diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h -index 8c0d3ce20..5b36c70de 100644 ---- a/src/cryptonote_config.h -+++ b/src/cryptonote_config.h -@@ -219,6 +219,8 @@ - - #define DNS_BLOCKLIST_LIFETIME (86400 * 8) - -+#define POLYSEED_COIN POLYSEED_WOWNERO -+ - //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 -diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt -new file mode 100644 -index 000000000..cca4eb746 ---- /dev/null -+++ b/src/polyseed/CMakeLists.txt -@@ -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} -+) -diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c -new file mode 100644 -index 000000000..1c45f4708 ---- /dev/null -+++ b/src/polyseed/pbkdf2.c -@@ -0,0 +1,85 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// 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 -+ -+#include -+#include -+ -+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); -+} -\ No newline at end of file -diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h -new file mode 100644 -index 000000000..f6253b9d7 ---- /dev/null -+++ b/src/polyseed/pbkdf2.h -@@ -0,0 +1,46 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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 -+#include -+ -+#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 -\ No newline at end of file -diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp -new file mode 100644 -index 000000000..231a48a94 ---- /dev/null -+++ b/src/polyseed/polyseed.cpp -@@ -0,0 +1,182 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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 -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+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(str), 0, buffer, POLYSEED_STR_SIZE, options); -+ if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) { -+ throw std::runtime_error("Unicode normalization failed"); -+ } -+ -+ result = utf8proc_reencode(buffer, result, options); -+ if (result < 0 || result > POLYSEED_STR_SIZE) { -+ throw std::runtime_error("Unicode normalization failed"); -+ } -+ -+ strcpy(norm, reinterpret_cast(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)); -+ } -+ -+ 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 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& 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 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); -+ } -+} -diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp -new file mode 100644 -index 000000000..2c8c777a7 ---- /dev/null -+++ b/src/polyseed/polyseed.hpp -@@ -0,0 +1,167 @@ -+// Copyright (c) 2023, The Monero Project -+// Copyright (c) 2021, tevador -+// -+// 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 -+#include -+#include -+#include -+#include -+#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& 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 -+ 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 -\ No newline at end of file -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index d81ddec93..db9c2b5d9 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -683,6 +683,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(m_wallet->get_device_type()); -@@ -820,6 +842,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> Wallet::getPolyseedLanguages() -+{ -+ std::vector> languages; -+ -+ auto langs = polyseed::get_langs(); -+ for (const auto &lang : langs) { -+ languages.emplace_back(std::pair(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(); -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 03b5a98e9..28135c82a 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -86,9 +86,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 *) {} -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 6df661dc2..a0ed60a39 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -800,6 +800,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> getPolyseedLanguages(); -+ - /** - * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) - */ -@@ -1432,6 +1436,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 -diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp -index b166d8ac7..f88bd9e64 100644 ---- a/src/wallet/api/wallet_manager.cpp -+++ b/src/wallet/api/wallet_manager.cpp -@@ -172,6 +172,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(wallet); -diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h -index 206aedc14..e3149300c 100644 ---- a/src/wallet/api/wallet_manager.h -+++ b/src/wallet/api/wallet_manager.h -@@ -82,6 +82,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; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index e586d67f7..dfcc206a0 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -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" - { -@@ -1272,7 +1273,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(true) -+ m_allow_mismatched_daemon_version(true), -+ m_polyseed(false) - { - set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); - } -@@ -1450,10 +1452,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; - } - //---------------------------------------------------------------------------------------------------- -@@ -4712,6 +4729,9 @@ boost::optional wallet2::get_keys_file_data(const epee: - 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()); -+ - // Serialize the JSON object - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); -@@ -4859,7 +4879,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_auto_mine_for_rpc_payment_threshold = -1.0f; - m_credits_target = 0; - m_enable_multisig = false; -- m_allow_mismatched_daemon_version = true; -+ m_allow_mismatched_daemon_version = false; -+ m_polyseed = false; - } - else if(json.IsObject()) - { -@@ -5098,6 +5119,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_credits_target = field_credits_target; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false); - m_enable_multisig = field_enable_multisig; -+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false); -+ m_polyseed = field_polyseed; - } - else - { -@@ -5370,6 +5393,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. -@@ -5497,7 +5562,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 -@@ -5521,7 +5586,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) -@@ -13546,7 +13611,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); -@@ -15262,15 +15327,6 @@ 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) - { -- uint32_t version; -- if (!check_connection(&version)) -- { -- throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); -- } -- if (version < MAKE_CORE_RPC_VERSION(1, 6)) -- { -- 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; -@@ -15279,7 +15335,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui - { - 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)) -+ { -+ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); -+ } -+ if (version < MAKE_CORE_RPC_VERSION(1, 6)) -+ { -+ throw std::runtime_error("this function requires RPC version 1.6 or higher"); -+ } -+ - std::string err; - uint64_t height_min = 0; - uint64_t height_max = get_daemon_blockchain_height(err) - 1; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index df34f9abf..db5c1feb3 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -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" -@@ -854,6 +855,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 -@@ -1018,6 +1033,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. - */ -@@ -1469,8 +1493,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 select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); - std::vector select_available_outputs(const std::function &f); - std::vector select_available_unmixable_outputs(); -@@ -1563,6 +1587,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 &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(); - -@@ -1900,6 +1925,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 m_multisig_signers; --- -2.45.2 - diff --git a/patches/wownero/0003-fix-is_trivially_copyable.patch b/patches/wownero/0003-fix-is_trivially_copyable.patch new file mode 100644 index 0000000..f600003 --- /dev/null +++ b/patches/wownero/0003-fix-is_trivially_copyable.patch @@ -0,0 +1,32 @@ +From b41551e7dd8d3f4968f330f60d14773eaabca818 Mon Sep 17 00:00:00 2001 +From: cyan +Date: Tue, 22 Oct 2024 10:23:18 +0000 +Subject: [PATCH 03/15] fix is_trivially_copyable + +--- + contrib/epee/include/span.h | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h +index 01dc387d6..5e3af4d11 100644 +--- a/contrib/epee/include/span.h ++++ b/contrib/epee/include/span.h +@@ -151,7 +151,6 @@ namespace epee + using value_type = typename T::value_type; + static_assert(!std::is_empty(), "empty value types will not work -> sizeof == 1"); + static_assert(std::is_standard_layout(), "value type must have standard layout"); +- static_assert(std::is_trivially_copyable(), "value type must be trivially copyable"); + static_assert(alignof(value_type) == 1, "value type may have padding"); + return {reinterpret_cast(src.data()), src.size() * sizeof(value_type)}; + } +@@ -162,7 +161,6 @@ namespace epee + { + static_assert(!std::is_empty(), "empty types will not work -> sizeof == 1"); + static_assert(std::is_standard_layout(), "type must have standard layout"); +- static_assert(std::is_trivially_copyable(), "type must be trivially copyable"); + static_assert(alignof(T) == 1, "type may have padding"); + return {reinterpret_cast(std::addressof(src)), sizeof(T)}; + } +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0003-wallet-background-sync-with-just-the-view-key.patch b/patches/wownero/0003-wallet-background-sync-with-just-the-view-key.patch deleted file mode 100644 index 10b6cd3..0000000 --- a/patches/wownero/0003-wallet-background-sync-with-just-the-view-key.patch +++ /dev/null @@ -1,4351 +0,0 @@ -From bc5c17f6c9fcd6122de2c60625388e7370f9e522 Mon Sep 17 00:00:00 2001 -From: j-berman -Date: Thu, 13 Oct 2022 18:33:33 -0700 -Subject: [PATCH 03/14] wallet: background sync with just the view key - -- When background syncing, the wallet wipes the spend key -from memory and processes all new transactions. The wallet saves -all receives, spends, and "plausible" spends of receives the -wallet does not know key images for. -- When background sync disabled, the wallet processes all -background synced txs and then clears the background sync cache. -- Adding "plausible" spends to the background sync cache ensures -that the wallet does not need to query the daemon to see if any -received outputs were spent while background sync was enabled. -This would harm privacy especially for users of 3rd party daemons. -- To enable the feature in the CLI wallet, the user can set -background-sync to reuse-wallet-password or -custom-background-password and the wallet automatically syncs in -the background when the wallet locks, then processes all -background synced txs when the wallet is unlocked. -- The custom-background-password option enables the user to -open a distinct background wallet that only has a view key saved -and can be opened/closed/synced separately from the main wallet. -When the main wallet opens, it processes the background wallet's -cache. -- To enable the feature in the RPC wallet, there is a new -`/setup_background_sync` endpoint. -- HW, multsig and view-only wallets cannot background sync. ---- - src/cryptonote_basic/account.cpp | 11 + - src/cryptonote_basic/account.h | 1 + - src/cryptonote_config.h | 2 + - src/simplewallet/simplewallet.cpp | 205 +++- - src/simplewallet/simplewallet.h | 1 + - src/wallet/api/wallet.cpp | 231 +++- - src/wallet/api/wallet.h | 12 + - src/wallet/api/wallet2_api.h | 42 + - src/wallet/wallet2.cpp | 1031 ++++++++++++++++-- - src/wallet/wallet2.h | 156 ++- - src/wallet/wallet_errors.h | 39 + - src/wallet/wallet_rpc_server.cpp | 162 +++ - src/wallet/wallet_rpc_server.h | 6 + - src/wallet/wallet_rpc_server_commands_defs.h | 64 ++ - src/wallet/wallet_rpc_server_error_codes.h | 2 + - tests/functional_tests/transfer.py | 400 ++++++- - tests/functional_tests/util_resources.py | 25 + - tests/functional_tests/wallet.py | 43 +- - tests/unit_tests/wipeable_string.cpp | 12 + - utils/python-rpc/framework/wallet.py | 42 + - 20 files changed, 2354 insertions(+), 133 deletions(-) - -diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp -index 4931c3740..2d556f285 100644 ---- a/src/cryptonote_basic/account.cpp -+++ b/src/cryptonote_basic/account.cpp -@@ -158,6 +158,17 @@ DISABLE_VS_WARNINGS(4244 4345) - m_keys.m_passphrase.wipe(); - } - //----------------------------------------------------------------- -+ void account_base::set_spend_key(const crypto::secret_key& spend_secret_key) -+ { -+ // make sure derived spend public key matches saved public spend key -+ crypto::public_key spend_public_key; -+ crypto::secret_key_to_public_key(spend_secret_key, spend_public_key); -+ CHECK_AND_ASSERT_THROW_MES(m_keys.m_account_address.m_spend_public_key == spend_public_key, -+ "Unexpected derived public spend key"); -+ -+ m_keys.m_spend_secret_key = spend_secret_key; -+ } -+ //----------------------------------------------------------------- - crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random) - { - crypto::secret_key first = generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, recovery_key, recover); -diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h -index 0099ebfe7..1f76febce 100644 ---- a/src/cryptonote_basic/account.h -+++ b/src/cryptonote_basic/account.h -@@ -101,6 +101,7 @@ namespace cryptonote - bool store(const std::string& file_path); - - void forget_spend_key(); -+ void set_spend_key(const crypto::secret_key& spend_secret_key); - const std::vector &get_multisig_keys() const { return m_keys.m_multisig_keys; } - - void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); } -diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h -index 5b36c70de..d9151e8d2 100644 ---- a/src/cryptonote_config.h -+++ b/src/cryptonote_config.h -@@ -255,6 +255,8 @@ namespace config - const unsigned char HASH_KEY_ENCRYPTED_PAYMENT_ID = 0x8d; - const unsigned char HASH_KEY_WALLET = 0x8c; - const unsigned char HASH_KEY_WALLET_CACHE = 0x8d; -+ const unsigned char HASH_KEY_BACKGROUND_CACHE = 0x8e; -+ const unsigned char HASH_KEY_BACKGROUND_KEYS_FILE = 0x8f; - const unsigned char HASH_KEY_RPC_PAYMENT_NONCE = 0x58; - const unsigned char HASH_KEY_MEMORY = 'k'; - const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp -index b437092a4..394201028 100644 ---- a/src/simplewallet/simplewallet.cpp -+++ b/src/simplewallet/simplewallet.cpp -@@ -155,6 +155,17 @@ typedef cryptonote::simple_wallet sw; - } \ - } while(0) - -+#define CHECK_IF_BACKGROUND_SYNCING(msg) \ -+ do \ -+ { \ -+ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) \ -+ { \ -+ std::string type = m_wallet->is_background_wallet() ? "background wallet" : "background syncing wallet"; \ -+ fail_msg_writer() << boost::format(tr("%s %s")) % type % msg; \ -+ return false; \ -+ } \ -+ } while (0) -+ - enum TransferType { - Transfer, - TransferLocked, -@@ -332,7 +343,7 @@ namespace - auto pwd_container = tools::password_container::prompt(verify, prompt); - if (!pwd_container) - { -- tools::fail_msg_writer() << sw::tr("failed to read wallet password"); -+ tools::fail_msg_writer() << sw::tr("failed to read password"); - } - return pwd_container; - } -@@ -342,6 +353,11 @@ namespace - return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify); - } - -+ boost::optional background_sync_cache_password_prompter(bool verify) -+ { -+ return password_prompter(verify ? sw::tr("Enter a custom password for the background sync cache") : sw::tr("Background sync cache password"), verify); -+ } -+ - inline std::string interpret_rpc_response(bool ok, const std::string& status) - { - std::string err; -@@ -459,6 +475,41 @@ namespace - return "invalid"; - } - -+ const struct -+ { -+ const char *name; -+ tools::wallet2::BackgroundSyncType background_sync_type; -+ } background_sync_type_names[] = -+ { -+ { "off", tools::wallet2::BackgroundSyncOff }, -+ { "reuse-wallet-password", tools::wallet2::BackgroundSyncReusePassword }, -+ { "custom-background-password", tools::wallet2::BackgroundSyncCustomPassword }, -+ }; -+ -+ bool parse_background_sync_type(const std::string &s, tools::wallet2::BackgroundSyncType &background_sync_type) -+ { -+ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) -+ { -+ if (s == background_sync_type_names[n].name) -+ { -+ background_sync_type = background_sync_type_names[n].background_sync_type; -+ return true; -+ } -+ } -+ fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse background sync type"); -+ return false; -+ } -+ -+ std::string get_background_sync_type_name(tools::wallet2::BackgroundSyncType type) -+ { -+ for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n) -+ { -+ if (type == background_sync_type_names[n].background_sync_type) -+ return background_sync_type_names[n].name; -+ } -+ return "invalid"; -+ } -+ - std::string get_version_string(uint32_t version) - { - return boost::lexical_cast(version >> 16) + "." + boost::lexical_cast(version & 0xffff); -@@ -812,6 +863,7 @@ bool simple_wallet::spendkey(const std::vector &args/* = std::vecto - fail_msg_writer() << tr("wallet is watch-only and has no spend key"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("has no spend key"); - // don't log - PAUSE_READLINE(); - if (m_wallet->key_on_device()) { -@@ -843,6 +895,7 @@ bool simple_wallet::print_seed(bool encrypted) - fail_msg_writer() << tr("wallet is watch-only and has no seed"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("has no seed"); - - multisig = m_wallet->multisig(&ready); - if (multisig) -@@ -920,6 +973,7 @@ bool simple_wallet::seed_set_language(const std::vector &args/* = s - fail_msg_writer() << tr("wallet is watch-only and has no seed"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("has no seed"); - - epee::wipeable_string password; - { -@@ -1066,6 +1120,7 @@ bool simple_wallet::prepare_multisig_main(const std::vector &args, - fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig"); - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot be made multisig"); - - if(m_wallet->get_num_transfer_details()) - { -@@ -2202,6 +2257,7 @@ bool simple_wallet::save_known_rings(const std::vector &args) - - bool simple_wallet::freeze_thaw(const std::vector &args, bool freeze) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot freeze/thaw"); - if (args.empty()) - { - fail_msg_writer() << boost::format(tr("usage: %s |")) % (freeze ? "freeze" : "thaw"); -@@ -2241,6 +2297,7 @@ bool simple_wallet::thaw(const std::vector &args) - - bool simple_wallet::frozen(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot see frozen key images"); - if (args.empty()) - { - size_t ntd = m_wallet->get_num_transfer_details(); -@@ -3012,6 +3069,56 @@ bool simple_wallet::set_track_uses(const std::vector &args/* = std: - return true; - } - -+bool simple_wallet::setup_background_sync(const std::vector &args/* = std::vector()*/) -+{ -+ if (m_wallet->multisig()) -+ { -+ fail_msg_writer() << tr("background sync not implemented for multisig wallet"); -+ return true; -+ } -+ if (m_wallet->watch_only()) -+ { -+ fail_msg_writer() << tr("background sync not implemented for watch only wallet"); -+ return true; -+ } -+ if (m_wallet->key_on_device()) -+ { -+ fail_msg_writer() << tr("command not supported by HW wallet"); -+ return true; -+ } -+ -+ const auto pwd_container = get_and_verify_password(); -+ if (pwd_container) -+ { -+ tools::wallet2::BackgroundSyncType background_sync_type; -+ if (!parse_background_sync_type(args[1], background_sync_type)) -+ { -+ fail_msg_writer() << tr("invalid option"); -+ return true; -+ } -+ -+ try -+ { -+ boost::optional background_cache_password = boost::none; -+ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) -+ { -+ const auto background_pwd_container = background_sync_cache_password_prompter(true); -+ if (!background_pwd_container) -+ return true; -+ background_cache_password = background_pwd_container->password(); -+ } -+ -+ LOCK_IDLE_SCOPE(); -+ m_wallet->setup_background_sync(background_sync_type, pwd_container->password(), background_cache_password); -+ } -+ catch (const std::exception &e) -+ { -+ fail_msg_writer() << tr("Error setting background sync type: ") << e.what(); -+ } -+ } -+ return true; -+} -+ - bool simple_wallet::set_show_wallet_name_when_locked(const std::vector &args/* = std::vector()*/) - { - const auto pwd_container = get_and_verify_password(); -@@ -3257,6 +3364,7 @@ bool simple_wallet::apropos(const std::vector &args) - - bool simple_wallet::scan_tx(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot scan tx"); - if (args.empty()) - { - PRINT_USAGE(USAGE_SCAN_TX); -@@ -3489,6 +3597,8 @@ simple_wallet::simple_wallet() - " Ignore outputs of amount below this threshold when spending.\n " - "track-uses <1|0>\n " - " Whether to keep track of owned outputs uses.\n " -+ "background-sync \n " -+ " Set this to enable scanning in the background with just the view key while the wallet is locked.\n " - "setup-background-mining <1|0>\n " - " Whether to enable background mining. Set this to support the network and to get a chance to receive new WOW.\n " - "device-name \n " -@@ -3909,6 +4019,7 @@ bool simple_wallet::set_variable(const std::vector &args) - success_msg_writer() << "ignore-outputs-above = " << cryptonote::print_money(m_wallet->ignore_outputs_above()); - success_msg_writer() << "ignore-outputs-below = " << cryptonote::print_money(m_wallet->ignore_outputs_below()); - success_msg_writer() << "track-uses = " << m_wallet->track_uses(); -+ success_msg_writer() << "background-sync = " << get_background_sync_type_name(m_wallet->background_sync_type()); - success_msg_writer() << "setup-background-mining = " << setup_background_mining_string; - success_msg_writer() << "device-name = " << m_wallet->device_name(); - success_msg_writer() << "export-format = " << (m_wallet->export_format() == tools::wallet2::ExportFormat::Ascii ? "ascii" : "binary"); -@@ -3928,6 +4039,7 @@ bool simple_wallet::set_variable(const std::vector &args) - } - else - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot change wallet settings"); - - #define CHECK_SIMPLE_VARIABLE(name, f, help) do \ - if (args[0] == name) { \ -@@ -3981,6 +4093,7 @@ bool simple_wallet::set_variable(const std::vector &args) - CHECK_SIMPLE_VARIABLE("ignore-outputs-above", set_ignore_outputs_above, tr("amount")); - CHECK_SIMPLE_VARIABLE("ignore-outputs-below", set_ignore_outputs_below, tr("amount")); - CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1")); -+ CHECK_SIMPLE_VARIABLE("background-sync", setup_background_sync, tr("off (default); reuse-wallet-password (reuse the wallet password to encrypt the background cache); custom-background-password (use a custom background password to encrypt the background cache)")); - CHECK_SIMPLE_VARIABLE("show-wallet-name-when-locked", set_show_wallet_name_when_locked, tr("1 or 0")); - CHECK_SIMPLE_VARIABLE("show-detailed-prompt", set_show_detailed_prompt, tr("1 or 0")); - CHECK_SIMPLE_VARIABLE("inactivity-lock-timeout", set_inactivity_lock_timeout, tr("unsigned integer (seconds, 0 to disable)")); -@@ -4936,7 +5049,10 @@ std::string simple_wallet::get_mnemonic_language() - //---------------------------------------------------------------------------------------------------- - boost::optional simple_wallet::get_and_verify_password() const - { -- auto pwd_container = default_password_prompter(m_wallet_file.empty()); -+ const bool verify = m_wallet_file.empty(); -+ auto pwd_container = (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) -+ ? background_sync_cache_password_prompter(verify) -+ : default_password_prompter(verify); - if (!pwd_container) - return boost::none; - -@@ -5240,6 +5356,8 @@ boost::optional simple_wallet::open_wallet(const boost::p - prefix = tr("Opened watch-only wallet"); - else if (m_wallet->multisig(&ready, &threshold, &total)) - prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); -+ else if (m_wallet->is_background_wallet()) -+ prefix = tr("Opened background wallet"); - else - prefix = tr("Opened wallet"); - message_writer(console_color_white, true) << -@@ -5447,6 +5565,10 @@ void simple_wallet::stop_background_mining() - //---------------------------------------------------------------------------------------------------- - void simple_wallet::check_background_mining(const epee::wipeable_string &password) - { -+ // Background mining can be toggled from the main wallet -+ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) -+ return; -+ - tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); - if (setup == tools::wallet2::BackgroundMiningNo) - { -@@ -6319,6 +6441,7 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::rescan_spent(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot rescan spent"); - if (!m_wallet->is_trusted_daemon()) - { - fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); -@@ -6616,10 +6739,27 @@ void simple_wallet::check_for_inactivity_lock(bool user) - " B B " << std::endl << - "" << std::endl; - } -+ -+ bool started_background_sync = false; -+ if (!m_wallet->is_background_wallet() && -+ m_wallet->background_sync_type() != tools::wallet2::BackgroundSyncOff) -+ { -+ LOCK_IDLE_SCOPE(); -+ m_wallet->start_background_sync(); -+ started_background_sync = true; -+ } -+ - while (1) - { - const char *inactivity_msg = user ? "" : tr("Locked due to inactivity."); -- tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << tr("The wallet password is required to unlock the console."); -+ tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << ( -+ (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword) -+ ? tr("The background password is required to unlock the console.") -+ : tr("The wallet password is required to unlock the console.") -+ ); -+ -+ if (m_wallet->is_background_syncing()) -+ tools::msg_writer() << tr("\nSyncing in the background while locked...") << std::endl; - - const bool show_wallet_name = m_wallet->show_wallet_name_when_locked(); - if (show_wallet_name) -@@ -6632,8 +6772,16 @@ void simple_wallet::check_for_inactivity_lock(bool user) - } - try - { -- if (get_and_verify_password()) -+ const auto pwd_container = get_and_verify_password(); -+ if (pwd_container) -+ { -+ if (started_background_sync) -+ { -+ LOCK_IDLE_SCOPE(); -+ m_wallet->stop_background_sync(pwd_container->password()); -+ } - break; -+ } - } - catch (...) { /* do nothing, just let the loop loop */ } - } -@@ -6660,6 +6808,7 @@ bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector &args_, bool called_by_mms) - { - // "transfer [index=[,,...]] [] []
[]" -+ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); - if (!try_connect_to_daemon()) - return false; - -@@ -7133,6 +7282,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); - if (args_.size() < 1) - { - PRINT_USAGE(USAGE_TRANSFER); -@@ -7144,6 +7294,7 @@ bool simple_wallet::transfer(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::locked_transfer(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot transfer"); - if (args_.size() < 1) - { - PRINT_USAGE(USAGE_LOCKED_TRANSFER); -@@ -7155,6 +7306,7 @@ bool simple_wallet::locked_transfer(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::locked_sweep_all(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - if (args_.size() < 1) - { - PRINT_USAGE(USAGE_LOCKED_SWEEP_ALL); -@@ -7167,6 +7319,7 @@ bool simple_wallet::locked_sweep_all(const std::vector &args_) - - bool simple_wallet::sweep_unmixable(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - if (!try_connect_to_daemon()) - return true; - -@@ -7274,6 +7427,7 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - auto print_usage = [this, account, below]() - { - if (below) -@@ -7590,6 +7744,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_single(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - if (!try_connect_to_daemon()) - return true; - -@@ -7828,12 +7983,14 @@ bool simple_wallet::sweep_single(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_all(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - sweep_main(m_current_subaddress_account, 0, false, args_); - return true; - } - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_account(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - auto local_args = args_; - if (local_args.empty()) - { -@@ -7854,6 +8011,7 @@ bool simple_wallet::sweep_account(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sweep_below(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sweep"); - uint64_t below = 0; - if (args_.size() < 1) - { -@@ -7872,6 +8030,7 @@ bool simple_wallet::sweep_below(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::donate(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot donate"); - std::vector local_args = args_; - if(local_args.empty() || local_args.size() > 5) - { -@@ -7933,6 +8092,7 @@ bool simple_wallet::donate(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot load tx"); - // gather info to ask the user - uint64_t amount = 0, amount_to_dests = 0, change = 0; - size_t min_ring_size = ~0; -@@ -8113,6 +8273,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) - fail_msg_writer() << tr("This is a watch only wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot sign transfer"); - - bool export_raw = false; - std::string unsigned_filename = "unsigned_wownero_tx"; -@@ -8220,6 +8381,8 @@ std::string get_tx_key_stream(crypto::secret_key tx_key, std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get tx key"); -+ - std::vector local_args = args_; - - if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR) -@@ -8260,6 +8423,8 @@ bool simple_wallet::get_tx_key(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::set_tx_key(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot set tx key"); -+ - std::vector local_args = args_; - - if(local_args.size() != 2 && local_args.size() != 3) { -@@ -8336,6 +8501,8 @@ bool simple_wallet::set_tx_key(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_tx_proof(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get tx proof"); -+ - if (args.size() != 2 && args.size() != 3) - { - PRINT_USAGE(USAGE_GET_TX_PROOF); -@@ -8542,6 +8709,7 @@ bool simple_wallet::check_tx_proof(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_spend_proof(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get spend proof"); - if (m_wallet->key_on_device()) - { - fail_msg_writer() << tr("command not supported by HW wallet"); -@@ -8626,6 +8794,7 @@ bool simple_wallet::check_spend_proof(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_reserve_proof(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get reserve proof"); - if (m_wallet->key_on_device()) - { - fail_msg_writer() << tr("command not supported by HW wallet"); -@@ -9312,6 +9481,8 @@ bool simple_wallet::unspent_outputs(const std::vector &args_) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::rescan_blockchain(const std::vector &args_) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot rescan"); -+ - uint64_t start_height = 0; - ResetType reset_type = ResetSoft; - -@@ -9622,6 +9793,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - if (command == "new") - { - // create a new account and switch to it -+ CHECK_IF_BACKGROUND_SYNCING("cannot create new account"); - std::string label = boost::join(local_args, " "); - if (label.empty()) - label = tr("(Untitled account)"); -@@ -9652,6 +9824,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - else if (command == "label" && local_args.size() >= 1) - { - // set label of the specified account -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - uint32_t index_major; - if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0])) - { -@@ -9673,6 +9846,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - } - else if (command == "tag" && local_args.size() >= 2) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - const std::string tag = local_args[0]; - std::set account_indices; - for (size_t i = 1; i < local_args.size(); ++i) -@@ -9697,6 +9871,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - } - else if (command == "untag" && local_args.size() >= 1) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - std::set account_indices; - for (size_t i = 0; i < local_args.size(); ++i) - { -@@ -9720,6 +9895,7 @@ bool simple_wallet::account(const std::vector &args/* = std::vector - } - else if (command == "tag_description" && local_args.size() >= 1) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify account"); - const std::string tag = local_args[0]; - std::string description; - if (local_args.size() > 1) -@@ -9837,6 +10013,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args[0] == "new") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); - local_args.erase(local_args.begin()); - std::string label; - if (local_args.size() > 0) -@@ -9849,6 +10026,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args[0] == "mnew") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot add addresses"); - local_args.erase(local_args.begin()); - if (local_args.size() != 1) - { -@@ -9874,6 +10052,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args[0] == "one-off") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot add address"); - local_args.erase(local_args.begin()); - std::string label; - if (local_args.size() != 2) -@@ -9892,6 +10071,7 @@ bool simple_wallet::print_address(const std::vector &args/* = std:: - } - else if (local_args.size() >= 2 && local_args[0] == "label") - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot modify address"); - if (!epee::string_tools::get_xtype_from_string(index, local_args[1])) - { - fail_msg_writer() << tr("failed to parse index: ") << local_args[1]; -@@ -10038,6 +10218,8 @@ bool simple_wallet::print_integrated_address(const std::vector &arg - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::address_book(const std::vector &args/* = std::vector()*/) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get address book"); -+ - if (args.size() == 0) - { - } -@@ -10098,6 +10280,8 @@ bool simple_wallet::address_book(const std::vector &args/* = std::v - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::set_tx_note(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot set tx note"); -+ - if (args.size() == 0) - { - PRINT_USAGE(USAGE_SET_TX_NOTE); -@@ -10126,6 +10310,8 @@ bool simple_wallet::set_tx_note(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_tx_note(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get tx note"); -+ - if (args.size() != 1) - { - PRINT_USAGE(USAGE_GET_TX_NOTE); -@@ -10151,6 +10337,8 @@ bool simple_wallet::get_tx_note(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::set_description(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot set description"); -+ - // 0 arguments allowed, for setting the description to empty string - - std::string description = ""; -@@ -10167,6 +10355,8 @@ bool simple_wallet::set_description(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::get_description(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot get description"); -+ - if (args.size() != 0) - { - PRINT_USAGE(USAGE_GET_DESCRIPTION); -@@ -10225,6 +10415,8 @@ bool simple_wallet::wallet_info(const std::vector &args) - type = tr("Watch only"); - else if (m_wallet->multisig(&ready, &threshold, &total)) - type = (boost::format(tr("%u/%u multisig%s")) % threshold % total % (ready ? "" : " (not yet finalized)")).str(); -+ else if (m_wallet->is_background_wallet()) -+ type = tr("Background wallet"); - else - type = tr("Normal"); - message_writer() << tr("Type: ") << type; -@@ -10236,6 +10428,7 @@ bool simple_wallet::wallet_info(const std::vector &args) - //---------------------------------------------------------------------------------------------------- - bool simple_wallet::sign(const std::vector &args) - { -+ CHECK_IF_BACKGROUND_SYNCING("cannot sign"); - if (m_wallet->key_on_device()) - { - fail_msg_writer() << tr("command not supported by HW wallet"); -@@ -10343,6 +10536,7 @@ bool simple_wallet::export_key_images(const std::vector &args_) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot export key images"); - auto args = args_; - - if (m_wallet->watch_only()) -@@ -10396,6 +10590,7 @@ bool simple_wallet::import_key_images(const std::vector &args) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot import key images"); - if (!m_wallet->is_trusted_daemon()) - { - fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); -@@ -10504,6 +10699,7 @@ bool simple_wallet::export_outputs(const std::vector &args_) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot export outputs"); - auto args = args_; - - bool all = false; -@@ -10553,6 +10749,7 @@ bool simple_wallet::import_outputs(const std::vector &args) - fail_msg_writer() << tr("command not supported by HW wallet"); - return true; - } -+ CHECK_IF_BACKGROUND_SYNCING("cannot import outputs"); - if (args.size() != 1) - { - PRINT_USAGE(USAGE_IMPORT_OUTPUTS); -diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h -index 8e3477ba3..ae1aa312b 100644 ---- a/src/simplewallet/simplewallet.h -+++ b/src/simplewallet/simplewallet.h -@@ -147,6 +147,7 @@ namespace cryptonote - bool set_ignore_outputs_above(const std::vector &args = std::vector()); - bool set_ignore_outputs_below(const std::vector &args = std::vector()); - bool set_track_uses(const std::vector &args = std::vector()); -+ bool setup_background_sync(const std::vector &args = std::vector()); - bool set_show_wallet_name_when_locked(const std::vector &args = std::vector()); - bool set_show_detailed_prompt(const std::vector &args = std::vector()); - bool set_inactivity_lock_timeout(const std::vector &args = std::vector()); -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index db9c2b5d9..27a4dc6c8 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -56,6 +56,40 @@ using namespace cryptonote; - #undef MONERO_DEFAULT_LOG_CATEGORY - #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" - -+#define LOCK_REFRESH() \ -+ bool refresh_enabled = m_refreshEnabled; \ -+ m_refreshEnabled = false; \ -+ m_wallet->stop(); \ -+ m_refreshCV.notify_one(); \ -+ boost::mutex::scoped_lock lock(m_refreshMutex); \ -+ boost::mutex::scoped_lock lock2(m_refreshMutex2); \ -+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \ -+ /* m_refreshMutex's still locked here */ \ -+ if (refresh_enabled) \ -+ startRefresh(); \ -+ }) -+ -+#define PRE_VALIDATE_BACKGROUND_SYNC() \ -+ do \ -+ { \ -+ clearStatus(); \ -+ if (m_wallet->key_on_device()) \ -+ { \ -+ setStatusError(tr("HW wallet cannot use background sync")); \ -+ return false; \ -+ } \ -+ if (m_wallet->watch_only()) \ -+ { \ -+ setStatusError(tr("View only wallet cannot use background sync")); \ -+ return false; \ -+ } \ -+ if (m_wallet->multisig()) \ -+ { \ -+ setStatusError(tr("Multisig wallet cannot use background sync")); \ -+ return false; \ -+ } \ -+ } while (0) -+ - namespace Monero { - - namespace { -@@ -836,6 +870,8 @@ bool WalletImpl::close(bool store) - - std::string WalletImpl::seed(const std::string& seed_offset) const - { -+ if (checkBackgroundSync("cannot get seed")) -+ return std::string(); - epee::wipeable_string seed; - if (m_wallet) - m_wallet->get_seed(seed, seed_offset); -@@ -898,6 +934,8 @@ std::string WalletImpl::getSeedLanguage() const - - void WalletImpl::setSeedLanguage(const std::string &arg) - { -+ if (checkBackgroundSync("cannot set seed language")) -+ return; - m_wallet->set_seed_language(arg); - } - -@@ -921,6 +959,8 @@ void WalletImpl::statusWithErrorString(int& status, std::string& errorString) co - - bool WalletImpl::setPassword(const std::string &password) - { -+ if (checkBackgroundSync("cannot change password")) -+ return false; - clearStatus(); - try { - m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password); -@@ -1102,6 +1142,8 @@ bool WalletImpl::lightWalletImportWalletRequest(std::string &payment_id, uint64_ - - void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height) - { -+ if (checkBackgroundSync("cannot change refresh height")) -+ return; - m_wallet->set_refresh_from_block_height(refresh_from_block_height); - } - -@@ -1219,6 +1261,8 @@ void WalletImpl::refreshAsync() - - bool WalletImpl::rescanBlockchain() - { -+ if (checkBackgroundSync("cannot rescan blockchain")) -+ return false; - clearStatus(); - m_refreshShouldRescan = true; - doRefresh(); -@@ -1227,6 +1271,8 @@ bool WalletImpl::rescanBlockchain() - - void WalletImpl::rescanBlockchainAsync() - { -+ if (checkBackgroundSync("cannot rescan blockchain")) -+ return; - m_refreshShouldRescan = true; - refreshAsync(); - } -@@ -1250,7 +1296,7 @@ int WalletImpl::autoRefreshInterval() const - UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_filename) { - clearStatus(); - UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); -- if (!m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ -+ if (checkBackgroundSync("cannot load tx") || !m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){ - setStatusError(tr("Failed to load unsigned transactions")); - transaction->m_status = UnsignedTransaction::Status::Status_Error; - transaction->m_errorString = errorString(); -@@ -1312,6 +1358,8 @@ PendingTransaction *WalletImpl::loadSignedTx(const std::string &signed_filename) - - bool WalletImpl::submitTransaction(const string &fileName) { - clearStatus(); -+ if (checkBackgroundSync("cannot submit tx")) -+ return false; - std::unique_ptr transaction(new PendingTransactionImpl(*this)); - - bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx); -@@ -1335,6 +1383,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) - setStatusError(tr("Wallet is view only")); - return false; - } -+ if (checkBackgroundSync("cannot export key images")) -+ return false; - - try - { -@@ -1355,6 +1405,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) - - bool WalletImpl::importKeyImages(const string &filename) - { -+ if (checkBackgroundSync("cannot import key images")) -+ return false; - if (!trustedDaemon()) { - setStatusError(tr("Key images can only be imported with a trusted daemon")); - return false; -@@ -1378,6 +1430,8 @@ bool WalletImpl::importKeyImages(const string &filename) - - bool WalletImpl::exportOutputs(const string &filename, bool all) - { -+ if (checkBackgroundSync("cannot export outputs")) -+ return false; - if (m_wallet->key_on_device()) - { - setStatusError(string(tr("Not supported on HW wallets.")) + filename); -@@ -1408,6 +1462,8 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) - - bool WalletImpl::importOutputs(const string &filename) - { -+ if (checkBackgroundSync("cannot import outputs")) -+ return false; - if (m_wallet->key_on_device()) - { - setStatusError(string(tr("Not supported on HW wallets.")) + filename); -@@ -1440,6 +1496,8 @@ bool WalletImpl::importOutputs(const string &filename) - - bool WalletImpl::scanTransactions(const std::vector &txids) - { -+ if (checkBackgroundSync("cannot scan transactions")) -+ return false; - if (txids.empty()) - { - setStatusError(string(tr("Failed to scan transactions: no transaction ids provided."))); -@@ -1563,8 +1621,86 @@ bool WalletImpl::importTransaction(const std::string &txid, std::vector &background_cache_password) -+{ -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ -+ tools::wallet2::BackgroundSyncType bgs_type; -+ switch (background_sync_type) -+ { -+ case Wallet::BackgroundSync_Off: bgs_type = tools::wallet2::BackgroundSyncOff; break; -+ case Wallet::BackgroundSync_ReusePassword: bgs_type = tools::wallet2::BackgroundSyncReusePassword; break; -+ case Wallet::BackgroundSync_CustomPassword: bgs_type = tools::wallet2::BackgroundSyncCustomPassword; break; -+ default: setStatusError(tr("Unknown background sync type")); return false; -+ } -+ -+ boost::optional bgc_password = background_cache_password -+ ? boost::optional(*background_cache_password) -+ : boost::none; -+ -+ LOCK_REFRESH(); -+ m_wallet->setup_background_sync(bgs_type, wallet_password, bgc_password); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to setup background sync: " << e.what()); -+ setStatusError(string(tr("Failed to setup background sync: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ -+Wallet::BackgroundSyncType WalletImpl::getBackgroundSyncType() const -+{ -+ switch (m_wallet->background_sync_type()) -+ { -+ case tools::wallet2::BackgroundSyncOff: return Wallet::BackgroundSync_Off; -+ case tools::wallet2::BackgroundSyncReusePassword: return Wallet::BackgroundSync_ReusePassword; -+ case tools::wallet2::BackgroundSyncCustomPassword: return Wallet::BackgroundSync_CustomPassword; -+ default: setStatusError(tr("Unknown background sync type")); return Wallet::BackgroundSync_Off; -+ } -+} -+ -+bool WalletImpl::startBackgroundSync() -+{ -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ LOCK_REFRESH(); -+ m_wallet->start_background_sync(); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to start background sync: " << e.what()); -+ setStatusError(string(tr("Failed to start background sync: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ -+bool WalletImpl::stopBackgroundSync(const std::string &wallet_password) -+{ -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ LOCK_REFRESH(); -+ m_wallet->stop_background_sync(epee::wipeable_string(wallet_password)); -+ } -+ catch (const std::exception &e) -+ { -+ LOG_ERROR("Failed to stop background sync: " << e.what()); -+ setStatusError(string(tr("Failed to stop background sync: ")) + e.what()); -+ return false; -+ } -+ return true; -+} -+ - void WalletImpl::addSubaddressAccount(const std::string& label) - { -+ if (checkBackgroundSync("cannot add account")) -+ return; - m_wallet->add_subaddress_account(label); - } - size_t WalletImpl::numSubaddressAccounts() const -@@ -1577,10 +1713,14 @@ size_t WalletImpl::numSubaddresses(uint32_t accountIndex) const - } - void WalletImpl::addSubaddress(uint32_t accountIndex, const std::string& label) - { -+ if (checkBackgroundSync("cannot add subbaddress")) -+ return; - m_wallet->add_subaddress(accountIndex, label); - } - std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const - { -+ if (checkBackgroundSync("cannot get subbaddress label")) -+ return ""; - try - { - return m_wallet->get_subaddress_label({accountIndex, addressIndex}); -@@ -1594,6 +1734,8 @@ std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addre - } - void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) - { -+ if (checkBackgroundSync("cannot set subbaddress label")) -+ return; - try - { - return m_wallet->set_subaddress_label({accountIndex, addressIndex}, label); -@@ -1607,12 +1749,16 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex - - MultisigState WalletImpl::multisig() const { - MultisigState state; -+ if (checkBackgroundSync("cannot use multisig")) -+ return state; - state.isMultisig = m_wallet->multisig(&state.isReady, &state.threshold, &state.total); - - return state; - } - - string WalletImpl::getMultisigInfo() const { -+ if (checkBackgroundSync("cannot use multisig")) -+ return string(); - try { - clearStatus(); - return m_wallet->get_multisig_first_kex_msg(); -@@ -1625,6 +1771,8 @@ string WalletImpl::getMultisigInfo() const { - } - - string WalletImpl::makeMultisig(const vector& info, const uint32_t threshold) { -+ if (checkBackgroundSync("cannot make multisig")) -+ return string(); - try { - clearStatus(); - -@@ -1765,6 +1913,9 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector extra; - std::string extra_nonce; - vector dsts; -@@ -2075,6 +2226,9 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() - PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); - - do { -+ if (checkBackgroundSync("cannot sweep")) -+ break; -+ - try { - transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions(); - pendingTxPostProcess(transaction); -@@ -2213,11 +2367,15 @@ uint32_t WalletImpl::defaultMixin() const - - void WalletImpl::setDefaultMixin(uint32_t arg) - { -+ if (checkBackgroundSync("cannot set default mixin")) -+ return; - m_wallet->default_mixin(arg); - } - - bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val) - { -+ if (checkBackgroundSync("cannot set cache attribute")) -+ return false; - m_wallet->set_attribute(key, val); - return true; - } -@@ -2231,6 +2389,8 @@ std::string WalletImpl::getCacheAttribute(const std::string &key) const - - bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) - { -+ if (checkBackgroundSync("cannot set user note")) -+ return false; - cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) - return false; -@@ -2242,6 +2402,8 @@ bool WalletImpl::setUserNote(const std::string &txid, const std::string ¬e) - - std::string WalletImpl::getUserNote(const std::string &txid) const - { -+ if (checkBackgroundSync("cannot get user note")) -+ return ""; - cryptonote::blobdata txid_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash)) - return ""; -@@ -2252,6 +2414,9 @@ std::string WalletImpl::getUserNote(const std::string &txid) const - - std::string WalletImpl::getTxKey(const std::string &txid_str) const - { -+ if (checkBackgroundSync("cannot get tx key")) -+ return ""; -+ - crypto::hash txid; - if(!epee::string_tools::hex_to_pod(txid_str, txid)) - { -@@ -2336,6 +2501,9 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str, - - std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const - { -+ if (checkBackgroundSync("cannot get tx proof")) -+ return ""; -+ - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(txid_str, txid)) - { -@@ -2392,6 +2560,9 @@ bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &ad - } - - std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::string &message) const { -+ if (checkBackgroundSync("cannot get spend proof")) -+ return ""; -+ - crypto::hash txid; - if(!epee::string_tools::hex_to_pod(txid_str, txid)) - { -@@ -2434,6 +2605,9 @@ bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string - } - - std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const { -+ if (checkBackgroundSync("cannot get reserve proof")) -+ return ""; -+ - try - { - clearStatus(); -@@ -2480,6 +2654,9 @@ bool WalletImpl::checkReserveProof(const std::string &address, const std::string - - std::string WalletImpl::signMessage(const std::string &message, const std::string &address) - { -+ if (checkBackgroundSync("cannot sign message")) -+ return ""; -+ - if (address.empty()) { - return m_wallet->sign(message, tools::wallet2::sign_with_spend_key); - } -@@ -2607,6 +2784,16 @@ bool WalletImpl::isDeterministic() const - return m_wallet->is_deterministic(); - } - -+bool WalletImpl::isBackgroundSyncing() const -+{ -+ return m_wallet->is_background_syncing(); -+} -+ -+bool WalletImpl::isBackgroundWallet() const -+{ -+ return m_wallet->is_background_wallet(); -+} -+ - void WalletImpl::clearStatus() const - { - boost::lock_guard l(m_statusMutex); -@@ -2670,11 +2857,23 @@ void WalletImpl::doRefresh() - boost::lock_guard guarg(m_refreshMutex2); - do try { - LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<rescan_blockchain(false); -- m_wallet->refresh(true); -- if (!m_synchronized) { -- m_synchronized = true; -+ -+ // Syncing daemon and refreshing wallet simultaneously is very resource intensive. -+ // Disable refresh if wallet is disconnected or daemon isn't synced. -+ if (m_wallet->light_wallet() || daemonSynced()) { -+ if(rescan) -+ m_wallet->rescan_blockchain(false); -+ m_wallet->refresh(trustedDaemon()); -+ m_synchronized = m_wallet->is_synced(); -+ // assuming if we have empty history, it wasn't initialized yet -+ // for further history changes client need to update history in -+ // "on_money_received" and "on_money_sent" callbacks -+ if (m_history->count() == 0) { -+ m_history->refresh(); -+ } -+ m_wallet->find_and_save_rings(false); -+ } else { -+ LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); - } - // assuming if we have empty history, it wasn't initialized yet - // for further history changes client need to update history in -@@ -2784,6 +2983,24 @@ bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_a - return true; - } - -+bool WalletImpl::checkBackgroundSync(const std::string &message) const -+{ -+ clearStatus(); -+ if (m_wallet->is_background_wallet()) -+ { -+ LOG_ERROR("Background wallets " + message); -+ setStatusError(tr("Background wallets ") + message); -+ return true; -+ } -+ if (m_wallet->is_background_syncing()) -+ { -+ LOG_ERROR(message + " while background syncing"); -+ setStatusError(message + tr(" while background syncing. Stop background syncing first.")); -+ return true; -+ } -+ return false; -+} -+ - bool WalletImpl::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 &unknown_parameters, std::string &error) - { - return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); -@@ -2802,6 +3019,8 @@ std::string WalletImpl::getDefaultDataDir() const - bool WalletImpl::rescanSpent() - { - clearStatus(); -+ if (checkBackgroundSync("cannot rescan spent")) -+ return false; - if (!trustedDaemon()) { - setStatusError(tr("Rescan spent can only be used with a trusted daemon")); - return false; -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 28135c82a..fc6341861 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -216,6 +216,13 @@ public: - virtual std::string printAddressBook() override; - virtual std::string printScannedPoolTxs() override; - -+ bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; -+ BackgroundSyncType getBackgroundSyncType() const override; -+ bool startBackgroundSync() override; -+ bool stopBackgroundSync(const std::string &wallet_password) override; -+ bool isBackgroundSyncing() const override; -+ bool isBackgroundWallet() const override; -+ - virtual void disposeTransaction(PendingTransaction * t) override; - virtual uint64_t estimateTransactionFee(const std::vector> &destinations, - PendingTransaction::Priority priority) const override; -@@ -285,6 +292,7 @@ private: - bool isNewWallet() const; - void pendingTxPostProcess(PendingTransactionImpl * pending); - bool doInit(const std::string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit = 0, bool ssl = false); -+ bool checkBackgroundSync(const std::string &message) const; - - private: - friend class PendingTransactionImpl; -@@ -302,6 +310,10 @@ private: - mutable boost::mutex m_statusMutex; - mutable int m_status; - mutable std::string m_errorString; -+ // TODO: harden password handling in the wallet API, see relevant discussion -+ // https://github.com/monero-project/monero-gui/issues/1537 -+ // https://github.com/feather-wallet/feather/issues/72#issuecomment-1405602142 -+ // https://github.com/monero-project/monero/pull/8619#issuecomment-1632951461 - std::string m_password; - std::unique_ptr m_history; - std::unique_ptr m_wallet2Callback; -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index a0ed60a39..db5509227 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -544,6 +544,12 @@ struct Wallet - ConnectionStatus_WrongVersion - }; - -+ enum BackgroundSyncType { -+ BackgroundSync_Off = 0, -+ BackgroundSync_ReusePassword = 1, -+ BackgroundSync_CustomPassword = 2 -+ }; -+ - virtual ~Wallet() = 0; - virtual std::string seed(const std::string& seed_offset = "") const = 0; - virtual std::string getSeedLanguage() const = 0; -@@ -1095,6 +1101,42 @@ struct Wallet - - virtual bool importTransaction(const std::string &txid, std::vector &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) = 0; - -+ /*! -+ * \brief setupBackgroundSync - setup background sync mode with just a view key -+ * \param background_sync_type - the mode the wallet background syncs in -+ * \param wallet_password -+ * \param background_cache_password - custom password to encrypt background cache, only needed for custom password background sync type -+ * \return - true on success -+ */ -+ virtual bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) = 0; -+ -+ /*! -+ * \brief getBackgroundSyncType - get mode the wallet background syncs in -+ * \return - the type, or off if type is unknown -+ */ -+ virtual BackgroundSyncType getBackgroundSyncType() const = 0; -+ -+ /** -+ * @brief startBackgroundSync - sync the chain in the background with just view key -+ */ -+ virtual bool startBackgroundSync() = 0; -+ -+ /** -+ * @brief stopBackgroundSync - bring back spend key and process background synced txs -+ * \param wallet_password -+ */ -+ virtual bool stopBackgroundSync(const std::string &wallet_password) = 0; -+ -+ /** -+ * @brief isBackgroundSyncing - returns true if the wallet is background syncing -+ */ -+ virtual bool isBackgroundSyncing() const = 0; -+ -+ /** -+ * @brief isBackgroundWallet - returns true if the wallet is a background wallet -+ */ -+ virtual bool isBackgroundWallet() const = 0; -+ - virtual TransactionHistory * history() = 0; - virtual AddressBook * addressBook() = 0; - virtual Coins * coins() = 0; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index dfcc206a0..e013313b9 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -158,6 +158,8 @@ static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1"; - - static const std::string ASCII_OUTPUT_MAGIC = "WowneroAsciiDataV1"; - -+static const std::string BACKGROUND_WALLET_SUFFIX = ".background"; -+ - boost::mutex tools::wallet2::default_daemon_address_lock; - std::string tools::wallet2::default_daemon_address = ""; - -@@ -1021,14 +1023,14 @@ uint64_t num_priv_multisig_keys_post_setup(uint64_t threshold, uint64_t total) - * @param keys_data_key the chacha key that encrypts wallet keys files - * @return crypto::chacha_key the chacha key that encrypts the wallet cache files - */ --crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key) -+crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key, const unsigned char domain_separator) - { - static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key"); - - crypto::chacha_key cache_key; - epee::mlocked> cache_key_data; - memcpy(cache_key_data.data(), &keys_data_key, HASH_SIZE); -- cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE; -+ cache_key_data[HASH_SIZE] = domain_separator; - cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&) cache_key); - - return cache_key; -@@ -1116,7 +1118,7 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional lock(lockers_lock); - if (lockers++ > 0) - locked = false; -- if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only()) -+ if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only() || w.is_background_syncing()) - { - locked = false; - return; -@@ -1233,6 +1235,11 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - m_ignore_outputs_above(MONEY_SUPPLY), - m_ignore_outputs_below(0), - m_track_uses(false), -+ m_is_background_wallet(false), -+ m_background_sync_type(BackgroundSyncOff), -+ m_background_syncing(false), -+ m_processing_background_cache(false), -+ m_custom_background_key(boost::none), - m_show_wallet_name_when_locked(true), - m_show_detailed_prompt(true), - m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT), -@@ -1897,6 +1904,9 @@ bool has_nonrequested_tx_at_height_or_above_requested(uint64_t height, const std - //---------------------------------------------------------------------------------------------------- - void wallet2::scan_tx(const std::unordered_set &txids) - { -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "cannot scan tx from background wallet"); -+ - // Get the transactions from daemon in batches sorted lowest height to highest - tx_entry_data txs_to_scan = get_tx_entries(txids); - if (txs_to_scan.tx_entries.empty()) -@@ -2206,11 +2216,11 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons - THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); - - // if keys are encrypted, ask for password -- if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k) -+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k && !m_background_syncing) - { - static critical_section password_lock; - CRITICAL_REGION_LOCAL(password_lock); -- if (!m_encrypt_keys_after_refresh) -+ if (!m_encrypt_keys_after_refresh && !m_processing_background_cache) - { - boost::optional pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received"); - THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming wownero")); -@@ -2222,7 +2232,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons - crypto::public_key output_public_key; - THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key"); - -- if (m_multisig) -+ if (m_multisig || m_background_syncing/*no spend key*/) - { - tx_scan_info.in_ephemeral.pub = output_public_key; - tx_scan_info.in_ephemeral.sec = crypto::null_skey; -@@ -2479,6 +2489,22 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error, - "transactions outputs size=" + std::to_string(tx.vout.size()) + - " not match with daemon response size=" + std::to_string(o_indices.size())); -+ -+ // we're going to re-process this receive when background sync is disabled -+ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding received tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } - } - - for(size_t o: outs) -@@ -2505,7 +2531,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - td.m_tx = (const cryptonote::transaction_prefix&)tx; - td.m_txid = txid; - td.m_key_image = tx_scan_info[o].ki; -- td.m_key_image_known = !m_watch_only && !m_multisig; -+ td.m_key_image_known = !m_watch_only && !m_multisig && !m_background_syncing; - if (!td.m_key_image_known) - { - // we might have cold signed, and have a mapping to key images -@@ -2696,10 +2722,25 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - 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); -+ -+ if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } - } - } - -- if (!pool && m_track_uses) -+ if (!pool && (m_track_uses || (m_background_syncing && it == m_key_images.end()))) - { - PERF_TIMER(track_uses); - const uint64_t amount = in_to_key.amount; -@@ -2713,7 +2754,27 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - { - size_t idx = i->second; - THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Output tracker cache index out of range"); -- m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); -+ -+ if (m_track_uses) -+ m_transfers[idx].m_uses.push_back(std::make_pair(height, txid)); -+ -+ // We'll re-process all txs which *might* be spends when we disable -+ // background sync and retrieve the spend key. We don't know if an -+ // output is a spend in this tx if we don't know its key image. -+ if (m_background_syncing && !m_transfers[idx].m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } - } - } - } -@@ -2723,7 +2784,24 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote - continue; - for (uint64_t offset: offsets) - if (offset == td.m_global_output_index) -- td.m_uses.push_back(std::make_pair(height, txid)); -+ { -+ if (m_track_uses) -+ td.m_uses.push_back(std::make_pair(height, txid)); -+ if (m_background_syncing && !td.m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end()) -+ { -+ size_t bgs_idx = m_background_sync_data.txs.size(); -+ background_synced_tx_t bgs_tx = { -+ .index_in_background_sync_data = bgs_idx, -+ .tx = tx, -+ .output_indices = o_indices, -+ .height = height, -+ .block_timestamp = ts, -+ .double_spend_seen = double_spend_seen -+ }; -+ LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")"); -+ m_background_sync_data.txs.insert({txid, std::move(bgs_tx)}); -+ } -+ } - } - } - } -@@ -3096,8 +3174,8 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh - req.start_height = start_height; - req.no_miner_tx = m_refresh_type == RefreshNoCoinbase; - -- req.requested_info = first ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; -- if (try_incremental) -+ req.requested_info = (first && !m_background_syncing) ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY; -+ if (try_incremental && !m_background_syncing) - req.pool_info_since = m_pool_info_query_time; - - { -@@ -3124,7 +3202,7 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh - << ", height " << blocks_start_height + blocks.size() << ", node height " << res.current_height - << ", pool info " << static_cast(res.pool_info_extent)); - -- if (first) -+ if (first && !m_background_syncing) - { - if (res.pool_info_extent != COMMAND_RPC_GET_BLOCKS_FAST::NONE) - { -@@ -3636,6 +3714,9 @@ void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash - // incremental update anymore, because with that we might miss some txs altogether. - void wallet2::update_pool_state(std::vector> &process_txs, bool refreshed, bool try_incremental) - { -+ process_txs.clear(); -+ if (m_background_syncing) -+ return; - bool updated = false; - if (m_pool_info_query_time != 0 && try_incremental) - { -@@ -4262,6 +4343,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo - } - - m_first_refresh_done = true; -+ if (m_background_syncing || m_is_background_wallet) -+ m_background_sync_data.first_refresh_done = true; - - LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all(false)) << ", unlocked: " << print_money(unlocked_balance_all(false))); - } -@@ -4347,6 +4430,14 @@ wallet2::detached_blockchain_data wallet2::detach_blockchain(uint64_t height, st - td.m_uses.pop_back(); - } - -+ for (auto it = m_background_sync_data.txs.begin(); it != m_background_sync_data.txs.end(); ) -+ { -+ if(height <= it->second.height) -+ it = m_background_sync_data.txs.erase(it); -+ else -+ ++it; -+ } -+ - if (output_tracker_cache) - output_tracker_cache->clear(); - -@@ -4421,8 +4512,12 @@ void wallet2::handle_reorg(uint64_t height, std::map m_blockchain.offset(), - error::wallet_internal_error, "Daemon claims reorg below last checkpoint"); -+ - detached_blockchain_data dbd = detach_blockchain(height, output_tracker_cache); - -+ if (m_background_syncing && height < m_background_sync_data.start_height) -+ m_background_sync_data.start_height = height; -+ - if (m_callback) - m_callback->on_reorg(height, dbd.detached_blockchain.size(), dbd.detached_tx_hashes.size()); - } -@@ -4432,6 +4527,7 @@ bool wallet2::deinit() - if(m_is_initialized) { - m_is_initialized = false; - unlock_keys_file(); -+ unlock_background_keys_file(); - m_account.deinit(); - } - return true; -@@ -4458,6 +4554,7 @@ bool wallet2::clear() - m_device_last_key_image_sync = 0; - m_pool_info_query_time = 0; - m_skip_to_height = 0; -+ m_background_sync_data = background_sync_data_t{}; - return true; - } - //---------------------------------------------------------------------------------------------------- -@@ -4476,13 +4573,30 @@ void wallet2::clear_soft(bool keep_key_images) - m_scanned_pool_txs[1].clear(); - m_pool_info_query_time = 0; - m_skip_to_height = 0; -+ m_background_sync_data = background_sync_data_t{}; - - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); - } -- -+//---------------------------------------------------------------------------------------------------- -+void wallet2::clear_user_data() -+{ -+ for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i) -+ i->second.m_dests.clear(); -+ for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i) -+ i->second.m_dests.clear(); -+ for (auto i = m_transfers.begin(); i != m_transfers.end(); ++i) -+ i->m_frozen = false; -+ m_tx_keys.clear(); -+ m_tx_notes.clear(); -+ m_address_book.clear(); -+ m_subaddress_labels.clear(); -+ m_attributes.clear(); -+ m_account_tags = std::pair, std::vector>(); -+} -+//---------------------------------------------------------------------------------------------------- - /*! - * \brief Stores wallet information to wallet file. - * \param keys_file_name Name of wallet file -@@ -4494,16 +4608,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable - { - boost::optional keys_file_data = get_keys_file_data(password, watch_only); - CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); -- -+ return store_keys_file_data(keys_file_name, keys_file_data.get()); -+} -+//---------------------------------------------------------------------------------------------------- -+bool wallet2::store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only, bool background_keys_file) -+{ -+ boost::optional keys_file_data = get_keys_file_data(key, watch_only, background_keys_file); -+ CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data"); -+ return store_keys_file_data(keys_file_name, keys_file_data.get(), background_keys_file); -+} -+//---------------------------------------------------------------------------------------------------- -+bool wallet2::store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file) -+{ - std::string tmp_file_name = keys_file_name + ".new"; - std::string buf; -- bool r = ::serialization::dump_binary(keys_file_data.get(), buf); -+ bool r = ::serialization::dump_binary(keys_file_data, buf); - r = r && save_to_file(tmp_file_name, buf); - CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name); - -- unlock_keys_file(); -+ if (!background_keys_file) -+ unlock_keys_file(); -+ else -+ unlock_background_keys_file(); -+ - std::error_code e = tools::replace_file(tmp_file_name, keys_file_name); -- lock_keys_file(); -+ -+ if (!background_keys_file) -+ lock_keys_file(); -+ else -+ lock_background_keys_file(keys_file_name); - - if (e) { - boost::filesystem::remove(tmp_file_name); -@@ -4515,26 +4648,27 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable - } - //---------------------------------------------------------------------------------------------------- - boost::optional wallet2::get_keys_file_data(const epee::wipeable_string& password, bool watch_only) -+{ -+ crypto::chacha_key key; -+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); -+ verify_password_with_cached_key(key); -+ return get_keys_file_data(key, watch_only); -+} -+//---------------------------------------------------------------------------------------------------- -+boost::optional wallet2::get_keys_file_data(const crypto::chacha_key& key, bool watch_only, bool background_keys_file) - { - epee::byte_slice account_data; - std::string multisig_signers; - std::string multisig_derivations; - cryptonote::account_base account = m_account; - -- crypto::chacha_key key; -- crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); -- -- // We use m_cache_key as a deterministic test to see if given key corresponds to original password -- const crypto::chacha_key cache_key = derive_cache_key(key); -- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); -- - if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) - { - account.encrypt_viewkey(key); - account.decrypt_keys(key); - } - -- if (watch_only) -+ if (watch_only || background_keys_file) - account.forget_spend_key(); - - account.encrypt_keys(key); -@@ -4669,6 +4803,9 @@ boost::optional wallet2::get_keys_file_data(const epee: - value2.SetInt(m_track_uses ? 1 : 0); - json.AddMember("track_uses", value2, json.GetAllocator()); - -+ value2.SetInt(m_background_sync_type); -+ json.AddMember("background_sync_type", value2, json.GetAllocator()); -+ - value2.SetInt(m_show_wallet_name_when_locked ? 1 : 0); - json.AddMember("show_wallet_name_when_locked", value2, json.GetAllocator()); - -@@ -4731,6 +4868,11 @@ boost::optional wallet2::get_keys_file_data(const epee: - - 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(m_custom_background_key.get().data()), m_custom_background_key.get().size()); -+ json.AddMember("custom_background_key", value, json.GetAllocator()); -+ } - - // Serialize the JSON object - rapidjson::StringBuffer buffer; -@@ -4758,13 +4900,81 @@ void wallet2::setup_keys(const epee::wipeable_string &password) - m_account.decrypt_viewkey(key); - } - -- m_cache_key = derive_cache_key(key); -+ m_cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); - - get_ringdb_key(); - } - //---------------------------------------------------------------------------------------------------- -+void validate_background_cache_password_usage(const tools::wallet2::BackgroundSyncType background_sync_type, const boost::optional &background_cache_password, const bool multisig, const bool watch_only, const bool key_on_device) -+{ -+ THROW_WALLET_EXCEPTION_IF(multisig || watch_only || key_on_device, error::wallet_internal_error, multisig -+ ? "Background sync not implemented for multisig wallets" : watch_only -+ ? "Background sync not implemented for view only wallets" -+ : "Background sync not implemented for HW wallets"); -+ -+ switch (background_sync_type) -+ { -+ case tools::wallet2::BackgroundSyncOff: -+ { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "background sync is not enabled"); -+ break; -+ } -+ case tools::wallet2::BackgroundSyncReusePassword: -+ { -+ THROW_WALLET_EXCEPTION_IF(background_cache_password, error::wallet_internal_error, -+ "unexpected custom background cache password"); -+ break; -+ } -+ case tools::wallet2::BackgroundSyncCustomPassword: -+ { -+ THROW_WALLET_EXCEPTION_IF(!background_cache_password, error::wallet_internal_error, -+ "expected custom background cache password"); -+ break; -+ } -+ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); -+ } -+} -+//---------------------------------------------------------------------------------------------------- -+void get_custom_background_key(const epee::wipeable_string &password, crypto::chacha_key &custom_background_key, const uint64_t kdf_rounds) -+{ -+ crypto::chacha_key key; -+ crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds); -+ custom_background_key = derive_cache_key(key, config::HASH_KEY_BACKGROUND_KEYS_FILE); -+} -+//---------------------------------------------------------------------------------------------------- -+const crypto::chacha_key wallet2::get_cache_key() -+{ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing) -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ // Domain separate keys used to encrypt background keys file and cache -+ return derive_cache_key(m_custom_background_key.get(), config::HASH_KEY_BACKGROUND_CACHE); -+ } -+ else -+ { -+ return m_cache_key; -+ } -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::verify_password_with_cached_key(const epee::wipeable_string &password) -+{ -+ crypto::chacha_key key; -+ crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds); -+ verify_password_with_cached_key(key); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::verify_password_with_cached_key(const crypto::chacha_key &key) -+{ -+ // We use m_cache_key as a deterministic test to see if given key corresponds to original password -+ const crypto::chacha_key cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE); -+ THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); -+} -+//---------------------------------------------------------------------------------------------------- - void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password) - { -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "cannot change password from background wallet"); -+ - if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) - decrypt_keys(original_password); - setup_keys(new_password); -@@ -4823,8 +5033,24 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - std::string account_data; - account_data.resize(keys_file_data.account_data.size()); - crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) -+ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); -+ if (try_v0_format) - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -+ -+ // Check if it's a background keys file if both of the above formats fail -+ { -+ m_is_background_wallet = false; -+ m_background_syncing = false; -+ cryptonote::account_base account_data_check; -+ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) -+ { -+ get_custom_background_key(password, key, m_kdf_rounds); -+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -+ m_is_background_wallet = !json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); -+ m_background_syncing = m_is_background_wallet; // start a background wallet background syncing -+ } -+ } -+ - // The contents should be JSON if the wallet follows the new format. - if (json.Parse(account_data.c_str()).HasParseError()) - { -@@ -4862,7 +5088,8 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_ignore_outputs_above = MONEY_SUPPLY; - m_ignore_outputs_below = 0; - m_track_uses = false; -- m_show_wallet_name_when_locked = true; -+ m_background_sync_type = BackgroundSyncOff; -+ m_show_wallet_name_when_locked = false; - m_show_detailed_prompt = true; - m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT; - m_setup_background_mining = BackgroundMiningNo; -@@ -4881,6 +5108,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_enable_multisig = false; - m_allow_mismatched_daemon_version = false; - m_polyseed = false; -+ m_custom_background_key = boost::none; - } - else if(json.IsObject()) - { -@@ -5121,6 +5349,39 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - m_enable_multisig = field_enable_multisig; - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, polyseed, int, Int, false, false); - m_polyseed = field_polyseed; -+ -+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff); -+ m_background_sync_type = field_background_sync_type; -+ -+ // Load encryption key used to encrypt background cache -+ crypto::chacha_key custom_background_key; -+ m_custom_background_key = boost::none; -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_is_background_wallet) -+ { -+ if (!json.HasMember("custom_background_key")) -+ { -+ LOG_ERROR("Field custom_background_key not found in JSON"); -+ return false; -+ } -+ else if (!json["custom_background_key"].IsString()) -+ { -+ LOG_ERROR("Field custom_background_key found in JSON, but not String"); -+ return false; -+ } -+ else if (json["custom_background_key"].GetStringLength() != sizeof(crypto::chacha_key)) -+ { -+ LOG_ERROR("Field custom_background_key found in JSON, but not correct length"); -+ return false; -+ } -+ const char *field_custom_background_key = json["custom_background_key"].GetString(); -+ memcpy(custom_background_key.data(), field_custom_background_key, sizeof(crypto::chacha_key)); -+ m_custom_background_key = boost::optional(custom_background_key); -+ LOG_PRINT_L1("Loaded custom background key derived from custom password"); -+ } -+ else if (json.HasMember("custom_background_key")) -+ { -+ LOG_ERROR("Unexpected field custom_background_key found in JSON"); -+ } - } - else - { -@@ -5184,12 +5445,17 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - const cryptonote::account_keys& keys = m_account.get_keys(); - hw::device &hwdev = m_account.get_device(); - r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); -- if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD) -+ if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD && !m_is_background_wallet) - r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); - - if (r) -- setup_keys(password); -+ { -+ if (!m_is_background_wallet) -+ setup_keys(password); -+ else -+ m_custom_background_key = boost::optional(key); -+ } - - return true; - } -@@ -5204,11 +5470,12 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st - * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password - * - */ --bool wallet2::verify_password(const epee::wipeable_string& password) -+bool wallet2::verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out) - { - // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded). - unlock_keys_file(); -- bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds); -+ const bool no_spend_key = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; -+ bool r = verify_password(m_keys_file, password, no_spend_key, m_account.get_device(), m_kdf_rounds, spend_key_out); - lock_keys_file(); - return r; - } -@@ -5226,7 +5493,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) - * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password - * - */ --bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) -+bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out) - { - rapidjson::Document json; - wallet2::keys_file_data keys_file_data; -@@ -5243,9 +5510,22 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip - std::string account_data; - account_data.resize(keys_file_data.account_data.size()); - crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -- if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) -+ const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject(); -+ if (try_v0_format) - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); - -+ // Check if it's a background keys file if both of the above formats fail -+ { -+ cryptonote::account_base account_data_check; -+ if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data)) -+ { -+ get_custom_background_key(password, key, kdf_rounds); -+ crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); -+ const bool is_background_wallet = json.Parse(account_data.c_str()).HasParseError() && json.IsObject(); -+ no_spend_key = no_spend_key || is_background_wallet; -+ } -+ } -+ - // The contents should be JSON if the wallet follows the new format. - if (json.Parse(account_data.c_str()).HasParseError()) - { -@@ -5270,6 +5550,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip - r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); - if(!no_spend_key) - r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); -+ spend_key_out = (!no_spend_key && r) ? keys.m_spend_secret_key : crypto::null_skey; - return r; - } - -@@ -5281,9 +5562,7 @@ void wallet2::encrypt_keys(const crypto::chacha_key &key) - - void wallet2::decrypt_keys(const crypto::chacha_key &key) - { -- // We use m_cache_key as a deterministic test to see if given key corresponds to original password -- const crypto::chacha_key cache_key = derive_cache_key(key); -- THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password); -+ verify_password_with_cached_key(key); - - m_account.encrypt_viewkey(key); - m_account.decrypt_keys(key); -@@ -6001,11 +6280,30 @@ void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_strin - { - if (wallet_name.empty()) - return; -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "cannot change wallet settings from background wallet"); - prepare_file_names(wallet_name); - boost::system::error_code ignored_ec; - THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file); - bool r = store_keys(m_keys_file, password, m_watch_only); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); -+ -+ // Update the background keys file when we rewrite the main wallet keys file -+ if (m_background_sync_type == BackgroundSyncCustomPassword && m_custom_background_key) -+ { -+ const std::string background_keys_filename = make_background_keys_file_name(wallet_name); -+ if (!lock_background_keys_file(background_keys_filename)) -+ { -+ LOG_ERROR("Background keys file " << background_keys_filename << " is opened by another wallet program and cannot be rewritten"); -+ return; // not fatal, background keys file will just have different wallet settings -+ } -+ store_background_keys(m_custom_background_key.get()); -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); -+ } -+ else if (m_background_sync_type == BackgroundSyncReusePassword) -+ { -+ reset_background_sync_data(m_background_sync_data); -+ } - } - /*! - * \brief Writes to a file named based on the normal wallet (doesn't generate key, assumes it's already there) -@@ -6348,6 +6646,16 @@ bool wallet2::wallet_valid_path_format(const std::string& file_path) - return !file_path.empty(); - } - //---------------------------------------------------------------------------------------------------- -+std::string wallet2::make_background_wallet_file_name(const std::string &wallet_file) -+{ -+ return wallet_file + BACKGROUND_WALLET_SUFFIX; -+} -+//---------------------------------------------------------------------------------------------------- -+std::string wallet2::make_background_keys_file_name(const std::string &wallet_file) -+{ -+ return make_background_wallet_file_name(wallet_file) + ".keys"; -+} -+//---------------------------------------------------------------------------------------------------- - bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) - { - cryptonote::blobdata payment_id_data; -@@ -6583,10 +6891,81 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, "failed to load keys from buffer"); - } - -- wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password); -+ wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_is_background_wallet, password); - - //keys loaded ok! - //try to load wallet cache. but even if we failed, it is not big problem -+ load_wallet_cache(use_fs, cache_buf); -+ -+ if (!m_persistent_rpc_client_id) -+ set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); -+ -+ // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. -+ // Here we erase these multisig keys if they're zero'd out to free up space. -+ for (auto &td : m_transfers) -+ { -+ auto mk_it = td.m_multisig_k.begin(); -+ while (mk_it != td.m_multisig_k.end()) -+ { -+ if (*mk_it == rct::zero()) -+ mk_it = td.m_multisig_k.erase(mk_it); -+ else -+ ++mk_it; -+ } -+ } -+ -+ cryptonote::block genesis; -+ generate_genesis(genesis); -+ crypto::hash genesis_hash = get_block_hash(genesis); -+ -+ if (m_blockchain.empty()) -+ { -+ m_blockchain.push_back(genesis_hash); -+ m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); -+ } -+ else -+ { -+ check_genesis(genesis_hash); -+ } -+ -+ trim_hashchain(); -+ -+ if (get_num_subaddress_accounts() == 0) -+ add_subaddress_account(tr("Primary account")); -+ -+ try -+ { -+ find_and_save_rings(false); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to save rings, will try again next time"); -+ } -+ -+ try -+ { -+ if (use_fs) -+ m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to initialize MMS, it will be unusable"); -+ } -+ -+ try -+ { -+ if (use_fs) -+ process_background_cache_on_open(); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to process background cache on open: " << e.what()); -+ } -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::load_wallet_cache(const bool use_fs, const std::string& cache_buf) -+{ -+ boost::system::error_code e; - bool cache_missing = use_fs ? (!boost::filesystem::exists(m_wallet_file, e) || e) : cache_buf.empty(); - if (cache_missing) - { -@@ -6600,7 +6979,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - bool r = true; - if (use_fs) - { -- load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); -+ r = load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits::max()); - THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); - } - -@@ -6613,7 +6992,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"'); - std::string cache_data; - cache_data.resize(cache_file_data.cache_data.size()); -- crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]); -+ crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), get_cache_key(), cache_file_data.iv, &cache_data[0]); - - try { - bool loaded = false; -@@ -6703,60 +7082,76 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass - m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key, - error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); - } -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::process_background_cache_on_open() -+{ -+ if (m_wallet_file.empty()) -+ return; -+ if (m_background_syncing || m_is_background_wallet) -+ return; -+ if (m_background_sync_type == BackgroundSyncOff) -+ return; - -- if (!m_persistent_rpc_client_id) -- set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); -+ if (m_background_sync_type == BackgroundSyncReusePassword) -+ { -+ const background_sync_data_t background_sync_data = m_background_sync_data; -+ const hashchain blockchain = m_blockchain; -+ process_background_cache(background_sync_data, blockchain, m_last_block_reward); - -- // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks. -- // Here we erase these multisig keys if they're zero'd out to free up space. -- for (auto &td : m_transfers) -+ // Reset the background cache after processing -+ reset_background_sync_data(m_background_sync_data); -+ } -+ else if (m_background_sync_type == BackgroundSyncCustomPassword) - { -- auto mk_it = td.m_multisig_k.begin(); -- while (mk_it != td.m_multisig_k.end()) -+ // If the background wallet files don't exist, recreate them -+ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); -+ const std::string background_wallet_file = make_background_wallet_file_name(m_wallet_file); -+ const bool background_keys_file_exists = boost::filesystem::exists(background_keys_file); -+ const bool background_wallet_exists = boost::filesystem::exists(background_wallet_file); -+ -+ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_keys_file), error::background_wallet_already_open, background_wallet_file); -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ -+ if (!background_keys_file_exists) - { -- if (*mk_it == rct::zero()) -- mk_it = td.m_multisig_k.erase(mk_it); -- else -- ++mk_it; -+ MDEBUG("Background keys file not found, restoring"); -+ store_background_keys(m_custom_background_key.get()); - } -- } - -- cryptonote::block genesis; -- generate_genesis(genesis); -- crypto::hash genesis_hash = get_block_hash(genesis); -+ if (!background_wallet_exists) -+ { -+ MDEBUG("Background cache not found, restoring"); -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); -+ return; -+ } - -- if (m_blockchain.empty()) -- { -- m_blockchain.push_back(genesis_hash); -- m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx); -- } -- else -- { -- check_genesis(genesis_hash); -- } -+ MDEBUG("Loading background cache"); - -- trim_hashchain(); -+ // Set up a minimal background wallet2 instance -+ std::unique_ptr background_w2(new wallet2(m_nettype)); -+ background_w2->m_is_background_wallet = true; -+ background_w2->m_background_syncing = true; -+ background_w2->m_background_sync_type = m_background_sync_type; -+ background_w2->m_custom_background_key = m_custom_background_key; - -- if (get_num_subaddress_accounts() == 0) -- add_subaddress_account(tr("Primary account")); -+ cryptonote::account_base account = m_account; -+ account.forget_spend_key(); -+ background_w2->m_account = account; - -- try -- { -- find_and_save_rings(false); -- } -- catch (const std::exception &e) -- { -- MERROR("Failed to save rings, will try again next time"); -- } -- -- try -- { -- if (use_fs) -- m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats); -+ // Load background cache from file -+ background_w2->clear(); -+ background_w2->prepare_file_names(background_wallet_file); -+ background_w2->load_wallet_cache(true/*use_fs*/); -+ -+ process_background_cache(background_w2->m_background_sync_data, background_w2->m_blockchain, background_w2->m_last_block_reward); -+ -+ // Reset the background cache after processing -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); - } -- catch (const std::exception &e) -+ else - { -- MERROR("Failed to initialize MMS, it will be unusable"); -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); - } - } - //---------------------------------------------------------------------------------------------------- -@@ -6838,6 +7233,8 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas - same_file = canonical_old_path == canonical_new_path; - } - -+ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet && !same_file, error::wallet_internal_error, -+ "Cannot save background wallet files to a different location"); - - if (!same_file) - { -@@ -6854,6 +7251,21 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas - } - } - } -+ else if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing && !m_is_background_wallet) -+ { -+ // We're background syncing, so store the wallet cache as a background cache -+ // keeping the background sync data -+ try -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ store_background_cache(m_custom_background_key.get(), false/*do_reset_background_sync_data*/); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to store background cache while background syncing: " << e.what()); -+ } -+ return; -+ } - - // get wallet cache data - boost::optional cache_file_data = get_cache_file_data(); -@@ -6947,6 +7359,22 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas - // store should only exist if the MMS is really active - m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file); - } -+ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_background_syncing && !m_is_background_wallet) -+ { -+ // Update the background wallet cache when we store the main wallet cache -+ // Note: if background syncing when this is called, it means the background -+ // wallet is open and was already stored above -+ try -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set"); -+ store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/); -+ } -+ catch (const std::exception &e) -+ { -+ MERROR("Failed to update background cache: " << e.what()); -+ } -+ } - } - //---------------------------------------------------------------------------------------------------- - boost::optional wallet2::get_cache_file_data() -@@ -6964,7 +7392,7 @@ boost::optional wallet2::get_cache_file_data() - std::string cipher; - cipher.resize(cache_file_data.get().cache_data.size()); - cache_file_data.get().iv = crypto::rand(); -- crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), m_cache_key, cache_file_data.get().iv, &cipher[0]); -+ crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), get_cache_key(), cache_file_data.get().iv, &cipher[0]); - cache_file_data.get().cache_data = cipher; - return cache_file_data; - } -@@ -9034,6 +9462,34 @@ bool wallet2::is_keys_file_locked() const - return m_keys_file_locker->locked(); - } - -+bool wallet2::lock_background_keys_file(const std::string &background_keys_file) -+{ -+ if (background_keys_file.empty() || !boost::filesystem::exists(background_keys_file)) -+ return true; -+ if (m_background_keys_file_locker && m_background_keys_file_locker->locked()) -+ return true; -+ m_background_keys_file_locker.reset(new tools::file_locker(background_keys_file)); -+ return m_background_keys_file_locker->locked(); -+} -+ -+bool wallet2::unlock_background_keys_file() -+{ -+ if (!m_background_keys_file_locker) -+ { -+ MDEBUG("background keys file locker is not set"); -+ return false; -+ } -+ m_background_keys_file_locker.reset(); -+ return true; -+} -+ -+bool wallet2::is_background_keys_file_locked() const -+{ -+ if (!m_background_keys_file_locker) -+ return false; -+ return m_background_keys_file_locker->locked(); -+} -+ - bool wallet2::tx_add_fake_output(std::vector> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set &valid_public_keys_cache) const - { - if (!unlocked) // don't add locked outs -@@ -14380,6 +14836,413 @@ bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool o - return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none); - } - -+/* -+ In background sync mode, we use just the view key when the wallet is scanning -+ to identify all txs where: -+ -+ 1. We received an output. -+ 2. We spent an output. -+ 3. We *may* have spent a received output but we didn't know for sure because -+ the spend key was not loaded while background sync was enabled. -+ -+ When the user is ready to use the spend key again, we call this function to -+ process all those background synced transactions with the spend key loaded, -+ so that we can properly generate key images for the transactions which we -+ we were not able to do so for while background sync was enabled. This allows -+ us to determine *all* receives and spends the user completed while the wallet -+ had background sync enabled. Once this function completes, we can continue -+ scanning from where the background sync left off. -+ -+ Txs of type 3 (txs which we *may* have spent received output(s)) are txs where -+ 1+ rings contain an output that the user received and the wallet does not know -+ the associated key image for that output. We don't know if the user spent in -+ this type of tx or not. This function will generate key images for all outputs -+ we don't know key images for, and then check if those outputs were spent in -+ the txs of type 3. -+ -+ By storing this type of "plausible spend tx" when scanning in background sync -+ mode, we avoid the need to query the daemon with key images when background -+ sync mode is disabled to see if those key images were spent. This would -+ reveal key images to 3rd party nodes for users who don't run their own. -+ Although this is not a perfect solution to avoid revealing key images to a 3rd -+ party node (since tx submission trivially reveals key images to a node), it's -+ probably better than revealing *unused* key images to a 3rd party node, which -+ would enable the 3rd party to deduce that a tx is spending an output at least -+ X old when the key image is included in the chain. -+*/ -+void wallet2::process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_synced_chain, uint64_t last_block_reward) -+{ -+ // We expect the spend key to be in a decrypted state while -+ // m_processing_background_cache is true -+ m_processing_background_cache = true; -+ auto done_processing = epee::misc_utils::create_scope_leave_handler([&, this]() { -+ m_processing_background_cache = false; -+ }); -+ -+ if (m_background_syncing || m_multisig || m_watch_only || key_on_device()) -+ return; -+ -+ if (!background_sync_data.first_refresh_done) -+ { -+ MDEBUG("Skipping processing background cache, background cache has not synced yet"); -+ return; -+ } -+ -+ // Skip processing if wallet cache is synced higher than background cache -+ const uint64_t current_height = m_blockchain.size(); -+ const uint64_t background_height = background_synced_chain.size(); -+ MDEBUG("Background cache height " << background_height << " , wallet height " << current_height); -+ if (current_height > background_height) -+ { -+ MWARNING("Skipping processing background cache, synced height is higher than background cache"); -+ return; -+ } -+ -+ if (m_refresh_from_block_height < background_sync_data.wallet_refresh_from_block_height || -+ m_subaddress_lookahead_major > background_sync_data.subaddress_lookahead_major || -+ m_subaddress_lookahead_minor > background_sync_data.subaddress_lookahead_minor || -+ m_refresh_type < background_sync_data.wallet_refresh_type) -+ { -+ MWARNING("Skipping processing background cache, background wallet sync settings did not match main wallet's"); -+ MDEBUG("Wallet settings: " << -+ ", m_refresh_from_block_height: " << m_refresh_from_block_height << " vs " << background_sync_data.wallet_refresh_from_block_height << -+ ", m_subaddress_lookahead_major: " << m_subaddress_lookahead_major << " vs " << background_sync_data.subaddress_lookahead_major << -+ ", m_subaddress_lookahead_minor: " << m_subaddress_lookahead_minor << " vs " << background_sync_data.subaddress_lookahead_minor << -+ ", m_refresh_type: " << m_refresh_type << " vs " << background_sync_data.wallet_refresh_type); -+ return; -+ } -+ -+ // Sort background synced txs in the order they appeared in the cache so that -+ // we process them in the order they appeared in the chain. Thus if tx2 spends -+ // from tx1, we will know because tx1 is processed before tx2. -+ std::vector> sorted_bgs_cache(background_sync_data.txs.begin(), background_sync_data.txs.end()); -+ std::sort(sorted_bgs_cache.begin(), sorted_bgs_cache.end(), -+ [](const std::pair& l, const std::pair& r) -+ { -+ uint64_t left_index = l.second.index_in_background_sync_data; -+ uint64_t right_index = r.second.index_in_background_sync_data; -+ THROW_WALLET_EXCEPTION_IF( -+ (left_index < right_index && l.second.height > r.second.height) || -+ (left_index > right_index && l.second.height < r.second.height), -+ error::wallet_internal_error, "Unexpected background sync data order"); -+ return left_index < right_index; -+ }); -+ -+ // All txs in the background cache should have height >= sync start height, -+ // but not fatal if not -+ if (!sorted_bgs_cache.empty() && sorted_bgs_cache[0].second.height < background_sync_data.start_height) -+ MWARNING("First tx in background cache has height (" << sorted_bgs_cache[0].second.height << ") lower than sync start height (" << background_sync_data.start_height << ")"); -+ -+ // We want to process all background synced txs in order to make sure -+ // the wallet state updates correctly. First we remove all txs from the wallet -+ // from before the background sync start height, then re-process them in -+ // chronological order. The background cache should contain a superset of -+ // *all* the wallet's txs from after the background sync start height. -+ MDEBUG("Processing " << background_sync_data.txs.size() << " background synced txs starting from height " << background_sync_data.start_height); -+ detached_blockchain_data dbd = detach_blockchain(background_sync_data.start_height); -+ -+ for (const auto &bgs_tx : sorted_bgs_cache) -+ { -+ MDEBUG("Processing background synced tx " << bgs_tx.first); -+ -+ process_new_transaction(bgs_tx.first, bgs_tx.second.tx, bgs_tx.second.output_indices, bgs_tx.second.height, 0, bgs_tx.second.block_timestamp, -+ cryptonote::is_coinbase(bgs_tx.second.tx), false/*pool*/, bgs_tx.second.double_spend_seen, {}, {}, true/*ignore_callbacks*/); -+ -+ // Re-set destination addresses if they were previously set -+ if (m_confirmed_txs.find(bgs_tx.first) != m_confirmed_txs.end() && -+ dbd.detached_confirmed_txs_dests.find(bgs_tx.first) != dbd.detached_confirmed_txs_dests.end()) -+ { -+ m_confirmed_txs[bgs_tx.first].m_dests = std::move(dbd.detached_confirmed_txs_dests[bgs_tx.first]); -+ } -+ } -+ -+ m_blockchain = background_synced_chain; -+ m_last_block_reward = last_block_reward; -+ -+ MDEBUG("Finished processing background sync data"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::reset_background_sync_data(background_sync_data_t &background_sync_data) -+{ -+ background_sync_data.first_refresh_done = false; -+ background_sync_data.start_height = get_blockchain_current_height(); -+ background_sync_data.txs.clear(); -+ -+ background_sync_data.wallet_refresh_from_block_height = m_refresh_from_block_height; -+ background_sync_data.subaddress_lookahead_major = m_subaddress_lookahead_major; -+ background_sync_data.subaddress_lookahead_minor = m_subaddress_lookahead_minor; -+ background_sync_data.wallet_refresh_type = m_refresh_type; -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data) -+{ -+ MDEBUG("Storing background cache (do_reset_background_sync_data=" << do_reset_background_sync_data << ")"); -+ -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, -+ "Can only write a background cache when using a custom background password"); -+ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, -+ "No wallet file known, can't store background cache"); -+ -+ std::unique_ptr background_w2(new wallet2(m_nettype)); -+ background_w2->prepare_file_names(make_background_wallet_file_name(m_wallet_file)); -+ -+ // Make sure background wallet is opened by this wallet -+ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_w2->m_keys_file), -+ error::background_wallet_already_open, background_w2->m_wallet_file); -+ -+ // Load a background wallet2 instance using this wallet2 instance -+ std::string this_wallet2; -+ bool r = ::serialization::dump_binary(*this, this_wallet2); -+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to serialize wallet cache"); -+ -+ background_w2->clear(); -+ r = ::serialization::parse_binary(this_wallet2, *background_w2); -+ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to deserialize wallet cache"); -+ -+ // Clear sensitive data from background cache not needed to sync -+ background_w2->clear_user_data(); -+ -+ background_w2->m_is_background_wallet = true; -+ if (do_reset_background_sync_data) -+ reset_background_sync_data(background_w2->m_background_sync_data); -+ else -+ background_w2->m_background_sync_data = m_background_sync_data; -+ background_w2->m_background_syncing = true; -+ -+ background_w2->m_custom_background_key = boost::optional(custom_background_key); -+ background_w2->m_background_sync_type = m_background_sync_type; -+ background_w2->store(); -+ -+ MDEBUG("Background cache stored (" << background_w2->m_transfers.size() << " transfers, " << background_w2->m_background_sync_data.txs.size() << " background synced txs)"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::store_background_keys(const crypto::chacha_key &custom_background_key) -+{ -+ MDEBUG("Storing background keys"); -+ -+ THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error, -+ "No wallet file known, can't store background keys"); -+ -+ const std::string background_keys_file = make_background_keys_file_name(m_wallet_file); -+ bool r = store_keys(background_keys_file, custom_background_key, false/*watch_only*/, true/*background_keys_file*/); -+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, background_keys_file); -+ THROW_WALLET_EXCEPTION_IF(!is_background_keys_file_locked(), error::wallet_internal_error, background_keys_file + "\" should be locked"); -+ -+ // GUI uses the address file to differentiate non-mainnet wallets in the UI -+ const std::string background_address_file = make_background_wallet_file_name(m_wallet_file) + ".address.txt"; -+ if (m_nettype != MAINNET && !boost::filesystem::exists(background_address_file)) -+ { -+ r = save_to_file(background_address_file, m_account.get_public_address_str(m_nettype), true); -+ if (!r) MERROR("String with address text not saved"); -+ } -+ -+ MDEBUG("Background keys stored"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password) -+{ -+ MDEBUG("Storing background sync wallet"); -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error, -+ "Can only write a background sync wallet when using a custom background password"); -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "Can't write background sync wallet from an existing background cache"); -+ THROW_WALLET_EXCEPTION_IF(wallet_password == background_cache_password, -+ error::background_custom_password_same_as_wallet_password); -+ -+ // Set the background encryption key -+ crypto::chacha_key custom_background_key; -+ get_custom_background_key(background_cache_password, custom_background_key, m_kdf_rounds); -+ -+ // Keep the background encryption key in memory so the main wallet can update -+ // the background cache when it stores the main wallet cache -+ m_custom_background_key = boost::optional(custom_background_key); -+ -+ if (m_wallet_file.empty() || m_keys_file.empty()) -+ return; -+ -+ // Save background keys file, then background cache, then update main wallet settings -+ store_background_keys(custom_background_key); -+ store_background_cache(custom_background_key, true/*do_reset_background_sync_data*/); -+ bool r = store_keys(m_keys_file, wallet_password, false/*watch_only*/); -+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file); -+ -+ MDEBUG("Background sync wallet saved successfully"); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password) -+{ -+ MDEBUG("Setting background sync to type " << background_sync_type); -+ THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error, -+ "Can't set background sync type from an existing background cache"); -+ verify_password_with_cached_key(wallet_password); -+ -+ if (background_sync_type != BackgroundSyncOff) -+ validate_background_cache_password_usage(background_sync_type, background_cache_password, m_multisig, m_watch_only, key_on_device()); -+ -+ THROW_WALLET_EXCEPTION_IF(background_sync_type == BackgroundSyncCustomPassword && wallet_password == background_cache_password, -+ error::background_custom_password_same_as_wallet_password); -+ -+ if (m_background_sync_type == background_sync_type && background_sync_type != BackgroundSyncCustomPassword) -+ return; // No need to make any changes -+ -+ if (!m_wallet_file.empty()) -+ { -+ // Delete existing background files if they already exist -+ const std::string old_background_wallet_file = make_background_wallet_file_name(m_wallet_file); -+ const std::string old_background_keys_file = make_background_keys_file_name(m_wallet_file); -+ const std::string old_background_address_file = old_background_wallet_file + ".address.txt"; -+ -+ // Make sure no other program is using the background wallet -+ THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(old_background_keys_file), -+ error::background_wallet_already_open, old_background_wallet_file); -+ -+ if (boost::filesystem::exists(old_background_wallet_file)) -+ if (!boost::filesystem::remove(old_background_wallet_file)) -+ LOG_ERROR("Error deleting background wallet file: " << old_background_wallet_file); -+ -+ if (boost::filesystem::exists(old_background_keys_file)) -+ if (!boost::filesystem::remove(old_background_keys_file)) -+ LOG_ERROR("Error deleting background keys file: " << old_background_keys_file); -+ -+ if (boost::filesystem::exists(old_background_address_file)) -+ if (!boost::filesystem::remove(old_background_address_file)) -+ LOG_ERROR("Error deleting background address file: " << old_background_address_file); -+ } -+ -+ m_background_sync_type = background_sync_type; -+ m_custom_background_key = boost::none; -+ -+ // Write the new files -+ switch (background_sync_type) -+ { -+ case BackgroundSyncOff: -+ case BackgroundSyncReusePassword: rewrite(m_wallet_file, wallet_password); break; -+ case BackgroundSyncCustomPassword: write_background_sync_wallet(wallet_password, background_cache_password.get()); break; -+ default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type"); -+ } -+ -+ MDEBUG("Done setting background sync type"); -+} -+//---------------------------------------------------------------------------------------------------- -+/* -+ When background syncing, the wallet scans using just the view key, without -+ keeping the spend key in decrypted state. When a user returns to the wallet -+ and decrypts the spend key, the wallet processes the background synced txs, -+ then the wallet picks up scanning normally right where the background sync -+ left off. -+*/ -+void wallet2::start_background_sync() -+{ -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, -+ "must setup background sync first before using background sync"); -+ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, -+ "Can't start background syncing from a background wallet (it is always background syncing)"); -+ -+ MDEBUG("Starting background sync"); -+ -+ if (m_background_syncing) -+ { -+ MDEBUG("Already background syncing"); -+ return; -+ } -+ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) -+ { -+ // Save the current state of the wallet cache. Only necessary when using a -+ // custom background password which uses distinct background wallet to sync. -+ // When reusing wallet password to sync we reuse the main wallet cache. -+ store(); -+ -+ // Wipe user data from the background wallet cache not needed to sync. -+ // Only wipe user data from background cache if wallet cache is stored -+ // on disk; otherwise we could lose the data. -+ clear_user_data(); -+ -+ // Wipe m_cache_key since it can be used to decrypt main wallet cache -+ m_cache_key.scrub(); -+ } -+ -+ reset_background_sync_data(m_background_sync_data); -+ m_background_syncing = true; -+ -+ // Wipe the spend key from memory -+ m_account.forget_spend_key(); -+ -+ MDEBUG("Background sync started at height " << m_background_sync_data.start_height); -+} -+//---------------------------------------------------------------------------------------------------- -+void wallet2::stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key) -+{ -+ MDEBUG("Stopping background sync"); -+ -+ // Verify provided password and spend secret key. If no spend secret key is -+ // provided, recover it from the wallet keys file -+ crypto::secret_key recovered_spend_key = crypto::null_skey; -+ if (!m_wallet_file.empty()) -+ { -+ THROW_WALLET_EXCEPTION_IF(!verify_password(wallet_password, recovered_spend_key), error::invalid_password); -+ } -+ else -+ { -+ verify_password_with_cached_key(wallet_password); -+ } -+ -+ if (spend_secret_key != crypto::null_skey) -+ { -+ THROW_WALLET_EXCEPTION_IF(!m_wallet_file.empty() && spend_secret_key != recovered_spend_key, -+ error::invalid_spend_key); -+ MDEBUG("Setting spend secret key with the provided key"); -+ recovered_spend_key = spend_secret_key; -+ } -+ -+ // Verify private spend key derives to wallet's public spend key -+ const auto verify_spend_key = [this](crypto::secret_key &recovered_spend_key) -> bool -+ { -+ crypto::public_key spend_public_key; -+ return recovered_spend_key != crypto::null_skey && -+ crypto::secret_key_to_public_key(recovered_spend_key, spend_public_key) && -+ m_account.get_keys().m_account_address.m_spend_public_key == spend_public_key; -+ }; -+ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); -+ -+ THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error, -+ "must setup background sync first before using background sync"); -+ THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error, -+ "Can't stop background syncing from a background wallet"); -+ -+ if (!m_background_syncing) -+ return; -+ -+ // Copy background cache, we're about to overwrite it -+ const background_sync_data_t background_sync_data = m_background_sync_data; -+ const hashchain background_synced_chain = m_blockchain; -+ const uint64_t last_block_reward = m_last_block_reward; -+ -+ if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty()) -+ { -+ // Reload the wallet from disk -+ load(m_wallet_file, wallet_password); -+ THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key); -+ } -+ m_background_syncing = false; -+ -+ // Set the plaintext spend key -+ m_account.set_spend_key(recovered_spend_key); -+ -+ // Encrypt the spend key when done if needed -+ epee::misc_utils::auto_scope_leave_caller keys_reencryptor; -+ if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only) -+ keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]{encrypt_keys(wallet_password);}); -+ -+ // Now we can use the decrypted spend key to process background cache -+ process_background_cache(background_sync_data, background_synced_chain, last_block_reward); -+ -+ // Reset the background cache after processing -+ reset_background_sync_data(m_background_sync_data); -+ -+ MDEBUG("Background sync stopped"); -+} -+//---------------------------------------------------------------------------------------------------- - wallet2::payment_container wallet2::export_payments() const - { - payment_container payments; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index db5c1feb3..009dce766 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -257,6 +257,20 @@ private: - BackgroundMiningNo = 2, - }; - -+ enum BackgroundSyncType { -+ BackgroundSyncOff = 0, -+ BackgroundSyncReusePassword = 1, -+ BackgroundSyncCustomPassword = 2, -+ }; -+ -+ static BackgroundSyncType background_sync_type_from_str(const std::string &background_sync_type_str) -+ { -+ if (background_sync_type_str == "off") return BackgroundSyncOff; -+ if (background_sync_type_str == "reuse-wallet-password") return BackgroundSyncReusePassword; -+ if (background_sync_type_str == "custom-background-password") return BackgroundSyncCustomPassword; -+ throw std::logic_error("Unknown background sync type"); -+ }; -+ - enum ExportFormat { - Binary = 0, - Ascii, -@@ -283,7 +297,12 @@ private: - //! Just parses variables. - static std::unique_ptr make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function(const char *, bool)> &password_prompter); - -- static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); -+ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds) -+ { -+ crypto::secret_key spend_key = crypto::null_skey; -+ return verify_password(keys_file_name, password, no_spend_key, hwdev, kdf_rounds, spend_key); -+ }; -+ static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out); - static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1); - - wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false, std::unique_ptr http_client_factory = std::unique_ptr(new net::http::client_factory())); -@@ -793,6 +812,54 @@ private: - END_SERIALIZE() - }; - -+ struct background_synced_tx_t -+ { -+ uint64_t index_in_background_sync_data; -+ cryptonote::transaction tx; -+ std::vector output_indices; -+ uint64_t height; -+ uint64_t block_timestamp; -+ bool double_spend_seen; -+ -+ BEGIN_SERIALIZE_OBJECT() -+ VERSION_FIELD(0) -+ VARINT_FIELD(index_in_background_sync_data) -+ -+ // prune tx; don't need to keep signature data -+ if (!tx.serialize_base(ar)) -+ return false; -+ -+ FIELD(output_indices) -+ VARINT_FIELD(height) -+ VARINT_FIELD(block_timestamp) -+ FIELD(double_spend_seen) -+ END_SERIALIZE() -+ }; -+ -+ struct background_sync_data_t -+ { -+ bool first_refresh_done = false; -+ uint64_t start_height = 0; -+ serializable_unordered_map txs; -+ -+ // Relevant wallet settings -+ uint64_t wallet_refresh_from_block_height; -+ size_t subaddress_lookahead_major; -+ size_t subaddress_lookahead_minor; -+ RefreshType wallet_refresh_type; -+ -+ BEGIN_SERIALIZE_OBJECT() -+ VERSION_FIELD(0) -+ FIELD(first_refresh_done) -+ FIELD(start_height) -+ FIELD(txs) -+ FIELD(wallet_refresh_from_block_height) -+ VARINT_FIELD(subaddress_lookahead_major) -+ VARINT_FIELD(subaddress_lookahead_minor) -+ VARINT_FIELD(wallet_refresh_type) -+ END_SERIALIZE() -+ }; -+ - typedef std::tuple get_outs_entry; - - struct parsed_block -@@ -989,7 +1056,8 @@ private: - /*! - * \brief verifies given password is correct for default wallet keys file - */ -- bool verify_password(const epee::wipeable_string& password); -+ bool verify_password(const epee::wipeable_string& password) {crypto::secret_key key = crypto::null_skey; return verify_password(password, key);}; -+ bool verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out); - cryptonote::account_base& get_account(){return m_account;} - const cryptonote::account_base& get_account()const{return m_account;} - -@@ -1094,6 +1162,7 @@ private: - cryptonote::network_type nettype() const { return m_nettype; } - bool watch_only() const { return m_watch_only; } - bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const; -+ bool is_background_wallet() const { return m_is_background_wallet; } - bool has_multisig_partial_key_images() const; - bool has_unknown_key_images() const; - bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string()) const; -@@ -1301,11 +1370,17 @@ private: - return; - } - a & m_has_ever_refreshed_from_node; -+ if(ver < 31) -+ { -+ m_background_sync_data = background_sync_data_t{}; -+ return; -+ } -+ a & m_background_sync_data; - } - - BEGIN_SERIALIZE_OBJECT() - MAGIC_FIELD("wownero wallet cache") -- VERSION_FIELD(1) -+ VERSION_FIELD(2) - FIELD(m_blockchain) - FIELD(m_transfers) - FIELD(m_account_public_address) -@@ -1337,6 +1412,12 @@ private: - return true; - } - FIELD(m_has_ever_refreshed_from_node) -+ if (version < 2) -+ { -+ m_background_sync_data = background_sync_data_t{}; -+ return true; -+ } -+ FIELD(m_background_sync_data) - END_SERIALIZE() - - /*! -@@ -1352,6 +1433,8 @@ private: - * \return Whether path is valid format - */ - static bool wallet_valid_path_format(const std::string& file_path); -+ static std::string make_background_wallet_file_name(const std::string &wallet_file); -+ static std::string make_background_keys_file_name(const std::string &wallet_file); - static bool parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); - static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id); - static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); -@@ -1400,6 +1483,9 @@ private: - void ignore_outputs_below(uint64_t value) { m_ignore_outputs_below = value; } - bool track_uses() const { return m_track_uses; } - void track_uses(bool value) { m_track_uses = value; } -+ BackgroundSyncType background_sync_type() const { return m_background_sync_type; } -+ void setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional &background_cache_password); -+ bool is_background_syncing() const { return m_background_syncing; } - bool show_wallet_name_when_locked() const { return m_show_wallet_name_when_locked; } - void show_wallet_name_when_locked(bool value) { m_show_wallet_name_when_locked = value; } - bool show_detailed_prompt() const { return m_show_detailed_prompt; } -@@ -1739,6 +1825,9 @@ private: - std::string printPaymentDetails(const payment_details &pd); - std::string printTxDestinationEntry(const cryptonote::tx_destination_entry &tx); - -+ void start_background_sync(); -+ void stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key = crypto::null_skey); -+ - // MMS ------------------------------------------------------------------------------------------------- - mms::message_store& get_message_store() { return m_message_store; }; - const mms::message_store& get_message_store() const { return m_message_store; }; -@@ -1775,6 +1864,9 @@ private: - * \return Whether it was successful. - */ - bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false); -+ bool store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); -+ boost::optional get_keys_file_data(const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false); -+ bool store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file = false); - /*! - * \brief Load wallet keys information from wallet file. - * \param keys_file_name Name of wallet file -@@ -1788,6 +1880,7 @@ private: - */ - bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password); - bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional& keys_to_encrypt); -+ 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 &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, 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, size_t tx_cache_data_offset, std::map, size_t> *output_tracker_cache = NULL); -@@ -1796,6 +1889,15 @@ private: - void get_short_chain_history(std::list& ids, uint64_t granularity = 1) const; - bool clear(); - void clear_soft(bool keep_key_images=false); -+ /* -+ * clear_user_data clears data created by the user, which is mostly data -+ * that a view key cannot identify on chain. This function was initially -+ * added to ensure that a "background" wallet (a wallet that syncs with just -+ * a view key hot in memory) does not have any sensitive data loaded that it -+ * does not need in order to sync. Future devs should take care to ensure -+ * that this function deletes data that is not useful for background syncing -+ */ -+ void clear_user_data(); - void pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &blocks, std::vector &o_indices, uint64_t ¤t_height); - void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list &short_chain_history, std::vector &hashes); - void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list &short_chain_history, bool force = false); -@@ -1847,10 +1949,23 @@ private: - bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector &outs); - crypto::chacha_key get_ringdb_key(); - void setup_keys(const epee::wipeable_string &password); -+ const crypto::chacha_key get_cache_key(); -+ void verify_password_with_cached_key(const epee::wipeable_string &password); -+ void verify_password_with_cached_key(const crypto::chacha_key &key); - size_t get_transfer_details(const crypto::key_image &ki) const; - tx_entry_data get_tx_entries(const std::unordered_set &txids); - void sort_scan_tx_entries(std::vector &unsorted_tx_entries); - void process_scan_txs(const tx_entry_data &txs_to_scan, const tx_entry_data &txs_to_reprocess, const std::unordered_set &tx_hashes_to_reprocess, detached_blockchain_data &dbd); -+ void write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password); -+ void process_background_cache_on_open(); -+ void process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_chain, uint64_t last_block_reward); -+ void reset_background_sync_data(background_sync_data_t &background_sync_data); -+ void store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data = true); -+ void store_background_keys(const crypto::chacha_key &custom_background_key); -+ -+ bool lock_background_keys_file(const std::string &background_keys_file); -+ bool unlock_background_keys_file(); -+ bool is_background_keys_file_locked() const; - - void register_devices(); - hw::device& lookup_device(const std::string & device_descriptor); -@@ -1966,6 +2081,8 @@ private: - uint64_t m_ignore_outputs_above; - uint64_t m_ignore_outputs_below; - bool m_track_uses; -+ bool m_is_background_wallet; -+ BackgroundSyncType m_background_sync_type; - bool m_show_wallet_name_when_locked; - bool m_show_detailed_prompt; - uint32_t m_inactivity_lock_timeout; -@@ -2012,6 +2129,7 @@ private: - - uint64_t m_last_block_reward; - std::unique_ptr m_keys_file_locker; -+ std::unique_ptr m_background_keys_file_locker; - - mms::message_store m_message_store; - bool m_original_keys_available; -@@ -2019,6 +2137,7 @@ private: - crypto::secret_key m_original_view_secret_key; - - crypto::chacha_key m_cache_key; -+ boost::optional m_custom_background_key = boost::none; - std::shared_ptr m_encrypt_keys_after_refresh; - - bool m_unattended; -@@ -2034,9 +2153,13 @@ private: - - static boost::mutex default_daemon_address_lock; - static std::string default_daemon_address; -+ -+ bool m_background_syncing; -+ bool m_processing_background_cache; -+ background_sync_data_t m_background_sync_data; - }; - } --BOOST_CLASS_VERSION(tools::wallet2, 30) -+BOOST_CLASS_VERSION(tools::wallet2, 31) - BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12) - BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1) - BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0) -@@ -2052,6 +2175,8 @@ BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1) - BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4) - BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) - BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 1) -+BOOST_CLASS_VERSION(tools::wallet2::background_synced_tx_t, 0) -+BOOST_CLASS_VERSION(tools::wallet2::background_sync_data_t, 0) - - namespace boost - { -@@ -2550,6 +2675,29 @@ namespace boost - return; - a & x.multisig_sigs; - } -+ -+ template -+ inline void serialize(Archive& a, tools::wallet2::background_synced_tx_t &x, const boost::serialization::version_type ver) -+ { -+ a & x.index_in_background_sync_data; -+ a & x.tx; -+ a & x.output_indices; -+ a & x.height; -+ a & x.block_timestamp; -+ a & x.double_spend_seen; -+ } -+ -+ template -+ inline void serialize(Archive& a, tools::wallet2::background_sync_data_t &x, const boost::serialization::version_type ver) -+ { -+ a & x.first_refresh_done; -+ a & x.start_height; -+ a & x.txs.parent(); -+ a & x.wallet_refresh_from_block_height; -+ a & x.subaddress_lookahead_major; -+ a & x.subaddress_lookahead_minor; -+ a & x.wallet_refresh_type; -+ } - } - } - -diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h -index 1f7e1c75d..d17b721a9 100644 ---- a/src/wallet/wallet_errors.h -+++ b/src/wallet/wallet_errors.h -@@ -63,6 +63,7 @@ namespace tools - // invalid_password - // invalid_priority - // invalid_multisig_seed -+ // invalid_spend_key - // refresh_error * - // acc_outs_lookup_error - // block_parse_error -@@ -96,6 +97,9 @@ namespace tools - // wallet_files_doesnt_correspond - // scan_tx_error * - // wont_reprocess_recent_txs_via_untrusted_daemon -+ // background_sync_error * -+ // background_wallet_already_open -+ // background_custom_password_same_as_wallet_password - // - // * - class with protected ctor - -@@ -303,6 +307,16 @@ namespace tools - std::string to_string() const { return wallet_logic_error::to_string(); } - }; - -+ struct invalid_spend_key : public wallet_logic_error -+ { -+ explicit invalid_spend_key(std::string&& loc) -+ : wallet_logic_error(std::move(loc), "invalid spend key") -+ { -+ } -+ -+ std::string to_string() const { return wallet_logic_error::to_string(); } -+ }; -+ - //---------------------------------------------------------------------------------------------------- - struct invalid_pregenerated_random : public wallet_logic_error - { -@@ -944,6 +958,31 @@ namespace tools - } - }; - //---------------------------------------------------------------------------------------------------- -+ struct background_sync_error : public wallet_logic_error -+ { -+ protected: -+ explicit background_sync_error(std::string&& loc, const std::string& message) -+ : wallet_logic_error(std::move(loc), message) -+ { -+ } -+ }; -+ //---------------------------------------------------------------------------------------------------- -+ struct background_wallet_already_open : public background_sync_error -+ { -+ explicit background_wallet_already_open(std::string&& loc, const std::string& background_wallet_file) -+ : background_sync_error(std::move(loc), "background wallet " + background_wallet_file + " is already opened by another wallet program") -+ { -+ } -+ }; -+ //---------------------------------------------------------------------------------------------------- -+ struct background_custom_password_same_as_wallet_password : public background_sync_error -+ { -+ explicit background_custom_password_same_as_wallet_password(std::string&& loc) -+ : background_sync_error(std::move(loc), "custom background password must be different than wallet password") -+ { -+ } -+ }; -+ //---------------------------------------------------------------------------------------------------- - - #if !defined(_MSC_VER) - -diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp -index 3bb045b6a..8d2c68ac4 100644 ---- a/src/wallet/wallet_rpc_server.cpp -+++ b/src/wallet/wallet_rpc_server.cpp -@@ -73,6 +73,54 @@ using namespace epee; - } \ - } while(0) - -+#define CHECK_IF_BACKGROUND_SYNCING() \ -+ do \ -+ { \ -+ if (!m_wallet) { return not_open(er); } \ -+ if (m_wallet->is_background_wallet()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET; \ -+ er.message = "This command is disabled for background wallets."; \ -+ return false; \ -+ } \ -+ if (m_wallet->is_background_syncing()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING; \ -+ er.message = "This command is disabled while background syncing. Stop background syncing to use this command."; \ -+ return false; \ -+ } \ -+ } while(0) -+ -+#define PRE_VALIDATE_BACKGROUND_SYNC() \ -+ do \ -+ { \ -+ if (!m_wallet) { return not_open(er); } \ -+ if (m_restricted) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_DENIED; \ -+ er.message = "Command unavailable in restricted mode."; \ -+ return false; \ -+ } \ -+ if (m_wallet->key_on_device()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ -+ er.message = "Command not supported by HW wallet"; \ -+ return false; \ -+ } \ -+ if (m_wallet->multisig()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \ -+ er.message = "Multisig wallet cannot enable background sync"; \ -+ return false; \ -+ } \ -+ if (m_wallet->watch_only()) \ -+ { \ -+ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; \ -+ er.message = "Watch-only wallet cannot enable background sync"; \ -+ return false; \ -+ } \ -+ } while (0) -+ - namespace - { - const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"}; -@@ -291,6 +339,9 @@ namespace tools - { - if (!m_wallet) - return; -+ // Background mining can be toggled from the main wallet -+ if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) -+ return; - - tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining(); - if (setup == tools::wallet2::BackgroundMiningNo) -@@ -581,6 +632,7 @@ namespace tools - bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.count < 1 || req.count > 64) { -@@ -618,6 +670,7 @@ namespace tools - bool wallet_rpc_server::on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_subaddress_label(req.index, req.label); -@@ -680,6 +733,7 @@ namespace tools - bool wallet_rpc_server::on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->add_subaddress_account(req.label); -@@ -697,6 +751,7 @@ namespace tools - bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_subaddress_label({req.account_index, 0}, req.label); -@@ -712,6 +767,7 @@ namespace tools - bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - const std::pair, std::vector> account_tags = m_wallet->get_account_tags(); - for (const std::pair& p : account_tags.first) - { -@@ -731,6 +787,7 @@ namespace tools - bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_account_tag(req.accounts, req.tag); -@@ -746,6 +803,7 @@ namespace tools - bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_account_tag(req.accounts, ""); -@@ -761,6 +819,7 @@ namespace tools - bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->set_account_tag_description(req.tag, req.description); -@@ -791,6 +850,7 @@ namespace tools - bool wallet_rpc_server::on_freeze(const wallet_rpc::COMMAND_RPC_FREEZE::request& req, wallet_rpc::COMMAND_RPC_FREEZE::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.key_image.empty()) -@@ -819,6 +879,7 @@ namespace tools - bool wallet_rpc_server::on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.key_image.empty()) -@@ -847,6 +908,7 @@ namespace tools - bool wallet_rpc_server::on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - if (req.key_image.empty()) -@@ -874,6 +936,8 @@ namespace tools - //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::validate_transfer(const std::list& destinations, const std::string& payment_id, std::vector& dsts, std::vector& extra, bool at_least_one_destination, epee::json_rpc::error& er) - { -+ CHECK_IF_BACKGROUND_SYNCING(); -+ - crypto::hash8 integrated_payment_id = crypto::null_hash8; - std::string extra_nonce; - for (auto it = destinations.begin(); it != destinations.end(); it++) -@@ -1191,6 +1255,7 @@ namespace tools - } - - CHECK_MULTISIG_ENABLED(); -+ CHECK_IF_BACKGROUND_SYNCING(); - - cryptonote::blobdata blob; - if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob)) -@@ -1272,6 +1337,7 @@ namespace tools - er.message = "command not supported by watch-only wallet"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - if(req.unsigned_txset.empty() && req.multisig_txset.empty()) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; -@@ -1541,6 +1607,7 @@ namespace tools - } - - CHECK_MULTISIG_ENABLED(); -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -@@ -2090,6 +2157,7 @@ namespace tools - er.message = "The wallet is watch-only. Cannot retrieve seed."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - if (!m_wallet->is_deterministic()) - { - er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC; -@@ -2118,6 +2186,7 @@ namespace tools - er.message = "The wallet is watch-only. Cannot retrieve spend key."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key); - res.key = std::string(key.data(), key.size()); - } -@@ -2139,6 +2208,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -@@ -2152,6 +2222,79 @@ namespace tools - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ -+ bool wallet_rpc_server::on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) -+ { -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ const tools::wallet2::BackgroundSyncType background_sync_type = tools::wallet2::background_sync_type_from_str(req.background_sync_type); -+ boost::optional background_cache_password = boost::none; -+ if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword) -+ background_cache_password = boost::optional(req.background_cache_password); -+ m_wallet->setup_background_sync(background_sync_type, req.wallet_password, background_cache_password); -+ } -+ catch (...) -+ { -+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); -+ return false; -+ } -+ return true; -+ } -+ //------------------------------------------------------------------------------------------------------------------------------ -+ bool wallet_rpc_server::on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) -+ { -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ m_wallet->start_background_sync(); -+ } -+ catch (...) -+ { -+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); -+ return false; -+ } -+ return true; -+ } -+ //------------------------------------------------------------------------------------------------------------------------------ -+ bool wallet_rpc_server::on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx) -+ { -+ try -+ { -+ PRE_VALIDATE_BACKGROUND_SYNC(); -+ crypto::secret_key spend_secret_key = crypto::null_skey; -+ -+ // Load the spend key from seed if seed is provided -+ if (!req.seed.empty()) -+ { -+ crypto::secret_key recovery_key; -+ std::string language; -+ -+ if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, language)) -+ { -+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; -+ er.message = "Electrum-style word list failed verification"; -+ return false; -+ } -+ -+ if (!req.seed_offset.empty()) -+ recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset); -+ -+ // generate spend key -+ cryptonote::account_base account; -+ account.generate(recovery_key, true, false); -+ spend_secret_key = account.get_keys().m_spend_secret_key; -+ } -+ -+ m_wallet->stop_background_sync(req.wallet_password, spend_secret_key); -+ } -+ catch (...) -+ { -+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); -+ return false; -+ } -+ return true; -+ } -+ //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -@@ -2161,6 +2304,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - tools::wallet2::message_signature_type_t signature_type = tools::wallet2::sign_with_spend_key; - if (req.signature_type == "spend" || req.signature_type == "") -@@ -2253,6 +2397,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - if (req.txids.size() != req.notes.size()) - { -@@ -2325,6 +2470,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - m_wallet->set_attribute(req.key, req.value); - -@@ -2352,6 +2498,7 @@ namespace tools - bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(req.txid, txid)) -@@ -2443,6 +2590,7 @@ namespace tools - bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - - crypto::hash txid; - if (!epee::string_tools::hex_to_pod(req.txid, txid)) -@@ -2559,6 +2707,7 @@ namespace tools - bool wallet_rpc_server::on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - - boost::optional> account_minreserve; - if (!req.all) -@@ -2801,6 +2950,7 @@ namespace tools - er.message = "command not supported by HW wallet"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -@@ -2830,6 +2980,7 @@ namespace tools - er.message = "command not supported by HW wallet"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - cryptonote::blobdata blob; - if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob)) -@@ -2855,6 +3006,7 @@ namespace tools - bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - std::pair>> ski = m_wallet->export_key_images(req.all); -@@ -2891,6 +3043,7 @@ namespace tools - er.message = "This command requires a trusted daemon."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - std::vector> ski; -@@ -2959,6 +3112,7 @@ namespace tools - bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx) - { - if (!m_wallet) return not_open(er); -+ CHECK_IF_BACKGROUND_SYNCING(); - const auto ab = m_wallet->get_address_book(); - if (req.entries.empty()) - { -@@ -3004,6 +3158,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - cryptonote::address_parse_info info; - er.message = ""; -@@ -3046,6 +3201,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - const auto ab = m_wallet->get_address_book(); - if (req.index >= ab.size()) -@@ -3108,6 +3264,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - const auto ab = m_wallet->get_address_book(); - if (req.index >= ab.size()) -@@ -3178,6 +3335,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - std::unordered_set txids; - std::list::const_iterator i = req.txids.begin(); -@@ -3217,6 +3375,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - try - { - m_wallet->rescan_spent(); -@@ -3481,6 +3640,7 @@ namespace tools - er.message = "Command unavailable in restricted mode."; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - if (m_wallet->verify_password(req.old_password)) - { - try -@@ -4008,6 +4168,7 @@ namespace tools - er.message = "wallet is watch-only and cannot be made multisig"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - res.multisig_info = m_wallet->get_multisig_first_kex_msg(); - return true; -@@ -4035,6 +4196,7 @@ namespace tools - er.message = "wallet is watch-only and cannot be made multisig"; - return false; - } -+ CHECK_IF_BACKGROUND_SYNCING(); - - try - { -diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h -index 3308d1751..c2329aafe 100644 ---- a/src/wallet/wallet_rpc_server.h -+++ b/src/wallet/wallet_rpc_server.h -@@ -160,6 +160,9 @@ namespace tools - MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES) - MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT) - MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION) -+ MAP_JON_RPC_WE("setup_background_sync", on_setup_background_sync, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC) -+ MAP_JON_RPC_WE("start_background_sync", on_start_background_sync, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC) -+ MAP_JON_RPC_WE("stop_background_sync", on_stop_background_sync, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC) - END_JSON_RPC_MAP() - END_URI_MAP2() - -@@ -251,6 +254,9 @@ namespace tools - bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); - bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); - bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -+ bool on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -+ bool on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -+ bool on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); - - //json rpc v2 - bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); -diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h -index b6098d95c..a44b56ed6 100644 ---- a/src/wallet/wallet_rpc_server_commands_defs.h -+++ b/src/wallet/wallet_rpc_server_commands_defs.h -@@ -2696,5 +2696,69 @@ namespace wallet_rpc - typedef epee::misc_utils::struct_init response; - }; - -+ struct COMMAND_RPC_SETUP_BACKGROUND_SYNC -+ { -+ struct request_t -+ { -+ std::string background_sync_type; -+ std::string wallet_password; -+ std::string background_cache_password; -+ -+ BEGIN_KV_SERIALIZE_MAP() -+ KV_SERIALIZE(background_sync_type) -+ KV_SERIALIZE(wallet_password) -+ KV_SERIALIZE_OPT(background_cache_password, (std::string)"") -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init request; -+ -+ struct response_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init response; -+ }; -+ -+ struct COMMAND_RPC_START_BACKGROUND_SYNC -+ { -+ struct request_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init request; -+ -+ struct response_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init response; -+ }; -+ -+ struct COMMAND_RPC_STOP_BACKGROUND_SYNC -+ { -+ struct request_t -+ { -+ std::string wallet_password; -+ std::string seed; -+ std::string seed_offset; -+ -+ BEGIN_KV_SERIALIZE_MAP() -+ KV_SERIALIZE(wallet_password) -+ KV_SERIALIZE_OPT(seed, (std::string)"") -+ KV_SERIALIZE_OPT(seed_offset, (std::string)"") -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init request; -+ -+ struct response_t -+ { -+ BEGIN_KV_SERIALIZE_MAP() -+ END_KV_SERIALIZE_MAP() -+ }; -+ typedef epee::misc_utils::struct_init response; -+ }; - } - } -diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h -index 734229380..b964036bd 100644 ---- a/src/wallet/wallet_rpc_server_error_codes.h -+++ b/src/wallet/wallet_rpc_server_error_codes.h -@@ -79,3 +79,5 @@ - #define WALLET_RPC_ERROR_CODE_ZERO_AMOUNT -46 - #define WALLET_RPC_ERROR_CODE_INVALID_SIGNATURE_TYPE -47 - #define WALLET_RPC_ERROR_CODE_DISABLED -48 -+#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET -49 -+#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING -50 -diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py -index 4063911f4..60eb09a10 100755 ---- a/tests/functional_tests/transfer.py -+++ b/tests/functional_tests/transfer.py -@@ -30,6 +30,7 @@ - - from __future__ import print_function - import json -+import util_resources - import pprint - from deepdiff import DeepDiff - pp = pprint.PrettyPrinter(indent=2) -@@ -46,6 +47,17 @@ seeds = [ - 'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid', - ] - -+def diff_transfers(actual_transfers, expected_transfers, ignore_order = True): -+ # The payments containers aren't ordered; re-scanning can lead to diff orders -+ diff = DeepDiff(actual_transfers, expected_transfers, ignore_order = ignore_order) -+ if diff != {}: -+ pp.pprint(diff) -+ assert diff == {} -+ -+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) -+ - class TransferTest(): - def run_test(self): - self.reset() -@@ -63,6 +75,8 @@ class TransferTest(): - self.check_is_key_image_spent() - self.check_scan_tx() - self.check_subtract_fee_from_outputs() -+ self.check_background_sync() -+ self.check_background_sync_reorg_recovery() - - def reset(self): - print('Resetting blockchain') -@@ -840,12 +854,6 @@ class TransferTest(): - - print('Testing scan_tx') - -- def diff_transfers(actual_transfers, expected_transfers): -- diff = DeepDiff(actual_transfers, expected_transfers) -- if diff != {}: -- pp.pprint(diff) -- assert diff == {} -- - # set up sender_wallet - sender_wallet = self.wallet[0] - try: sender_wallet.close_wallet() -@@ -1127,5 +1135,385 @@ class TransferTest(): - except AssertionError: - pass - -+ def check_background_sync(self): -+ daemon = Daemon() -+ -+ print('Testing background sync') -+ -+ # Some helper functions -+ def stop_with_wrong_inputs(wallet, wallet_password, seed = ''): -+ invalid = False -+ try: wallet.stop_background_sync(wallet_password = wallet_password, seed = seed) -+ except: invalid = True -+ assert invalid -+ -+ def open_with_wrong_password(wallet, filename, password): -+ invalid_password = False -+ try: wallet.open_wallet(filename, password = password) -+ 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) -+ assert wallet.get_balance().balance == expected_balance -+ -+ # Set up sender_wallet. Prepare to sweep single output to receiver. -+ # 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] -+ 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] -+ assert len(unlocked) > 0 -+ ki = unlocked[0].key_image -+ amount = unlocked[0].amount -+ spent_txid = unlocked[0].tx_hash -+ sender_wallet.refresh() -+ res = sender_wallet.get_transfers() -+ out_len = 0 if 'out' not in res else len(res.out) -+ sender_starting_balance = sender_wallet.get_balance().balance -+ -+ # Background sync type options -+ reuse_password = sender_wallet.background_sync_options.reuse_password -+ custom_password = sender_wallet.background_sync_options.custom_password -+ -+ # set up receiver_wallet -+ receiver_wallet = self.wallet[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']) -+ receiver_starting_balance = receiver_wallet.get_balance().balance -+ -+ # transfer from sender_wallet to receiver_wallet -+ dst = '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW' -+ res = sender_wallet.sweep_single(dst, key_image = ki) -+ assert len(res.tx_hash) == 32*2 -+ txid = res.tx_hash -+ assert res.fee > 0 -+ fee = res.fee -+ assert res.amount == amount - fee -+ -+ expected_sender_balance = sender_starting_balance - amount -+ expected_receiver_balance = receiver_starting_balance + (amount - fee) -+ -+ print('Checking background sync on outgoing wallet') -+ sender_wallet.setup_background_sync(background_sync_type = reuse_password) -+ sender_wallet.start_background_sync() -+ # Mine block to an uninvolved wallet -+ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) -+ # sender should still be able to scan the transfer normally because we -+ # spent an output that had a known key image -+ sender_wallet.refresh() -+ transfers = sender_wallet.get_transfers() -+ assert 'pending' not in transfers or len(transfers.pending) == 0 -+ assert 'pool' not in transfers or len (transfers.pool) == 0 -+ assert len(transfers.out) == out_len + 1 -+ tx = [x for x in transfers.out if x.txid == txid] -+ assert len(tx) == 1 -+ tx = tx[0] -+ assert tx.amount == amount - fee -+ assert tx.fee == fee -+ assert len(tx.destinations) == 1 -+ assert tx.destinations[0].amount == amount - fee -+ assert tx.destinations[0].address == dst -+ incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all') -+ assert len([x for x in incoming_transfers.transfers if x.tx_hash == spent_txid and x.key_image == ki and x.spent]) == 1 -+ assert sender_wallet.get_balance().balance == expected_sender_balance -+ -+ # Restore and check background syncing outgoing wallet -+ restore_wallet(sender_wallet, seeds[0]) -+ sender_wallet.setup_background_sync(background_sync_type = reuse_password) -+ sender_wallet.start_background_sync() -+ sender_wallet.refresh() -+ for i, out_tx in enumerate(transfers.out): -+ if 'destinations' in out_tx: -+ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore -+ # sender's balance should be higher because can't detect spends while -+ # background sync enabled, only receives -+ background_bal = sender_wallet.get_balance().balance -+ assert background_bal > expected_sender_balance -+ background_transfers = sender_wallet.get_transfers() -+ assert 'out' not in background_transfers or len(background_transfers.out) == 0 -+ assert 'in' in background_transfers and len(background_transfers['in']) > 0 -+ background_incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all') -+ assert len(background_incoming_transfers) == len(incoming_transfers) -+ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0 -+ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == spent_txid]) == 1 -+ -+ # Try to stop background sync with the wrong seed -+ stop_with_wrong_inputs(sender_wallet, wallet_password = '', seed = seeds[1]) -+ -+ # Stop background sync and check transfers update correctly -+ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0]) -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ -+ # 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], '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() -+ sender_wallet.refresh() -+ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) -+ stop_with_wrong_inputs(sender_wallet, 'wrong_password') -+ sender_wallet.stop_background_sync(wallet_password = 'test_password') -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ -+ # Close wallet while background syncing, then reopen -+ for background_sync_type in [reuse_password, custom_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() -+ sender_wallet.refresh() -+ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) -+ sender_wallet.close_wallet() -+ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password') -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ # It should reopen with spend key loaded and correctly scan all transfers -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ -+ # Close wallet while syncing normally, then reopen -+ for background_sync_type in [reuse_password, custom_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() -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ sender_wallet.close_wallet() -+ open_with_wrong_password(sender_wallet, 'test1', 'wrong_password') -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance) -+ -+ # 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], '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) -+ assert util_resources.file_exists('test1.background') -+ assert util_resources.file_exists('test1.background.keys') -+ sender_wallet.close_wallet() -+ open_with_wrong_password(sender_wallet, 'test1.background', 'test_password') -+ sender_wallet.open_wallet('test1.background', password = background_cache_password) -+ sender_wallet.refresh() -+ assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal) -+ sender_wallet.close_wallet() -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ 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], '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) -+ sender_wallet.close_wallet() -+ sender_wallet.open_wallet('test1.background', password = 'background_password') -+ 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], 'test1', 'test_password') -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ same_password = False -+ try: sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = 'test_password') -+ except: same_password = True -+ assert same_password -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ -+ # Turn off background sync -+ for background_sync_type in [reuse_password, custom_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: -+ assert util_resources.file_exists('test1.background') -+ assert util_resources.file_exists('test1.background.keys') -+ sender_wallet.close_wallet() -+ assert util_resources.file_exists('test1.background') -+ assert util_resources.file_exists('test1.background.keys') -+ else: -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ sender_wallet.close_wallet() -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ sender_wallet.setup_background_sync(background_sync_type = sender_wallet.background_sync_options.off, wallet_password = 'test_password') -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ sender_wallet.close_wallet() -+ assert not util_resources.file_exists('test1.background') -+ assert not util_resources.file_exists('test1.background.keys') -+ sender_wallet.open_wallet('test1', password = 'test_password') -+ -+ # Sanity check against outgoing wallet restored at 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) -+ -+ print('Checking background sync on incoming wallet') -+ receiver_wallet.setup_background_sync(background_sync_type = reuse_password) -+ receiver_wallet.start_background_sync() -+ receiver_wallet.refresh() -+ transfers = receiver_wallet.get_transfers() -+ assert 'pending' not in transfers or len(transfers.pending) == 0 -+ assert 'pool' not in transfers or len (transfers.pool) == 0 -+ assert len(transfers['in']) == in_len + 1 -+ tx = [x for x in transfers['in'] if x.txid == txid] -+ assert len(tx) == 1 -+ tx = tx[0] -+ assert tx.amount == amount - fee -+ assert tx.fee == fee -+ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') -+ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image == '' and not x.spent]) == 1 -+ assert receiver_wallet.get_balance().balance == expected_receiver_balance -+ -+ # Restore and check background syncing incoming wallet -+ restore_wallet(receiver_wallet, seeds[1]) -+ receiver_wallet.setup_background_sync(background_sync_type = reuse_password) -+ receiver_wallet.start_background_sync() -+ receiver_wallet.refresh() -+ if 'out' in transfers: -+ for i, out_tx in enumerate(transfers.out): -+ if 'destinations' in out_tx: -+ del transfers.out[i]['destinations'] # destinations are not expected after wallet restore -+ background_bal = receiver_wallet.get_balance().balance -+ assert background_bal >= expected_receiver_balance -+ background_transfers = receiver_wallet.get_transfers() -+ assert 'out' not in background_transfers or len(background_transfers.out) == 0 -+ assert 'in' in background_transfers and len(background_transfers['in']) > 0 -+ background_incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') -+ assert len(background_incoming_transfers) == len(incoming_transfers) -+ assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0 -+ assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == txid]) == 1 -+ -+ # Stop background sync and check transfers update correctly -+ receiver_wallet.stop_background_sync(wallet_password = '', seed = seeds[1]) -+ diff_transfers(receiver_wallet.get_transfers(), transfers) -+ incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all') -+ assert len(background_incoming_transfers) == len(incoming_transfers) -+ assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image != '' and not x.spent]) == 1 -+ 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], '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() -+ assert_correct_transfers(receiver_wallet, background_transfers, background_incoming_transfers, background_bal) -+ stop_with_wrong_inputs(receiver_wallet, 'wrong_password') -+ receiver_wallet.stop_background_sync(wallet_password = 'test_password') -+ 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], '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() -+ diff_transfers(receiver_wallet.get_transfers(), background_transfers) -+ diff_incoming_transfers(receiver_wallet.incoming_transfers(transfer_type = 'all'), background_incoming_transfers) -+ assert receiver_wallet.get_balance().balance == background_bal -+ receiver_wallet.close_wallet() -+ receiver_wallet.open_wallet('test2', password = 'test_password') -+ # It should reopen with spend key loaded and correctly scan all transfers -+ assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance) -+ -+ # Sanity check against incoming wallet restored at 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) -+ -+ # Clean up -+ util_resources.remove_wallet_files('test1') -+ util_resources.remove_wallet_files('test2') -+ for i in range(2): -+ self.wallet[i].close_wallet() -+ self.wallet[i].restore_deterministic_wallet(seed = seeds[i]) -+ -+ def check_background_sync_reorg_recovery(self): -+ daemon = Daemon() -+ -+ print('Testing background sync reorg recovery') -+ -+ # Disconnect daemon from peers -+ daemon.out_peers(0) -+ -+ # Background sync type options -+ sender_wallet = self.wallet[0] -+ reuse_password = sender_wallet.background_sync_options.reuse_password -+ custom_password = sender_wallet.background_sync_options.custom_password -+ -+ for background_sync_type in [reuse_password, custom_password]: -+ # Set up wallet saved to disk -+ sender_wallet.close_wallet() -+ util_resources.remove_wallet_files('test1') -+ sender_wallet.restore_deterministic_wallet(seed = seeds[0], filename = 'test1', password = '') -+ sender_wallet.auto_refresh(enable = False) -+ sender_wallet.refresh() -+ sender_starting_balance = sender_wallet.get_balance().balance -+ -+ # Send tx and mine a block -+ amount = 1000000000000 -+ assert sender_starting_balance > amount -+ dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': amount} -+ res = sender_wallet.transfer([dst]) -+ assert len(res.tx_hash) == 32*2 -+ txid = res.tx_hash -+ -+ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) -+ -+ # Make sure the wallet can see the tx -+ sender_wallet.refresh() -+ transfers = sender_wallet.get_transfers() -+ assert 'pool' not in transfers or len (transfers.pool) == 0 -+ tx = [x for x in transfers.out if x.txid == txid] -+ assert len(tx) == 1 -+ tx = tx[0] -+ assert sender_wallet.get_balance().balance < (sender_starting_balance - amount) -+ -+ # Pop the block while background syncing -+ 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 = '', background_cache_password = background_cache_password) -+ sender_wallet.start_background_sync() -+ daemon.pop_blocks(1) -+ daemon.flush_txpool() -+ -+ daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1) -+ -+ # Make sure the wallet can no longer see the tx -+ sender_wallet.refresh() -+ sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0]) -+ transfers = sender_wallet.get_transfers() -+ no_tx = [x for x in transfers.out if x.txid == txid] -+ assert len(no_tx) == 0 -+ assert sender_wallet.get_balance().balance == sender_starting_balance -+ -+ # Clean up -+ daemon.out_peers(12) -+ util_resources.remove_wallet_files('test1') -+ self.wallet[0].close_wallet() -+ self.wallet[0].restore_deterministic_wallet(seed = seeds[0]) -+ - if __name__ == '__main__': - TransferTest().run_test() -diff --git a/tests/functional_tests/util_resources.py b/tests/functional_tests/util_resources.py -index e030312da..3ca6fdb86 100755 ---- a/tests/functional_tests/util_resources.py -+++ b/tests/functional_tests/util_resources.py -@@ -37,6 +37,8 @@ - from __future__ import print_function - import subprocess - import psutil -+import os -+import errno - - def available_ram_gb(): - ram_bytes = psutil.virtual_memory().available -@@ -51,3 +53,26 @@ def get_time_pi_seconds(cores, app_dir='.'): - miliseconds = int(decoded) - - return miliseconds / 1000.0 -+ -+def remove_file(name): -+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -+ assert WALLET_DIRECTORY != '' -+ try: -+ os.unlink(WALLET_DIRECTORY + '/' + name) -+ except OSError as e: -+ if e.errno != errno.ENOENT: -+ raise -+ -+def get_file_path(name): -+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -+ assert WALLET_DIRECTORY != '' -+ return WALLET_DIRECTORY + '/' + name -+ -+def remove_wallet_files(name): -+ for suffix in ['', '.keys', '.background', '.background.keys', '.address.txt']: -+ remove_file(name + suffix) -+ -+def file_exists(name): -+ WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -+ assert WALLET_DIRECTORY != '' -+ return os.path.isfile(WALLET_DIRECTORY + '/' + name) -diff --git a/tests/functional_tests/wallet.py b/tests/functional_tests/wallet.py -index 1ad05c98f..8182cecb2 100755 ---- a/tests/functional_tests/wallet.py -+++ b/tests/functional_tests/wallet.py -@@ -34,8 +34,7 @@ - - from __future__ import print_function - import sys --import os --import errno -+import util_resources - - from framework.wallet import Wallet - from framework.daemon import Daemon -@@ -54,24 +53,6 @@ class WalletTest(): - self.change_password() - self.store() - -- def remove_file(self, name): -- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -- assert WALLET_DIRECTORY != '' -- try: -- os.unlink(WALLET_DIRECTORY + '/' + name) -- except OSError as e: -- if e.errno != errno.ENOENT: -- raise -- -- def remove_wallet_files(self, name): -- for suffix in ['', '.keys']: -- self.remove_file(name + suffix) -- -- def file_exists(self, name): -- WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY'] -- assert WALLET_DIRECTORY != '' -- return os.path.isfile(WALLET_DIRECTORY + '/' + name) -- - def reset(self): - print('Resetting blockchain') - daemon = Daemon() -@@ -333,7 +314,7 @@ class WalletTest(): - try: wallet.close_wallet() - except: pass - -- self.remove_wallet_files('test1') -+ util_resources.remove_wallet_files('test1') - - seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' - res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') -@@ -359,7 +340,7 @@ class WalletTest(): - - wallet.close_wallet() - -- self.remove_wallet_files('test1') -+ util_resources.remove_wallet_files('test1') - - def store(self): - print('Testing store') -@@ -369,22 +350,26 @@ class WalletTest(): - try: wallet.close_wallet() - except: pass - -- self.remove_wallet_files('test1') -+ util_resources.remove_wallet_files('test1') - - seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted' - res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1') - assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm' - assert res.seed == seed - -- self.remove_file('test1') -- assert self.file_exists('test1.keys') -- assert not self.file_exists('test1') -+ util_resources.remove_file('test1') -+ assert util_resources.file_exists('test1.keys') -+ assert not util_resources.file_exists('test1') - wallet.store() -- assert self.file_exists('test1.keys') -- assert self.file_exists('test1') -+ assert util_resources.file_exists('test1.keys') -+ assert util_resources.file_exists('test1') - - wallet.close_wallet() -- self.remove_wallet_files('test1') -+ -+ wallet.open_wallet(filename = 'test1', password = '') -+ wallet.close_wallet() -+ -+ util_resources.remove_wallet_files('test1') - - - if __name__ == '__main__': -diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp -index ef6964f9e..25121a02e 100644 ---- a/tests/unit_tests/wipeable_string.cpp -+++ b/tests/unit_tests/wipeable_string.cpp -@@ -211,3 +211,15 @@ TEST(wipeable_string, to_hex) - ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"", 0)) == epee::wipeable_string("")); - ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span((const uint8_t*)"abc", 3)) == epee::wipeable_string("616263")); - } -+ -+TEST(wipeable_string, to_string) -+{ -+ // Converting a wipeable_string to a string defeats the purpose of wipeable_string, -+ // but nice to know this works -+ std::string str; -+ { -+ epee::wipeable_string wipeable_str("foo"); -+ str = std::string(wipeable_str.data(), wipeable_str.size()); -+ } -+ ASSERT_TRUE(str == std::string("foo")); -+} -diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py -index 1e10e1f86..bff33a561 100644 ---- a/utils/python-rpc/framework/wallet.py -+++ b/utils/python-rpc/framework/wallet.py -@@ -1138,3 +1138,45 @@ class Wallet(object): - 'id': '0' - } - return self.rpc.send_json_rpc_request(frozen) -+ -+ class BackgroundSyncOptions(object): -+ def __init__(self): -+ self.off = 'off' -+ self.reuse_password = 'reuse-wallet-password' -+ self.custom_password = 'custom-background-password' -+ background_sync_options = BackgroundSyncOptions() -+ -+ def setup_background_sync(self, background_sync_type = background_sync_options.off, wallet_password = '', background_cache_password = ''): -+ setup_background_sync = { -+ 'method': 'setup_background_sync', -+ 'jsonrpc': '2.0', -+ 'params' : { -+ 'background_sync_type': background_sync_type, -+ 'wallet_password': wallet_password, -+ 'background_cache_password': background_cache_password, -+ }, -+ 'id': '0' -+ } -+ return self.rpc.send_json_rpc_request(setup_background_sync) -+ -+ def start_background_sync(self): -+ start_background_sync = { -+ 'method': 'start_background_sync', -+ 'jsonrpc': '2.0', -+ 'params' : {}, -+ 'id': '0' -+ } -+ return self.rpc.send_json_rpc_request(start_background_sync) -+ -+ def stop_background_sync(self, wallet_password = '', seed = '', seed_offset = ''): -+ stop_background_sync = { -+ 'method': 'stop_background_sync', -+ 'jsonrpc': '2.0', -+ 'params' : { -+ 'wallet_password': wallet_password, -+ 'seed': seed, -+ 'seed_offset': seed_offset, -+ }, -+ 'id': '0' -+ } -+ return self.rpc.send_json_rpc_request(stop_background_sync) --- -2.45.2 - diff --git a/patches/wownero/0004-airgap.patch b/patches/wownero/0004-airgap.patch deleted file mode 100644 index 4bdd4ef..0000000 --- a/patches/wownero/0004-airgap.patch +++ /dev/null @@ -1,170 +0,0 @@ -From dd3b82ecf391fbaa91d16634198e58fa9ed0091f Mon Sep 17 00:00:00 2001 -From: tobtoht -Date: Tue, 26 Mar 2024 09:06:32 +0100 -Subject: [PATCH 04/14] airgap - ---- - src/wallet/api/wallet.cpp | 23 +++++++++++++++++++++++ - src/wallet/api/wallet.h | 2 ++ - src/wallet/api/wallet2_api.h | 3 +++ - src/wallet/wallet2.cpp | 28 ++++++++++++++++++++++------ - src/wallet/wallet2.h | 1 + - 5 files changed, 51 insertions(+), 6 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 27a4dc6c8..4b2693f23 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -1172,6 +1172,24 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const - return m_wallet->unlocked_balance(accountIndex, false); - } - -+uint64_t WalletImpl::viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const -+{ -+ clearStatus(); -+ -+ std::vector kis; -+ for (const auto &key_image : key_images) { -+ crypto::key_image ki; -+ if (!epee::string_tools::hex_to_pod(key_image, ki)) -+ { -+ setStatusError(tr("failed to parse key image")); -+ return 0; -+ } -+ kis.push_back(ki); -+ } -+ -+ return m_wallet->view_only_balance(accountIndex, kis); -+} -+ - uint64_t WalletImpl::blockChainHeight() const - { - if(m_wallet->light_wallet()) { -@@ -1376,6 +1394,11 @@ bool WalletImpl::submitTransaction(const string &fileName) { - return true; - } - -+bool WalletImpl::hasUnknownKeyImages() const -+{ -+ return m_wallet->has_unknown_key_images(); -+} -+ - bool WalletImpl::exportKeyImages(const string &filename, bool all) - { - if (m_wallet->watch_only()) -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index fc6341861..7b885e866 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -130,6 +130,7 @@ public: - bool setProxy(const std::string &address) override; - uint64_t balance(uint32_t accountIndex = 0) const override; - uint64_t unlockedBalance(uint32_t accountIndex = 0) const override; -+ uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const override; - uint64_t blockChainHeight() const override; - uint64_t approximateBlockChainHeight() const override; - uint64_t estimateBlockChainHeight() const override; -@@ -190,6 +191,7 @@ public: - virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; - virtual UnsignedTransaction * loadUnsignedTxFromStr(const std::string &unsigned_tx) override; - virtual UnsignedTransaction * loadUnsignedTxFromBase64Str(const std::string &unsigned_tx) override; -+ bool hasUnknownKeyImages() const override; - virtual PendingTransaction * loadSignedTx(const std::string &signed_filename) override; - bool exportKeyImages(const std::string &filename, bool all = false) override; - bool importKeyImages(const std::string &filename) override; -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index db5509227..5dcaeaaab 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -726,6 +726,7 @@ struct Wallet - result += unlockedBalance(i); - return result; - } -+ virtual uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images = {}) const = 0; - - /** - * @brief watchOnly - checks if wallet is watch only -@@ -1045,6 +1046,8 @@ struct Wallet - virtual uint64_t estimateTransactionFee(const std::vector> &destinations, - PendingTransaction::Priority priority) const = 0; - -+ virtual bool hasUnknownKeyImages() const = 0; -+ - /*! - * \brief exportKeyImages - exports key images to file - * \param filename -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index e013313b9..2a5f17a9f 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -7432,6 +7432,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * - return amount; - } - //---------------------------------------------------------------------------------------------------- -+uint64_t wallet2::view_only_balance(uint32_t index_major, const std::vector& selected_inputs) -+{ -+ uint64_t amount = 0; -+ for (const auto &td : m_transfers) { -+ if (is_preferred_input(selected_inputs, td.m_key_image) && -+ !is_spent(td, false) && -+ !td.m_frozen && -+ !td.m_key_image_partial && -+ td.m_key_image_known && -+ td.is_rct() && -+ is_transfer_unlocked(td) && -+ td.m_subaddr_index.major == index_major) -+ { -+ amount += td.m_amount; -+ } -+ } -+ return amount; -+} -+//---------------------------------------------------------------------------------------------------- - std::map wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const - { - std::map amount_per_subaddr; -@@ -8282,9 +8301,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector additional_derivations; - -- // compute public keys from out secret keys -- crypto::public_key tx_pub_key; -- crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key); -+ crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - std::vector additional_tx_pub_keys; - for (const crypto::secret_key &skey: txs[n].additional_tx_keys) - { -@@ -11692,7 +11709,7 @@ std::vector wallet2::create_transactions_2(std::vector m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) - { -@@ -11742,8 +11759,7 @@ std::vector wallet2::create_transactions_2(std::vector(); -+ THROW_WALLET_EXCEPTION_IF(unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty(), error::wallet_internal_error, "No enotes available to spend") - - // if empty, put dummy entry so that the front can be referenced later in the loop - if (unused_dust_indices_per_subaddr.empty()) -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 009dce766..294a2d1f6 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1173,6 +1173,7 @@ private: - // locked & unlocked balance of given or current subaddress account - uint64_t balance(uint32_t subaddr_index_major, bool strict) const; - uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL); -+ uint64_t view_only_balance(uint32_t index_major, const std::vector& selected_inputs = {}); - // locked & unlocked balance per subaddress of given or current subaddress account - std::map balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; - std::map>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict); --- -2.45.2 - diff --git a/patches/wownero/0004-store-crash-fix.patch b/patches/wownero/0004-store-crash-fix.patch new file mode 100644 index 0000000..0f15183 --- /dev/null +++ b/patches/wownero/0004-store-crash-fix.patch @@ -0,0 +1,210 @@ +From 9dc9c8048d3918fe3615e91ab8be342471167c26 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Sat, 11 May 2024 16:25:10 +0200 +Subject: [PATCH 04/15] store crash fix + +Monero wallet crashes (sometimes) when it is syncing, +while the proper solution (that can be seen in feather) +is to not store wallet while it is being synced, this is not +acceptable for mobile wallets where OS can just come +and kill the wallet because it felt like it. + +This patch depends on the background-sync patch, but +to use it as a standalone fix grabbing the definition for the +LOCK_REFRESH macro should be enough. + +tobtoht suggested: +_say you want to store every 15 minutes during background sync. you stop the refresh every 15 minutes. then do something like this in the callback:_ + +``` +// Make sure this doesn't run in the refresh thread +onRefreshed() { + if (hasItBeen15MinutesSinceWeStored()) { + store(); + } + + if (shouldWeContinueRefreshing()) { + startRefresh(); + } +} +``` + +which works for crashes after the wallet is initially synced +but doesn't solve the issue for wallet that are syncing (it +would just wait for it to finish before actually storing). + +Also imo store() functin should store the wallet, no matter +the current state. +--- + src/wallet/api/wallet.cpp | 25 ++++++++++++------------- + src/wallet/api/wallet.h | 1 - + src/wallet/wallet2.cpp | 12 +++++++++++- + src/wallet/wallet2.h | 3 +++ + 4 files changed, 26 insertions(+), 15 deletions(-) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index e868fa039..899ef044a 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -55,8 +55,8 @@ using namespace cryptonote; + #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" + + #define LOCK_REFRESH() \ +- bool refresh_enabled = m_refreshEnabled; \ +- m_refreshEnabled = false; \ ++ bool refresh_enabled = m_wallet->get_refresh_enabled(); \ ++ m_wallet->set_refresh_enabled(false); \ + m_wallet->stop(); \ + m_refreshCV.notify_one(); \ + boost::mutex::scoped_lock lock(m_refreshMutex); \ +@@ -466,7 +466,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) + m_wallet2Callback.reset(new Wallet2CallbackImpl(this)); + m_wallet->callback(m_wallet2Callback.get()); + m_refreshThreadDone = false; +- m_refreshEnabled = false; ++ m_wallet->set_refresh_enabled(false); + m_addressBook.reset(new AddressBookImpl(this)); + m_subaddress.reset(new SubaddressImpl(this)); + m_subaddressAccount.reset(new SubaddressAccountImpl(this)); +@@ -962,6 +962,7 @@ void WalletImpl::stop() + bool WalletImpl::store(const std::string &path) + { + clearStatus(); ++ LOCK_REFRESH(); + try { + if (path.empty()) { + m_wallet->store(); +@@ -2448,10 +2449,10 @@ void WalletImpl::refreshThreadFunc() + } + + LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired..."); +- LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled); ++ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_wallet->get_refresh_enabled()); + LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status()); + LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan); +- if (m_refreshEnabled) { ++ if (m_wallet->get_refresh_enabled()) { + LOG_PRINT_L3(__FUNCTION__ << ": refreshing..."); + doRefresh(); + } +@@ -2481,12 +2482,12 @@ void WalletImpl::doRefresh() + } + m_wallet->find_and_save_rings(false); + } else { +- LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); ++ LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); + } + } catch (const std::exception &e) { + setStatusError(e.what()); + break; +- }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested ++ }while(m_wallet->get_refresh_enabled() && !rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested + + if (m_wallet2Callback->getListener()) { + m_wallet2Callback->getListener()->refreshed(); +@@ -2496,9 +2497,9 @@ void WalletImpl::doRefresh() + + void WalletImpl::startRefresh() + { +- if (!m_refreshEnabled) { ++ if (!m_wallet->get_refresh_enabled()) { + LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed..."); +- m_refreshEnabled = true; ++ m_wallet->set_refresh_enabled(true); + m_refreshCV.notify_one(); + } + } +@@ -2508,7 +2509,7 @@ void WalletImpl::startRefresh() + void WalletImpl::stopRefresh() + { + if (!m_refreshThreadDone) { +- m_refreshEnabled = false; ++ m_wallet->set_refresh_enabled(false); + m_refreshThreadDone = true; + m_refreshCV.notify_one(); + m_refreshThread.join(); +@@ -2519,9 +2520,7 @@ void WalletImpl::pauseRefresh() + { + LOG_PRINT_L2(__FUNCTION__ << ": refresh paused..."); + // TODO synchronize access +- if (!m_refreshThreadDone) { +- m_refreshEnabled = false; +- } ++ m_wallet->set_refresh_enabled(false); + } + + +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 1f199a72c..ac7ce2f6a 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -273,7 +273,6 @@ private: + std::unique_ptr m_subaddressAccount; + + // multi-threaded refresh stuff +- std::atomic m_refreshEnabled; + std::atomic m_refreshThreadDone; + std::atomic m_refreshIntervalMillis; + std::atomic m_refreshShouldRescan; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 8ce515c57..651161d14 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -1192,6 +1192,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std + m_upper_transaction_weight_limit(0), + m_run(true), + m_callback(0), ++ m_refreshEnabled(false), + m_trusted_daemon(false), + m_nettype(nettype), + m_multisig_rounds_passed(0), +@@ -1404,6 +1405,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optionalset_proxy(address); +@@ -4098,8 +4107,9 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo + // infer when we get an incoming output + + bool first = true, last = false; +- while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks) ++ while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks && m_refreshEnabled) + { ++ LOG_ERROR("SYNCING"); + uint64_t next_blocks_start_height; + std::vector next_blocks; + std::vector next_parsed_blocks; +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index 295976b53..c07a47c11 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1071,6 +1071,8 @@ private: + boost::optional daemon_login = boost::none, bool trusted_daemon = true, + epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); + bool set_proxy(const std::string &address); ++ bool get_refresh_enabled(); ++ void set_refresh_enabled(bool val); + + void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } + +@@ -1981,6 +1983,7 @@ private: + + boost::recursive_mutex m_daemon_rpc_mutex; + ++ bool m_refreshEnabled; + bool m_trusted_daemon; + i_wallet2_callback* m_callback; + hw::device::device_type m_key_device_type; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0005-coin-control.patch b/patches/wownero/0005-coin-control.patch deleted file mode 100644 index 4502af5..0000000 --- a/patches/wownero/0005-coin-control.patch +++ /dev/null @@ -1,225 +0,0 @@ -From 820424507f43711e5e6b31a68dfda7e430cdae9d Mon Sep 17 00:00:00 2001 -From: tobtoht -Date: Wed, 27 Mar 2024 16:31:36 +0100 -Subject: [PATCH 05/14] coin control - ---- - src/wallet/api/coins.cpp | 62 ++++++++++++++++++++++++++++++++++++ - src/wallet/api/coins.h | 4 +++ - src/wallet/api/wallet.cpp | 4 +-- - src/wallet/api/wallet2_api.h | 3 ++ - src/wallet/wallet2.cpp | 22 +++++++++++++ - src/wallet/wallet2.h | 4 +++ - 6 files changed, 97 insertions(+), 2 deletions(-) - -diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp -index fe54b82cf..5ce69b5b9 100644 ---- a/src/wallet/api/coins.cpp -+++ b/src/wallet/api/coins.cpp -@@ -90,6 +90,26 @@ namespace Monero { - } - } - -+ void CoinsImpl::setFrozen(std::string public_key) -+ { -+ crypto::public_key pk; -+ if (!epee::string_tools::hex_to_pod(public_key, pk)) -+ { -+ LOG_ERROR("Invalid public key: " << public_key); -+ return; -+ } -+ -+ try -+ { -+ m_wallet->m_wallet->freeze(pk); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("setFrozen: " << e.what()); -+ } -+ } -+ - void CoinsImpl::setFrozen(int index) - { - try -@@ -103,6 +123,26 @@ namespace Monero { - } - } - -+ void CoinsImpl::thaw(std::string public_key) -+ { -+ crypto::public_key pk; -+ if (!epee::string_tools::hex_to_pod(public_key, pk)) -+ { -+ LOG_ERROR("Invalid public key: " << public_key); -+ return; -+ } -+ -+ try -+ { -+ m_wallet->m_wallet->thaw(pk); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("thaw: " << e.what()); -+ } -+ } -+ - void CoinsImpl::thaw(int index) - { - try -@@ -120,4 +160,26 @@ namespace Monero { - return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight); - } - -+ void CoinsImpl::setDescription(const std::string &public_key, const std::string &description) -+ { -+ crypto::public_key pk; -+ if (!epee::string_tools::hex_to_pod(public_key, pk)) -+ { -+ LOG_ERROR("Invalid public key: " << public_key); -+ return; -+ } -+ -+ try -+ { -+ const size_t index = m_wallet->m_wallet->get_transfer_details(pk); -+ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index); -+ m_wallet->m_wallet->set_tx_note(td.m_txid, description); -+ refresh(); -+ } -+ catch (const std::exception& e) -+ { -+ LOG_ERROR("setDescription: " << e.what()); -+ } -+ } -+ - } // namespace -diff --git a/src/wallet/api/coins.h b/src/wallet/api/coins.h -index 3293d8ae9..bcd8b517f 100644 ---- a/src/wallet/api/coins.h -+++ b/src/wallet/api/coins.h -@@ -18,11 +18,15 @@ namespace Monero { - std::vector getAll() const override; - void refresh() override; - -+ void setFrozen(std::string public_key) override; - void setFrozen(int index) override; -+ void thaw(std::string public_key) override; - void thaw(int index) override; - - bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override; - -+ void setDescription(const std::string &public_key, const std::string &description) override; -+ - private: - WalletImpl *m_wallet; - std::vector m_rows; -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 4b2693f23..ed309de34 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -2018,11 +2018,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorm_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, - 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, 0 /* unlock_time */, - adjusted_priority, -- extra, subaddr_account, subaddr_indices); -+ extra, subaddr_account, subaddr_indices, preferred_input_list); - } - pendingTxPostProcess(transaction); - -diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index 5dcaeaaab..347e6d002 100644 ---- a/src/wallet/api/wallet2_api.h -+++ b/src/wallet/api/wallet2_api.h -@@ -352,9 +352,12 @@ struct Coins - virtual CoinsInfo * coin(int index) const = 0; - virtual std::vector getAll() const = 0; - virtual void refresh() = 0; -+ virtual void setFrozen(std::string public_key) = 0; - virtual void setFrozen(int index) = 0; - virtual void thaw(int index) = 0; -+ virtual void thaw(std::string public_key) = 0; - virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; -+ virtual void setDescription(const std::string &public_key, const std::string &description) = 0; - }; - - struct SubaddressRow { -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 2a5f17a9f..2b3eaad2e 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -2094,11 +2094,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)); -@@ -2109,6 +2119,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) -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index 294a2d1f6..d1e68baac 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1563,6 +1563,8 @@ 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); -@@ -1793,7 +1795,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; --- -2.45.2 - diff --git a/patches/wownero/0005-uint64_t-missing-definition-fix.patch b/patches/wownero/0005-uint64_t-missing-definition-fix.patch new file mode 100644 index 0000000..05bef1f --- /dev/null +++ b/patches/wownero/0005-uint64_t-missing-definition-fix.patch @@ -0,0 +1,25 @@ +From 223e2e4b5b2c6366d0dd4130a726c4cfcdb13ffb Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 2 Sep 2024 16:40:31 +0200 +Subject: [PATCH 05/15] uint64_t missing definition fix + +--- + contrib/epee/include/net/http_base.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h +index 4af4da790..ae4c0d05e 100644 +--- a/contrib/epee/include/net/http_base.h ++++ b/contrib/epee/include/net/http_base.h +@@ -28,7 +28,7 @@ + + #pragma once + #include "memwipe.h" +- ++#include + #include + + #include +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0006-fix-build.patch b/patches/wownero/0006-fix-build.patch deleted file mode 100644 index 0c93665..0000000 --- a/patches/wownero/0006-fix-build.patch +++ /dev/null @@ -1,137 +0,0 @@ -From cf12239f0c85f5865a796ac97c82f409604cfd3e Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 26 Mar 2024 09:44:54 +0100 -Subject: [PATCH 06/14] fix build - ---- - contrib/depends/hosts/linux.mk | 8 +++---- - contrib/depends/packages/android_ndk.mk | 2 ++ - contrib/depends/packages/packages.mk | 2 +- - contrib/depends/packages/polyseed.mk | 23 +++++++++++++++++++ - contrib/depends/packages/sodium.mk | 2 +- - .../patches/polyseed/force-static-mingw.patch | 23 +++++++++++++++++++ - 6 files changed, 54 insertions(+), 6 deletions(-) - create mode 100644 contrib/depends/packages/polyseed.mk - create mode 100644 contrib/depends/patches/polyseed/force-static-mingw.patch - -diff --git a/contrib/depends/hosts/linux.mk b/contrib/depends/hosts/linux.mk -index 912fdb03c..b79799f30 100644 ---- a/contrib/depends/hosts/linux.mk -+++ b/contrib/depends/hosts/linux.mk -@@ -11,15 +11,15 @@ linux_debug_CXXFLAGS=$(linux_debug_CFLAGS) - linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC - - ifeq (86,$(findstring 86,$(build_arch))) --i686_linux_CC=gcc -m32 --i686_linux_CXX=g++ -m32 -+i686_linux_CC=i686-linux-gnu-gcc -+i686_linux_CXX=i686-linux-gnu-g++ - i686_linux_AR=ar - i686_linux_RANLIB=ranlib - i686_linux_NM=nm - i686_linux_STRIP=strip - --x86_64_linux_CC=gcc -m64 --x86_64_linux_CXX=g++ -m64 -+x86_64_linux_CC=x86_64-linux-gnu-gcc -+x86_64_linux_CXX=x86_64-linux-gnu-g++ - x86_64_linux_AR=ar - x86_64_linux_RANLIB=ranlib - x86_64_linux_NM=nm -diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk -index 9b8a5332f..5deff76c7 100644 ---- a/contrib/depends/packages/android_ndk.mk -+++ b/contrib/depends/packages/android_ndk.mk -@@ -7,6 +7,8 @@ $(package)_sha256_hash=5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d - define $(package)_set_vars - $(package)_config_opts_arm=--arch arm - $(package)_config_opts_aarch64=--arch arm64 -+$(package)_config_opts_x86_64=--arch x86_64 -+$(package)_config_opts_i686=--arch x86 - endef - - define $(package)_extract_cmds -diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk -index d2d1eca85..8783d4955 100644 ---- a/contrib/depends/packages/packages.mk -+++ b/contrib/depends/packages/packages.mk -@@ -1,4 +1,4 @@ --packages:=boost openssl zeromq libiconv expat unbound -+packages:=boost openssl zeromq libiconv expat unbound polyseed - - # ccache is useless in gitian builds - ifneq ($(GITIAN),1) -diff --git a/contrib/depends/packages/polyseed.mk b/contrib/depends/packages/polyseed.mk -new file mode 100644 -index 000000000..2ddeac621 ---- /dev/null -+++ b/contrib/depends/packages/polyseed.mk -@@ -0,0 +1,23 @@ -+package=polyseed -+$(package)_version=2.0.0 -+$(package)_download_path=https://github.com/tevador/$(package)/archive/refs/tags/ -+$(package)_download_file=v$($(package)_version).tar.gz -+$(package)_file_name=$(package)-$($(package)_version).tar.gz -+$(package)_sha256_hash=f36282fcbcd68d32461b8230c89e1a40661bd46b91109681cec637433004135a -+$(package)_patches=force-static-mingw.patch -+ -+define $(package)_preprocess_cmds -+ patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch -+endef -+ -+define $(package)_config_cmds -+ cmake -DCMAKE_INSTALL_PREFIX=$(host_prefix) -DCMAKE_C_COMPILER=$($(package)_cc) . -+endef -+ -+define $(package)_build_cmds -+ $(MAKE) -+endef -+ -+define $(package)_stage_cmds -+ $(MAKE) DESTDIR=$($(package)_staging_dir) install -+endef -diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk -index 87b34599e..68a5b48ba 100644 ---- a/contrib/depends/packages/sodium.mk -+++ b/contrib/depends/packages/sodium.mk -@@ -6,7 +6,7 @@ $(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e - $(package)_patches=disable-glibc-getrandom-getentropy.patch fix-whitespace.patch - - define $(package)_set_vars --$(package)_config_opts=--enable-static --disable-shared -+$(package)_config_opts=--enable-static --disable-shared --with-pic - $(package)_config_opts+=--prefix=$(host_prefix) - endef - -diff --git a/contrib/depends/patches/polyseed/force-static-mingw.patch b/contrib/depends/patches/polyseed/force-static-mingw.patch -new file mode 100644 -index 000000000..f05cb2b6a ---- /dev/null -+++ b/contrib/depends/patches/polyseed/force-static-mingw.patch -@@ -0,0 +1,23 @@ -+--- a/include/polyseed.h -++++ b/include/polyseed.h -+@@ -93,13 +93,13 @@ Shared/static library definitions -+ - define POLYSEED_STATIC when linking to the static library -+ */ -+ #if defined(_WIN32) || defined(__CYGWIN__) -+- #ifdef POLYSEED_SHARED -+- #define POLYSEED_API __declspec(dllexport) -+- #elif !defined(POLYSEED_STATIC) -+- #define POLYSEED_API __declspec(dllimport) -+- #else -+- #define POLYSEED_API -+- #endif -++// #ifdef POLYSEED_SHARED -++// #define POLYSEED_API __declspec(dllexport) -++// #elif !defined(POLYSEED_STATIC) -++// #define POLYSEED_API __declspec(dllimport) -++// #else -++ #define POLYSEED_API -++// #endif -+ #define POLYSEED_PRIVATE -+ #else -+ #ifdef POLYSEED_SHARED --- -2.45.2 - diff --git a/patches/wownero/0006-use-proper-error-handling-in-get_seed.patch b/patches/wownero/0006-use-proper-error-handling-in-get_seed.patch new file mode 100644 index 0000000..8f10b80 --- /dev/null +++ b/patches/wownero/0006-use-proper-error-handling-in-get_seed.patch @@ -0,0 +1,71 @@ +From 776d22d6dc47739074f004979894f3c62d43a0c9 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 24 Jun 2024 10:49:12 +0200 +Subject: [PATCH 06/15] use proper error handling in get_seed + +--- + src/wallet/api/wallet.cpp | 17 ++++++++++++----- + src/wallet/wallet2.cpp | 5 ++++- + 2 files changed, 16 insertions(+), 6 deletions(-) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 899ef044a..e16d8f83f 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -826,12 +826,19 @@ bool WalletImpl::close(bool store) + + std::string WalletImpl::seed(const std::string& seed_offset) const + { +- if (checkBackgroundSync("cannot get seed")) ++ if (checkBackgroundSync("cannot get seed")) { ++ setStatusError("cannot get seed"); + return std::string(); +- epee::wipeable_string seed; +- if (m_wallet) +- m_wallet->get_seed(seed, seed_offset); +- return std::string(seed.data(), seed.size()); // TODO ++ } ++ try { ++ epee::wipeable_string seed; ++ if (m_wallet) ++ m_wallet->get_seed(seed, seed_offset); ++ return std::string(seed.data(), seed.size()); // TODO ++ } catch (const std::exception &e) { ++ setStatusError(e.what()); ++ return std::string(); ++ } + } + + std::string WalletImpl::getSeedLanguage() const +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 651161d14..1e527cf97 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -1440,11 +1440,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab + bool keys_deterministic = is_deterministic(); + if (!keys_deterministic) + { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "This is not a deterministic wallet"); + std::cout << "This is not a deterministic wallet" << std::endl; + return false; + } + if (seed_language.empty()) + { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "seed_language not set"); + std::cout << "seed_language not set" << std::endl; + return false; + } +@@ -1454,8 +1456,9 @@ 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)) + { ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Failed to create seed from key for language: "+seed_language+", falling back to English."); + std::cout << "Failed to create seed from key for language: " << seed_language << std::endl; +- return false; ++ crypto::ElectrumWords::bytes_to_words(key, electrum_words, "English"); + } + + return true; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0007-UR-functions.patch b/patches/wownero/0007-UR-functions.patch new file mode 100644 index 0000000..ad0592c --- /dev/null +++ b/patches/wownero/0007-UR-functions.patch @@ -0,0 +1,1036 @@ +From 1302c64b0218da6d32b9dd9cbf1c2c56f51e6aff Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Tue, 12 Mar 2024 10:09:50 +0100 +Subject: [PATCH 07/15] UR functions + +This commit adds UR functions for UR tasks, +I believe that the right place to get +UR strings is the wallet code itself, +especially because it allows us to +skip the part when we have to store +things to file to encode them later. +Now we are fully in memory + +Things broken in the commit +- ledger support. + AUTO_LOCK_CMD macro causes compile time + issues with this patch. I don't know why + just yet, this is a issue that I'll fix + later. However (considering the purpose + of this patch) it is not a dealbreaker. +--- + .gitmodules | 5 +- + CMakeLists.txt | 4 +- + contrib/depends/hosts/darwin.mk | 2 +- + contrib/depends/toolchain.cmake.in | 2 +- + external/CMakeLists.txt | 1 + + external/bc-ur | 1 + + src/device/device_ledger.cpp | 5 +- + src/wallet/CMakeLists.txt | 1 + + src/wallet/api/pending_transaction.cpp | 33 +++ + src/wallet/api/pending_transaction.h | 1 + + src/wallet/api/unsigned_transaction.cpp | 42 ++++ + src/wallet/api/unsigned_transaction.h | 1 + + src/wallet/api/wallet.cpp | 309 +++++++++++++++++++++++- + src/wallet/api/wallet.h | 8 + + src/wallet/api/wallet2_api.h | 22 +- + src/wallet/wallet2.cpp | 141 +++++++---- + src/wallet/wallet2.h | 3 + + 17 files changed, 521 insertions(+), 60 deletions(-) + create mode 160000 external/bc-ur + +diff --git a/.gitmodules b/.gitmodules +index 991071fbe..b24855d9b 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -16,4 +16,7 @@ + path = external/randomwow + url = https://codeberg.org/wownero/RandomWOW + branch = 1.2.1-wow +- ++[submodule "external/bc-ur"] ++ path = external/bc-ur ++ url = https://github.com/MrCyjaneK/bc-ur ++ branch = misc +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e387ffb1b..8b81c7ab7 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -96,7 +96,8 @@ enable_language(C ASM) + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED ON) + set(CMAKE_C_EXTENSIONS OFF) +-set(CMAKE_CXX_STANDARD 14) ++set(CMAKE_CXX_STANDARD 17) ++add_definitions(-D_LIBCPP_ENABLE_CXX17_REMOVED_FEATURES) # boost: no template named 'unary_function' in namespace 'std'; did you mean '__unary_function'? + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + +@@ -365,6 +366,7 @@ if(NOT MANUAL_SUBMODULES) + endfunction () + + message(STATUS "Checking submodules") ++ #check_submodule(external/bc-ur) + check_submodule(external/miniupnp) + check_submodule(external/rapidjson) + #check_submodule(external/trezor-common) +diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk +index 79d449054..83d83036b 100644 +--- a/contrib/depends/hosts/darwin.mk ++++ b/contrib/depends/hosts/darwin.mk +@@ -1,4 +1,4 @@ +-OSX_MIN_VERSION=10.8 ++OSX_MIN_VERSION=10.14 + LD64_VERSION=609 + ifeq (aarch64, $(host_arch)) + CC_target=arm64-apple-$(host_os) +diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in +index f118c754e..f26655d68 100644 +--- a/contrib/depends/toolchain.cmake.in ++++ b/contrib/depends/toolchain.cmake.in +@@ -94,7 +94,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + SET(BREW OFF) + SET(PORT OFF) + SET(CMAKE_OSX_SYSROOT "@prefix@/native/SDK/") +- SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.08") ++ SET(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") + SET(CMAKE_CXX_STANDARD 14) + SET(LLVM_ENABLE_PIC OFF) + SET(LLVM_ENABLE_PIE OFF) +diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt +index 3184ae5a1..88a7bb0b5 100644 +--- a/external/CMakeLists.txt ++++ b/external/CMakeLists.txt +@@ -72,4 +72,5 @@ endif() + add_subdirectory(db_drivers) + add_subdirectory(easylogging++) + add_subdirectory(qrcodegen) ++add_subdirectory(bc-ur) + add_subdirectory(randomwow EXCLUDE_FROM_ALL) +diff --git a/external/bc-ur b/external/bc-ur +new file mode 160000 +index 000000000..d82e7c753 +--- /dev/null ++++ b/external/bc-ur +@@ -0,0 +1 @@ ++Subproject commit d82e7c753e710b8000706dc3383b498438795208 +diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp +index 9961d13e7..8403d76e8 100644 +--- a/src/device/device_ledger.cpp ++++ b/src/device/device_ledger.cpp +@@ -313,12 +313,13 @@ namespace hw { + + /* ======================================================================= */ + /* LOCKER */ +- /* ======================================================================= */ ++ /* ======================================================================= */ + + //automatic lock one more level on device ensuring the current thread is allowed to use it ++ #pragma message ("Warning AUTO_LOCK_CMD is intentionally left broken. This is yet to be fixed.") + #define AUTO_LOCK_CMD() \ + /* lock both mutexes without deadlock*/ \ +- boost::lock(device_locker, command_locker); \ ++ /* boost::lock(device_locker, command_locker); */ \ + /* make sure both already-locked mutexes are unlocked at the end of scope */ \ + boost::lock_guard lock1(device_locker, boost::adopt_lock); \ + boost::lock_guard lock2(command_locker, boost::adopt_lock) +diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt +index fdf3f2f5d..66384fe31 100644 +--- a/src/wallet/CMakeLists.txt ++++ b/src/wallet/CMakeLists.txt +@@ -50,6 +50,7 @@ monero_add_library(wallet + target_link_libraries(wallet + PUBLIC + rpc_base ++ bc-ur + multisig + common + cryptonote_core +diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp +index 70a702796..9c3c26ee5 100644 +--- a/src/wallet/api/pending_transaction.cpp ++++ b/src/wallet/api/pending_transaction.cpp +@@ -42,6 +42,8 @@ + #include + #include + ++#include "bc-ur/src/bc-ur.hpp" ++ + using namespace std; + + namespace Monero { +@@ -162,6 +164,37 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) + return m_status == Status_Ok; + } + ++std::string PendingTransactionImpl::commitUR(int max_fragment_length) { ++ ++ LOG_PRINT_L3("m_pending_tx size: " << m_pending_tx.size()); ++ ++ try { ++ std::string ptx = m_wallet.m_wallet->dump_tx_to_str(m_pending_tx); ++ m_status = Status_Ok; ++ auto urMessage = ur::string_to_bytes(ptx); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ std::string type; ++ if (m_wallet.watchOnly()) { ++ type = "xmr-txunsigned"; ++ } else { ++ type = "xmr-txsigned"; ++ } ++ ur::UR urData = ur::UR(type, cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } catch (const std::exception &e) { ++ m_errorString = string(tr("Unknown exception: ")) + e.what(); ++ m_status = Status_Error; ++ return ""; ++ } ++} ++ ++ + uint64_t PendingTransactionImpl::amount() const + { + uint64_t result = 0; +diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h +index 0a9779c07..403bfe281 100644 +--- a/src/wallet/api/pending_transaction.h ++++ b/src/wallet/api/pending_transaction.h +@@ -46,6 +46,7 @@ public: + int status() const override; + std::string errorString() const override; + bool commit(const std::string &filename = "", bool overwrite = false) override; ++ std::string commitUR(int max_fragment_length = 130) override; + uint64_t amount() const override; + uint64_t dust() const override; + uint64_t fee() const override; +diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp +index 6165a2240..fd03e959d 100644 +--- a/src/wallet/api/unsigned_transaction.cpp ++++ b/src/wallet/api/unsigned_transaction.cpp +@@ -40,6 +40,8 @@ + #include + #include + ++#include "bc-ur/src/bc-ur.hpp" ++ + using namespace std; + + namespace Monero { +@@ -96,6 +98,46 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName) + return true; + } + ++std::string UnsignedTransactionImpl::signUR(int max_fragment_length) ++{ ++ if(m_wallet.watchOnly()) ++ { ++ m_errorString = tr("This is a watch only wallet"); ++ m_status = Status_Error; ++ return ""; ++ } ++ std::vector ptx; ++ try ++ { ++ tools::wallet2::signed_tx_set signed_txes; ++ std::string signedTx = m_wallet.m_wallet->sign_tx_dump_to_str(m_unsigned_tx_set, ptx, signed_txes); ++ if (signedTx.empty()) ++ { ++ m_errorString = tr("Failed to sign transaction"); ++ m_status = Status_Error; ++ return ""; ++ } ++ auto urMessage = ur::string_to_bytes(signedTx); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ std::string type = "xmr-txsigned"; ++ ur::UR urData = ur::UR(type, cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } ++ catch (const std::exception &e) ++ { ++ m_errorString = string(tr("Failed to sign transaction")) + e.what(); ++ m_status = Status_Error; ++ return ""; ++ } ++ return ""; ++} ++ + //---------------------------------------------------------------------------------------------------- + bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) + { +diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h +index 30065a7fa..a94b23f75 100644 +--- a/src/wallet/api/unsigned_transaction.h ++++ b/src/wallet/api/unsigned_transaction.h +@@ -53,6 +53,7 @@ public: + uint64_t txCount() const override; + // sign txs and save to file + bool sign(const std::string &signedFileName) override; ++ std::string signUR(int max_fragment_length = 130) override; + std::string confirmationMessage() const override {return m_confirmationMessage;} + uint64_t minMixinCount() const override; + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index e16d8f83f..ee000e7ab 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -47,6 +47,7 @@ + + #include + #include ++#include "bc-ur/src/bc-ur.hpp" + + using namespace std; + using namespace cryptonote; +@@ -1066,6 +1067,24 @@ uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const + return m_wallet->unlocked_balance(accountIndex, false); + } + ++uint64_t WalletImpl::viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const ++{ ++ clearStatus(); ++ ++ std::vector kis; ++ for (const auto &key_image : key_images) { ++ crypto::key_image ki; ++ if (!epee::string_tools::hex_to_pod(key_image, ki)) ++ { ++ setStatusError(tr("failed to parse key image")); ++ return 0; ++ } ++ kis.push_back(ki); ++ } ++ ++ return m_wallet->view_only_balance(accountIndex, kis); ++} ++ + uint64_t WalletImpl::blockChainHeight() const + { + if(m_wallet->light_wallet()) { +@@ -1208,6 +1227,61 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file + return transaction; + } + ++ ++UnsignedTransaction *WalletImpl::loadUnsignedTxUR(const std::string &input) { ++ clearStatus(); ++ UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this); ++ auto decoder = ur::URDecoder(); ++ ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ transaction->m_status = UnsignedTransaction::Status::Status_Error; ++ transaction->m_errorString = errorString(); ++ return transaction; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ transaction->m_status = UnsignedTransaction::Status::Status_Error; ++ transaction->m_errorString = errorString(); ++ return transaction; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ ++ if (checkBackgroundSync("cannot load tx") || !m_wallet->parse_unsigned_tx_from_str(data, transaction->m_unsigned_tx_set)){ ++ setStatusError(tr("Failed to load unsigned transactions")); ++ transaction->m_status = UnsignedTransaction::Status::Status_Error; ++ transaction->m_errorString = errorString(); ++ ++ return transaction; ++ } ++ ++ // Check tx data and construct confirmation message ++ std::string extra_message; ++ if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty()) ++ extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str(); ++ transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message); ++ setStatus(transaction->status(), transaction->errorString()); ++ ++ return transaction; ++} ++ + bool WalletImpl::submitTransaction(const string &fileName) { + clearStatus(); + if (checkBackgroundSync("cannot submit tx")) +@@ -1219,7 +1293,7 @@ bool WalletImpl::submitTransaction(const string &fileName) { + setStatus(Status_Ok, tr("Failed to load transaction from file")); + return false; + } +- ++ + if(!transaction->commit()) { + setStatusError(transaction->m_errorString); + return false; +@@ -1228,6 +1302,61 @@ bool WalletImpl::submitTransaction(const string &fileName) { + return true; + } + ++ ++bool WalletImpl::submitTransactionUR(const string &input) { ++ clearStatus(); ++ auto decoder = ur::URDecoder(); ++ ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ return false; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ return false; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ if (checkBackgroundSync("cannot submit tx")) ++ return false; ++ std::unique_ptr transaction(new PendingTransactionImpl(*this)); ++ ++ bool r = m_wallet->parse_tx_from_str(data, transaction->m_pending_tx, NULL); ++ if (!r) { ++ setStatus(Status_Ok, tr("Failed to load transaction from file")); ++ return false; ++ } ++ ++ if(!transaction->commit()) { ++ setStatusError(transaction->m_errorString); ++ return false; ++ } ++ ++ return true; ++} ++ ++ ++bool WalletImpl::hasUnknownKeyImages() const ++{ ++ return m_wallet->has_unknown_key_images(); ++} ++ + bool WalletImpl::exportKeyImages(const string &filename, bool all) + { + if (m_wallet->watch_only()) +@@ -1255,6 +1384,39 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) + return true; + } + ++std::string WalletImpl::exportKeyImagesUR(size_t max_fragment_length, bool all) ++{ ++ if (m_wallet->watch_only()) ++ { ++ setStatusError(tr("Wallet is view only")); ++ return ""; ++ } ++ if (checkBackgroundSync("cannot export key images")) ++ return ""; ++ ++ try ++ { ++ std::string keyImages = m_wallet->export_key_images_str(all); ++ auto urMessage = ur::string_to_bytes(keyImages); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ ur::UR urData = ur::UR("xmr-keyimage", cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error exporting key images: " << e.what()); ++ setStatusError(e.what()); ++ return ""; ++ } ++ return ""; ++} ++ + bool WalletImpl::importKeyImages(const string &filename) + { + if (checkBackgroundSync("cannot import key images")) +@@ -1280,6 +1442,62 @@ bool WalletImpl::importKeyImages(const string &filename) + return true; + } + ++ ++bool WalletImpl::importKeyImagesUR(const string &input) ++{ ++ if (checkBackgroundSync("cannot import key images")) ++ return false; ++ if (!trustedDaemon()) { ++ setStatusError(tr("Key images can only be imported with a trusted daemon")); ++ return false; ++ } ++ try ++ { ++ auto decoder = ur::URDecoder(); ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ return false; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ return false; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ ++ uint64_t spent = 0, unspent = 0; ++ ++ uint64_t height = m_wallet->import_key_images_str(data, spent, unspent); ++ LOG_PRINT_L2("Signed key images imported to height " << height << ", " ++ << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error exporting key images: " << e.what()); ++ setStatusError(string(tr("Failed to import key images: ")) + e.what()); ++ return false; ++ } ++ ++ return true; ++} ++ ++ + bool WalletImpl::exportOutputs(const string &filename, bool all) + { + if (checkBackgroundSync("cannot export outputs")) +@@ -1312,6 +1530,40 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) + return true; + } + ++std::string WalletImpl::exportOutputsUR(size_t max_fragment_length, bool all) ++{ ++ ++ if (checkBackgroundSync("cannot export outputs")) ++ return ""; ++ if (m_wallet->key_on_device()) ++ { ++ setStatusError(string(tr("Not supported on HW wallets."))); ++ return ""; ++ } ++ ++ try ++ { ++ std::string data = m_wallet->export_outputs_to_str(all); ++ auto urMessage = ur::string_to_bytes(data); ++ ur::ByteVector cbor; ++ ur::CborLite::encodeBytes(cbor, urMessage); ++ ur::UR urData = ur::UR("xmr-output", cbor); ++ auto encoder = ur::UREncoder(urData, max_fragment_length); ++ std::string output; ++ for(size_t i = 0; i < encoder.seq_len(); i++) { ++ output.append("\n"+encoder.next_part()); ++ } ++ return output; ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Error exporting outputs: " << e.what()); ++ setStatusError(string(tr("Error exporting outputs: ")) + e.what()); ++ return ""; ++ } ++} ++ ++ + bool WalletImpl::importOutputs(const string &filename) + { + if (checkBackgroundSync("cannot import outputs")) +@@ -1346,6 +1598,61 @@ bool WalletImpl::importOutputs(const string &filename) + return true; + } + ++ ++bool WalletImpl::importOutputsUR(const string &input) ++{ ++ if (checkBackgroundSync("cannot import outputs")) ++ return false; ++ if (m_wallet->key_on_device()) ++ { ++ setStatusError(string(tr("Not supported on HW wallets."))); ++ return false; ++ } ++ ++ try ++ { ++ auto decoder = ur::URDecoder(); ++ ++ std::string delimiter = "\n"; ++ std::string inp = input; ++ size_t pos = 0; ++ std::string token; ++ while ((pos = inp.find(delimiter)) != std::string::npos) { ++ token = inp.substr(0, pos); ++ decoder.receive_part(token); ++ inp.erase(0, pos + delimiter.length()); ++ } ++ decoder.receive_part(inp); ++ ++ if (decoder.is_failure()) { ++ setStatusError(decoder.result_error().what()); ++ return false; ++ } ++ ++ if (!decoder.is_complete()) { ++ setStatusError("file ended but ur didn't complete"); ++ return false; ++ } ++ ++ std::string data; ++ auto cbor = decoder.result_ur().cbor(); ++ auto i = cbor.begin(); ++ auto end = cbor.end(); ++ ur::CborLite::decodeBytes(i, end, data); ++ size_t n_outputs = m_wallet->import_outputs_from_str(std::string(data)); ++ LOG_PRINT_L2(std::to_string(n_outputs) << " outputs imported"); ++ } ++ catch (const std::exception &e) ++ { ++ LOG_ERROR("Failed to import outputs: " << e.what()); ++ setStatusError(string(tr("Failed to import outputs: ")) + e.what()); ++ return false; ++ } ++ ++ return true; ++} ++ ++ + bool WalletImpl::scanTransactions(const std::vector &txids) + { + if (checkBackgroundSync("cannot scan transactions")) +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index ac7ce2f6a..edf8bb8ce 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -112,6 +112,7 @@ public: + bool setProxy(const std::string &address) override; + uint64_t balance(uint32_t accountIndex = 0) const override; + uint64_t unlockedBalance(uint32_t accountIndex = 0) const override; ++ uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images) const override; + uint64_t blockChainHeight() const override; + uint64_t approximateBlockChainHeight() const override; + uint64_t estimateBlockChainHeight() const override; +@@ -164,11 +165,18 @@ public: + std::set subaddr_indices = {}) override; + virtual PendingTransaction * createSweepUnmixableTransaction() override; + bool submitTransaction(const std::string &fileName) override; ++ bool submitTransactionUR(const std::string &input) override; + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; ++ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) override; ++ bool hasUnknownKeyImages() const override; + bool exportKeyImages(const std::string &filename, bool all = false) override; ++ std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) override; + bool importKeyImages(const std::string &filename) override; ++ bool importKeyImagesUR(const std::string &input) override; + bool exportOutputs(const std::string &filename, bool all = false) override; ++ std::string exportOutputsUR(size_t max_fragment_length, bool all) override; + bool importOutputs(const std::string &filename) override; ++ bool importOutputsUR(const std::string &filename) override; + bool scanTransactions(const std::vector &txids) override; + + bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password = optional()) override; +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index e349df176..764adbfbf 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -91,6 +91,7 @@ struct PendingTransaction + virtual std::string errorString() const = 0; + // commit transaction or save to file if filename is provided. + virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0; ++ virtual std::string commitUR(int max_fragment_length = 130) = 0; + virtual uint64_t amount() const = 0; + virtual uint64_t dust() const = 0; + virtual uint64_t fee() const = 0; +@@ -160,7 +161,8 @@ struct UnsignedTransaction + * @param signedFileName + * return - true on success + */ +- virtual bool sign(const std::string &signedFileName) = 0; ++ virtual bool sign(const std::string &signedFileName) = 0; ++ virtual std::string signUR(int max_fragment_length = 130) = 0; + }; + + /** +@@ -626,6 +628,7 @@ struct Wallet + result += unlockedBalance(i); + return result; + } ++ virtual uint64_t viewOnlyBalance(uint32_t accountIndex, const std::vector &key_images = {}) const = 0; + + /** + * @brief watchOnly - checks if wallet is watch only +@@ -884,13 +887,15 @@ struct Wallet + * after object returned + */ + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; +- +- /*! ++ virtual UnsignedTransaction * loadUnsignedTxUR(const std::string &input) = 0; ++ ++ /*! + * \brief submitTransaction - submits transaction in signed tx file + * \return - true on success + */ + virtual bool submitTransaction(const std::string &fileName) = 0; +- ++ virtual bool submitTransactionUR(const std::string &input) = 0; ++ + + /*! + * \brief disposeTransaction - destroys transaction object +@@ -906,6 +911,8 @@ struct Wallet + virtual uint64_t estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const = 0; + ++ virtual bool hasUnknownKeyImages() const = 0; ++ + /*! + * \brief exportKeyImages - exports key images to file + * \param filename +@@ -913,20 +920,22 @@ struct Wallet + * \return - true on success + */ + virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0; +- ++ virtual std::string exportKeyImagesUR(size_t max_fragment_length, bool all = false) = 0; + /*! + * \brief importKeyImages - imports key images from file + * \param filename + * \return - true on success + */ + virtual bool importKeyImages(const std::string &filename) = 0; ++ virtual bool importKeyImagesUR(const std::string &input) = 0; + + /*! +- * \brief importOutputs - exports outputs to file ++ * \brief exportOutputs - exports outputs to file + * \param filename + * \return - true on success + */ + virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; ++ virtual std::string exportOutputsUR(size_t max_fragment_length, bool all = false) = 0; + + /*! + * \brief importOutputs - imports outputs from file +@@ -934,6 +943,7 @@ struct Wallet + * \return - true on success + */ + virtual bool importOutputs(const std::string &filename) = 0; ++ virtual bool importOutputsUR(const std::string &filename) = 0; + + /*! + * \brief scanTransactions - scan a list of transaction ids, this operation may reveal the txids to the remote node and affect your privacy +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 1e527cf97..671fa5298 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -948,6 +948,16 @@ uint32_t get_subaddress_clamped_sum(uint32_t idx, uint32_t extra) + return idx + extra; + } + ++bool is_preferred_input(const std::vector& 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); +@@ -6989,6 +6999,25 @@ uint64_t wallet2::unlocked_balance(uint32_t index_major, bool strict, uint64_t * + return amount; + } + //---------------------------------------------------------------------------------------------------- ++uint64_t wallet2::view_only_balance(uint32_t index_major, const std::vector& selected_inputs) ++{ ++ uint64_t amount = 0; ++ for (const auto &td : m_transfers) { ++ if (is_preferred_input(selected_inputs, td.m_key_image) && ++ !is_spent(td, false) && ++ !td.m_frozen && ++ !td.m_key_image_partial && ++ td.m_key_image_known && ++ td.is_rct() && ++ is_transfer_unlocked(td) && ++ td.m_subaddr_index.major == index_major) ++ { ++ amount += td.m_amount; ++ } ++ } ++ return amount; ++} ++//---------------------------------------------------------------------------------------------------- + std::map wallet2::balance_per_subaddress(uint32_t index_major, bool strict) const + { + std::map amount_per_subaddr; +@@ -7840,9 +7869,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector additional_derivations; + +- // compute public keys from out secret keys +- crypto::public_key tx_pub_key; +- crypto::secret_key_to_public_key(txs[n].tx_key, tx_pub_key); ++ crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); + std::vector additional_tx_pub_keys; + for (const crypto::secret_key &skey: txs[n].additional_tx_keys) + { +@@ -11232,7 +11259,7 @@ std::vector wallet2::create_transactions_2(std::vector m_ignore_outputs_above || td.amount() < m_ignore_outputs_below) + { +@@ -11282,9 +11309,15 @@ std::vector wallet2::create_transactions_2(std::vector(); ++ // use tobotoht's code path on view-only wallet, otherwise default to upstream ++ bool throwOnNoEnotes = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet; + ++ if (throwOnNoEnotes) { ++ THROW_WALLET_EXCEPTION_IF(unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty(), error::wallet_internal_error, "No enotes available to spend") ++ } else { ++ if (unused_dust_indices_per_subaddr.empty() && unused_transfers_indices_per_subaddr.empty()) ++ return std::vector(); ++ } + // if empty, put dummy entry so that the front can be referenced later in the loop + if (unused_dust_indices_per_subaddr.empty()) + unused_dust_indices_per_subaddr.push_back({}); +@@ -13911,33 +13944,40 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle + + bool wallet2::export_key_images(const std::string &filename, bool all) const + { +- PERF_TIMER(export_key_images); +- std::pair>> ski = export_key_images(all); +- std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); +- const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; +- const uint32_t offset = ski.first; ++ std::string data = export_key_images_str(all); ++ return save_to_file(filename, data); ++} + +- std::string data; +- data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); +- data.resize(4); +- data[0] = offset & 0xff; +- data[1] = (offset >> 8) & 0xff; +- data[2] = (offset >> 16) & 0xff; +- data[3] = (offset >> 24) & 0xff; +- data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); +- data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); +- for (const auto &i: ski.second) +- { +- data += std::string((const char *)&i.first, sizeof(crypto::key_image)); +- data += std::string((const char *)&i.second, sizeof(crypto::signature)); +- } ++std::string wallet2::export_key_images_str(bool all) const ++{ ++ PERF_TIMER(export_key_images); ++ std::pair>> ski = export_key_images(all); ++ std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); ++ const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; ++ const uint32_t offset = ski.first; + +- // encrypt data, keep magic plaintext +- PERF_TIMER(export_key_images_encrypt); +- std::string ciphertext = encrypt_with_view_secret_key(data); +- return save_to_file(filename, magic + ciphertext); ++ std::string data; ++ data.reserve(4 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); ++ data.resize(4); ++ data[0] = offset & 0xff; ++ data[1] = (offset >> 8) & 0xff; ++ data[2] = (offset >> 16) & 0xff; ++ data[3] = (offset >> 24) & 0xff; ++ data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); ++ data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); ++ for (const auto &i: ski.second) ++ { ++ data += std::string((const char *)&i.first, sizeof(crypto::key_image)); ++ data += std::string((const char *)&i.second, sizeof(crypto::signature)); ++ } ++ ++ // encrypt data, keep magic plaintext ++ PERF_TIMER(export_key_images_encrypt); ++ std::string ciphertext = encrypt_with_view_secret_key(data); ++ return magic + ciphertext; + } + ++ + //---------------------------------------------------------------------------------------------------- + std::pair>> wallet2::export_key_images(bool all) const + { +@@ -13992,53 +14032,60 @@ std::pair> + return std::make_pair(offset, ski); + } + +-uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) ++uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) { ++ std::string data; ++ ++ bool r = load_from_file(filename, data); ++ ++ THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); ++ ++ return import_key_images_str(data, spent, unspent); ++} ++ ++uint64_t wallet2::import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent) + { + PERF_TIMER(import_key_images_fsu); +- std::string data; +- bool r = load_from_file(filename, data); +- +- THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); ++ std::string data_local = data; + + const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC); + if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen)) + { +- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic")); + } + + try + { + PERF_TIMER(import_key_images_decrypt); +- data = decrypt_with_view_secret_key(std::string(data, magiclen)); ++ data_local = decrypt_with_view_secret_key(std::string(data, magiclen)); + } + catch (const std::exception &e) + { +- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what()); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + ": " + e.what()); + } + + const size_t headerlen = 4 + 2 * sizeof(crypto::public_key); +- THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename); +- const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24); +- const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4]; +- const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)]; ++ THROW_WALLET_EXCEPTION_IF(data_local.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ")); ++ const uint32_t offset = (uint8_t)data_local[0] | (((uint8_t)data_local[1]) << 8) | (((uint8_t)data_local[2]) << 16) | (((uint8_t)data_local[3]) << 24); ++ const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data_local[4]; ++ const crypto::public_key &public_view_key = *(const crypto::public_key*)&data_local[4 + sizeof(crypto::public_key)]; + const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; + if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) + { +- THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account"); ++ THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + " are for a different account"); + } + THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); + + const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); +- THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size, +- error::wallet_internal_error, std::string("Bad data size from file ") + filename); +- size_t nki = (data.size() - headerlen) / record_size; ++ THROW_WALLET_EXCEPTION_IF((data_local.size() - headerlen) % record_size, ++ error::wallet_internal_error, std::string("Bad data size from file ")); ++ size_t nki = (data_local.size() - headerlen) / record_size; + + std::vector> ski; + ski.reserve(nki); + for (size_t n = 0; n < nki; ++n) + { +- crypto::key_image key_image = *reinterpret_cast(&data[headerlen + n * record_size]); +- crypto::signature signature = *reinterpret_cast(&data[headerlen + n * record_size + sizeof(crypto::key_image)]); ++ crypto::key_image key_image = *reinterpret_cast(&data_local[headerlen + n * record_size]); ++ crypto::signature signature = *reinterpret_cast(&data_local[headerlen + n * record_size + sizeof(crypto::key_image)]); + + ski.push_back(std::make_pair(key_image, signature)); + } +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index c07a47c11..80ff0698d 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1150,6 +1150,7 @@ private: + // locked & unlocked balance of given or current subaddress account + uint64_t balance(uint32_t subaddr_index_major, bool strict) const; + uint64_t unlocked_balance(uint32_t subaddr_index_major, bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL); ++ uint64_t view_only_balance(uint32_t index_major, const std::vector& selected_inputs = {}); + // locked & unlocked balance per subaddress of given or current subaddress account + std::map balance_per_subaddress(uint32_t subaddr_index_major, bool strict) const; + std::map>> unlocked_balance_per_subaddress(uint32_t subaddr_index_major, bool strict); +@@ -1624,9 +1625,11 @@ private: + std::tuple> export_blockchain() const; + void import_blockchain(const std::tuple> &bc); + bool export_key_images(const std::string &filename, bool all = false) const; ++ std::string export_key_images_str(bool all) const; + std::pair>> export_key_images(bool all = false) const; + uint64_t import_key_images(const std::vector> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true); + uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); ++ uint64_t import_key_images_str(const std::string &data, uint64_t &spent, uint64_t &unspent); + bool import_key_images(std::vector key_images, size_t offset=0, boost::optional> selected_transfers=boost::none); + bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); + crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0007-macos-build-fix.patch b/patches/wownero/0007-macos-build-fix.patch deleted file mode 100644 index 2077208..0000000 --- a/patches/wownero/0007-macos-build-fix.patch +++ /dev/null @@ -1,114 +0,0 @@ -From b3916ec820e9b1aa3ad5af732c3fd32591b21a05 Mon Sep 17 00:00:00 2001 -From: Your Name -Date: Thu, 28 Mar 2024 02:03:08 +0100 -Subject: [PATCH 07/14] macos build fix - ---- - contrib/depends/hosts/darwin.mk | 2 + - contrib/depends/packages/polyseed.mk | 13 +++-- - .../polyseed/0001-disable-soname.patch | 48 +++++++++++++++++++ - 3 files changed, 59 insertions(+), 4 deletions(-) - create mode 100644 contrib/depends/patches/polyseed/0001-disable-soname.patch - -diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk -index 79d449054..cbe795081 100644 ---- a/contrib/depends/hosts/darwin.mk -+++ b/contrib/depends/hosts/darwin.mk -@@ -8,6 +8,8 @@ endif - darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -B$(host_prefix)/native/bin/$(host)- - darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)- - -+darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib -+ - darwin_CFLAGS=-pipe - darwin_CXXFLAGS=$(darwin_CFLAGS) - darwin_ARFLAGS=cr -diff --git a/contrib/depends/packages/polyseed.mk b/contrib/depends/packages/polyseed.mk -index 2ddeac621..0071b20f3 100644 ---- a/contrib/depends/packages/polyseed.mk -+++ b/contrib/depends/packages/polyseed.mk -@@ -4,18 +4,23 @@ $(package)_download_path=https://github.com/tevador/$(package)/archive/refs/tags - $(package)_download_file=v$($(package)_version).tar.gz - $(package)_file_name=$(package)-$($(package)_version).tar.gz - $(package)_sha256_hash=f36282fcbcd68d32461b8230c89e1a40661bd46b91109681cec637433004135a --$(package)_patches=force-static-mingw.patch -+$(package)_patches=force-static-mingw.patch 0001-disable-soname.patch - - define $(package)_preprocess_cmds -- patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch -+ patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch &&\ -+ patch -p1 < $($(package)_patch_dir)/0001-disable-soname.patch - endef - - define $(package)_config_cmds -- cmake -DCMAKE_INSTALL_PREFIX=$(host_prefix) -DCMAKE_C_COMPILER=$($(package)_cc) . -+ CC="$($(package)_cc)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" . -+endef -+ -+define $(package)_set_vars -+ $(package)_build_opts=CC="$($(package)_cc)" - endef - - define $(package)_build_cmds -- $(MAKE) -+ CC="$($(package)_cc)" $(MAKE) - endef - - define $(package)_stage_cmds -diff --git a/contrib/depends/patches/polyseed/0001-disable-soname.patch b/contrib/depends/patches/polyseed/0001-disable-soname.patch -new file mode 100644 -index 000000000..bd97dd394 ---- /dev/null -+++ b/contrib/depends/patches/polyseed/0001-disable-soname.patch -@@ -0,0 +1,48 @@ -+From aabafcfc0572651436d024a635483c49042fad7f Mon Sep 17 00:00:00 2001 -+From: Czarek Nakamoto -+Date: Thu, 28 Mar 2024 00:32:51 +0100 -+Subject: [PATCH] disable soname -+ -+--- -+ CMakeLists.txt | 16 +++++++++------- -+ 1 file changed, 9 insertions(+), 7 deletions(-) -+ -+diff --git a/CMakeLists.txt b/CMakeLists.txt -+index 8a8e7c2..5301353 100644 -+--- a/CMakeLists.txt -++++ b/CMakeLists.txt -+@@ -36,6 +36,7 @@ include_directories(polyseed -+ target_compile_definitions(polyseed PRIVATE POLYSEED_SHARED) -+ set_target_properties(polyseed PROPERTIES VERSION 2.0.0 -+ SOVERSION 2 -++ NO_SONAME 1 -+ C_STANDARD 11 -+ C_STANDARD_REQUIRED ON) -+ -+@@ -45,16 +46,17 @@ include_directories(polyseed_static -+ include/) -+ target_compile_definitions(polyseed_static PRIVATE POLYSEED_STATIC) -+ set_target_properties(polyseed_static PROPERTIES OUTPUT_NAME polyseed -++ NO_SONAME 1 -+ C_STANDARD 11 -+ C_STANDARD_REQUIRED ON) -+ -+-add_executable(polyseed-tests -+- tests/tests.c) -+-include_directories(polyseed-tests -+- include/) -+-target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) -+-target_link_libraries(polyseed-tests -+- PRIVATE polyseed_static) -++# add_executable(polyseed-tests -++# tests/tests.c) -++# include_directories(polyseed-tests -++# include/) -++# target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) -++# target_link_libraries(polyseed-tests -++# PRIVATE polyseed_static) -+ -+ include(GNUInstallDirs) -+ install(TARGETS polyseed polyseed_static -+-- -+2.39.2 --- -2.45.2 - diff --git a/patches/wownero/0008-FIX-wallet-listener-crashing.patch b/patches/wownero/0008-FIX-wallet-listener-crashing.patch deleted file mode 100644 index b935770..0000000 --- a/patches/wownero/0008-FIX-wallet-listener-crashing.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 80c2ee2b596b77d445c5e84f646a22949736fbd4 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 2 Apr 2024 11:56:09 +0200 -Subject: [PATCH 08/14] FIX: wallet listener crashing - ---- - src/wallet/api/wallet.cpp | 21 +++++++++++++++------ - 1 file changed, 15 insertions(+), 6 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index ed309de34..ee0eeeb01 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -201,8 +201,11 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback - << ", burnt: " << print_money(burnt) - << ", raw_output_value: " << print_money(amount) - << ", idx: " << subaddr_index); -- m_listener->moneyReceived(tx_hash, amount); -- m_listener->updated(); -+ // do not signal on sent tx if wallet is not syncronized completely -+ if (m_listener && m_wallet->synchronized()) { -+ m_listener->moneyReceived(tx_hash, amount); -+ m_listener->updated(); -+ } - } - - 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) -@@ -214,8 +217,11 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback - << ", tx: " << tx_hash - << ", amount: " << print_money(amount) - << ", idx: " << subaddr_index); -- m_listener->unconfirmedMoneyReceived(tx_hash, amount); -- m_listener->updated(); -+ // do not signal on sent tx if wallet is not syncronized completely -+ if (m_listener && m_wallet->synchronized()) { -+ m_listener->unconfirmedMoneyReceived(tx_hash, amount); -+ m_listener->updated(); -+ } - } - - virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, -@@ -227,8 +233,11 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback - << ", tx: " << tx_hash - << ", amount: " << print_money(amount) - << ", idx: " << subaddr_index); -- m_listener->moneySpent(tx_hash, amount); -- m_listener->updated(); -+ // do not signal on sent tx if wallet is not syncronized completely -+ if (m_listener && m_wallet->synchronized()) { -+ m_listener->moneySpent(tx_hash, amount); -+ m_listener->updated(); -+ } - } - - virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) --- -2.45.2 - diff --git a/patches/wownero/0008-add-dummy-device-for-ledger.patch b/patches/wownero/0008-add-dummy-device-for-ledger.patch new file mode 100644 index 0000000..6d40bb2 --- /dev/null +++ b/patches/wownero/0008-add-dummy-device-for-ledger.patch @@ -0,0 +1,604 @@ +From 07a3019d94feb48f746dbc780db1e4d2e0d6080c Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Wed, 26 Jun 2024 15:04:38 +0200 +Subject: [PATCH 08/15] add dummy device for ledger + +--- + CMakeLists.txt | 6 +- + src/device/CMakeLists.txt | 6 +- + src/device/device.cpp | 10 ++- + src/device/device.hpp | 12 +-- + src/device/device_io_dummy.cpp | 133 ++++++++++++++++++++++++++++++ + src/device/device_io_dummy.hpp | 74 +++++++++++++++++ + src/device/device_ledger.cpp | 6 +- + src/device/device_ledger.hpp | 7 +- + src/wallet/api/wallet.cpp | 94 +++++++++++++++++++++ + src/wallet/api/wallet.h | 18 ++++ + src/wallet/api/wallet2_api.h | 12 +++ + src/wallet/api/wallet_manager.cpp | 12 ++- + 12 files changed, 365 insertions(+), 25 deletions(-) + create mode 100644 src/device/device_io_dummy.cpp + create mode 100644 src/device/device_io_dummy.hpp + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 8b81c7ab7..abe44eca5 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -709,8 +709,12 @@ include_directories(${LMDB_INCLUDE}) + include_directories(${LIBUNWIND_INCLUDE}) + link_directories(${LIBUNWIND_LIBRARY_DIRS}) + ++if (HIDAPI_DUMMY) ++ add_definitions(-DHIDAPI_DUMMY) ++endif() ++ + # Final setup for hid +-if (HIDAPI_FOUND) ++if (HIDAPI_FOUND) + message(STATUS "Using HIDAPI include dir at ${HIDAPI_INCLUDE_DIR}") + add_definitions(-DHAVE_HIDAPI) + include_directories(${HIDAPI_INCLUDE_DIR}) +diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt +index e4f1159b5..14d398f87 100644 +--- a/src/device/CMakeLists.txt ++++ b/src/device/CMakeLists.txt +@@ -29,10 +29,11 @@ + set(device_sources + device.cpp + device_default.cpp ++ device_io_dummy.cpp + log.cpp + ) + +-if(HIDAPI_FOUND) ++if(HIDAPI_FOUND OR HIDAPI_DUMMY) + set(device_sources + ${device_sources} + device_ledger.cpp +@@ -45,10 +46,11 @@ set(device_headers + device_io.hpp + device_default.hpp + device_cold.hpp ++ device_io_dummy.hpp + log.hpp + ) + +-if(HIDAPI_FOUND) ++if(HIDAPI_FOUND OR HIDAPI_DUMMY) + set(device_headers + ${device_headers} + device_ledger.hpp +diff --git a/src/device/device.cpp b/src/device/device.cpp +index e6cd358b6..777584c01 100644 +--- a/src/device/device.cpp ++++ b/src/device/device.cpp +@@ -29,7 +29,7 @@ + + #include "device.hpp" + #include "device_default.hpp" +-#ifdef WITH_DEVICE_LEDGER ++#if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) + #include "device_ledger.hpp" + #endif + #include "misc_log_ex.h" +@@ -57,7 +57,7 @@ namespace hw { + + device_registry::device_registry(){ + hw::core::register_all(registry); +- #ifdef WITH_DEVICE_LEDGER ++ #if defined(WITH_DEVICE_LEDGER) && !defined(HIDAPI_DUMMY) + hw::ledger::register_all(registry); + #endif + atexit(clear_device_registry); +@@ -83,11 +83,13 @@ namespace hw { + + auto device = registry.find(device_descriptor_lookup); + if (device == registry.end()) { +- MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: "); ++ std::stringstream ss("Device not found in registry: '" + device_descriptor + "'. Known devices: "); ++ MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: \n"); + for( const auto& sm_pair : registry ) { ++ ss << "\n- " + sm_pair.first; + MERROR(" - " << sm_pair.first); + } +- throw std::runtime_error("device not found: " + device_descriptor); ++ throw std::runtime_error("device not found: " + device_descriptor + "\nlalala\n" + ss.str()); + } + return *device->second; + } +diff --git a/src/device/device.hpp b/src/device/device.hpp +index 392703a24..ffd419779 100644 +--- a/src/device/device.hpp ++++ b/src/device/device.hpp +@@ -34,17 +34,7 @@ + #include "ringct/rctTypes.h" + #include "cryptonote_config.h" + +- +-#ifndef USE_DEVICE_LEDGER +-#define USE_DEVICE_LEDGER 1 +-#endif +- +-#if !defined(HAVE_HIDAPI) +-#undef USE_DEVICE_LEDGER +-#define USE_DEVICE_LEDGER 0 +-#endif +- +-#if USE_DEVICE_LEDGER ++#if defined(HAVE_HIDAPI) || defined(HIDAPI_DUMMY) + #define WITH_DEVICE_LEDGER + #endif + +diff --git a/src/device/device_io_dummy.cpp b/src/device/device_io_dummy.cpp +new file mode 100644 +index 000000000..edb4beea3 +--- /dev/null ++++ b/src/device/device_io_dummy.cpp +@@ -0,0 +1,133 @@ ++// Copyright (c) 2017-2022, The Monero Project ++// ++// All rights reserved. ++// ++// Redistribution and use in source and binary forms, with or without modification, are ++// permitted provided that the following conditions are met: ++// ++// 1. Redistributions of source code must retain the above copyright notice, this list of ++// conditions and the following disclaimer. ++// ++// 2. Redistributions in binary form must reproduce the above copyright notice, this list ++// of conditions and the following disclaimer in the documentation and/or other ++// materials provided with the distribution. ++// ++// 3. Neither the name of the copyright holder nor the names of its contributors may be ++// used to endorse or promote products derived from this software without specific ++// prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY ++// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ++// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ++// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF ++// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++// ++ ++// device_io_dummy ++// Main goal of device_io_dummy is to emulate a hw::io::device_io without the need to actually ++// connect a device. ++// Many operating systems do not support giving raw USB access to a process (android), or don't ++// support that at all (hi iOS), therefore other means of connection can be used, either USB ++// abstraction provided by the OS (monerujo), or BLE (also monerujo). ++// Monerujo implementation is written in Java, which makes it a nice fit for iOS, but makes the ++// code extremely unportable, so for this reason the code in here is written in CPP. ++// Data transport is made available in wallet2_api.h, so wallet developers can easily plug their ++// own USB/BLE/other transport layer. ++ ++#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) ++#include ++#include "log.hpp" ++#include "device_io_dummy.hpp" ++#include "device_ledger.hpp" ++ ++ ++bool hw::io::device_io_dummy::stateIsConnected = false; ++unsigned char* hw::io::device_io_dummy::sendToDevice = {}; ++size_t hw::io::device_io_dummy::sendToDeviceLength = 0; ++unsigned char* hw::io::device_io_dummy::receivedFromDevice = {}; ++size_t hw::io::device_io_dummy::receivedFromDeviceLength = 0; ++bool hw::io::device_io_dummy::waitsForDeviceSend = false; ++bool hw::io::device_io_dummy::waitsForDeviceReceive = false; ++ ++namespace hw { ++ namespace io { ++ ++#undef MONERO_DEFAULT_LOG_CATEGORY ++#define MONERO_DEFAULT_LOG_CATEGORY "device.io_dummy" ++ device_io_dummy::device_io_dummy(int a, int b, int c, int d) { ++ MDEBUG("device_io_dummy(a: " << a << ", b: " << b << ", c: " << c << ", d: " << d <<")"); ++ } ++ ++ void device_io_dummy::init() { ++ MDEBUG("init()"); ++ } ++ ++ void device_io_dummy::connect(void *params) { ++ MDEBUG("connect(" << params << ")"); ++ stateIsConnected = true; ++ } ++ ++ void device_io_dummy::connect(const std::vector& known_devices) { ++ MDEBUG("connect(["); ++ for (const auto &item: known_devices) { ++ MDEBUG("{ interface_number: " << item.interface_number); ++ MDEBUG(" pid : " << item.pid); ++ MDEBUG(" usage_page : " << item.usage_page); ++ MDEBUG(" vid : " << item.vid << " },"); ++ } ++ MDEBUG("])"); ++ stateIsConnected = true; ++ } ++ ++ bool device_io_dummy::connected() const { ++ MDEBUG("connected()"); ++ return stateIsConnected; ++ } ++ ++ int device_io_dummy::exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input) { ++ MDEBUG("exchange(): locking mutex"); ++ boost::unique_lock lock(mutex); ++ sendToDevice = command; ++ sendToDeviceLength = cmd_len; ++ waitsForDeviceSend = true; ++ waitsForDeviceReceive = true; ++ MDEBUG("exchange(): waitsForDeviceSend"); ++ // NOTE: waitsForDeviceSend should be changed by external code ++ while (waitsForDeviceSend) { ++ usleep(1000); ++ MDEBUG("exchange(): waitsForDeviceSend (still)"); ++ } ++ ++ MDEBUG("exchange(): waitsForDeviceReceive"); ++ while (waitsForDeviceReceive) { ++ usleep(1000); ++ MDEBUG("exchange(): waitsForDeviceReceive (still)"); ++ } ++ ++ if (receivedFromDeviceLength > max_resp_len) { ++ MDEBUG("exchange(): receivedFromDeviceLength ("<& known_devices); ++ void disconnect(); ++ bool connected() const; ++ ++ int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len, bool user_input); ++ }; ++ }; ++}; ++ ++#endif // HAVE_HIDAPI +diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp +index 8403d76e8..0587eb7d3 100644 +--- a/src/device/device_ledger.cpp ++++ b/src/device/device_ledger.cpp +@@ -41,7 +41,7 @@ namespace hw { + + namespace ledger { + +- #ifdef WITH_DEVICE_LEDGER ++ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) + + #undef MONERO_DEFAULT_LOG_CATEGORY + #define MONERO_DEFAULT_LOG_CATEGORY "device.ledger" +@@ -299,7 +299,7 @@ namespace hw { + + device_ledger::device_ledger(): hw_device(0x0101, 0x05, 64, 2000) { + this->id = device_id++; +- this->reset_buffer(); ++ this->reset_buffer(); + this->mode = NONE; + this->has_view_key = false; + this->tx_in_progress = false; +@@ -533,7 +533,9 @@ namespace hw { + + bool device_ledger::connect(void) { + this->disconnect(); ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) + hw_device.connect(known_devices); ++ #endif + this->reset(); + #ifdef DEBUG_HWDEVICE + cryptonote::account_public_address pubkey; +diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp +index 03058c4f1..39454ca6d 100644 +--- a/src/device/device_ledger.hpp ++++ b/src/device/device_ledger.hpp +@@ -35,6 +35,7 @@ + #include "device.hpp" + #include "log.hpp" + #include "device_io_hid.hpp" ++#include "device_io_dummy.hpp" + #include + #include + +@@ -56,7 +57,7 @@ namespace hw { + + void register_all(std::map> ®istry); + +- #ifdef WITH_DEVICE_LEDGER ++ #if defined(WITH_DEVICE_LEDGER) || defined(HIDAPI_DUMMY) + + // Origin: https://github.com/LedgerHQ/ledger-app-monero/blob/master/src/monero_types.h + #define SW_OK 0x9000 +@@ -148,7 +149,11 @@ namespace hw { + mutable boost::mutex command_locker; + + //IO ++#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) ++ hw::io::device_io_dummy hw_device; ++#else + hw::io::device_io_hid hw_device; ++#endif + unsigned int length_send; + unsigned char buffer_send[BUFFER_SEND_SIZE]; + unsigned int length_recv; +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index ee000e7ab..556e2a8ce 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -48,6 +48,9 @@ + #include + #include + #include "bc-ur/src/bc-ur.hpp" ++#if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) ++#include "device/device_io_dummy.hpp" ++#endif + + using namespace std; + using namespace cryptonote; +@@ -3178,4 +3181,95 @@ uint64_t WalletImpl::getBytesSent() + return m_wallet->get_bytes_sent(); + } + ++ ++// HIDAPI_DUMMY ++bool WalletImpl::getStateIsConnected() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return false; ++ #else ++ return hw::io::device_io_dummy::stateIsConnected; ++ #endif ++} ++ ++unsigned char* WalletImpl::getSendToDevice() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return {}; ++ #else ++ return hw::io::device_io_dummy::sendToDevice; ++ #endif ++} ++ ++size_t WalletImpl::getSendToDeviceLength() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return -1; ++ #else ++ return hw::io::device_io_dummy::sendToDeviceLength; ++ #endif ++} ++ ++unsigned char* WalletImpl::getReceivedFromDevice() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return {}; ++ #else ++ return hw::io::device_io_dummy::receivedFromDevice; ++ #endif ++} ++ ++size_t WalletImpl::getReceivedFromDeviceLength() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return -1; ++ #else ++ return hw::io::device_io_dummy::receivedFromDeviceLength; ++ #endif ++} ++ ++bool WalletImpl::getWaitsForDeviceSend() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return false; ++ #else ++ return hw::io::device_io_dummy::receivedFromDeviceLength; ++ #endif ++} ++ ++bool WalletImpl::getWaitsForDeviceReceive() { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return false; ++ #else ++ return hw::io::device_io_dummy::waitsForDeviceReceive; ++ #endif ++} ++ ++void WalletImpl::setDeviceReceivedData(unsigned char* data, size_t len) { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return; ++ #else ++ hw::io::device_io_dummy::receivedFromDevice = static_cast(malloc(len)); ++ hw::io::device_io_dummy::receivedFromDeviceLength = len; ++ memset(hw::io::device_io_dummy::receivedFromDevice, 0, len); ++ memcpy(hw::io::device_io_dummy::receivedFromDevice, data, len); ++ hw::io::device_io_dummy::waitsForDeviceReceive = false; ++ #endif ++} ++ ++void WalletImpl::setDeviceSendData(unsigned char* data, size_t len) { ++ #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI)) ++ setStatusError("MONERO compiled with #if !(defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI))"); ++ return; ++ #else ++ hw::io::device_io_dummy::sendToDevice = static_cast(malloc(len)); ++ hw::io::device_io_dummy::sendToDeviceLength = len; ++ memset(hw::io::device_io_dummy::sendToDevice, 0, len); ++ memcpy(hw::io::device_io_dummy::sendToDevice, data, len); ++ hw::io::device_io_dummy::waitsForDeviceSend = false; ++ #endif ++} ++ + } // namespace +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index edf8bb8ce..4e9c21ecb 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -301,6 +301,24 @@ private: + // cache connection status to avoid unnecessary RPC calls + mutable std::atomic m_is_connected; + boost::optional m_daemon_login{}; ++ ++ bool getStateIsConnected(); ++ ++ unsigned char *getSendToDevice(); ++ ++ size_t getSendToDeviceLength(); ++ ++ unsigned char *getReceivedFromDevice(); ++ ++ size_t getReceivedFromDeviceLength(); ++ ++ bool getWaitsForDeviceSend(); ++ ++ bool getWaitsForDeviceReceive(); ++ ++ void setDeviceReceivedData(unsigned char *data, size_t len); ++ ++ void setDeviceSendData(unsigned char *data, size_t len); + }; + + +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 764adbfbf..53ec4abfc 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -1150,6 +1150,18 @@ struct Wallet + + //! get bytes sent + virtual uint64_t getBytesSent() = 0; ++ ++ // HIDAPI_DUMMY ++ virtual bool getStateIsConnected() = 0; ++ virtual unsigned char* getSendToDevice() = 0; ++ virtual size_t getSendToDeviceLength() = 0; ++ virtual unsigned char* getReceivedFromDevice() = 0; ++ virtual size_t getReceivedFromDeviceLength() = 0; ++ virtual bool getWaitsForDeviceSend() = 0; ++ virtual bool getWaitsForDeviceReceive() = 0; ++ ++ virtual void setDeviceReceivedData(unsigned char* data, size_t len) = 0; ++ virtual void setDeviceSendData(unsigned char* data, size_t len) = 0; + }; + + /** +diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp +index e81b8f83a..277be6ac9 100644 +--- a/src/wallet/api/wallet_manager.cpp ++++ b/src/wallet/api/wallet_manager.cpp +@@ -188,10 +188,14 @@ bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, + + bool WalletManagerImpl::queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds) const + { +- hw::device::device_type type; +- bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds); +- device_type = static_cast(type); +- return r; ++ try { ++ hw::device::device_type type; ++ bool r = tools::wallet2::query_device(type, keys_file_name, password, kdf_rounds); ++ device_type = static_cast(type); ++ return r; ++ } catch (...) { ++ return false; ++ } + } + + std::vector WalletManagerImpl::findWallets(const std::string &path) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0009-fix-missing-___clear_cache-when-targetting-iOS.patch b/patches/wownero/0009-fix-missing-___clear_cache-when-targetting-iOS.patch deleted file mode 100644 index 513a105..0000000 --- a/patches/wownero/0009-fix-missing-___clear_cache-when-targetting-iOS.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 05c6c9b1febdf4bb1073acd7b30d1ef981cd56b1 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Tue, 2 Apr 2024 17:07:19 +0200 -Subject: [PATCH 09/14] fix missing ___clear_cache when targetting iOS - ---- - .gitmodules | 5 ++--- - external/randomwow | 2 +- - 2 files changed, 3 insertions(+), 4 deletions(-) - -diff --git a/.gitmodules b/.gitmodules -index bd30f0444..6c3e36a72 100644 ---- a/.gitmodules -+++ b/.gitmodules -@@ -20,6 +20,5 @@ - branch = monero - [submodule "external/randomwow"] - path = external/randomwow -- url = https://github.com/wownero-mirror/RandomWOW -- branch = 1.2.1-wow -- -+ url = https://github.com/mrcyjanek/RandomWOW -+ branch = cyjan-fix-ios -diff --git a/external/randomwow b/external/randomwow -index 27b099b6d..6f30d4b92 160000 ---- a/external/randomwow -+++ b/external/randomwow -@@ -1 +1 @@ --Subproject commit 27b099b6dd6fef6e17f58c6dfe00009e9c5df587 -+Subproject commit 6f30d4b924fecb231e5b683915cc75d18b3b5866 --- -2.45.2 - diff --git a/patches/wownero/0009-polyseed.patch b/patches/wownero/0009-polyseed.patch new file mode 100644 index 0000000..aa5af28 --- /dev/null +++ b/patches/wownero/0009-polyseed.patch @@ -0,0 +1,1464 @@ +From c1f5cc2d9b71b7b923b17c33c23b23da918e4751 Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Tue, 12 Mar 2024 09:42:37 +0100 +Subject: [PATCH 09/15] polyseed + +Co-authored-by: Czarek Nakamoto +--- + .gitmodules | 6 + + CMakeLists.txt | 4 +- + contrib/depends/hosts/darwin.mk | 2 + + contrib/depends/hosts/linux.mk | 8 +- + contrib/depends/packages/packages.mk | 2 +- + contrib/depends/packages/polyseed.mk | 28 +++ + contrib/depends/packages/sodium.mk | 2 +- + .../polyseed/0001-disable-soname.patch | 48 +++++ + .../patches/polyseed/force-static-mingw.patch | 23 +++ + contrib/epee/include/wipeable_string.h | 7 + + contrib/epee/src/wipeable_string.cpp | 10 + + external/CMakeLists.txt | 2 + + external/polyseed | 1 + + external/utf8proc | 1 + + src/CMakeLists.txt | 1 + + src/cryptonote_basic/CMakeLists.txt | 1 + + src/cryptonote_basic/account.cpp | 23 ++- + src/cryptonote_basic/account.h | 6 + + src/cryptonote_config.h | 2 + + src/polyseed/CMakeLists.txt | 25 +++ + src/polyseed/pbkdf2.c | 85 ++++++++ + src/polyseed/pbkdf2.h | 46 +++++ + src/polyseed/polyseed.cpp | 182 ++++++++++++++++++ + src/polyseed/polyseed.hpp | 167 ++++++++++++++++ + src/wallet/api/wallet.cpp | 70 +++++++ + src/wallet/api/wallet.h | 10 + + src/wallet/api/wallet2_api.h | 25 +++ + src/wallet/api/wallet_manager.cpp | 9 + + src/wallet/api/wallet_manager.h | 10 + + src/wallet/wallet2.cpp | 99 ++++++++-- + src/wallet/wallet2.h | 30 ++- + 31 files changed, 912 insertions(+), 23 deletions(-) + create mode 100644 contrib/depends/packages/polyseed.mk + create mode 100644 contrib/depends/patches/polyseed/0001-disable-soname.patch + create mode 100644 contrib/depends/patches/polyseed/force-static-mingw.patch + create mode 160000 external/polyseed + create mode 160000 external/utf8proc + create mode 100644 src/polyseed/CMakeLists.txt + create mode 100644 src/polyseed/pbkdf2.c + create mode 100644 src/polyseed/pbkdf2.h + create mode 100644 src/polyseed/polyseed.cpp + create mode 100644 src/polyseed/polyseed.hpp + +diff --git a/.gitmodules b/.gitmodules +index b24855d9b..589676649 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -20,3 +20,9 @@ + path = external/bc-ur + url = https://github.com/MrCyjaneK/bc-ur + branch = misc ++[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 +\ No newline at end of file +diff --git a/CMakeLists.txt b/CMakeLists.txt +index abe44eca5..85a62ef7b 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -372,6 +372,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() + +@@ -461,7 +463,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) +diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk +index 83d83036b..b14ee5c5b 100644 +--- a/contrib/depends/hosts/darwin.mk ++++ b/contrib/depends/hosts/darwin.mk +@@ -8,6 +8,8 @@ endif + darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -B$(host_prefix)/native/bin/$(host)- + darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)- + ++darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib ++ + darwin_CFLAGS=-pipe + darwin_CXXFLAGS=$(darwin_CFLAGS) + darwin_ARFLAGS=cr +diff --git a/contrib/depends/hosts/linux.mk b/contrib/depends/hosts/linux.mk +index 912fdb03c..b79799f30 100644 +--- a/contrib/depends/hosts/linux.mk ++++ b/contrib/depends/hosts/linux.mk +@@ -11,15 +11,15 @@ linux_debug_CXXFLAGS=$(linux_debug_CFLAGS) + linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC + + ifeq (86,$(findstring 86,$(build_arch))) +-i686_linux_CC=gcc -m32 +-i686_linux_CXX=g++ -m32 ++i686_linux_CC=i686-linux-gnu-gcc ++i686_linux_CXX=i686-linux-gnu-g++ + i686_linux_AR=ar + i686_linux_RANLIB=ranlib + i686_linux_NM=nm + i686_linux_STRIP=strip + +-x86_64_linux_CC=gcc -m64 +-x86_64_linux_CXX=g++ -m64 ++x86_64_linux_CC=x86_64-linux-gnu-gcc ++x86_64_linux_CXX=x86_64-linux-gnu-g++ + x86_64_linux_AR=ar + x86_64_linux_RANLIB=ranlib + x86_64_linux_NM=nm +diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk +index d2d1eca85..8783d4955 100644 +--- a/contrib/depends/packages/packages.mk ++++ b/contrib/depends/packages/packages.mk +@@ -1,4 +1,4 @@ +-packages:=boost openssl zeromq libiconv expat unbound ++packages:=boost openssl zeromq libiconv expat unbound polyseed + + # ccache is useless in gitian builds + ifneq ($(GITIAN),1) +diff --git a/contrib/depends/packages/polyseed.mk b/contrib/depends/packages/polyseed.mk +new file mode 100644 +index 000000000..0071b20f3 +--- /dev/null ++++ b/contrib/depends/packages/polyseed.mk +@@ -0,0 +1,28 @@ ++package=polyseed ++$(package)_version=2.0.0 ++$(package)_download_path=https://github.com/tevador/$(package)/archive/refs/tags/ ++$(package)_download_file=v$($(package)_version).tar.gz ++$(package)_file_name=$(package)-$($(package)_version).tar.gz ++$(package)_sha256_hash=f36282fcbcd68d32461b8230c89e1a40661bd46b91109681cec637433004135a ++$(package)_patches=force-static-mingw.patch 0001-disable-soname.patch ++ ++define $(package)_preprocess_cmds ++ patch -p1 < $($(package)_patch_dir)/force-static-mingw.patch &&\ ++ patch -p1 < $($(package)_patch_dir)/0001-disable-soname.patch ++endef ++ ++define $(package)_config_cmds ++ CC="$($(package)_cc)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" . ++endef ++ ++define $(package)_set_vars ++ $(package)_build_opts=CC="$($(package)_cc)" ++endef ++ ++define $(package)_build_cmds ++ CC="$($(package)_cc)" $(MAKE) ++endef ++ ++define $(package)_stage_cmds ++ $(MAKE) DESTDIR=$($(package)_staging_dir) install ++endef +diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk +index 87b34599e..68a5b48ba 100644 +--- a/contrib/depends/packages/sodium.mk ++++ b/contrib/depends/packages/sodium.mk +@@ -6,7 +6,7 @@ $(package)_sha256_hash=6f504490b342a4f8a4c4a02fc9b866cbef8622d5df4e5452b46be121e + $(package)_patches=disable-glibc-getrandom-getentropy.patch fix-whitespace.patch + + define $(package)_set_vars +-$(package)_config_opts=--enable-static --disable-shared ++$(package)_config_opts=--enable-static --disable-shared --with-pic + $(package)_config_opts+=--prefix=$(host_prefix) + endef + +diff --git a/contrib/depends/patches/polyseed/0001-disable-soname.patch b/contrib/depends/patches/polyseed/0001-disable-soname.patch +new file mode 100644 +index 000000000..bd97dd394 +--- /dev/null ++++ b/contrib/depends/patches/polyseed/0001-disable-soname.patch +@@ -0,0 +1,48 @@ ++From aabafcfc0572651436d024a635483c49042fad7f Mon Sep 17 00:00:00 2001 ++From: Czarek Nakamoto ++Date: Thu, 28 Mar 2024 00:32:51 +0100 ++Subject: [PATCH] disable soname ++ ++--- ++ CMakeLists.txt | 16 +++++++++------- ++ 1 file changed, 9 insertions(+), 7 deletions(-) ++ ++diff --git a/CMakeLists.txt b/CMakeLists.txt ++index 8a8e7c2..5301353 100644 ++--- a/CMakeLists.txt +++++ b/CMakeLists.txt ++@@ -36,6 +36,7 @@ include_directories(polyseed ++ target_compile_definitions(polyseed PRIVATE POLYSEED_SHARED) ++ set_target_properties(polyseed PROPERTIES VERSION 2.0.0 ++ SOVERSION 2 +++ NO_SONAME 1 ++ C_STANDARD 11 ++ C_STANDARD_REQUIRED ON) ++ ++@@ -45,16 +46,17 @@ include_directories(polyseed_static ++ include/) ++ target_compile_definitions(polyseed_static PRIVATE POLYSEED_STATIC) ++ set_target_properties(polyseed_static PROPERTIES OUTPUT_NAME polyseed +++ NO_SONAME 1 ++ C_STANDARD 11 ++ C_STANDARD_REQUIRED ON) ++ ++-add_executable(polyseed-tests ++- tests/tests.c) ++-include_directories(polyseed-tests ++- include/) ++-target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) ++-target_link_libraries(polyseed-tests ++- PRIVATE polyseed_static) +++# add_executable(polyseed-tests +++# tests/tests.c) +++# include_directories(polyseed-tests +++# include/) +++# target_compile_definitions(polyseed-tests PRIVATE POLYSEED_STATIC) +++# target_link_libraries(polyseed-tests +++# PRIVATE polyseed_static) ++ ++ include(GNUInstallDirs) ++ install(TARGETS polyseed polyseed_static ++-- ++2.39.2 +diff --git a/contrib/depends/patches/polyseed/force-static-mingw.patch b/contrib/depends/patches/polyseed/force-static-mingw.patch +new file mode 100644 +index 000000000..f05cb2b6a +--- /dev/null ++++ b/contrib/depends/patches/polyseed/force-static-mingw.patch +@@ -0,0 +1,23 @@ ++--- a/include/polyseed.h +++++ b/include/polyseed.h ++@@ -93,13 +93,13 @@ Shared/static library definitions ++ - define POLYSEED_STATIC when linking to the static library ++ */ ++ #if defined(_WIN32) || defined(__CYGWIN__) ++- #ifdef POLYSEED_SHARED ++- #define POLYSEED_API __declspec(dllexport) ++- #elif !defined(POLYSEED_STATIC) ++- #define POLYSEED_API __declspec(dllimport) ++- #else ++- #define POLYSEED_API ++- #endif +++// #ifdef POLYSEED_SHARED +++// #define POLYSEED_API __declspec(dllexport) +++// #elif !defined(POLYSEED_STATIC) +++// #define POLYSEED_API __declspec(dllimport) +++// #else +++ #define POLYSEED_API +++// #endif ++ #define POLYSEED_PRIVATE ++ #else ++ #ifdef POLYSEED_SHARED +diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h +index 65977cd97..594e15de4 100644 +--- a/contrib/epee/include/wipeable_string.h ++++ b/contrib/epee/include/wipeable_string.h +@@ -34,6 +34,7 @@ + #include + #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); +diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp +index b016f2f48..f2f365b1b 100644 +--- a/contrib/epee/src/wipeable_string.cpp ++++ b/contrib/epee/src/wipeable_string.cpp +@@ -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]; ++} ++ + } +diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt +index 88a7bb0b5..95be500b1 100644 +--- a/external/CMakeLists.txt ++++ b/external/CMakeLists.txt +@@ -73,4 +73,6 @@ add_subdirectory(db_drivers) + add_subdirectory(easylogging++) + add_subdirectory(qrcodegen) + add_subdirectory(bc-ur) ++add_subdirectory(polyseed EXCLUDE_FROM_ALL) ++add_subdirectory(utf8proc EXCLUDE_FROM_ALL) + add_subdirectory(randomwow EXCLUDE_FROM_ALL) +diff --git a/external/polyseed b/external/polyseed +new file mode 160000 +index 000000000..bd79f5014 +--- /dev/null ++++ b/external/polyseed +@@ -0,0 +1 @@ ++Subproject commit bd79f5014c331273357277ed8a3d756fb61b9fa1 +diff --git a/external/utf8proc b/external/utf8proc +new file mode 160000 +index 000000000..3de4596fb +--- /dev/null ++++ b/external/utf8proc +@@ -0,0 +1 @@ ++Subproject commit 3de4596fbe28956855df2ecb3c11c0bbc3535838 +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index 9216bcaa5..c043ba150 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -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) +diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt +index 1414be1b2..414936a05 100644 +--- a/src/cryptonote_basic/CMakeLists.txt ++++ b/src/cryptonote_basic/CMakeLists.txt +@@ -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} +diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp +index 4e87d4477..2d556f285 100644 +--- a/src/cryptonote_basic/account.cpp ++++ b/src/cryptonote_basic/account.cpp +@@ -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 &multisig_keys) + { + m_keys.m_account_address.m_spend_public_key = spend_public_key; +diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h +index 93d1d28f0..1f76febce 100644 +--- a/src/cryptonote_basic/account.h ++++ b/src/cryptonote_basic/account.h +@@ -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 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 &multisig_keys); + const account_keys& get_keys() const; + std::string get_public_address_str(network_type nettype) const; +diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h +index 8b5091a46..d9151e8d2 100644 +--- a/src/cryptonote_config.h ++++ b/src/cryptonote_config.h +@@ -219,6 +219,8 @@ + + #define DNS_BLOCKLIST_LIFETIME (86400 * 8) + ++#define POLYSEED_COIN POLYSEED_WOWNERO ++ + //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 +diff --git a/src/polyseed/CMakeLists.txt b/src/polyseed/CMakeLists.txt +new file mode 100644 +index 000000000..cca4eb746 +--- /dev/null ++++ b/src/polyseed/CMakeLists.txt +@@ -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} ++) +diff --git a/src/polyseed/pbkdf2.c b/src/polyseed/pbkdf2.c +new file mode 100644 +index 000000000..1c45f4708 +--- /dev/null ++++ b/src/polyseed/pbkdf2.c +@@ -0,0 +1,85 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// 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 ++ ++#include ++#include ++ ++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); ++} +\ No newline at end of file +diff --git a/src/polyseed/pbkdf2.h b/src/polyseed/pbkdf2.h +new file mode 100644 +index 000000000..f6253b9d7 +--- /dev/null ++++ b/src/polyseed/pbkdf2.h +@@ -0,0 +1,46 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// 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 ++#include ++ ++#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 +\ No newline at end of file +diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp +new file mode 100644 +index 000000000..231a48a94 +--- /dev/null ++++ b/src/polyseed/polyseed.cpp +@@ -0,0 +1,182 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// 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 ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++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(str), 0, buffer, POLYSEED_STR_SIZE, options); ++ if (result < 0 || result > (POLYSEED_STR_SIZE - 1)) { ++ throw std::runtime_error("Unicode normalization failed"); ++ } ++ ++ result = utf8proc_reencode(buffer, result, options); ++ if (result < 0 || result > POLYSEED_STR_SIZE) { ++ throw std::runtime_error("Unicode normalization failed"); ++ } ++ ++ strcpy(norm, reinterpret_cast(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)); ++ } ++ ++ 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 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& 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 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); ++ } ++} +diff --git a/src/polyseed/polyseed.hpp b/src/polyseed/polyseed.hpp +new file mode 100644 +index 000000000..2c8c777a7 +--- /dev/null ++++ b/src/polyseed/polyseed.hpp +@@ -0,0 +1,167 @@ ++// Copyright (c) 2023, The Monero Project ++// Copyright (c) 2021, tevador ++// ++// 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 ++#include ++#include ++#include ++#include ++#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& 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 ++ 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 +\ No newline at end of file +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 556e2a8ce..704e5e148 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -728,6 +728,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(m_wallet->get_device_type()); +@@ -845,6 +867,54 @@ std::string WalletImpl::seed(const std::string& seed_offset) const + } + } + ++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> Wallet::getPolyseedLanguages() ++ { ++ std::vector> languages; ++ ++ auto langs = polyseed::get_langs(); ++ for (const auto &lang : langs) { ++ languages.emplace_back(std::pair(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(); +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 4e9c21ecb..32e12284b 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -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 *) {} +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 53ec4abfc..be1c3704e 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -709,6 +709,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> getPolyseedLanguages(); ++ + /** + * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) + */ +@@ -1320,6 +1324,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 +diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp +index 277be6ac9..da2056d8a 100644 +--- a/src/wallet/api/wallet_manager.cpp ++++ b/src/wallet/api/wallet_manager.cpp +@@ -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(wallet); +diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h +index a223e1df9..28fcd36c9 100644 +--- a/src/wallet/api/wallet_manager.h ++++ b/src/wallet/api/wallet_manager.h +@@ -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; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 671fa5298..3e49c21f8 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -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" + { +@@ -1278,7 +1279,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(true) ++ m_allow_mismatched_daemon_version(true), ++ m_polyseed(false) + { + set_rpc_client_secret_key(rct::rct2sk(rct::skGen())); + } +@@ -1474,6 +1476,20 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab + 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; ++} ++//---------------------------------------------------------------------------------------------------- + bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase) const + { + bool ready; +@@ -4792,6 +4808,9 @@ boost::optional 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(m_custom_background_key.get().data()), m_custom_background_key.get().size()); +@@ -5031,6 +5050,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st + m_enable_multisig = false; + m_allow_mismatched_daemon_version = true; + m_custom_background_key = boost::none; ++ m_polyseed = false; + } + else if(json.IsObject()) + { +@@ -5271,6 +5291,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; +@@ -5590,6 +5613,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. +@@ -5717,7 +5782,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 +@@ -5741,7 +5806,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) +@@ -13634,9 +13699,10 @@ 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; ++ // uint64_t approx_blockchain_height = fork_block + ((t > 0 ? t : time(NULL)) - fork_time)/seconds_per_block; + LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); + return approx_blockchain_height; + } +@@ -15771,15 +15837,6 @@ 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) + { +- uint32_t version; +- if (!check_connection(&version)) +- { +- throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); +- } +- if (version < MAKE_CORE_RPC_VERSION(1, 6)) +- { +- 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; +@@ -15788,7 +15845,23 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui + { + 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)) ++ { ++ throw std::runtime_error("failed to connect to daemon: " + get_daemon_address()); ++ } ++ if (version < MAKE_CORE_RPC_VERSION(1, 6)) ++ { ++ throw std::runtime_error("this function requires RPC version 1.6 or higher"); ++ } ++ + std::string err; + uint64_t height_min = 0; + uint64_t height_max = get_daemon_blockchain_height(err) - 1; +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index 80ff0698d..c26349ce3 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -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" +@@ -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 +@@ -1088,6 +1103,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. + */ +@@ -1555,8 +1579,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 select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct); + std::vector select_available_outputs(const std::function &f); + std::vector select_available_unmixable_outputs(); +@@ -1650,6 +1674,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 &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(); + +@@ -1995,6 +2020,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 m_multisig_signers; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0010-build-wownero-seed.patch b/patches/wownero/0010-build-wownero-seed.patch deleted file mode 100644 index 95b6d49..0000000 --- a/patches/wownero/0010-build-wownero-seed.patch +++ /dev/null @@ -1,613 +0,0 @@ -From 6b220ce6f3bb9f57375d6bf06b70f82f043856c8 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Thu, 25 Apr 2024 10:06:04 +0200 -Subject: [PATCH 10/14] build wownero-seed - ---- - contrib/depends/hosts/android.mk | 3 +- - contrib/depends/hosts/darwin.mk | 1 + - contrib/depends/hosts/mingw32.mk | 3 + - contrib/depends/packages/packages.mk | 2 +- - contrib/depends/packages/wownero_seed.mk | 35 ++ - .../0001-fix-duplicate-symbol-error.patch | 497 ++++++++++++++++++ - 6 files changed, 539 insertions(+), 2 deletions(-) - create mode 100644 contrib/depends/packages/wownero_seed.mk - create mode 100644 contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch - -diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk -index d6f8b99dd..5b2309972 100644 ---- a/contrib/depends/hosts/android.mk -+++ b/contrib/depends/hosts/android.mk -@@ -6,7 +6,8 @@ endif - - android_CC=$(host_toolchain)clang - android_CXX=$(host_toolchain)clang++ --android_RANLIB=: -+android_RANLIB=$(host_toolchain)ranlib -+android_AR=$(host_toolchain)ar - - android_CFLAGS=-pipe - android_CXXFLAGS=$(android_CFLAGS) -diff --git a/contrib/depends/hosts/darwin.mk b/contrib/depends/hosts/darwin.mk -index cbe795081..ce2f5669d 100644 ---- a/contrib/depends/hosts/darwin.mk -+++ b/contrib/depends/hosts/darwin.mk -@@ -9,6 +9,7 @@ darwin_CC=clang -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --s - darwin_CXX=clang++ -target $(CC_target) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(host_prefix)/native/SDK/ -mlinker-version=$(LD64_VERSION) -stdlib=libc++ -B$(host_prefix)/native/bin/$(host)- - - darwin_RANLIB=$(host_prefix)/native/bin/$(host)-ranlib -+darwin_AR=$(host_prefix)/native/bin/$(host)-ar - - darwin_CFLAGS=-pipe - darwin_CXXFLAGS=$(darwin_CFLAGS) -diff --git a/contrib/depends/hosts/mingw32.mk b/contrib/depends/hosts/mingw32.mk -index ccc4c5082..4677694a6 100644 ---- a/contrib/depends/hosts/mingw32.mk -+++ b/contrib/depends/hosts/mingw32.mk -@@ -2,6 +2,9 @@ mingw32_CFLAGS=-pipe - mingw32_CXXFLAGS=$(mingw32_CFLAGS) - mingw32_ARFLAGS=cr - -+mingw32_RANLIB=$(shell which $(host)-ranlib) -+mingw32_AR=$(shell which $(host)-ar) -+ - mingw32_release_CFLAGS=-O2 - mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) - -diff --git a/contrib/depends/packages/packages.mk b/contrib/depends/packages/packages.mk -index 8783d4955..3d513c5a2 100644 ---- a/contrib/depends/packages/packages.mk -+++ b/contrib/depends/packages/packages.mk -@@ -1,4 +1,4 @@ --packages:=boost openssl zeromq libiconv expat unbound polyseed -+packages:=boost openssl zeromq libiconv expat unbound polyseed wownero_seed - - # ccache is useless in gitian builds - ifneq ($(GITIAN),1) -diff --git a/contrib/depends/packages/wownero_seed.mk b/contrib/depends/packages/wownero_seed.mk -new file mode 100644 -index 000000000..b376f80c5 ---- /dev/null -+++ b/contrib/depends/packages/wownero_seed.mk -@@ -0,0 +1,35 @@ -+package=wownero_seed -+$(package)_version=0.3.0 -+$(package)_download_path=https://github.com/MrCyjaneK/wownero-seed/archive/ -+$(package)_download_file=d3f68be347facfeebbd8f68fd74982c705cb917b.tar.gz -+$(package)_file_name=$(package)-$($(package)_version).tar.gz -+$(package)_sha256_hash=3b59ccde08e0fee204680240af4b270a18a677aa0e6036a3504570193d232406 -+$(package)_patches=0001-fix-duplicate-symbol-error.patch -+ -+define $(package)_preprocess_cmds -+ patch -p1 < $($(package)_patch_dir)/0001-fix-duplicate-symbol-error.patch -+endef -+ -+ -+ifeq ($(host_os),darwin) -+ define $(package)_config_cmds -+ CC="$($(package)_cc)" CXX="$($(package)_cxx)" cmake -DCMAKE_RANLIB="$($(package)_ranlib)" -DCMAKE_AR="$($(package)_ar)" -DCMAKE_INSTALL_PREFIX="$(host_prefix)" -DCMAKE_POSITION_INDEPENDENT_CODE=ON . -+ endef -+else -+ define $(package)_config_cmds -+ CC="$($(package)_cc)" CXX="$($(package)_cxx)" cmake -DCMAKE_INSTALL_PREFIX="$(host_prefix)" -DCMAKE_POSITION_INDEPENDENT_CODE=ON . -+ endef -+endif -+ -+define $(package)_set_vars -+ $(package)_build_opts=CC="$($(package)_cc)" CXX="$($(package)_cxx)" -+endef -+ -+ -+define $(package)_build_cmds -+ CC="$($(package)_cc)" CXX="$($(package)_cxx)" $(MAKE) VERBOSE=1 -+endef -+ -+define $(package)_stage_cmds -+ CC="$($(package)_cc)" CXX="$($(package)_cxx)" $(MAKE) DESTDIR=$($(package)_staging_dir) install -+endef -diff --git a/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch b/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch -new file mode 100644 -index 000000000..a8f8fe059 ---- /dev/null -+++ b/contrib/depends/patches/wownero_seed/0001-fix-duplicate-symbol-error.patch -@@ -0,0 +1,497 @@ -+From 4be93209afb80b11834a0849391ee6eeb68aec4a Mon Sep 17 00:00:00 2001 -+From: Czarek Nakamoto -+Date: Thu, 25 Apr 2024 09:37:37 +0200 -+Subject: [PATCH] fix duplicate symbol error -+ -+--- -+ src/argon2/argon2.c | 8 +++--- -+ src/argon2/argon2.h | 4 +-- -+ src/argon2/blake2/blake2.h | 12 ++++----- -+ src/argon2/blake2/blake2b.c | 46 ++++++++++++++++---------------- -+ src/argon2/core.c | 52 ++++++++++++++++++------------------- -+ src/argon2/core.h | 17 ++++++------ -+ src/argon2/ref.c | 2 +- -+ 7 files changed, 70 insertions(+), 71 deletions(-) -+ -+diff --git a/src/argon2/argon2.c b/src/argon2/argon2.c -+index e9882b7..470dc26 100644 -+--- a/src/argon2/argon2.c -++++ b/src/argon2/argon2.c -+@@ -37,7 +37,7 @@ const char *argon2_type2string(argon2_type type, int uppercase) { -+ -+ int argon2_ctx(argon2_context *context, argon2_type type) { -+ /* 1. Validate all inputs */ -+- int result = validate_inputs(context); -++ int result = validate_inputs_wowseed(context); -+ uint32_t memory_blocks, segment_length; -+ argon2_instance_t instance; -+ -+@@ -78,20 +78,20 @@ int argon2_ctx(argon2_context *context, argon2_type type) { -+ /* 3. Initialization: Hashing inputs, allocating memory, filling first -+ * blocks -+ */ -+- result = initialize(&instance, context); -++ result = initialize_wowseed(&instance, context); -+ -+ if (ARGON2_OK != result) { -+ return result; -+ } -+ -+ /* 4. Filling memory */ -+- result = fill_memory_blocks(&instance); -++ result = _fill_memory_blocks_wowseed(&instance); -+ -+ if (ARGON2_OK != result) { -+ return result; -+ } -+ /* 5. Finalization */ -+- finalize(context, &instance); -++ finalize_wowseed(context, &instance); -+ -+ return ARGON2_OK; -+ } -+diff --git a/src/argon2/argon2.h b/src/argon2/argon2.h -+index 1b471f6..f60e269 100644 -+--- a/src/argon2/argon2.h -++++ b/src/argon2/argon2.h -+@@ -185,7 +185,7 @@ typedef void (*deallocate_fptr)(uint8_t *memory, size_t bytes_to_allocate); -+ * 4 parallel lanes. -+ * You want to erase the password, but you're OK with last pass not being -+ * erased. You want to use the default memory allocator. -+- * Then you initialize: -++ * Then you initialize_wowseed: -+ Argon2_Context(out,8,pwd,32,salt,16,NULL,0,NULL,0,5,1<<20,4,4,NULL,NULL,true,false,false,false) -+ */ -+ typedef struct Argon2_Context { -+@@ -329,7 +329,7 @@ ARGON2_PUBLIC int argon2_hash(const uint32_t t_cost, const uint32_t m_cost, -+ -+ /** -+ * Verifies a password against an encoded string -+- * Encoded string is restricted as in validate_inputs() -++ * Encoded string is restricted as in validate_inputs_wowseed() -+ * @param encoded String encoding parameters, salt, hash -+ * @param pwd Pointer to password -+ * @pre Returns ARGON2_OK if successful -+diff --git a/src/argon2/blake2/blake2.h b/src/argon2/blake2/blake2.h -+index 9f97e1c..25b445d 100644 -+--- a/src/argon2/blake2/blake2.h -++++ b/src/argon2/blake2/blake2.h -+@@ -67,15 +67,15 @@ enum { -+ }; -+ -+ /* Streaming API */ -+-ARGON2_LOCAL int blake2b_init(blake2b_state *S, size_t outlen); -+-ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, -++ARGON2_LOCAL int blake2b_init_wowseed(blake2b_state *S, size_t outlen); -++ARGON2_LOCAL int blake2b_init_key_wowseed(blake2b_state *S, size_t outlen, const void *key, -+ size_t keylen); -+-ARGON2_LOCAL int blake2b_init_param(blake2b_state *S, const blake2b_param *P); -+-ARGON2_LOCAL int blake2b_update(blake2b_state *S, const void *in, size_t inlen); -+-ARGON2_LOCAL int blake2b_final(blake2b_state *S, void *out, size_t outlen); -++ARGON2_LOCAL int blake2b_init_param_wowseed(blake2b_state *S, const blake2b_param *P); -++ARGON2_LOCAL int blake2b_update_wowseed(blake2b_state *S, const void *in, size_t inlen); -++ARGON2_LOCAL int blake2b_final_wowseed(blake2b_state *S, void *out, size_t outlen); -+ -+ /* Simple API */ -+-ARGON2_LOCAL int blake2b(void *out, size_t outlen, const void *in, size_t inlen, -++ARGON2_LOCAL int blake2b_wowseed(void *out, size_t outlen, const void *in, size_t inlen, -+ const void *key, size_t keylen); -+ -+ /* Argon2 Team - Begin Code */ -+diff --git a/src/argon2/blake2/blake2b.c b/src/argon2/blake2/blake2b.c -+index ca05df5..8138b86 100644 -+--- a/src/argon2/blake2/blake2b.c -++++ b/src/argon2/blake2/blake2b.c -+@@ -70,7 +70,7 @@ static BLAKE2_INLINE void blake2b_init0(blake2b_state *S) { -+ memcpy(S->h, blake2b_IV, sizeof(S->h)); -+ } -+ -+-int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { -++int blake2b_init_param_wowseed(blake2b_state *S, const blake2b_param *P) { -+ const unsigned char *p = (const unsigned char *)P; -+ unsigned int i; -+ -+@@ -88,7 +88,7 @@ int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { -+ } -+ -+ /* Sequential blake2b initialization */ -+-int blake2b_init(blake2b_state *S, size_t outlen) { -++int blake2b_init_wowseed(blake2b_state *S, size_t outlen) { -+ blake2b_param P; -+ -+ if (S == NULL) { -+@@ -113,10 +113,10 @@ int blake2b_init(blake2b_state *S, size_t outlen) { -+ memset(P.salt, 0, sizeof(P.salt)); -+ memset(P.personal, 0, sizeof(P.personal)); -+ -+- return blake2b_init_param(S, &P); -++ return blake2b_init_param_wowseed(S, &P); -+ } -+ -+-int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, -++int blake2b_init_key_wowseed(blake2b_state *S, size_t outlen, const void *key, -+ size_t keylen) { -+ blake2b_param P; -+ -+@@ -147,7 +147,7 @@ int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, -+ memset(P.salt, 0, sizeof(P.salt)); -+ memset(P.personal, 0, sizeof(P.personal)); -+ -+- if (blake2b_init_param(S, &P) < 0) { -++ if (blake2b_init_param_wowseed(S, &P) < 0) { -+ blake2b_invalidate_state(S); -+ return -1; -+ } -+@@ -156,7 +156,7 @@ int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, -+ uint8_t block[BLAKE2B_BLOCKBYTES]; -+ memset(block, 0, BLAKE2B_BLOCKBYTES); -+ memcpy(block, key, keylen); -+- blake2b_update(S, block, BLAKE2B_BLOCKBYTES); -++ blake2b_update_wowseed(S, block, BLAKE2B_BLOCKBYTES); -+ /* Burn the key from stack */ -+ clear_internal_memory(block, BLAKE2B_BLOCKBYTES); -+ } -+@@ -221,7 +221,7 @@ static void blake2b_compress(blake2b_state *S, const uint8_t *block) { -+ #undef ROUND -+ } -+ -+-int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { -++int blake2b_update_wowseed(blake2b_state *S, const void *in, size_t inlen) { -+ const uint8_t *pin = (const uint8_t *)in; -+ -+ if (inlen == 0) { -+@@ -261,7 +261,7 @@ int blake2b_update(blake2b_state *S, const void *in, size_t inlen) { -+ return 0; -+ } -+ -+-int blake2b_final(blake2b_state *S, void *out, size_t outlen) { -++int blake2b_final_wowseed(blake2b_state *S, void *out, size_t outlen) { -+ uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; -+ unsigned int i; -+ -+@@ -291,7 +291,7 @@ int blake2b_final(blake2b_state *S, void *out, size_t outlen) { -+ return 0; -+ } -+ -+-int blake2b(void *out, size_t outlen, const void *in, size_t inlen, -++int blake2b_wowseed(void *out, size_t outlen, const void *in, size_t inlen, -+ const void *key, size_t keylen) { -+ blake2b_state S; -+ int ret = -1; -+@@ -310,19 +310,19 @@ int blake2b(void *out, size_t outlen, const void *in, size_t inlen, -+ } -+ -+ if (keylen > 0) { -+- if (blake2b_init_key(&S, outlen, key, keylen) < 0) { -++ if (blake2b_init_key_wowseed(&S, outlen, key, keylen) < 0) { -+ goto fail; -+ } -+ } else { -+- if (blake2b_init(&S, outlen) < 0) { -++ if (blake2b_init_wowseed(&S, outlen) < 0) { -+ goto fail; -+ } -+ } -+ -+- if (blake2b_update(&S, in, inlen) < 0) { -++ if (blake2b_update_wowseed(&S, in, inlen) < 0) { -+ goto fail; -+ } -+- ret = blake2b_final(&S, out, outlen); -++ ret = blake2b_final_wowseed(&S, out, outlen); -+ -+ fail: -+ clear_internal_memory(&S, sizeof(S)); -+@@ -352,25 +352,25 @@ int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { -+ } while ((void)0, 0) -+ -+ if (outlen <= BLAKE2B_OUTBYTES) { -+- TRY(blake2b_init(&blake_state, outlen)); -+- TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); -+- TRY(blake2b_update(&blake_state, in, inlen)); -+- TRY(blake2b_final(&blake_state, out, outlen)); -++ TRY(blake2b_init_wowseed(&blake_state, outlen)); -++ TRY(blake2b_update_wowseed(&blake_state, outlen_bytes, sizeof(outlen_bytes))); -++ TRY(blake2b_update_wowseed(&blake_state, in, inlen)); -++ TRY(blake2b_final_wowseed(&blake_state, out, outlen)); -+ } else { -+ uint32_t toproduce; -+ uint8_t out_buffer[BLAKE2B_OUTBYTES]; -+ uint8_t in_buffer[BLAKE2B_OUTBYTES]; -+- TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES)); -+- TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes))); -+- TRY(blake2b_update(&blake_state, in, inlen)); -+- TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); -++ TRY(blake2b_init_wowseed(&blake_state, BLAKE2B_OUTBYTES)); -++ TRY(blake2b_update_wowseed(&blake_state, outlen_bytes, sizeof(outlen_bytes))); -++ TRY(blake2b_update_wowseed(&blake_state, in, inlen)); -++ TRY(blake2b_final_wowseed(&blake_state, out_buffer, BLAKE2B_OUTBYTES)); -+ memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); -+ out += BLAKE2B_OUTBYTES / 2; -+ toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2; -+ -+ while (toproduce > BLAKE2B_OUTBYTES) { -+ memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); -+- TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer, -++ TRY(blake2b_wowseed(out_buffer, BLAKE2B_OUTBYTES, in_buffer, -+ BLAKE2B_OUTBYTES, NULL, 0)); -+ memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2); -+ out += BLAKE2B_OUTBYTES / 2; -+@@ -378,7 +378,7 @@ int blake2b_long(void *pout, size_t outlen, const void *in, size_t inlen) { -+ } -+ -+ memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES); -+- TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, -++ TRY(blake2b_wowseed(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL, -+ 0)); -+ memcpy(out, out_buffer, toproduce); -+ } -+diff --git a/src/argon2/core.c b/src/argon2/core.c -+index 5eafe08..c25eb53 100644 -+--- a/src/argon2/core.c -++++ b/src/argon2/core.c -+@@ -151,7 +151,7 @@ void clear_internal_memory(void *v, size_t n) { -+ } -+ } -+ -+-void finalize(const argon2_context *context, argon2_instance_t *instance) { -++void finalize_wowseed(const argon2_context *context, argon2_instance_t *instance) { -+ if (context != NULL && instance != NULL) { -+ block blockhash; -+ uint32_t l; -+@@ -256,7 +256,7 @@ uint32_t index_alpha(const argon2_instance_t *instance, -+ } -+ -+ /* Single-threaded version for p=1 case */ -+-static int fill_memory_blocks_st(argon2_instance_t *instance) { -++static int _fill_memory_blocks_wowseed_st(argon2_instance_t *instance) { -+ uint32_t r, s, l; -+ -+ for (r = 0; r < instance->passes; ++r) { -+@@ -273,14 +273,14 @@ static int fill_memory_blocks_st(argon2_instance_t *instance) { -+ return ARGON2_OK; -+ } -+ -+-int fill_memory_blocks(argon2_instance_t *instance) { -++int _fill_memory_blocks_wowseed(argon2_instance_t *instance) { -+ if (instance == NULL || instance->lanes == 0) { -+ return ARGON2_INCORRECT_PARAMETER; -+ } -+- return fill_memory_blocks_st(instance); -++ return _fill_memory_blocks_wowseed_st(instance); -+ } -+ -+-int validate_inputs(const argon2_context *context) { -++int validate_inputs_wowseed(const argon2_context *context) { -+ if (NULL == context) { -+ return ARGON2_INCORRECT_PARAMETER; -+ } -+@@ -407,7 +407,7 @@ int validate_inputs(const argon2_context *context) { -+ return ARGON2_OK; -+ } -+ -+-void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { -++void fill_first_blocks_wowseed(uint8_t *blockhash, const argon2_instance_t *instance) { -+ uint32_t l; -+ /* Make the first and second block in each lane as G(H0||0||i) or -+ G(H0||1||i) */ -+@@ -430,7 +430,7 @@ void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) { -+ clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); -+ } -+ -+-void initial_hash(uint8_t *blockhash, argon2_context *context, -++void initial_hash_wowseed(uint8_t *blockhash, argon2_context *context, -+ argon2_type type) { -+ blake2b_state BlakeHash; -+ uint8_t value[sizeof(uint32_t)]; -+@@ -439,31 +439,31 @@ void initial_hash(uint8_t *blockhash, argon2_context *context, -+ return; -+ } -+ -+- blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); -++ blake2b_init_wowseed(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH); -+ -+ store32(&value, context->lanes); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->outlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->m_cost); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->t_cost); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->version); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, (uint32_t)type); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ store32(&value, context->pwdlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ if (context->pwd != NULL) { -+- blake2b_update(&BlakeHash, (const uint8_t *)context->pwd, -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->pwd, -+ context->pwdlen); -+ -+ if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) { -+@@ -473,18 +473,18 @@ void initial_hash(uint8_t *blockhash, argon2_context *context, -+ } -+ -+ store32(&value, context->saltlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ if (context->salt != NULL) { -+- blake2b_update(&BlakeHash, (const uint8_t *)context->salt, -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->salt, -+ context->saltlen); -+ } -+ -+ store32(&value, context->secretlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ if (context->secret != NULL) { -+- blake2b_update(&BlakeHash, (const uint8_t *)context->secret, -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->secret, -+ context->secretlen); -+ -+ if (context->flags & ARGON2_FLAG_CLEAR_SECRET) { -+@@ -494,17 +494,17 @@ void initial_hash(uint8_t *blockhash, argon2_context *context, -+ } -+ -+ store32(&value, context->adlen); -+- blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)&value, sizeof(value)); -+ -+ if (context->ad != NULL) { -+- blake2b_update(&BlakeHash, (const uint8_t *)context->ad, -++ blake2b_update_wowseed(&BlakeHash, (const uint8_t *)context->ad, -+ context->adlen); -+ } -+ -+- blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); -++ blake2b_final_wowseed(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH); -+ } -+ -+-int initialize(argon2_instance_t *instance, argon2_context *context) { -++int initialize_wowseed(argon2_instance_t *instance, argon2_context *context) { -+ uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; -+ int result = ARGON2_OK; -+ -+@@ -523,7 +523,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context) { -+ /* H_0 + 8 extra bytes to produce the first blocks */ -+ /* uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH]; */ -+ /* Hashing all inputs */ -+- initial_hash(blockhash, context, instance->type); -++ initial_hash_wowseed(blockhash, context, instance->type); -+ /* Zeroing 8 extra bytes */ -+ clear_internal_memory(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, -+ ARGON2_PREHASH_SEED_LENGTH - -+@@ -535,7 +535,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context) { -+ -+ /* 3. Creating first blocks, we always have at least two blocks in a slice -+ */ -+- fill_first_blocks(blockhash, instance); -++ fill_first_blocks_wowseed(blockhash, instance); -+ /* Clearing the hash */ -+ clear_internal_memory(blockhash, ARGON2_PREHASH_SEED_LENGTH); -+ -+diff --git a/src/argon2/core.h b/src/argon2/core.h -+index 78000ba..6b0154c 100644 -+--- a/src/argon2/core.h -++++ b/src/argon2/core.h -+@@ -53,7 +53,7 @@ typedef struct block_ { uint64_t v[ARGON2_QWORDS_IN_BLOCK]; } block; -+ -+ /*****************Functions that work with the block******************/ -+ -+-/* Initialize each byte of the block with @in */ -++/* initialize_wowseed each byte of the block with @in */ -+ void init_block_value(block *b, uint8_t in); -+ -+ /* Copy block @src to block @dst */ -+@@ -158,7 +158,7 @@ uint32_t index_alpha(const argon2_instance_t *instance, -+ * @return ARGON2_OK if everything is all right, otherwise one of error codes -+ * (all defined in -+ */ -+-int validate_inputs(const argon2_context *context); -++int validate_inputs_wowseed(const argon2_context *context); -+ -+ /* -+ * Hashes all the inputs into @a blockhash[PREHASH_DIGEST_LENGTH], clears -+@@ -170,28 +170,27 @@ int validate_inputs(const argon2_context *context); -+ * @pre @a blockhash must have at least @a PREHASH_DIGEST_LENGTH bytes -+ * allocated -+ */ -+-void initial_hash(uint8_t *blockhash, argon2_context *context, -++void initial_hash_wowseed(uint8_t *blockhash, argon2_context *context, -+ argon2_type type); -+- -+ /* -+ * Function creates first 2 blocks per lane -+ * @param instance Pointer to the current instance -+ * @param blockhash Pointer to the pre-hashing digest -+ * @pre blockhash must point to @a PREHASH_SEED_LENGTH allocated values -+ */ -+-void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance); -++void fill_first_blocks_wowseed(uint8_t *blockhash, const argon2_instance_t *instance); -+ -+ /* -+ * Function allocates memory, hashes the inputs with Blake, and creates first -+ * two blocks. Returns the pointer to the main memory with 2 blocks per lane -+- * initialized -++ * initialize_wowseedd -+ * @param context Pointer to the Argon2 internal structure containing memory -+ * pointer, and parameters for time and space requirements. -+ * @param instance Current Argon2 instance -+ * @return Zero if successful, -1 if memory failed to allocate. @context->state -+ * will be modified if successful. -+ */ -+-int initialize(argon2_instance_t *instance, argon2_context *context); -++int initialize_wowseed(argon2_instance_t *instance, argon2_context *context); -+ -+ /* -+ * XORing the last block of each lane, hashing it, making the tag. Deallocates -+@@ -204,7 +203,7 @@ int initialize(argon2_instance_t *instance, argon2_context *context); -+ * @pre if context->free_cbk is not NULL, it should point to a function that -+ * deallocates memory -+ */ -+-void finalize(const argon2_context *context, argon2_instance_t *instance); -++void finalize_wowseed(const argon2_context *context, argon2_instance_t *instance); -+ -+ /* -+ * Function that fills the segment using previous segments also from other -+@@ -223,6 +222,6 @@ void fill_segment(const argon2_instance_t *instance, -+ * @param instance Pointer to the current instance -+ * @return ARGON2_OK if successful, @context->state -+ */ -+-int fill_memory_blocks(argon2_instance_t *instance); -++int _fill_memory_blocks_wowseed(argon2_instance_t *instance); -+ -+ #endif -+diff --git a/src/argon2/ref.c b/src/argon2/ref.c -+index ad1cf46..7edbd6e 100644 -+--- a/src/argon2/ref.c -++++ b/src/argon2/ref.c -+@@ -29,7 +29,7 @@ -+ -+ /* -+ * Function fills a new memory block and optionally XORs the old block over the new one. -+- * @next_block must be initialized. -++ * @next_block must be initialize_wowseedd. -+ * @param prev_block Pointer to the previous block -+ * @param ref_block Pointer to the reference block -+ * @param next_block Pointer to the block to be constructed -+-- -+2.39.2 --- -2.45.2 - diff --git a/patches/wownero/0010-coin-control.patch b/patches/wownero/0010-coin-control.patch new file mode 100644 index 0000000..2362fed --- /dev/null +++ b/patches/wownero/0010-coin-control.patch @@ -0,0 +1,979 @@ +From 14bcfd4bb49e7697c034d3d38c988f90a1842145 Mon Sep 17 00:00:00 2001 +From: tobtoht +Date: Tue, 12 Mar 2024 11:07:57 +0100 +Subject: [PATCH 10/15] coin control + +--- + src/simplewallet/simplewallet.cpp | 2 +- + src/wallet/api/CMakeLists.txt | 8 +- + src/wallet/api/coins.cpp | 186 ++++++++++++++++++++++++++++++ + src/wallet/api/coins.h | 40 +++++++ + src/wallet/api/coins_info.cpp | 122 ++++++++++++++++++++ + src/wallet/api/coins_info.h | 71 ++++++++++++ + src/wallet/api/wallet.cpp | 64 +++++++++- + src/wallet/api/wallet.h | 10 +- + src/wallet/api/wallet2_api.h | 52 ++++++++- + src/wallet/wallet2.cpp | 46 +++++++- + src/wallet/wallet2.h | 11 +- + 11 files changed, 593 insertions(+), 19 deletions(-) + create mode 100644 src/wallet/api/coins.cpp + create mode 100644 src/wallet/api/coins.h + create mode 100644 src/wallet/api/coins_info.cpp + create mode 100644 src/wallet/api/coins_info.h + +diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp +index 8c5122097..cfdb8935f 100644 +--- a/src/simplewallet/simplewallet.cpp ++++ b/src/simplewallet/simplewallet.cpp +@@ -6981,7 +6981,7 @@ bool simple_wallet::transfer_main(const std::vector &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()) + { +diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt +index af7948d8a..bb740e2ac 100644 +--- a/src/wallet/api/CMakeLists.txt ++++ b/src/wallet/api/CMakeLists.txt +@@ -40,7 +40,9 @@ set(wallet_api_sources + address_book.cpp + subaddress.cpp + subaddress_account.cpp +- unsigned_transaction.cpp) ++ unsigned_transaction.cpp ++ coins.cpp ++ coins_info.cpp) + + set(wallet_api_headers + wallet2_api.h) +@@ -55,7 +57,9 @@ set(wallet_api_private_headers + address_book.h + subaddress.h + subaddress_account.h +- unsigned_transaction.h) ++ unsigned_transaction.h ++ coins.h ++ coins_info.h) + + monero_private_headers(wallet_api + ${wallet_api_private_headers}) +diff --git a/src/wallet/api/coins.cpp b/src/wallet/api/coins.cpp +new file mode 100644 +index 000000000..ef12141cf +--- /dev/null ++++ b/src/wallet/api/coins.cpp +@@ -0,0 +1,186 @@ ++#include "coins.h" ++#include "coins_info.h" ++#include "wallet.h" ++#include "crypto/hash.h" ++#include "wallet/wallet2.h" ++#include "common_defines.h" ++ ++#include ++#include ++ ++using namespace epee; ++ ++namespace Monero { ++ ++Coins::~Coins() = default; ++ ++CoinsImpl::CoinsImpl(WalletImpl *wallet) ++ : m_wallet(wallet) {} ++ ++CoinsImpl::~CoinsImpl() ++{ ++ for (auto t : m_rows) ++ delete t; ++} ++ ++int CoinsImpl::count() const ++{ ++ boost::shared_lock lock(m_rowsMutex); ++ int result = m_rows.size(); ++ return result; ++} ++ ++CoinsInfo *CoinsImpl::coin(int index) const ++{ ++ boost::shared_lock lock(m_rowsMutex); ++ // sanity check ++ if (index < 0) ++ return nullptr; ++ auto index_ = static_cast(index); ++ return index_ < m_rows.size() ? m_rows[index_] : nullptr; ++} ++ ++std::vector CoinsImpl::getAll() const ++{ ++ boost::shared_lock lock(m_rowsMutex); ++ return m_rows; ++} ++ ++ ++void CoinsImpl::refresh() ++{ ++ LOG_PRINT_L2("Refreshing coins"); ++ ++ boost::unique_lock lock(m_rowsMutex); ++ boost::shared_lock transfers_lock(m_wallet->m_wallet->m_transfers_mutex); ++ ++ // delete old outputs; ++ for (auto t : m_rows) ++ delete t; ++ m_rows.clear(); ++ ++ for (size_t i = 0; i < m_wallet->m_wallet->get_num_transfer_details(); ++i) ++ { ++ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(i); ++ ++ auto ci = new CoinsInfoImpl(); ++ ci->m_blockHeight = td.m_block_height; ++ ci->m_hash = string_tools::pod_to_hex(td.m_txid); ++ ci->m_internalOutputIndex = td.m_internal_output_index; ++ ci->m_globalOutputIndex = td.m_global_output_index; ++ ci->m_spent = td.m_spent; ++ ci->m_frozen = td.m_frozen; ++ ci->m_spentHeight = td.m_spent_height; ++ ci->m_amount = td.m_amount; ++ ci->m_rct = td.m_rct; ++ ci->m_keyImageKnown = td.m_key_image_known; ++ ci->m_pkIndex = td.m_pk_index; ++ ci->m_subaddrIndex = td.m_subaddr_index.minor; ++ ci->m_subaddrAccount = td.m_subaddr_index.major; ++ ci->m_address = m_wallet->m_wallet->get_subaddress_as_str(td.m_subaddr_index); // todo: this is expensive, cache maybe? ++ ci->m_addressLabel = m_wallet->m_wallet->get_subaddress_label(td.m_subaddr_index); ++ ci->m_keyImage = string_tools::pod_to_hex(td.m_key_image); ++ ci->m_unlockTime = td.m_tx.unlock_time; ++ ci->m_unlocked = m_wallet->m_wallet->is_transfer_unlocked(td); ++ ci->m_pubKey = string_tools::pod_to_hex(td.get_public_key()); ++ ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen); ++ ci->m_description = m_wallet->m_wallet->get_tx_note(td.m_txid); ++ ++ m_rows.push_back(ci); ++ } ++} ++ ++void CoinsImpl::setFrozen(std::string public_key) ++{ ++ crypto::public_key pk; ++ if (!epee::string_tools::hex_to_pod(public_key, pk)) ++ { ++ LOG_ERROR("Invalid public key: " << public_key); ++ return; ++ } ++ ++ try ++ { ++ m_wallet->m_wallet->freeze(pk); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("setFrozen: " << e.what()); ++ } ++} ++ ++void CoinsImpl::setFrozen(int index) ++{ ++ try ++ { ++ LOG_ERROR("Freezing coin: " << index); ++ m_wallet->m_wallet->freeze(index); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("setLabel: " << e.what()); ++ } ++} ++ ++void CoinsImpl::thaw(std::string public_key) ++{ ++ crypto::public_key pk; ++ if (!epee::string_tools::hex_to_pod(public_key, pk)) ++ { ++ LOG_ERROR("Invalid public key: " << public_key); ++ return; ++ } ++ ++ try ++ { ++ m_wallet->m_wallet->thaw(pk); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("thaw: " << e.what()); ++ } ++} ++ ++void CoinsImpl::thaw(int index) ++{ ++ try ++ { ++ m_wallet->m_wallet->thaw(index); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("thaw: " << e.what()); ++ } ++} ++ ++bool CoinsImpl::isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) { ++ return m_wallet->m_wallet->is_transfer_unlocked(unlockTime, blockHeight); ++} ++ ++void CoinsImpl::setDescription(const std::string &public_key, const std::string &description) ++{ ++ crypto::public_key pk; ++ if (!epee::string_tools::hex_to_pod(public_key, pk)) ++ { ++ LOG_ERROR("Invalid public key: " << public_key); ++ return; ++ } ++ ++ try ++ { ++ const size_t index = m_wallet->m_wallet->get_transfer_details(pk); ++ const tools::wallet2::transfer_details &td = m_wallet->m_wallet->get_transfer_details(index); ++ m_wallet->m_wallet->set_tx_note(td.m_txid, description); ++ refresh(); ++ } ++ catch (const std::exception& e) ++ { ++ LOG_ERROR("setDescription: " << e.what()); ++ } ++} ++ ++} // namespace +diff --git a/src/wallet/api/coins.h b/src/wallet/api/coins.h +new file mode 100644 +index 000000000..b7a0a8642 +--- /dev/null ++++ b/src/wallet/api/coins.h +@@ -0,0 +1,40 @@ ++#ifndef FEATHER_COINS_H ++#define FEATHER_COINS_H ++ ++#include "wallet/api/wallet2_api.h" ++#include "wallet/wallet2.h" ++ ++namespace Monero { ++ ++class WalletImpl; ++ ++class CoinsImpl : public Coins ++{ ++public: ++ explicit CoinsImpl(WalletImpl * wallet); ++ ~CoinsImpl() override; ++ int count() const override; ++ CoinsInfo * coin(int index) const override; ++ std::vector getAll() const override; ++ void refresh() override; ++ ++ void setFrozen(std::string public_key) override; ++ void setFrozen(int index) override; ++ void thaw(std::string public_key) override; ++ void thaw(int index) override; ++ ++ bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) override; ++ ++ void setDescription(const std::string &public_key, const std::string &description) override; ++ ++private: ++ WalletImpl *m_wallet; ++ std::vector m_rows; ++ mutable boost::shared_mutex m_rowsMutex; ++}; ++ ++} ++ ++namespace Bitmonero = Monero; ++ ++#endif //FEATHER_COINS_H +diff --git a/src/wallet/api/coins_info.cpp b/src/wallet/api/coins_info.cpp +new file mode 100644 +index 000000000..5f2c4e1e4 +--- /dev/null ++++ b/src/wallet/api/coins_info.cpp +@@ -0,0 +1,122 @@ ++#include "coins_info.h" ++ ++using namespace std; ++ ++namespace Monero { ++ ++CoinsInfo::~CoinsInfo() = default; ++ ++CoinsInfoImpl::CoinsInfoImpl() ++ : m_blockHeight(0) ++ , m_internalOutputIndex(0) ++ , m_globalOutputIndex(0) ++ , m_spent(false) ++ , m_frozen(false) ++ , m_spentHeight(0) ++ , m_amount(0) ++ , m_rct(false) ++ , m_keyImageKnown(false) ++ , m_pkIndex(0) ++ , m_subaddrAccount(0) ++ , m_subaddrIndex(0) ++ , m_unlockTime(0) ++ , m_unlocked(false) ++{ ++ ++} ++ ++CoinsInfoImpl::~CoinsInfoImpl() = default; ++ ++uint64_t CoinsInfoImpl::blockHeight() const ++{ ++ return m_blockHeight; ++} ++ ++string CoinsInfoImpl::hash() const ++{ ++ return m_hash; ++} ++ ++size_t CoinsInfoImpl::internalOutputIndex() const { ++ return m_internalOutputIndex; ++} ++ ++uint64_t CoinsInfoImpl::globalOutputIndex() const ++{ ++ return m_globalOutputIndex; ++} ++ ++bool CoinsInfoImpl::spent() const ++{ ++ return m_spent; ++} ++ ++bool CoinsInfoImpl::frozen() const ++{ ++ return m_frozen; ++} ++ ++uint64_t CoinsInfoImpl::spentHeight() const ++{ ++ return m_spentHeight; ++} ++ ++uint64_t CoinsInfoImpl::amount() const ++{ ++ return m_amount; ++} ++ ++bool CoinsInfoImpl::rct() const { ++ return m_rct; ++} ++ ++bool CoinsInfoImpl::keyImageKnown() const { ++ return m_keyImageKnown; ++} ++ ++size_t CoinsInfoImpl::pkIndex() const { ++ return m_pkIndex; ++} ++ ++uint32_t CoinsInfoImpl::subaddrIndex() const { ++ return m_subaddrIndex; ++} ++ ++uint32_t CoinsInfoImpl::subaddrAccount() const { ++ return m_subaddrAccount; ++} ++ ++string CoinsInfoImpl::address() const { ++ return m_address; ++} ++ ++string CoinsInfoImpl::addressLabel() const { ++ return m_addressLabel; ++} ++ ++string CoinsInfoImpl::keyImage() const { ++ return m_keyImage; ++} ++ ++uint64_t CoinsInfoImpl::unlockTime() const { ++ return m_unlockTime; ++} ++ ++bool CoinsInfoImpl::unlocked() const { ++ return m_unlocked; ++} ++ ++string CoinsInfoImpl::pubKey() const { ++ return m_pubKey; ++} ++ ++bool CoinsInfoImpl::coinbase() const { ++ return m_coinbase; ++} ++ ++string CoinsInfoImpl::description() const { ++ return m_description; ++} ++} // namespace ++ ++namespace Bitmonero = Monero; +diff --git a/src/wallet/api/coins_info.h b/src/wallet/api/coins_info.h +new file mode 100644 +index 000000000..c43e45abd +--- /dev/null ++++ b/src/wallet/api/coins_info.h +@@ -0,0 +1,71 @@ ++#ifndef FEATHER_COINS_INFO_H ++#define FEATHER_COINS_INFO_H ++ ++#include "wallet/api/wallet2_api.h" ++#include ++#include ++ ++namespace Monero { ++ ++class CoinsImpl; ++ ++class CoinsInfoImpl : public CoinsInfo ++{ ++public: ++ CoinsInfoImpl(); ++ ~CoinsInfoImpl(); ++ ++ virtual uint64_t blockHeight() const override; ++ virtual std::string hash() const override; ++ virtual size_t internalOutputIndex() const override; ++ virtual uint64_t globalOutputIndex() const override; ++ virtual bool spent() const override; ++ virtual bool frozen() const override; ++ virtual uint64_t spentHeight() const override; ++ virtual uint64_t amount() const override; ++ virtual bool rct() const override; ++ virtual bool keyImageKnown() const override; ++ virtual size_t pkIndex() const override; ++ virtual uint32_t subaddrIndex() const override; ++ virtual uint32_t subaddrAccount() const override; ++ virtual std::string address() const override; ++ virtual std::string addressLabel() const override; ++ virtual std::string keyImage() const override; ++ virtual uint64_t unlockTime() const override; ++ virtual bool unlocked() const override; ++ virtual std::string pubKey() const override; ++ virtual bool coinbase() const override; ++ virtual std::string description() const override; ++ ++private: ++ uint64_t m_blockHeight; ++ std::string m_hash; ++ size_t m_internalOutputIndex; ++ uint64_t m_globalOutputIndex; ++ bool m_spent; ++ bool m_frozen; ++ uint64_t m_spentHeight; ++ uint64_t m_amount; ++ bool m_rct; ++ bool m_keyImageKnown; ++ size_t m_pkIndex; ++ uint32_t m_subaddrIndex; ++ uint32_t m_subaddrAccount; ++ std::string m_address; ++ std::string m_addressLabel; ++ std::string m_keyImage; ++ uint64_t m_unlockTime; ++ bool m_unlocked; ++ std::string m_pubKey; ++ bool m_coinbase; ++ std::string m_description; ++ ++ friend class CoinsImpl; ++ ++}; ++ ++} // namespace ++ ++namespace Bitmonero = Monero; ++ ++#endif //FEATHER_COINS_INFO_H +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index 704e5e148..e69910e69 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -35,6 +35,7 @@ + #include "transaction_history.h" + #include "address_book.h" + #include "subaddress.h" ++#include "coins.h" + #include "subaddress_account.h" + #include "common_defines.h" + #include "common/util.h" +@@ -473,6 +474,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) + m_wallet->set_refresh_enabled(false); + m_addressBook.reset(new AddressBookImpl(this)); + m_subaddress.reset(new SubaddressImpl(this)); ++ m_coins.reset(new CoinsImpl(this)); + m_subaddressAccount.reset(new SubaddressAccountImpl(this)); + + +@@ -2046,7 +2048,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat + // - unconfirmed_transfer_details; + // - confirmed_transfer_details) + +-PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) ++PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector &dst_addr, const string &payment_id, optional> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) + + { + clearStatus(); +@@ -2084,6 +2086,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectornettype(), dst_addr[i])) { + // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982 +@@ -2105,6 +2108,7 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorunlocked_balance(subaddr_account, true); ++ // if (maxAllowedSpend < amountSum) { ++ // error = true; ++ // setStatusError(tr("Amount you are trying to spend is larger than unlocked amount")); ++ // break; ++ // } ++ std::vector preferred_input_list; ++ if (!preferred_inputs.empty()) { ++ LOG_ERROR("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; ++ } ++ if (m_wallet->frozen(keyImage)) { ++ error = true; ++ setStatusError(tr("refusing to spend frozen coin")); ++ break; ++ } ++ ++ preferred_input_list.push_back(keyImage); ++ } ++ } else { ++ LOG_ERROR("not empty"); ++ ++ boost::shared_lock transfers_lock(m_wallet->m_transfers_mutex); ++ 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); ++ LOG_ERROR("COIN: " << i << ": " << td.amount() << "; "<frozen(td)); ++ if (td.m_spent) continue; ++ LOG_ERROR("is frozen"); ++ if (!td.m_frozen) { ++ LOG_ERROR("isn't:"); ++ LOG_ERROR("hash: " << td.m_key_image << "; " << td.amount()); ++ preferred_input_list.push_back(td.m_key_image); ++ } ++ } ++ } ++ for (const auto &de : preferred_input_list) { ++ LOG_ERROR("preferred input: " << de); ++ } + if (error) { + break; + } +@@ -2129,11 +2178,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorm_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); + +@@ -2214,10 +2263,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector amount, uint32_t mixin_count, +- PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) ++ PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices, const std::set &preferred_inputs) + + { +- return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices); ++ return createTransactionMultDest(std::vector {dst_addr}, payment_id, amount ? (std::vector {*amount}) : (optional>()), mixin_count, priority, subaddr_account, subaddr_indices, preferred_inputs); + } + + PendingTransaction *WalletImpl::createSweepUnmixableTransaction() +@@ -2342,6 +2391,11 @@ AddressBook *WalletImpl::addressBook() + return m_addressBook.get(); + } + ++Coins *WalletImpl::coins() ++{ ++ return m_coins.get(); ++} ++ + Subaddress *WalletImpl::subaddress() + { + return m_subaddress.get(); +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index 32e12284b..a82f270e4 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -46,6 +46,7 @@ class PendingTransactionImpl; + class UnsignedTransactionImpl; + class AddressBookImpl; + class SubaddressImpl; ++class CoinsImpl; + class SubaddressAccountImpl; + struct Wallet2CallbackImpl; + +@@ -167,12 +168,14 @@ public: + optional> amount, uint32_t mixin_count, + PendingTransaction::Priority priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) override; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) override; + PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, + optional amount, uint32_t mixin_count, + PendingTransaction::Priority priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) override; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) override; + virtual PendingTransaction * createSweepUnmixableTransaction() override; + bool submitTransaction(const std::string &fileName) override; + bool submitTransactionUR(const std::string &input) override; +@@ -201,6 +204,7 @@ public: + PendingTransaction::Priority priority) const override; + virtual TransactionHistory * history() override; + virtual AddressBook * addressBook() override; ++ virtual Coins * coins() override; + virtual Subaddress * subaddress() override; + virtual SubaddressAccount * subaddressAccount() override; + virtual void setListener(WalletListener * l) override; +@@ -272,6 +276,7 @@ private: + friend class TransactionHistoryImpl; + friend struct Wallet2CallbackImpl; + friend class AddressBookImpl; ++ friend class CoinsImpl; + friend class SubaddressImpl; + friend class SubaddressAccountImpl; + +@@ -288,6 +293,7 @@ private: + std::unique_ptr m_wallet2Callback; + std::unique_ptr m_addressBook; + std::unique_ptr m_subaddress; ++ std::unique_ptr m_coins; + std::unique_ptr m_subaddressAccount; + + // multi-threaded refresh stuff +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index be1c3704e..013b5bcba 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -263,6 +263,51 @@ struct AddressBook + virtual int lookupPaymentID(const std::string &payment_id) const = 0; + }; + ++/** ++ * @brief The CoinsInfo - interface for displaying coins information ++ */ ++struct CoinsInfo ++{ ++ virtual ~CoinsInfo() = 0; ++ ++ virtual uint64_t blockHeight() const = 0; ++ virtual std::string hash() const = 0; ++ virtual size_t internalOutputIndex() const = 0; ++ virtual uint64_t globalOutputIndex() const = 0; ++ virtual bool spent() const = 0; ++ virtual bool frozen() const = 0; ++ virtual uint64_t spentHeight() const = 0; ++ virtual uint64_t amount() const = 0; ++ virtual bool rct() const = 0; ++ virtual bool keyImageKnown() const = 0; ++ virtual size_t pkIndex() const = 0; ++ virtual uint32_t subaddrIndex() const = 0; ++ virtual uint32_t subaddrAccount() const = 0; ++ virtual std::string address() const = 0; ++ virtual std::string addressLabel() const = 0; ++ virtual std::string keyImage() const = 0; ++ virtual uint64_t unlockTime() const = 0; ++ virtual bool unlocked() const = 0; ++ virtual std::string pubKey() const = 0; ++ virtual bool coinbase() const = 0; ++ virtual std::string description() const = 0; ++}; ++ ++struct Coins ++{ ++ virtual ~Coins() = 0; ++ virtual int count() const = 0; ++ virtual CoinsInfo * coin(int index) const = 0; ++ virtual std::vector getAll() const = 0; ++ virtual void refresh() = 0; ++ virtual void setFrozen(std::string public_key) = 0; ++ virtual void setFrozen(int index) = 0; ++ virtual void thaw(std::string public_key) = 0; ++ virtual void thaw(int index) = 0; ++ virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; ++ virtual void setDescription(const std::string &public_key, const std::string &description) = 0; ++}; ++ + struct SubaddressRow { + public: + SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): +@@ -856,7 +901,8 @@ struct Wallet + optional> amount, uint32_t mixin_count, + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) = 0; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) = 0; + + /*! + * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored +@@ -875,7 +921,8 @@ struct Wallet + optional amount, uint32_t mixin_count, + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, +- std::set subaddr_indices = {}) = 0; ++ std::set subaddr_indices = {}, ++ const std::set &preferred_inputs = {}) = 0; + + /*! + * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. +@@ -994,6 +1041,7 @@ struct Wallet + + virtual TransactionHistory * history() = 0; + virtual AddressBook * addressBook() = 0; ++ virtual Coins * coins() = 0; + virtual Subaddress * subaddress() = 0; + virtual SubaddressAccount * subaddressAccount() = 0; + virtual void setListener(WalletListener *) = 0; +diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp +index 3e49c21f8..4d756567f 100644 +--- a/src/wallet/wallet2.cpp ++++ b/src/wallet/wallet2.cpp +@@ -2094,12 +2094,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)); +@@ -2110,6 +2119,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) +@@ -2523,6 +2544,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 lock(m_transfers_mutex); + m_transfers.push_back(transfer_details{}); + transfer_details& td = m_transfers.back(); + td.m_block_height = height; +@@ -2626,6 +2648,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote + uint64_t extra_amount = amount - burnt; + if (!pool) + { ++ boost::unique_lock lock(m_transfers_mutex); + transfer_details &td = m_transfers[kit->second]; + td.m_block_height = height; + td.m_internal_output_index = o; +@@ -10497,7 +10520,7 @@ void wallet2::transfer_selected_rct(std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices) ++std::vector wallet2::pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& preferred_input_list) + { + std::vector picks; + float current_output_relatdness = 1.0f; +@@ -10508,6 +10531,9 @@ std::vector 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) +@@ -10528,6 +10554,9 @@ std::vector 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) +@@ -10539,6 +10568,9 @@ std::vector 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) << "]"); +@@ -11111,7 +11143,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::create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs) ++std::vector wallet2::create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& 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(); +@@ -11319,6 +11351,9 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector &ptx_vector, c + return true; + } + +-std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices) ++std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list) + { + std::vector unused_transfers_indices; + std::vector unused_dust_indices; +@@ -11918,6 +11953,9 @@ std::vector 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)); +diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h +index c26349ce3..a48ba33b6 100644 +--- a/src/wallet/wallet2.h ++++ b/src/wallet/wallet2.h +@@ -1209,8 +1209,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 &ptx, std::function accept_func = NULL); + bool parse_tx_from_str(const std::string &signed_tx_st, std::vector &ptx, std::function accept_func); +- std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose +- std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices); ++ std::vector create_transactions_2(std::vector dsts, const size_t fake_outs_count, uint32_t priority, const std::vector& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose ++ std::vector 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& extra, uint32_t subaddr_account, std::set subaddr_indices, const std::vector& preferred_input_list = {}); + std::vector 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& extra); + std::vector create_transactions_from(const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, std::vector unused_transfers_indices, std::vector unused_dust_indices, const size_t fake_outs_count, uint32_t priority, const std::vector& extra); + bool sanity_check(const std::vector &ptx_vector, const std::vector& dsts, const unique_index_container& subtract_fee_from_outputs = {}) const; +@@ -1562,6 +1562,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); +@@ -1793,7 +1794,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; +@@ -1834,6 +1837,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. +@@ -1905,7 +1910,7 @@ private: + std::vector 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 pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices); ++ std::vector pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set &subaddr_indices, const std::vector& 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; +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0011-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch b/patches/wownero/0011-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch new file mode 100644 index 0000000..f130f51 --- /dev/null +++ b/patches/wownero/0011-Add-hex-encoding-and-tx-key-getter-for-PendingTransc.patch @@ -0,0 +1,68 @@ +From 7c4f0b36d627bf4d1ce198240c1e974eff27bbec Mon Sep 17 00:00:00 2001 +From: M +Date: Fri, 21 Apr 2023 15:43:47 -0400 +Subject: [PATCH 11/15] Add hex encoding and tx key getter for + PendingTransction in wallet api. + +--- + src/wallet/api/pending_transaction.cpp | 16 ++++++++++++++++ + src/wallet/api/pending_transaction.h | 2 ++ + src/wallet/api/wallet2_api.h | 2 ++ + 3 files changed, 20 insertions(+) + +diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp +index 9c3c26ee5..1f714d229 100644 +--- a/src/wallet/api/pending_transaction.cpp ++++ b/src/wallet/api/pending_transaction.cpp +@@ -80,6 +80,22 @@ std::vector PendingTransactionImpl::txid() const + return txid; + } + ++std::vector PendingTransactionImpl::hex() const ++{ ++ std::vector hexs; ++ for (const auto &pt: m_pending_tx) ++ hexs.push_back(epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(pt.tx))); ++ return hexs; ++} ++ ++std::vector PendingTransactionImpl::txKey() const ++{ ++ std::vector keys; ++ for (const auto& pt: m_pending_tx) ++ keys.push_back(epee::string_tools::pod_to_hex(pt.tx_key)); ++ return keys; ++} ++ + bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) + { + +diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h +index 403bfe281..0cc6c58e9 100644 +--- a/src/wallet/api/pending_transaction.h ++++ b/src/wallet/api/pending_transaction.h +@@ -59,6 +59,8 @@ public: + std::string multisigSignData() override; + void signMultisigTx() override; + std::vector signersKeys() const override; ++ std::vector hex() const override; ++ std::vector txKey() const override; + + private: + friend class WalletImpl; +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index 013b5bcba..f421fdc05 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -127,6 +127,8 @@ struct PendingTransaction + * @return vector of base58-encoded signers' public keys + */ + virtual std::vector signersKeys() const = 0; ++ virtual std::vector hex() const = 0; ++ virtual std::vector txKey() const = 0; + }; + + /** +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0011-android-ndk-version-bump.patch b/patches/wownero/0011-android-ndk-version-bump.patch deleted file mode 100644 index 888f00d..0000000 --- a/patches/wownero/0011-android-ndk-version-bump.patch +++ /dev/null @@ -1,120 +0,0 @@ -From aff9394b39ccfa9842d47007a644f08b6b370e55 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Thu, 23 May 2024 08:02:49 +0200 -Subject: [PATCH 11/14] android ndk version bump - ---- - contrib/depends/hosts/android.mk | 16 +++++++++++++--- - contrib/depends/packages/android_ndk.mk | 20 ++++++++++++++------ - contrib/depends/packages/boost.mk | 1 + - contrib/depends/toolchain.cmake.in | 11 ++++++----- - 4 files changed, 34 insertions(+), 14 deletions(-) - -diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk -index 5b2309972..c7b6b6d36 100644 ---- a/contrib/depends/hosts/android.mk -+++ b/contrib/depends/hosts/android.mk -@@ -1,12 +1,22 @@ - ANDROID_API=21 -- -+host_toolchain=nonexistent - ifeq ($(host_arch),arm) --host_toolchain=arm-linux-androideabi- -+host_toolchain=armv7a-linux-androideabi${ANDROID_API}- -+endif -+ifeq ($(host_arch),x86_64) -+host_toolchain=x86_64-linux-android${ANDROID_API}- -+endif -+ifeq ($(host_arch),i686) -+host_toolchain=i686-linux-android${ANDROID_API}- -+endif -+ifeq ($(host_arch),aarch64) -+host_toolchain=aarch64-linux-android${ANDROID_API}- - endif - - android_CC=$(host_toolchain)clang - android_CXX=$(host_toolchain)clang++ --android_RANLIB=$(host_toolchain)ranlib -+android_RANLIB=llvm-ranlib -+android_AR=llvm-ar - android_AR=$(host_toolchain)ar - - android_CFLAGS=-pipe -diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk -index 5deff76c7..1ad250653 100644 ---- a/contrib/depends/packages/android_ndk.mk -+++ b/contrib/depends/packages/android_ndk.mk -@@ -1,8 +1,10 @@ - package=android_ndk --$(package)_version=17b -+$(package)_version=26d - $(package)_download_path=https://dl.google.com/android/repository/ --$(package)_file_name=android-ndk-r$($(package)_version)-linux-x86_64.zip --$(package)_sha256_hash=5dfbbdc2d3ba859fed90d0e978af87c71a91a5be1f6e1c40ba697503d48ccecd -+$(package)_file_name=android-ndk-r$($(package)_version)-linux.zip -+$(package)_sha256_hash=eefeafe7ccf177de7cc57158da585e7af119bb7504a63604ad719e4b2a328b54 -+ -+$(package)_version_apiversion=21 - - define $(package)_set_vars - $(package)_config_opts_arm=--arch arm -@@ -17,8 +19,14 @@ define $(package)_extract_cmds - endef - - define $(package)_stage_cmds -- android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api 21 \ -- --install-dir $(build_prefix) --stl=libc++ $($(package)_config_opts) &&\ -- mv $(build_prefix) $($(package)_staging_dir)/$(host_prefix) -+ mkdir -p $(build_prefix) &&\ -+ echo $(build_prefix)/toolchain && \ -+ android-ndk-r$($(package)_version)/build/tools/make_standalone_toolchain.py --api $($(package)_version_apiversion) \ -+ --install-dir $(build_prefix)/toolchain --stl=libc++ $($(package)_config_opts) &&\ -+ mv $(build_prefix)/toolchain $($(package)_staging_dir)/$(host_prefix)/native && \ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ar &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ranlib &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ar &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ranlib - endef - -diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk -index fd06c5393..c17e863cc 100644 ---- a/contrib/depends/packages/boost.mk -+++ b/contrib/depends/packages/boost.mk -@@ -25,6 +25,7 @@ $(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_android=-fPIC - $(package)_cxxflags_freebsd=-fPIC - endef - -diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in -index f118c754e..249213b20 100644 ---- a/contrib/depends/toolchain.cmake.in -+++ b/contrib/depends/toolchain.cmake.in -@@ -104,16 +104,17 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") - SET(CMAKE_ANDROID_ARCH_ABI "armeabi-v7a") - SET(CMAKE_SYSTEM_PROCESSOR "armv7-a") - SET(CMAKE_ANDROID_ARM_MODE ON) -- SET(CMAKE_C_COMPILER_TARGET arm-linux-androideabi) -- SET(CMAKE_CXX_COMPILER_TARGET arm-linux-androideabi) -- SET(_CMAKE_TOOLCHAIN_PREFIX arm-linux-androideabi-) -+ SET(CMAKE_C_COMPILER_TARGET arm-linux-androideabi21) -+ SET(CMAKE_CXX_COMPILER_TARGET arm-linux-androideabi21) -+ SET(_CMAKE_TOOLCHAIN_PREFIX arm-linux-androideabi21-) - elseif(ARCHITECTURE STREQUAL "aarch64") - SET(CMAKE_ANDROID_ARCH_ABI "arm64-v8a") - SET(CMAKE_SYSTEM_PROCESSOR "aarch64") - endif() - SET(CMAKE_ANDROID_STANDALONE_TOOLCHAIN @prefix@/native) -- SET(CMAKE_C_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang") -- SET(CMAKE_CXX_COMPILER "${_CMAKE_TOOLCHAIN_PREFIX}clang++") -+ SET(_ANDROID_STANDALONE_TOOLCHAIN_API 21) -+ SET(CMAKE_C_COMPILER @CC@) -+ SET(CMAKE_CXX_COMPILER @CXX@) - else() - SET(CMAKE_C_COMPILER @CC@) - SET(CMAKE_CXX_COMPILER @CXX@) --- -2.45.2 - diff --git a/patches/wownero/0012-Add-recoverDeterministicWalletFromSpendKey.patch b/patches/wownero/0012-Add-recoverDeterministicWalletFromSpendKey.patch new file mode 100644 index 0000000..a15e54c --- /dev/null +++ b/patches/wownero/0012-Add-recoverDeterministicWalletFromSpendKey.patch @@ -0,0 +1,153 @@ +From 298c7e0745306605d7ed1cee320b92eacf8aaeb6 Mon Sep 17 00:00:00 2001 +From: Konstantin Ullrich +Date: Wed, 11 Oct 2023 16:47:59 +0200 +Subject: [PATCH 12/15] Add recoverDeterministicWalletFromSpendKey + +This function is used by Cake Wallet to enable polyseed (dart implementation) +support. + +Sourced from the following commit: +https://github.com/cake-tech/monero/commit/cb6fb5ab218878702ed151c0e3d5d68eb2732788 + +Co-authored-by: Godwin Asuquo +--- + src/wallet/api/wallet.cpp | 29 +++++++++++++++++++++++++++++ + src/wallet/api/wallet.h | 4 ++++ + src/wallet/api/wallet2_api.h | 19 +++++++++++++++++++ + src/wallet/api/wallet_manager.cpp | 16 ++++++++++++++++ + src/wallet/api/wallet_manager.h | 7 +++++++ + 5 files changed, 75 insertions(+) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index e69910e69..e650e6044 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -824,6 +824,35 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c + return status() == Status_Ok; + } + ++bool WalletImpl::recoverDeterministicWalletFromSpendKey(const std::string &path, const std::string &password, const std::string &language, const std::string &spendkey_string) ++{ ++ clearStatus(); ++ m_errorString.clear(); ++ ++ m_recoveringFromSeed = true; ++ m_recoveringFromDevice = false; ++ ++ // parse spend key ++ crypto::secret_key spendkey; ++ if (!spendkey_string.empty()) { ++ cryptonote::blobdata spendkey_data; ++ if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) ++ { ++ setStatusError(tr("failed to parse secret spend key")); ++ return false; ++ } ++ spendkey = *reinterpret_cast(spendkey_data.data()); ++ } ++ ++ try { ++ m_wallet->generate(path, password, spendkey, true, false); ++ setSeedLanguage(language); ++ } catch (const std::exception &e) { ++ setStatusCritical(e.what()); ++ } ++ return status() == Status_Ok; ++} ++ + bool WalletImpl::close(bool store) + { + +diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h +index a82f270e4..9e1fbb40b 100644 +--- a/src/wallet/api/wallet.h ++++ b/src/wallet/api/wallet.h +@@ -77,6 +77,10 @@ public: + const std::string &address_string, + const std::string &viewkey_string, + const std::string &spendkey_string = ""); ++ bool recoverDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ const std::string &spendkey_string); + bool recoverFromDevice(const std::string &path, + const std::string &password, + const std::string &device_name); +diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h +index f421fdc05..c8d6bb179 100644 +--- a/src/wallet/api/wallet2_api.h ++++ b/src/wallet/api/wallet2_api.h +@@ -1323,6 +1323,25 @@ struct WalletManager + return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); + } + ++ /*! ++ * \brief recover deterministic wallet from spend key. ++ * \param path Name of wallet file to be created ++ * \param password Password of wallet file ++ * \param language language ++ * \param nettype Network type ++ * \param restoreHeight restore from start height ++ * \param spendKeyString spend key ++ * \param kdf_rounds Number of rounds for key derivation function ++ * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) ++ */ ++ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ NetworkType nettype, ++ uint64_t restoreHeight, ++ const std::string &spendKeyString, ++ uint64_t kdf_rounds = 1) = 0; ++ + /*! + * \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead + * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted +diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp +index da2056d8a..c200f52ae 100644 +--- a/src/wallet/api/wallet_manager.cpp ++++ b/src/wallet/api/wallet_manager.cpp +@@ -127,6 +127,22 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, + return wallet; + } + ++Wallet *WalletManagerImpl::createDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ NetworkType nettype, ++ uint64_t restoreHeight, ++ const std::string &spendkey_string, ++ uint64_t kdf_rounds) ++{ ++ WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds); ++ if(restoreHeight > 0){ ++ wallet->setRefreshFromBlockHeight(restoreHeight); ++ } ++ wallet->recoverDeterministicWalletFromSpendKey(path, password, language, spendkey_string); ++ return wallet; ++} ++ + Wallet *WalletManagerImpl::createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, +diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h +index 28fcd36c9..be3ff8184 100644 +--- a/src/wallet/api/wallet_manager.h ++++ b/src/wallet/api/wallet_manager.h +@@ -67,6 +67,13 @@ public: + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = "") override; ++ virtual Wallet * createDeterministicWalletFromSpendKey(const std::string &path, ++ const std::string &password, ++ const std::string &language, ++ NetworkType nettype, ++ uint64_t restoreHeight, ++ const std::string &spendkey_string, ++ uint64_t kdf_rounds) override; + virtual Wallet * createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0012-rename-arm-armv7a.patch b/patches/wownero/0012-rename-arm-armv7a.patch deleted file mode 100644 index 9f6b91f..0000000 --- a/patches/wownero/0012-rename-arm-armv7a.patch +++ /dev/null @@ -1,94 +0,0 @@ -From c40118e32ff966d0796b0b8f36f26a6f08dcfa90 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Wed, 12 Jun 2024 15:48:01 +0200 -Subject: [PATCH 12/14] rename arm -> armv7a - ---- - contrib/depends/hosts/android.mk | 2 +- - contrib/depends/packages/android_ndk.mk | 6 +++++- - contrib/depends/packages/openssl.mk | 2 +- - contrib/depends/toolchain.cmake.in | 8 ++++---- - 4 files changed, 11 insertions(+), 7 deletions(-) - -diff --git a/contrib/depends/hosts/android.mk b/contrib/depends/hosts/android.mk -index c7b6b6d36..9477b4e22 100644 ---- a/contrib/depends/hosts/android.mk -+++ b/contrib/depends/hosts/android.mk -@@ -1,6 +1,6 @@ - ANDROID_API=21 - host_toolchain=nonexistent --ifeq ($(host_arch),arm) -+ifeq ($(host_arch),armv7a) - host_toolchain=armv7a-linux-androideabi${ANDROID_API}- - endif - ifeq ($(host_arch),x86_64) -diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk -index 1ad250653..2c2914ec2 100644 ---- a/contrib/depends/packages/android_ndk.mk -+++ b/contrib/depends/packages/android_ndk.mk -@@ -7,7 +7,7 @@ $(package)_sha256_hash=eefeafe7ccf177de7cc57158da585e7af119bb7504a63604ad719e4b2 - $(package)_version_apiversion=21 - - define $(package)_set_vars --$(package)_config_opts_arm=--arch arm -+$(package)_config_opts_armv7a=--arch arm - $(package)_config_opts_aarch64=--arch arm64 - $(package)_config_opts_x86_64=--arch x86_64 - $(package)_config_opts_i686=--arch x86 -@@ -18,6 +18,8 @@ define $(package)_extract_cmds - unzip -q $($(1)_source_dir)/$($(package)_file_name) - endef - -+# arm-linux-androideabi-ar - openssl workaround -+ - define $(package)_stage_cmds - mkdir -p $(build_prefix) &&\ - echo $(build_prefix)/toolchain && \ -@@ -25,7 +27,9 @@ define $(package)_stage_cmds - --install-dir $(build_prefix)/toolchain --stl=libc++ $($(package)_config_opts) &&\ - mv $(build_prefix)/toolchain $($(package)_staging_dir)/$(host_prefix)/native && \ - cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ar &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ar &&\ - cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)$($(package)_version_apiversion)-ranlib &&\ -+ cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/arm-linux-androideabi-ranlib &&\ - cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ar $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ar &&\ - cp $($(package)_staging_dir)/$(host_prefix)/native/bin/llvm-ranlib $($(package)_staging_dir)/$(host_prefix)/native/bin/$(host)-ranlib - endef -diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk -index a157762c7..2430f6495 100644 ---- a/contrib/depends/packages/openssl.mk -+++ b/contrib/depends/packages/openssl.mk -@@ -34,7 +34,7 @@ $(package)_config_opts_x86_64_linux=linux-x86_64 - $(package)_config_opts_i686_linux=linux-generic32 - $(package)_config_opts_arm_linux=linux-generic32 - $(package)_config_opts_aarch64_linux=linux-generic64 --$(package)_config_opts_arm_android=--static android-arm -+$(package)_config_opts_armv7a_android=--static android-arm - $(package)_config_opts_aarch64_android=--static android-arm64 - $(package)_config_opts_aarch64_darwin=darwin64-arm64-cc - $(package)_config_opts_riscv64_linux=linux-generic64 -diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in -index 249213b20..cc1d9b5c5 100644 ---- a/contrib/depends/toolchain.cmake.in -+++ b/contrib/depends/toolchain.cmake.in -@@ -100,13 +100,13 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - SET(LLVM_ENABLE_PIE OFF) - elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") - SET(ANDROID TRUE) -- if(ARCHITECTURE STREQUAL "arm") -+ if(ARCHITECTURE STREQUAL "armv7a") - SET(CMAKE_ANDROID_ARCH_ABI "armeabi-v7a") - SET(CMAKE_SYSTEM_PROCESSOR "armv7-a") - SET(CMAKE_ANDROID_ARM_MODE ON) -- SET(CMAKE_C_COMPILER_TARGET arm-linux-androideabi21) -- SET(CMAKE_CXX_COMPILER_TARGET arm-linux-androideabi21) -- SET(_CMAKE_TOOLCHAIN_PREFIX arm-linux-androideabi21-) -+ SET(CMAKE_C_COMPILER_TARGET armv7a-linux-androideabi21) -+ SET(CMAKE_CXX_COMPILER_TARGET armv7a-linux-androideabi21) -+ SET(_CMAKE_TOOLCHAIN_PREFIX armv7a-linux-androideabi21-) - elseif(ARCHITECTURE STREQUAL "aarch64") - SET(CMAKE_ANDROID_ARCH_ABI "arm64-v8a") - SET(CMAKE_SYSTEM_PROCESSOR "aarch64") --- -2.45.2 - diff --git a/patches/wownero/0013-add-monero-submodule-support.patch b/patches/wownero/0013-add-monero-submodule-support.patch new file mode 100644 index 0000000..f220055 --- /dev/null +++ b/patches/wownero/0013-add-monero-submodule-support.patch @@ -0,0 +1,65 @@ +From 1bd61531746e983799fcae9d3a7228620bbc84f5 Mon Sep 17 00:00:00 2001 +From: cyan +Date: Thu, 7 Nov 2024 16:46:24 +0000 +Subject: [PATCH 13/15] add monero submodule support + +--- + CMakeLists.txt | 6 +++--- + cmake/CheckLinkerFlag.cmake | 2 +- + src/wallet/wallet_rpc_server.cpp | 2 +- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 85a62ef7b..763d240fc 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -223,9 +223,9 @@ function(forbid_undefined_symbols) + cmake_minimum_required(VERSION 3.1) + project(test) + option(EXPECT_SUCCESS "" ON) +-file(WRITE "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }") ++file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" "void undefined_symbol(); void symbol() { undefined_symbol(); }") + if (EXPECT_SUCCESS) +- file(APPEND "${CMAKE_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ") ++ file(APPEND "${CMAKE_CURRENT_SOURCE_DIR}/incorrect_source.cpp" " void undefined_symbol() {}; ") + endif() + add_library(l0 SHARED incorrect_source.cpp) + add_library(l1 MODULE incorrect_source.cpp) +@@ -391,7 +391,7 @@ else() + endif() + + list(INSERT CMAKE_MODULE_PATH 0 +- "${CMAKE_SOURCE_DIR}/cmake") ++ "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + + if (NOT DEFINED ENV{DEVELOPER_LOCAL_TOOLS}) + message(STATUS "Could not find DEVELOPER_LOCAL_TOOLS in env (not required)") +diff --git a/cmake/CheckLinkerFlag.cmake b/cmake/CheckLinkerFlag.cmake +index 7ecf5f610..89fb9d167 100644 +--- a/cmake/CheckLinkerFlag.cmake ++++ b/cmake/CheckLinkerFlag.cmake +@@ -6,7 +6,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE) + message(STATUS "Looking for ${flag} linker flag") + endif() + +- set(_cle_source ${CMAKE_SOURCE_DIR}/cmake/CheckLinkerFlag.c) ++ set(_cle_source ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckLinkerFlag.c) + + set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) + set(CMAKE_C_FLAGS "${flag}") +diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp +index 3cc6b278f..071434486 100644 +--- a/src/wallet/wallet_rpc_server.cpp ++++ b/src/wallet/wallet_rpc_server.cpp +@@ -1162,7 +1162,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 ptx_vector = m_wallet->create_transactions_2(dsts, mixin, priority, extra, req.account_index, req.subaddr_indices, req.subtract_fee_from_outputs); ++ std::vector 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()) + { +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0013-use-proper-error-handling-in-get_seed.patch b/patches/wownero/0013-use-proper-error-handling-in-get_seed.patch deleted file mode 100644 index ca98c84..0000000 --- a/patches/wownero/0013-use-proper-error-handling-in-get_seed.patch +++ /dev/null @@ -1,68 +0,0 @@ -From f813a27e5842afde721494b9a1038d8f9d00016b Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 24 Jun 2024 10:49:12 +0200 -Subject: [PATCH 13/14] use proper error handling in get_seed - ---- - src/wallet/api/wallet.cpp | 17 ++++++++++++----- - src/wallet/wallet2.cpp | 3 +++ - 2 files changed, 15 insertions(+), 5 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index ee0eeeb01..02cf7c79b 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -879,12 +879,19 @@ bool WalletImpl::close(bool store) - - std::string WalletImpl::seed(const std::string& seed_offset) const - { -- if (checkBackgroundSync("cannot get seed")) -+ if (checkBackgroundSync("cannot get seed")) { -+ setStatusError("cannot get seed"); - return std::string(); -- epee::wipeable_string seed; -- if (m_wallet) -- m_wallet->get_seed(seed, seed_offset); -- return std::string(seed.data(), seed.size()); // TODO -+ } -+ try { -+ epee::wipeable_string seed; -+ if (m_wallet) -+ m_wallet->get_seed(seed, seed_offset); -+ return std::string(seed.data(), seed.size()); // TODO -+ } catch (const std::exception &e) { -+ setStatusError(e.what()); -+ return std::string(); -+ } - } - - bool WalletImpl::getPolyseed(std::string &seed_words, std::string &passphrase) const -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 2b3eaad2e..4073974d9 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -1445,11 +1445,13 @@ bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeab - bool keys_deterministic = is_deterministic(); - if (!keys_deterministic) - { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "This is not a deterministic wallet"); - std::cout << "This is not a deterministic wallet" << std::endl; - return false; - } - if (seed_language.empty()) - { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "seed_language not set"); - std::cout << "seed_language not set" << std::endl; - return false; - } -@@ -1459,6 +1461,7 @@ 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)) - { -+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Failed to create seed from key for language: "+seed_language+", falling back to English."); - 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"); - } --- -2.45.2 - diff --git a/patches/wownero/0014-fix-iOS-depends-build.patch b/patches/wownero/0014-fix-iOS-depends-build.patch new file mode 100644 index 0000000..5e97b89 --- /dev/null +++ b/patches/wownero/0014-fix-iOS-depends-build.patch @@ -0,0 +1,104 @@ +From f9bf382c57e4a36044e7a0697dc36073487eadd3 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Thu, 21 Nov 2024 06:05:03 -0500 +Subject: [PATCH 14/15] fix iOS depends build + +--- + CMakeLists.txt | 4 ---- + src/checkpoints/CMakeLists.txt | 6 +++++- + src/cryptonote_basic/CMakeLists.txt | 6 +++++- + src/cryptonote_basic/miner.cpp | 8 ++++---- + 4 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 763d240fc..e3a0faacd 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -39,10 +39,6 @@ include(CheckLibraryExists) + include(CheckFunctionExists) + include(FindPythonInterp) + +-if (IOS) +- INCLUDE(CmakeLists_IOS.txt) +-endif() +- + cmake_minimum_required(VERSION 3.5) + message(STATUS "CMake version ${CMAKE_VERSION}") + +diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt +index 665441f62..841df3256 100644 +--- a/src/checkpoints/CMakeLists.txt ++++ b/src/checkpoints/CMakeLists.txt +@@ -28,7 +28,11 @@ + + if(APPLE) + if(DEPENDS) +- list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") ++ if(${CMAKE_SYSTEM_NAME} STREQUAL "iOS") ++ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") ++ else() ++ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") ++ endif() + else() + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) +diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt +index 414936a05..81c81767f 100644 +--- a/src/cryptonote_basic/CMakeLists.txt ++++ b/src/cryptonote_basic/CMakeLists.txt +@@ -28,7 +28,11 @@ + + if(APPLE) + if(DEPENDS) +- list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") ++ if(${CMAKE_SYSTEM_NAME} STREQUAL "iOS") ++ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework IOKit") ++ else() ++ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") ++ endif() + else() + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) +diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp +index 83bea8b5b..dfb4b5c5a 100644 +--- a/src/cryptonote_basic/miner.cpp ++++ b/src/cryptonote_basic/miner.cpp +@@ -45,7 +45,7 @@ + #include "boost/logic/tribool.hpp" + #include + +-#ifdef __APPLE__ ++#if defined(__APPLE__) && !defined(TARGET_OS_IPHONE) + #include + #include + #include +@@ -971,7 +971,7 @@ namespace cryptonote + + return true; + +- #elif defined(__APPLE__) ++ #elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE) + + mach_msg_type_number_t count; + kern_return_t status; +@@ -1037,7 +1037,7 @@ namespace cryptonote + return true; + } + +- #elif (defined(__linux__) && defined(_SC_CLK_TCK)) || defined(__APPLE__) || defined(__FreeBSD__) ++ #elif (defined(__linux__) && defined(_SC_CLK_TCK)) || (defined(__APPLE__) && !defined(TARGET_OS_IPHONE)) || defined(__FreeBSD__) + + struct tms tms; + if ( times(&tms) != (clock_t)-1 ) +@@ -1066,7 +1066,7 @@ namespace cryptonote + return boost::logic::tribool(power_status.ACLineStatus != 1); + } + +- #elif defined(__APPLE__) ++ #elif defined(__APPLE__) && !defined(TARGET_OS_IPHONE) + + #if TARGET_OS_MAC && (!defined(MAC_OS_X_VERSION_MIN_REQUIRED) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7) + return boost::logic::tribool(IOPSGetTimeRemainingEstimate() != kIOPSTimeRemainingUnlimited); +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0014-store-crash-fix.patch b/patches/wownero/0014-store-crash-fix.patch deleted file mode 100644 index 8fa2eb0..0000000 --- a/patches/wownero/0014-store-crash-fix.patch +++ /dev/null @@ -1,213 +0,0 @@ -From 462a261e97814d71b976fe04af26cda2637deb4f Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Sat, 11 May 2024 16:25:10 +0200 -Subject: [PATCH 14/14] store crash fix - -Monero wallet crashes (sometimes) when it is syncing, -while the proper solution (that can be seen in feather) -is to not store wallet while it is being synced, this is not -acceptable for mobile wallets where OS can just come -and kill the wallet because it felt like it. - -This patch depends on the background-sync patch, but -to use it as a standalone fix grabbing the definition for the -LOCK_REFRESH macro should be enough. - -tobtoht suggested: -_say you want to store every 15 minutes during background sync. you stop the refresh every 15 minutes. then do something like this in the callback:_ - -``` -// Make sure this doesn't run in the refresh thread -onRefreshed() { - if (hasItBeen15MinutesSinceWeStored()) { - store(); - } - - if (shouldWeContinueRefreshing()) { - startRefresh(); - } -} -``` - -which works for crashes after the wallet is initially synced -but doesn't solve the issue for wallet that are syncing (it -would just wait for it to finish before actually storing). - -Also imo store() functin should store the wallet, no matter -the current state. ---- - src/wallet/api/wallet.cpp | 25 ++++++++++++------------- - src/wallet/api/wallet.h | 1 - - src/wallet/wallet2.cpp | 12 +++++++++++- - src/wallet/wallet2.h | 3 +++ - 4 files changed, 26 insertions(+), 15 deletions(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 02cf7c79b..b416c9535 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -57,8 +57,8 @@ using namespace cryptonote; - #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI" - - #define LOCK_REFRESH() \ -- bool refresh_enabled = m_refreshEnabled; \ -- m_refreshEnabled = false; \ -+ bool refresh_enabled = m_wallet->get_refresh_enabled(); \ -+ m_wallet->set_refresh_enabled(false); \ - m_wallet->stop(); \ - m_refreshCV.notify_one(); \ - boost::mutex::scoped_lock lock(m_refreshMutex); \ -@@ -468,7 +468,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) - m_wallet2Callback.reset(new Wallet2CallbackImpl(this)); - m_wallet->callback(m_wallet2Callback.get()); - m_refreshThreadDone = false; -- m_refreshEnabled = false; -+ m_wallet->set_refresh_enabled(false); - m_addressBook.reset(new AddressBookImpl(this)); - m_subaddress.reset(new SubaddressImpl(this)); - m_coins.reset(new CoinsImpl(this)); -@@ -1092,6 +1092,7 @@ void WalletImpl::stop() - bool WalletImpl::store(const std::string &path) - { - clearStatus(); -+ LOCK_REFRESH(); - try { - if (path.empty()) { - m_wallet->store(); -@@ -2877,10 +2878,10 @@ void WalletImpl::refreshThreadFunc() - } - - LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired..."); -- LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled); -+ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_wallet->get_refresh_enabled()); - LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status()); - LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan); -- if (m_refreshEnabled) { -+ if (m_wallet->get_refresh_enabled()) { - LOG_PRINT_L3(__FUNCTION__ << ": refreshing..."); - doRefresh(); - } -@@ -2912,7 +2913,7 @@ void WalletImpl::doRefresh() - } - m_wallet->find_and_save_rings(false); - } else { -- LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); -+ LOG_PRINT_L3(__FUNCTION__ << ": skipping refresh - daemon is not synced"); - } - // assuming if we have empty history, it wasn't initialized yet - // for further history changes client need to update history in -@@ -2925,7 +2926,7 @@ void WalletImpl::doRefresh() - success = false; - setStatusError(e.what()); - break; -- }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested -+ }while(m_wallet->get_refresh_enabled() && !rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested - - m_is_connected = success; - if (m_wallet2Callback->getListener()) { -@@ -2936,9 +2937,9 @@ void WalletImpl::doRefresh() - - void WalletImpl::startRefresh() - { -- if (!m_refreshEnabled) { -+ if (!m_wallet->get_refresh_enabled()) { - LOG_PRINT_L2(__FUNCTION__ << ": refresh started/resumed..."); -- m_refreshEnabled = true; -+ m_wallet->set_refresh_enabled(true); - m_refreshCV.notify_one(); - } - } -@@ -2948,7 +2949,7 @@ void WalletImpl::startRefresh() - void WalletImpl::stopRefresh() - { - if (!m_refreshThreadDone) { -- m_refreshEnabled = false; -+ m_wallet->set_refresh_enabled(false); - m_refreshThreadDone = true; - m_refreshCV.notify_one(); - m_refreshThread.join(); -@@ -2959,9 +2960,7 @@ void WalletImpl::pauseRefresh() - { - LOG_PRINT_L2(__FUNCTION__ << ": refresh paused..."); - // TODO synchronize access -- if (!m_refreshThreadDone) { -- m_refreshEnabled = false; -- } -+ m_wallet->set_refresh_enabled(false); - } - - -diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 7b885e866..23d266de2 100644 ---- a/src/wallet/api/wallet.h -+++ b/src/wallet/api/wallet.h -@@ -325,7 +325,6 @@ private: - std::unique_ptr m_subaddressAccount; - - // multi-threaded refresh stuff -- std::atomic m_refreshEnabled; - std::atomic m_refreshThreadDone; - std::atomic m_refreshIntervalMillis; - std::atomic m_refreshShouldRescan; -diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp -index 4073974d9..0a0541cc8 100644 ---- a/src/wallet/wallet2.cpp -+++ b/src/wallet/wallet2.cpp -@@ -1204,6 +1204,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std - m_upper_transaction_weight_limit(0), - m_run(true), - m_callback(0), -+ m_refreshEnabled(false), - m_trusted_daemon(false), - m_nettype(nettype), - m_multisig_rounds_passed(0), -@@ -1418,6 +1419,14 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optionalset_proxy(address); -@@ -4217,8 +4226,9 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo - // infer when we get an incoming output - - bool first = true, last = false; -- while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks) -+ while(m_run.load(std::memory_order_relaxed) && blocks_fetched < max_blocks && m_refreshEnabled) - { -+ LOG_ERROR("SYNCING"); - uint64_t next_blocks_start_height; - std::vector next_blocks; - std::vector next_parsed_blocks; -diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h -index d1e68baac..556306f20 100644 ---- a/src/wallet/wallet2.h -+++ b/src/wallet/wallet2.h -@@ -1086,6 +1086,8 @@ private: - boost::optional daemon_login = boost::none, bool trusted_daemon = true, - epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect); - bool set_proxy(const std::string &address); -+ bool get_refresh_enabled(); -+ void set_refresh_enabled(bool val); - - void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); } - -@@ -2037,6 +2039,7 @@ private: - - boost::recursive_mutex m_daemon_rpc_mutex; - -+ bool m_refreshEnabled; - bool m_trusted_daemon; - i_wallet2_callback* m_callback; - hw::device::device_type m_key_device_type; --- -2.45.2 - diff --git a/patches/wownero/0015-include-locale-only-when-targeting-WIN32.patch b/patches/wownero/0015-include-locale-only-when-targeting-WIN32.patch new file mode 100644 index 0000000..512e012 --- /dev/null +++ b/patches/wownero/0015-include-locale-only-when-targeting-WIN32.patch @@ -0,0 +1,43 @@ +From a6538084be4fdfbf84e7cbae2bce040aeaf4a4f9 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 18 Nov 2024 10:57:37 -0500 +Subject: [PATCH 15/15] include locale only when targeting WIN32 + +--- + CMakeLists.txt | 5 ++++- + src/wallet/api/wallet.cpp | 2 ++ + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e3a0faacd..b9207ef2a 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1096,7 +1096,10 @@ if(STATIC) + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_STATIC_RUNTIME ON) + endif() +-find_package(Boost 1.58 QUIET REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options locale) ++if(WIN32) ++ set(BOOST_LOCALE locale) ++endif() ++find_package(Boost 1.58 QUIET REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options ${BOOST_LOCALE}) + add_definitions(-DBOOST_ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION) + add_definitions(-DBOOST_NO_AUTO_PTR) + add_definitions(-DBOOST_UUID_DISABLE_ALIGNMENT) # This restores UUID's std::has_unique_object_representations property +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index e650e6044..6d7553a1d 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -46,7 +46,9 @@ + #include + #include + ++#ifdef WIN32 + #include ++#endif + #include + #include "bc-ur/src/bc-ur.hpp" + #if defined(HIDAPI_DUMMY) && !defined(HAVE_HIDAPI) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/wownero/0015-uint64_t-missing-definition-fix.patch b/patches/wownero/0015-uint64_t-missing-definition-fix.patch deleted file mode 100644 index e555829..0000000 --- a/patches/wownero/0015-uint64_t-missing-definition-fix.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 9fe376e0024dfdbea47219477e797cb20c56305f Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 2 Sep 2024 16:40:31 +0200 -Subject: [PATCH] uint64_t missing definition fix - ---- - contrib/epee/include/net/http_base.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h -index 4af4da790..ae4c0d05e 100644 ---- a/contrib/epee/include/net/http_base.h -+++ b/contrib/epee/include/net/http_base.h -@@ -28,7 +28,7 @@ - - #pragma once - #include "memwipe.h" -- -+#include - #include - - #include --- -2.43.0 - diff --git a/patches/wownero/README.md b/patches/wownero/README.md deleted file mode 100644 index 1aac8bc..0000000 --- a/patches/wownero/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# 0000-fix-wallet_api-compilation-target - -https://git.wownero.com/wownero/wownero/issues/483 - -# 0001-polyseed - -see monero directory - -# 0002-background-sync - -see monero directory - -# 0003-airgap - -see monero directory - -# 0004-coin-control - -Small fix to make it compatible with monero's implementation - to have the same standard API. - -# 0005-fix-build - -see monero directory - -# 0006-macos-build-fix - -see monero directory - -# 0007-FIX-wallet-listener-crashing - -https://git.wownero.com/wownero/wownero/issues/484 - -# 0008-fix-missing-___clear_cache-when-targetting-iOS - -https://git.wownero.com/wownero/RandomWOW/pulls/2 \ No newline at end of file diff --git a/wownero b/wownero index e25963c..dd46a31 160000 --- a/wownero +++ b/wownero @@ -1 +1 @@ -Subproject commit e25963cbc11ca0a0fe5eb34b9bd7c72e4f51b795 +Subproject commit dd46a31f3cab67b316e9239b15acf7d5cea60aa9 diff --git a/wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp b/wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp index 19a14ba..0d53cb5 100644 --- a/wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp +++ b/wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp @@ -5,7 +5,7 @@ #include #include #include "../../../../wownero/src/wallet/api/wallet2_api.h" -#include "../../../../external/wownero-seed/include/wownero_seed/wownero_seed.hpp" +#include "../../../wownero-seed/include/wownero_seed/wownero_seed.hpp" #include "wownero_checksum.h" #ifdef __cplusplus @@ -64,74 +64,102 @@ extern "C" // PendingTransaction int WOWNERO_PendingTransaction_status(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->status(); + DEBUG_END() } const char* WOWNERO_PendingTransaction_errorString(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::string str = pendingTx->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool WOWNERO_PendingTransaction_commit(void* pendingTx_ptr, const char* filename, bool overwrite) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->commit(std::string(filename), overwrite); + DEBUG_END() } uint64_t WOWNERO_PendingTransaction_amount(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->amount(); + DEBUG_END() } uint64_t WOWNERO_PendingTransaction_dust(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->dust(); + DEBUG_END() } uint64_t WOWNERO_PendingTransaction_fee(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->fee(); + DEBUG_END() } const char* WOWNERO_PendingTransaction_txid(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector txid = pendingTx->txid(); return vectorToString(txid, std::string(separator)); + DEBUG_END() } uint64_t WOWNERO_PendingTransaction_txCount(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->txCount(); + DEBUG_END() } const char* WOWNERO_PendingTransaction_subaddrAccount(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector subaddrAccount = pendingTx->subaddrAccount(); return vectorToString(subaddrAccount, std::string(separator)); + DEBUG_END() } const char* WOWNERO_PendingTransaction_subaddrIndices(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector> subaddrIndices = pendingTx->subaddrIndices(); return vectorToString(subaddrIndices, std::string(separator)); + DEBUG_END() } const char* WOWNERO_PendingTransaction_multisigSignData(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::string str = pendingTx->multisigSignData(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void WOWNERO_PendingTransaction_signMultisigTx(void* pendingTx_ptr) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); return pendingTx->signMultisigTx(); + DEBUG_END() } const char* WOWNERO_PendingTransaction_signersKeys(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector txid = pendingTx->signersKeys(); return vectorToString(txid, std::string(separator)); + DEBUG_END() } const char* WOWNERO_PendingTransaction_hex(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector txid = pendingTx->hex(); return vectorToString(txid, std::string(separator)); + DEBUG_END() } // const char* WOWNERO_PendingTransaction_txHex(void* pendingTx_ptr, const char* separator) { @@ -141,166 +169,228 @@ const char* WOWNERO_PendingTransaction_hex(void* pendingTx_ptr, const char* sepa // } const char* WOWNERO_PendingTransaction_txKey(void* pendingTx_ptr, const char* separator) { + DEBUG_START() Monero::PendingTransaction *pendingTx = reinterpret_cast(pendingTx_ptr); std::vector txid = pendingTx->txKey(); return vectorToString(txid, std::string(separator)); + DEBUG_END() } // UnsignedTransaction int WOWNERO_UnsignedTransaction_status(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return unsignedTx->status(); + DEBUG_END() } const char* WOWNERO_UnsignedTransaction_errorString(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); std::string str = unsignedTx->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_UnsignedTransaction_amount(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->amount(), std::string(separator)); + DEBUG_END() } const char* WOWNERO_UnsignedTransaction_fee(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->fee(), std::string(separator)); + DEBUG_END() } const char* WOWNERO_UnsignedTransaction_mixin(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->mixin(), std::string(separator)); + DEBUG_END() } const char* WOWNERO_UnsignedTransaction_confirmationMessage(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); std::string str = unsignedTx->confirmationMessage(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_UnsignedTransaction_paymentId(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->paymentId(), std::string(separator)); + DEBUG_END() } const char* WOWNERO_UnsignedTransaction_recipientAddress(void* unsignedTx_ptr, const char* separator) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return vectorToString(unsignedTx->recipientAddress(), std::string(separator)); + DEBUG_END() } uint64_t WOWNERO_UnsignedTransaction_minMixinCount(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return unsignedTx->minMixinCount(); + DEBUG_END() } uint64_t WOWNERO_UnsignedTransaction_txCount(void* unsignedTx_ptr) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return unsignedTx->txCount(); + DEBUG_END() } bool WOWNERO_UnsignedTransaction_sign(void* unsignedTx_ptr, const char* signedFileName) { + DEBUG_START() Monero::UnsignedTransaction *unsignedTx = reinterpret_cast(unsignedTx_ptr); return unsignedTx->sign(std::string(signedFileName)); + DEBUG_END() } // TransactionInfo int WOWNERO_TransactionInfo_direction(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->direction(); + DEBUG_END() } bool WOWNERO_TransactionInfo_isPending(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->isPending(); + DEBUG_END() } bool WOWNERO_TransactionInfo_isFailed(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->isFailed(); + DEBUG_END() } bool WOWNERO_TransactionInfo_isCoinbase(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->isCoinbase(); + DEBUG_END() } uint64_t WOWNERO_TransactionInfo_amount(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->amount(); + DEBUG_END() } uint64_t WOWNERO_TransactionInfo_fee(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->fee(); + DEBUG_END() } uint64_t WOWNERO_TransactionInfo_blockHeight(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->blockHeight(); + DEBUG_END() } const char* WOWNERO_TransactionInfo_description(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->description(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_TransactionInfo_subaddrIndex(void* txInfo_ptr, const char* separator) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::set subaddrIndex = txInfo->subaddrIndex(); return vectorToString(subaddrIndex, std::string(separator)); + DEBUG_END() } uint32_t WOWNERO_TransactionInfo_subaddrAccount(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->subaddrAccount(); + DEBUG_END() } const char* WOWNERO_TransactionInfo_label(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->label(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } uint64_t WOWNERO_TransactionInfo_confirmations(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->confirmations(); + DEBUG_END() } uint64_t WOWNERO_TransactionInfo_unlockTime(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->unlockTime(); + DEBUG_END() } const char* WOWNERO_TransactionInfo_hash(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->hash(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } uint64_t WOWNERO_TransactionInfo_timestamp(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->timestamp(); + DEBUG_END() } const char* WOWNERO_TransactionInfo_paymentId(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->paymentId(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } int WOWNERO_TransactionInfo_transfers_count(void* txInfo_ptr) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->transfers().size(); + DEBUG_END() } uint64_t WOWNERO_TransactionInfo_transfers_amount(void* txInfo_ptr, int index) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); return txInfo->transfers()[index].amount; + DEBUG_END() } const char* WOWNERO_TransactionInfo_transfers_address(void* txInfo_ptr, int index) { + DEBUG_START() Monero::TransactionInfo *txInfo = reinterpret_cast(txInfo_ptr); std::string str = txInfo->transfers()[index].address; const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } @@ -308,249 +398,329 @@ const char* WOWNERO_TransactionInfo_transfers_address(void* txInfo_ptr, int inde // TransactionHistory int WOWNERO_TransactionHistory_count(void* txHistory_ptr) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return txHistory->count(); + DEBUG_END() } void* WOWNERO_TransactionHistory_transaction(void* txHistory_ptr, int index) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return reinterpret_cast(txHistory->transaction(index)); + DEBUG_END() } void* WOWNERO_TransactionHistory_transactionById(void* txHistory_ptr, const char* id) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return reinterpret_cast(txHistory->transaction(std::string(id))); + DEBUG_END() } void WOWNERO_TransactionHistory_refresh(void* txHistory_ptr) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return txHistory->refresh(); + DEBUG_END() } void WOWNERO_TransactionHistory_setTxNote(void* txHistory_ptr, const char* txid, const char* note) { + DEBUG_START() Monero::TransactionHistory *txHistory = reinterpret_cast(txHistory_ptr); return txHistory->setTxNote(std::string(txid), std::string(note)); + DEBUG_END() } // AddressBokRow // std::string extra; const char* WOWNERO_AddressBookRow_extra(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); std::string str = addressBookRow->extra; const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getAddress() const {return m_address;} const char* WOWNERO_AddressBookRow_getAddress(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); std::string str = addressBookRow->getAddress(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getDescription() const {return m_description;} const char* WOWNERO_AddressBookRow_getDescription(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); std::string str = addressBookRow->getDescription(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getPaymentId() const {return m_paymentId;} const char* WOWNERO_AddressBookRow_getPaymentId(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); std::string str = addressBookRow->getPaymentId(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::size_t getRowId() const {return m_rowId;} size_t WOWNERO_AddressBookRow_getRowId(void* addressBookRow_ptr) { + DEBUG_START() Monero::AddressBookRow *addressBookRow = reinterpret_cast(addressBookRow_ptr); return addressBookRow->getRowId(); + DEBUG_END() } // AddressBook // virtual std::vector getAll() const = 0; int WOWNERO_AddressBook_getAll_size(void* addressBook_ptr) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->getAll().size(); + DEBUG_END() } void* WOWNERO_AddressBook_getAll_byIndex(void* addressBook_ptr, int index) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->getAll()[index]; + DEBUG_END() } // virtual bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) = 0; bool WOWNERO_AddressBook_addRow(void* addressBook_ptr, const char* dst_addr , const char* payment_id, const char* description) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->addRow(std::string(dst_addr), std::string(payment_id), std::string(description)); + DEBUG_END() } // virtual bool deleteRow(std::size_t rowId) = 0; bool WOWNERO_AddressBook_deleteRow(void* addressBook_ptr, size_t rowId) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->deleteRow(rowId); + DEBUG_END() } // virtual bool setDescription(std::size_t index, const std::string &description) = 0; bool WOWNERO_AddressBook_setDescription(void* addressBook_ptr, size_t rowId, const char* description) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->setDescription(rowId, std::string(description)); + DEBUG_END() } // virtual void refresh() = 0; void WOWNERO_AddressBook_refresh(void* addressBook_ptr) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->refresh(); + DEBUG_END() } // virtual std::string errorString() const = 0; const char* WOWNERO_AddressBook_errorString(void* addressBook_ptr) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); std::string str = addressBook->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual int errorCode() const = 0; int WOWNERO_AddressBook_errorCode(void* addressBook_ptr) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->errorCode(); + DEBUG_END() } // virtual int lookupPaymentID(const std::string &payment_id) const = 0; int WOWNERO_AddressBook_lookupPaymentID(void* addressBook_ptr, const char* payment_id) { + DEBUG_START() Monero::AddressBook *addressBook = reinterpret_cast(addressBook_ptr); return addressBook->lookupPaymentID(std::string(payment_id)); + DEBUG_END() } // CoinsInfo uint64_t WOWNERO_CoinsInfo_blockHeight(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->blockHeight(); + DEBUG_END() } // virtual std::string hash() const = 0; const char* WOWNERO_CoinsInfo_hash(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->hash(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual size_t internalOutputIndex() const = 0; size_t WOWNERO_CoinsInfo_internalOutputIndex(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->internalOutputIndex(); + DEBUG_END() } // virtual uint64_t globalOutputIndex() const = 0; uint64_t WOWNERO_CoinsInfo_globalOutputIndex(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->globalOutputIndex(); + DEBUG_END() } // virtual bool spent() const = 0; bool WOWNERO_CoinsInfo_spent(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->spent(); + DEBUG_END() } // virtual bool frozen() const = 0; bool WOWNERO_CoinsInfo_frozen(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->frozen(); + DEBUG_END() } // virtual uint64_t spentHeight() const = 0; uint64_t WOWNERO_CoinsInfo_spentHeight(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->spentHeight(); + DEBUG_END() } // virtual uint64_t amount() const = 0; uint64_t WOWNERO_CoinsInfo_amount(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->amount(); + DEBUG_END() } // virtual bool rct() const = 0; bool WOWNERO_CoinsInfo_rct(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->rct(); + DEBUG_END() } // virtual bool keyImageKnown() const = 0; bool WOWNERO_CoinsInfo_keyImageKnown(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->keyImageKnown(); + DEBUG_END() } // virtual size_t pkIndex() const = 0; size_t WOWNERO_CoinsInfo_pkIndex(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->pkIndex(); + DEBUG_END() } // virtual uint32_t subaddrIndex() const = 0; uint32_t WOWNERO_CoinsInfo_subaddrIndex(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->subaddrIndex(); + DEBUG_END() } // virtual uint32_t subaddrAccount() const = 0; uint32_t WOWNERO_CoinsInfo_subaddrAccount(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->subaddrAccount(); + DEBUG_END() } // virtual std::string address() const = 0; const char* WOWNERO_CoinsInfo_address(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->address(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual std::string addressLabel() const = 0; const char* WOWNERO_CoinsInfo_addressLabel(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->addressLabel(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual std::string keyImage() const = 0; const char* WOWNERO_CoinsInfo_keyImage(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->keyImage(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual uint64_t unlockTime() const = 0; uint64_t WOWNERO_CoinsInfo_unlockTime(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->unlockTime(); + DEBUG_END() } // virtual bool unlocked() const = 0; bool WOWNERO_CoinsInfo_unlocked(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->unlocked(); + DEBUG_END() } // virtual std::string pubKey() const = 0; const char* WOWNERO_CoinsInfo_pubKey(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->pubKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual bool coinbase() const = 0; bool WOWNERO_CoinsInfo_coinbase(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); return coinsInfo->coinbase(); + DEBUG_END() } // virtual std::string description() const = 0; const char* WOWNERO_CoinsInfo_description(void* coinsInfo_ptr) { + DEBUG_START() Monero::CoinsInfo *coinsInfo = reinterpret_cast(coinsInfo_ptr); std::string str = coinsInfo->description(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } @@ -559,173 +729,225 @@ const char* WOWNERO_CoinsInfo_description(void* coinsInfo_ptr) { // virtual ~Coins() = 0; // virtual int count() const = 0; int WOWNERO_Coins_count(void* coins_ptr) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->count(); + DEBUG_END() } // virtual CoinsInfo * coin(int index) const = 0; void* WOWNERO_Coins_coin(void* coins_ptr, int index) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->coin(index); + DEBUG_END() } int WOWNERO_Coins_getAll_size(void* coins_ptr) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->getAll().size(); + DEBUG_END() } void* WOWNERO_Coins_getAll_byIndex(void* coins_ptr, int index) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->getAll()[index]; + DEBUG_END() } // virtual std::vector getAll() const = 0; // virtual void refresh() = 0; void WOWNERO_Coins_refresh(void* coins_ptr) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->refresh(); + DEBUG_END() } // virtual void setFrozen(std::string public_key) = 0; void WOWNERO_Coins_setFrozenByPublicKey(void* coins_ptr, const char* public_key) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->setFrozen(std::string(public_key)); + DEBUG_END() } // virtual void setFrozen(int index) = 0; void WOWNERO_Coins_setFrozen(void* coins_ptr, int index) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->setFrozen(index); + DEBUG_END() } // virtual void thaw(int index) = 0; void WOWNERO_Coins_thaw(void* coins_ptr, int index) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->thaw(index); + DEBUG_END() } // virtual void thaw(std::string public_key) = 0; void WOWNERO_Coins_thawByPublicKey(void* coins_ptr, const char* public_key) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->thaw(std::string(public_key)); + DEBUG_END() } // virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; bool WOWNERO_Coins_isTransferUnlocked(void* coins_ptr, uint64_t unlockTime, uint64_t blockHeight) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); return coins->isTransferUnlocked(unlockTime, blockHeight); + DEBUG_END() } // virtual void setDescription(const std::string &public_key, const std::string &description) = 0; void WOWNERO_Coins_setDescription(void* coins_ptr, const char* public_key, const char* description) { + DEBUG_START() Monero::Coins *coins = reinterpret_cast(coins_ptr); coins->setDescription(std::string(public_key), std::string(description)); + DEBUG_END() } // SubaddressRow // std::string extra; const char* WOWNERO_SubaddressRow_extra(void* subaddressRow_ptr) { + DEBUG_START() Monero::SubaddressRow *subaddressRow = reinterpret_cast(subaddressRow_ptr); std::string str = subaddressRow->extra; const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getAddress() const {return m_address;} const char* WOWNERO_SubaddressRow_getAddress(void* subaddressRow_ptr) { + DEBUG_START() Monero::SubaddressRow *subaddressRow = reinterpret_cast(subaddressRow_ptr); std::string str = subaddressRow->getAddress(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getLabel() const {return m_label;} const char* WOWNERO_SubaddressRow_getLabel(void* subaddressRow_ptr) { + DEBUG_START() Monero::SubaddressRow *subaddressRow = reinterpret_cast(subaddressRow_ptr); std::string str = subaddressRow->getLabel(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::size_t getRowId() const {return m_rowId;} size_t WOWNERO_SubaddressRow_getRowId(void* subaddressRow_ptr) { + DEBUG_START() Monero::SubaddressRow *subaddressRow = reinterpret_cast(subaddressRow_ptr); return subaddressRow->getRowId(); + DEBUG_END() } // Subaddress int WOWNERO_Subaddress_getAll_size(void* subaddress_ptr) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->getAll().size(); + DEBUG_END() } void* WOWNERO_Subaddress_getAll_byIndex(void* subaddress_ptr, int index) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->getAll()[index]; + DEBUG_END() } // virtual void addRow(uint32_t accountIndex, const std::string &label) = 0; void WOWNERO_Subaddress_addRow(void* subaddress_ptr, uint32_t accountIndex, const char* label) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->addRow(accountIndex, std::string(label)); + DEBUG_END() } // virtual void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0; void WOWNERO_Subaddress_setLabel(void* subaddress_ptr, uint32_t accountIndex, uint32_t addressIndex, const char* label) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->setLabel(accountIndex, addressIndex, std::string(label)); + DEBUG_END() } // virtual void refresh(uint32_t accountIndex) = 0; void WOWNERO_Subaddress_refresh(void* subaddress_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Subaddress *subaddress = reinterpret_cast(subaddress_ptr); return subaddress->refresh(accountIndex); + DEBUG_END() } // SubaddressAccountRow // std::string extra; const char* WOWNERO_SubaddressAccountRow_extra(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->extra; const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getAddress() const {return m_address;} const char* WOWNERO_SubaddressAccountRow_getAddress(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->getAddress(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getLabel() const {return m_label;} const char* WOWNERO_SubaddressAccountRow_getLabel(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->getLabel(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getBalance() const {return m_balance;} const char* WOWNERO_SubaddressAccountRow_getBalance(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->getBalance(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::string getUnlockedBalance() const {return m_unlockedBalance;} const char* WOWNERO_SubaddressAccountRow_getUnlockedBalance(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); std::string str = subaddressAccountRow->getUnlockedBalance(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // std::size_t getRowId() const {return m_rowId;} size_t WOWNERO_SubaddressAccountRow_getRowId(void* subaddressAccountRow_ptr) { + DEBUG_START() Monero::SubaddressAccountRow *subaddressAccountRow = reinterpret_cast(subaddressAccountRow_ptr); return subaddressAccountRow->getRowId(); + DEBUG_END() } // struct SubaddressAccount @@ -733,50 +955,68 @@ size_t WOWNERO_SubaddressAccountRow_getRowId(void* subaddressAccountRow_ptr) { // virtual ~SubaddressAccount() = 0; // virtual std::vector getAll() const = 0; int WOWNERO_SubaddressAccount_getAll_size(void* subaddressAccount_ptr) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->getAll().size(); + DEBUG_END() } void* WOWNERO_SubaddressAccount_getAll_byIndex(void* subaddressAccount_ptr, int index) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->getAll()[index]; + DEBUG_END() } // virtual void addRow(const std::string &label) = 0; void WOWNERO_SubaddressAccount_addRow(void* subaddressAccount_ptr, const char* label) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->addRow(std::string(label)); + DEBUG_END() } // virtual void setLabel(uint32_t accountIndex, const std::string &label) = 0; void WOWNERO_SubaddressAccount_setLabel(void* subaddressAccount_ptr, uint32_t accountIndex, const char* label) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->setLabel(accountIndex, std::string(label)); + DEBUG_END() } // virtual void refresh() = 0; void WOWNERO_SubaddressAccount_refresh(void* subaddressAccount_ptr) { + DEBUG_START() Monero::SubaddressAccount *subaddress = reinterpret_cast(subaddressAccount_ptr); return subaddress->refresh(); + DEBUG_END() } // MultisigState // bool isMultisig; bool WOWNERO_MultisigState_isMultisig(void* multisigState_ptr) { + DEBUG_START() Monero::MultisigState *multisigState = reinterpret_cast(multisigState_ptr); return multisigState->isMultisig; + DEBUG_END() } // bool isReady; bool WOWNERO_MultisigState_isReady(void* multisigState_ptr) { + DEBUG_START() Monero::MultisigState *multisigState = reinterpret_cast(multisigState_ptr); return multisigState->isReady; + DEBUG_END() } // uint32_t threshold; uint32_t WOWNERO_MultisigState_threshold(void* multisigState_ptr) { + DEBUG_START() Monero::MultisigState *multisigState = reinterpret_cast(multisigState_ptr); return multisigState->threshold; + DEBUG_END() } // uint32_t total; uint32_t WOWNERO_MultisigState_total(void* multisigState_ptr) { + DEBUG_START() Monero::MultisigState *multisigState = reinterpret_cast(multisigState_ptr); return multisigState->total; + DEBUG_END() } // DeviceProgress @@ -784,364 +1024,467 @@ uint32_t WOWNERO_MultisigState_total(void* multisigState_ptr) { // virtual double progress() const { return m_progress; } bool WOWNERO_DeviceProgress_progress(void* deviceProgress_ptr) { + DEBUG_START() Monero::DeviceProgress *deviceProgress = reinterpret_cast(deviceProgress_ptr); return deviceProgress->progress(); + DEBUG_END() } // virtual bool indeterminate() const { return m_indeterminate; } bool WOWNERO_DeviceProgress_indeterminate(void* deviceProgress_ptr) { + DEBUG_START() Monero::DeviceProgress *deviceProgress = reinterpret_cast(deviceProgress_ptr); return deviceProgress->indeterminate(); + DEBUG_END() } // Wallet const char* WOWNERO_Wallet_seed(void* wallet_ptr, const char* seed_offset) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->seed(std::string(seed_offset)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_getSeedLanguage(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getSeedLanguage(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void WOWNERO_Wallet_setSeedLanguage(void* wallet_ptr, const char* arg) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setSeedLanguage(std::string(arg)); + DEBUG_END() } int WOWNERO_Wallet_status(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->status(); + DEBUG_END() } const char* WOWNERO_Wallet_errorString(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool WOWNERO_Wallet_setPassword(void* wallet_ptr, const char* password) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setPassword(std::string(password)); + DEBUG_END() } const char* WOWNERO_Wallet_getPassword(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getPassword(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool WOWNERO_Wallet_setDevicePin(void* wallet_ptr, const char* pin) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setDevicePin(std::string(pin)); + DEBUG_END() } bool WOWNERO_Wallet_setDevicePassphrase(void* wallet_ptr, const char* passphrase) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setDevicePassphrase(std::string(passphrase)); + DEBUG_END() } const char* WOWNERO_Wallet_address(void* wallet_ptr, uint64_t accountIndex, uint64_t addressIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->address(accountIndex, addressIndex); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_path(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->path(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } int WOWNERO_Wallet_nettype(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->nettype(); + DEBUG_END() } uint8_t WOWNERO_Wallet_useForkRules(void* wallet_ptr, uint8_t version, int64_t early_blocks) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->useForkRules(version, early_blocks); + DEBUG_END() } const char* WOWNERO_Wallet_integratedAddress(void* wallet_ptr, const char* payment_id) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->integratedAddress(std::string(payment_id)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_secretViewKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->secretViewKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_publicViewKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->publicViewKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_secretSpendKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->secretSpendKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_publicSpendKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->publicSpendKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_publicMultisigSignerKey(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->publicMultisigSignerKey(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void WOWNERO_Wallet_stop(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); wallet->stop(); + DEBUG_END() } bool WOWNERO_Wallet_store(void* wallet_ptr, const char* path) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->store(std::string(path)); + DEBUG_END() } const char* WOWNERO_Wallet_filename(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->filename(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_keysFilename(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->keysFilename(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false, const std::string &proxy_address = "") = 0; bool WOWNERO_Wallet_init(void* wallet_ptr, const char* daemon_address, uint64_t upper_transaction_size_limit, const char* daemon_username, const char* daemon_password, bool use_ssl, bool lightWallet, const char* proxy_address) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->init(std::string(daemon_address), upper_transaction_size_limit, std::string(daemon_username), std::string(daemon_password), use_ssl, lightWallet, std::string(proxy_address)); + DEBUG_END() } bool WOWNERO_Wallet_createWatchOnly(void* wallet_ptr, const char* path, const char* password, const char* language) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->createWatchOnly(std::string(path), std::string(password), std::string(language)); + DEBUG_END() } void WOWNERO_Wallet_setRefreshFromBlockHeight(void* wallet_ptr, uint64_t refresh_from_block_height) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setRefreshFromBlockHeight(refresh_from_block_height); + DEBUG_END() } uint64_t WOWNERO_Wallet_getRefreshFromBlockHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getRefreshFromBlockHeight(); + DEBUG_END() } void WOWNERO_Wallet_setRecoveringFromSeed(void* wallet_ptr, bool recoveringFromSeed) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setRecoveringFromSeed(recoveringFromSeed); + DEBUG_END() } void WOWNERO_Wallet_setRecoveringFromDevice(void* wallet_ptr, bool recoveringFromDevice) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setRecoveringFromDevice(recoveringFromDevice); + DEBUG_END() } void WOWNERO_Wallet_setSubaddressLookahead(void* wallet_ptr, uint32_t major, uint32_t minor) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setSubaddressLookahead(major, minor); + DEBUG_END() } bool WOWNERO_Wallet_connectToDaemon(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->connectToDaemon(); + DEBUG_END() } int WOWNERO_Wallet_connected(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->connected(); + DEBUG_END() } void WOWNERO_Wallet_setTrustedDaemon(void* wallet_ptr, bool arg) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setTrustedDaemon(arg); + DEBUG_END() } bool WOWNERO_Wallet_trustedDaemon(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->trustedDaemon(); + DEBUG_END() } bool WOWNERO_Wallet_setProxy(void* wallet_ptr, const char* address) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setProxy(std::string(address)); + DEBUG_END() } uint64_t WOWNERO_Wallet_balance(void* wallet_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->balance(accountIndex); + DEBUG_END() } uint64_t WOWNERO_Wallet_unlockedBalance(void* wallet_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->unlockedBalance(accountIndex); + DEBUG_END() } uint64_t WOWNERO_Wallet_viewOnlyBalance(void* wallet_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->viewOnlyBalance(accountIndex); + DEBUG_END() } // TODO bool WOWNERO_Wallet_watchOnly(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->watchOnly(); + DEBUG_END() } bool WOWNERO_Wallet_isDeterministic(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->isDeterministic(); + DEBUG_END() } uint64_t WOWNERO_Wallet_blockChainHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->blockChainHeight(); + DEBUG_END() } uint64_t WOWNERO_Wallet_approximateBlockChainHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->approximateBlockChainHeight(); + DEBUG_END() } uint64_t WOWNERO_Wallet_estimateBlockChainHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->estimateBlockChainHeight(); + DEBUG_END() } uint64_t WOWNERO_Wallet_daemonBlockChainHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->daemonBlockChainHeight(); -} - -uint64_t daemonBlockChainHeight_cached = 0; - -uint64_t WOWNERO_Wallet_daemonBlockChainHeight_cached(void* wallet_ptr) { - return daemonBlockChainHeight_cached; -} - -void WOWNERO_Wallet_daemonBlockChainHeight_runThread(void* wallet_ptr, int seconds) { - std::cout << "DEPRECATED: this was used as an experiment, and will be removed in newer release. use ${COIN}_cw_* listener functions instead." << std::endl; - while (true) { - Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); - daemonBlockChainHeight_cached = wallet->daemonBlockChainHeight(); - sleep(seconds); - std::cout << "MONERO: TICK: WOWNERO_Wallet_daemonBlockChainHeight_runThread(" << seconds << "): " << daemonBlockChainHeight_cached << std::endl; - } + DEBUG_END() } uint64_t WOWNERO_Wallet_daemonBlockChainTargetHeight(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->daemonBlockChainTargetHeight(); + DEBUG_END() } bool WOWNERO_Wallet_synchronized(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->synchronized(); + DEBUG_END() } const char* WOWNERO_Wallet_displayAmount(uint64_t amount) { + DEBUG_START() std::string str = Monero::Wallet::displayAmount(amount); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // static uint64_t amountFromString(const std::string &amount); uint64_t WOWNERO_Wallet_amountFromString(const char* amount) { + DEBUG_START() return Monero::Wallet::amountFromString(amount); + DEBUG_END() } // static uint64_t amountFromDouble(double amount); uint64_t WOWNERO_Wallet_amountFromDouble(double amount) { + DEBUG_START() return Monero::Wallet::amountFromDouble(amount); + DEBUG_END() } // static std::string genPaymentId(); const char* WOWNERO_Wallet_genPaymentId() { + DEBUG_START() std::string str = Monero::Wallet::genPaymentId(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // static bool paymentIdValid(const std::string &paiment_id); bool WOWNERO_Wallet_paymentIdValid(const char* paiment_id) { + DEBUG_START() return Monero::Wallet::paymentIdValid(std::string(paiment_id)); + DEBUG_END() } bool WOWNERO_Wallet_addressValid(const char* str, int nettype) { + DEBUG_START() // Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return Monero::Wallet::addressValid(std::string(str), nettype); + DEBUG_END() } bool WOWNERO_Wallet_keyValid(const char* secret_key_string, const char* address_string, bool isViewKey, int nettype) { + DEBUG_START() std::string error; return Monero::Wallet::keyValid(std::string(secret_key_string), std::string(address_string), isViewKey, nettype, error); + DEBUG_END() } const char* WOWNERO_Wallet_keyValid_error(const char* secret_key_string, const char* address_string, bool isViewKey, int nettype) { + DEBUG_START() std::string str; Monero::Wallet::keyValid(std::string(secret_key_string), std::string(address_string), isViewKey, nettype, str); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_paymentIdFromAddress(const char* strarg, int nettype) { + DEBUG_START() std::string str = Monero::Wallet::paymentIdFromAddress(std::string(strarg), nettype); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } uint64_t WOWNERO_Wallet_maximumAllowedAmount() { + DEBUG_START() return Monero::Wallet::maximumAllowedAmount(); + DEBUG_END() } void WOWNERO_Wallet_init3(void* wallet_ptr, const char* argv0, const char* default_log_base_name, const char* log_path, bool console) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->init(argv0, default_log_base_name, log_path, console); + DEBUG_END() } const char* WOWNERO_Wallet_getPolyseed(void* wallet_ptr, const char* passphrase) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string seed = ""; std::string _passphrase = std::string(passphrase); @@ -1151,9 +1494,11 @@ const char* WOWNERO_Wallet_getPolyseed(void* wallet_ptr, const char* passphrase) char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // static bool createPolyseed(std::string &seed_words, std::string &err, const std::string &language = "English"); const char* WOWNERO_Wallet_createPolyseed(const char* language) { + DEBUG_START() std::string seed_words = ""; std::string err; Monero::Wallet::createPolyseed(seed_words, err, std::string(language)); @@ -1164,76 +1509,107 @@ const char* WOWNERO_Wallet_createPolyseed(const char* language) { char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void WOWNERO_Wallet_startRefresh(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->startRefresh(); + DEBUG_END() } void WOWNERO_Wallet_pauseRefresh(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->pauseRefresh(); + DEBUG_END() } bool WOWNERO_Wallet_refresh(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->refresh(); + DEBUG_END() } void WOWNERO_Wallet_refreshAsync(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->refreshAsync(); + DEBUG_END() } bool WOWNERO_Wallet_rescanBlockchain(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->rescanBlockchain(); + DEBUG_END() } void WOWNERO_Wallet_rescanBlockchainAsync(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->rescanBlockchainAsync(); + DEBUG_END() } void WOWNERO_Wallet_setAutoRefreshInterval(void* wallet_ptr, int millis) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setAutoRefreshInterval(millis); + DEBUG_END() } int WOWNERO_Wallet_autoRefreshInterval(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->autoRefreshInterval(); + DEBUG_END() } void WOWNERO_Wallet_addSubaddressAccount(void* wallet_ptr, const char* label) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->addSubaddressAccount(std::string(label)); + DEBUG_END() } size_t WOWNERO_Wallet_numSubaddressAccounts(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->numSubaddressAccounts(); + DEBUG_END() } size_t WOWNERO_Wallet_numSubaddresses(void* wallet_ptr, uint32_t accountIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->numSubaddresses(accountIndex); + DEBUG_END() } void WOWNERO_Wallet_addSubaddress(void* wallet_ptr, uint32_t accountIndex, const char* label) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->addSubaddress(accountIndex, std::string(label)); + DEBUG_END() } const char* WOWNERO_Wallet_getSubaddressLabel(void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getSubaddressLabel(accountIndex, addressIndex); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void WOWNERO_Wallet_setSubaddressLabel(void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex, const char* label) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setSubaddressLabel(accountIndex, addressIndex, std::string(label)); + DEBUG_END() } const char* WOWNERO_Wallet_getMultisigInfo(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getMultisigInfo(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } Monero::PendingTransaction::Priority PendingTransaction_Priority_fromInt(int value) { @@ -1251,6 +1627,7 @@ void* WOWNERO_Wallet_createTransactionMultDest(void* wallet_ptr, const char* dst int pendingTransactionPriority, uint32_t subaddr_account, const char* preferredInputs, const char* preferredInputs_separator) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::vector dst_addr = splitStringVector(std::string(dst_addr_list), std::string(dst_addr_list_separator)); @@ -1269,6 +1646,7 @@ void* WOWNERO_Wallet_createTransactionMultDest(void* wallet_ptr, const char* dst subaddr_indices, preferred_inputs ); + DEBUG_END() } void* WOWNERO_Wallet_createTransaction(void* wallet_ptr, const char* dst_addr, const char* payment_id, @@ -1276,6 +1654,7 @@ void* WOWNERO_Wallet_createTransaction(void* wallet_ptr, const char* dst_addr, c int pendingTransactionPriority, uint32_t subaddr_account, const char* preferredInputs, const char* separator) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); Monero::optional optAmount; if (amount != 0) { @@ -1287,210 +1666,288 @@ void* WOWNERO_Wallet_createTransaction(void* wallet_ptr, const char* dst_addr, c optAmount, mixin_count, PendingTransaction_Priority_fromInt(pendingTransactionPriority), subaddr_account, subaddr_indices, preferred_inputs); + DEBUG_END() } void* WOWNERO_Wallet_loadUnsignedTx(void* wallet_ptr, const char* fileName) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->loadUnsignedTx(std::string(fileName)); + DEBUG_END() } bool WOWNERO_Wallet_submitTransaction(void* wallet_ptr, const char* fileName) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->submitTransaction(std::string(fileName)); + DEBUG_END() } bool WOWNERO_Wallet_hasUnknownKeyImages(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->hasUnknownKeyImages(); + DEBUG_END() } bool WOWNERO_Wallet_exportKeyImages(void* wallet_ptr, const char* filename, bool all) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->exportKeyImages(std::string(filename), all); + DEBUG_END() } bool WOWNERO_Wallet_importKeyImages(void* wallet_ptr, const char* filename) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->importKeyImages(std::string(filename)); + DEBUG_END() } bool WOWNERO_Wallet_exportOutputs(void* wallet_ptr, const char* filename, bool all) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->exportOutputs(std::string(filename), all); + DEBUG_END() } bool WOWNERO_Wallet_importOutputs(void* wallet_ptr, const char* filename) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->importOutputs(std::string(filename)); + DEBUG_END() } // virtual bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional &background_cache_password) = 0; bool WOWNERO_Wallet_setupBackgroundSync(void* wallet_ptr, int background_sync_type, const char* wallet_password, const char* background_cache_password) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setupBackgroundSync(Monero::Wallet::BackgroundSyncType::BackgroundSync_CustomPassword, std::string(wallet_password), std::string(background_cache_password)); + DEBUG_END() } // virtual BackgroundSyncType getBackgroundSyncType() const = 0; int WOWNERO_Wallet_getBackgroundSyncType(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getBackgroundSyncType(); + DEBUG_END() } // virtual bool startBackgroundSync() = 0; bool WOWNERO_Wallet_startBackgroundSync(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->startBackgroundSync(); + DEBUG_END() } // virtual bool stopBackgroundSync(const std::string &wallet_password) = 0; bool WOWNERO_Wallet_stopBackgroundSync(void* wallet_ptr, const char* wallet_password) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->stopBackgroundSync(std::string(wallet_password)); + DEBUG_END() } // virtual bool isBackgroundSyncing() const = 0; bool WOWNERO_Wallet_isBackgroundSyncing(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->hasUnknownKeyImages(); + DEBUG_END() } // virtual bool isBackgroundWallet() const = 0; bool WOWNERO_Wallet_isBackgroundWallet(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->isBackgroundWallet(); + DEBUG_END() } void* WOWNERO_Wallet_history(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->history(); + DEBUG_END() } void* WOWNERO_Wallet_addressBook(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->addressBook(); + DEBUG_END() } // virtual Coins * coins() = 0; void* WOWNERO_Wallet_coins(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->coins(); + DEBUG_END() } // virtual Subaddress * subaddress() = 0; void* WOWNERO_Wallet_subaddress(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->subaddress(); + DEBUG_END() } // virtual SubaddressAccount * subaddressAccount() = 0; void* WOWNERO_Wallet_subaddressAccount(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->subaddressAccount(); + DEBUG_END() } // virtual uint32_t defaultMixin() const = 0; uint32_t WOWNERO_Wallet_defaultMixin(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->defaultMixin(); + DEBUG_END() } // virtual void setDefaultMixin(uint32_t arg) = 0; void WOWNERO_Wallet_setDefaultMixin(void* wallet_ptr, uint32_t arg) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setDefaultMixin(arg); + DEBUG_END() } // virtual bool setCacheAttribute(const std::string &key, const std::string &val) = 0; bool WOWNERO_Wallet_setCacheAttribute(void* wallet_ptr, const char* key, const char* val) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setCacheAttribute(std::string(key), std::string(val)); + DEBUG_END() } // virtual std::string getCacheAttribute(const std::string &key) const = 0; const char* WOWNERO_Wallet_getCacheAttribute(void* wallet_ptr, const char* key) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getCacheAttribute(std::string(key)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual bool setUserNote(const std::string &txid, const std::string ¬e) = 0; bool WOWNERO_Wallet_setUserNote(void* wallet_ptr, const char* txid, const char* note) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setUserNote(std::string(txid), std::string(note)); + DEBUG_END() } // virtual std::string getUserNote(const std::string &txid) const = 0; const char* WOWNERO_Wallet_getUserNote(void* wallet_ptr, const char* txid) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getUserNote(std::string(txid)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_getTxKey(void* wallet_ptr, const char* txid) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->getTxKey(std::string(txid)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } const char* WOWNERO_Wallet_signMessage(void* wallet_ptr, const char* message, const char* address) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = wallet->signMessage(std::string(message), std::string(address)); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } bool WOWNERO_Wallet_verifySignedMessage(void* wallet_ptr, const char* message, const char* address, const char* signature) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); bool v = wallet->verifySignedMessage(std::string(message), std::string(address), std::string(signature)); return v; + DEBUG_END() } bool WOWNERO_Wallet_rescanSpent(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->rescanSpent(); + DEBUG_END() } void WOWNERO_Wallet_setOffline(void* wallet_ptr, bool offline) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->setOffline(offline); + DEBUG_END() } // virtual bool isOffline() const = 0; bool WOWNERO_Wallet_isOffline(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->isOffline(); + DEBUG_END() } void WOWNERO_Wallet_segregatePreForkOutputs(void* wallet_ptr, bool segregate) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->segregatePreForkOutputs(segregate); + DEBUG_END() } // virtual void segregationHeight(uint64_t height) = 0; void WOWNERO_Wallet_segregationHeight(void* wallet_ptr, uint64_t height) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->segregationHeight(height); + DEBUG_END() } // virtual void keyReuseMitigation2(bool mitigation) = 0; void WOWNERO_Wallet_keyReuseMitigation2(void* wallet_ptr, bool mitigation) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->keyReuseMitigation2(mitigation); + DEBUG_END() } // virtual bool lightWalletLogin(bool &isNewWallet) const = 0; // virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0; // virtual bool lockKeysFile() = 0; bool WOWNERO_Wallet_lockKeysFile(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->lockKeysFile(); + DEBUG_END() } // virtual bool unlockKeysFile() = 0; bool WOWNERO_Wallet_unlockKeysFile(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->unlockKeysFile(); + DEBUG_END() } // virtual bool isKeysFileLocked() = 0; bool WOWNERO_Wallet_isKeysFileLocked(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->isKeysFileLocked(); + DEBUG_END() } // virtual Device getDeviceType() const = 0; int WOWNERO_Wallet_getDeviceType(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getDeviceType(); + DEBUG_END() } // virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) = 0; uint64_t WOWNERO_Wallet_coldKeyImageSync(void* wallet_ptr, uint64_t spent, uint64_t unspent) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->coldKeyImageSync(spent, unspent); + DEBUG_END() } // virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) = 0; const char* WOWNERO_Wallet_deviceShowAddress(void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); std::string str = ""; wallet->deviceShowAddress(accountIndex, addressIndex, str); @@ -1498,24 +1955,32 @@ const char* WOWNERO_Wallet_deviceShowAddress(void* wallet_ptr, uint32_t accountI char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // virtual bool reconnectDevice() = 0; bool WOWNERO_Wallet_reconnectDevice(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->reconnectDevice(); + DEBUG_END() }; uint64_t WOWNERO_Wallet_getBytesReceived(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getBytesReceived(); + DEBUG_END() } uint64_t WOWNERO_Wallet_getBytesSent(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wallet->getBytesSent(); + DEBUG_END() } void* WOWNERO_WalletManager_createWallet(void* wm_ptr, const char* path, const char* password, const char* language, int networkType) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = wm->createWallet( std::string(path), @@ -1523,17 +1988,21 @@ void* WOWNERO_WalletManager_createWallet(void* wm_ptr, const char* path, const c std::string(language), static_cast(networkType)); return reinterpret_cast(wallet); + DEBUG_END() } void* WOWNERO_WalletManager_openWallet(void* wm_ptr, const char* path, const char* password, int networkType) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = wm->openWallet( std::string(path), std::string(password), static_cast(networkType)); return reinterpret_cast(wallet); + DEBUG_END() } void* WOWNERO_WalletManager_recoveryWallet(void* wm_ptr, const char* path, const char* password, const char* mnemonic, int networkType, uint64_t restoreHeight, uint64_t kdfRounds, const char* seedOffset) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); // (const std::string &path, const std::string &password, const std::string &mnemonic, // NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1, @@ -1547,6 +2016,7 @@ void* WOWNERO_WalletManager_recoveryWallet(void* wm_ptr, const char* path, const kdfRounds, std::string(seedOffset)); return reinterpret_cast(wallet); + DEBUG_END() } // virtual Wallet * createWalletFromKeys(const std::string &path, // const std::string &password, @@ -1558,6 +2028,7 @@ void* WOWNERO_WalletManager_recoveryWallet(void* wm_ptr, const char* path, const // const std::string &spendKeyString = "", // uint64_t kdf_rounds = 1) = 0; void* WOWNERO_WalletManager_createWalletFromKeys(void* wm_ptr, const char* path, const char* password, const char* language, int nettype, uint64_t restoreHeight, const char* addressString, const char* viewKeyString, const char* spendKeyString, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = wm->createWalletFromKeys( std::string(path), @@ -1569,11 +2040,13 @@ void* WOWNERO_WalletManager_createWalletFromKeys(void* wm_ptr, const char* path, std::string(viewKeyString), std::string(spendKeyString)); return reinterpret_cast(wallet); + DEBUG_END() } void* WOWNERO_WalletManager_createDeterministicWalletFromSpendKey(void* wm_ptr, const char* path, const char* password, const char* language, int nettype, uint64_t restoreHeight, const char* spendKeyString, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = wm->createDeterministicWalletFromSpendKey( std::string(path), @@ -1585,11 +2058,13 @@ void* WOWNERO_WalletManager_createDeterministicWalletFromSpendKey(void* wm_ptr, kdf_rounds ); return reinterpret_cast(wallet); + DEBUG_END() } void* WOWNERO_WalletManager_createWalletFromPolyseed(void* wm_ptr, const char* path, const char* password, int nettype, const char* mnemonic, const char* passphrase, bool newWallet, uint64_t restore_height, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->createWalletFromPolyseed(std::string(path), std::string(password), @@ -1599,24 +2074,31 @@ void* WOWNERO_WalletManager_createWalletFromPolyseed(void* wm_ptr, const char* p newWallet, restore_height, kdf_rounds); + DEBUG_END() } bool WOWNERO_WalletManager_closeWallet(void* wm_ptr, void* wallet_ptr, bool store) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return wm->closeWallet(wallet, store); + DEBUG_END() } bool WOWNERO_WalletManager_walletExists(void* wm_ptr, const char* path) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->walletExists(std::string(path)); + DEBUG_END() } // virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const = 0; bool WOWNERO_WalletManager_verifyWalletPassword(void* wm_ptr, const char* keys_file_name, const char* password, bool no_spend_key, uint64_t kdf_rounds) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->verifyWalletPassword(std::string(keys_file_name), std::string(password), no_spend_key, kdf_rounds); + DEBUG_END() } // virtual bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const = 0; // bool WOWNERO_WalletManager_queryWalletDevice(void* wm_ptr, int device_type, const char* keys_file_name, const char* password, uint64_t kdf_rounds) { @@ -1625,95 +2107,127 @@ bool WOWNERO_WalletManager_verifyWalletPassword(void* wm_ptr, const char* keys_f // } // virtual std::vector findWallets(const std::string &path) = 0; const char* WOWNERO_WalletManager_findWallets(void* wm_ptr, const char* path, const char* separator) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return vectorToString(wm->findWallets(std::string(path)), std::string(separator)); + DEBUG_END() } const char* WOWNERO_WalletManager_errorString(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); std::string str = wm->errorString(); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } void WOWNERO_WalletManager_setDaemonAddress(void* wm_ptr, const char* address) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->setDaemonAddress(std::string(address)); + DEBUG_END() } bool WOWNERO_WalletManager_setProxy(void* wm_ptr, const char* address) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->setProxy(std::string(address)); + DEBUG_END() } // virtual bool connected(uint32_t *version = NULL) = 0; // virtual uint64_t blockchainHeight() = 0; uint64_t WOWNERO_WalletManager_blockchainHeight(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->blockchainHeight(); + DEBUG_END() } // virtual uint64_t blockchainTargetHeight() = 0; uint64_t WOWNERO_WalletManager_blockchainTargetHeight(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->blockchainTargetHeight(); + DEBUG_END() } // virtual uint64_t networkDifficulty() = 0; uint64_t WOWNERO_WalletManager_networkDifficulty(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->networkDifficulty(); + DEBUG_END() } // virtual double miningHashRate() = 0; double WOWNERO_WalletManager_miningHashRate(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->miningHashRate(); + DEBUG_END() } // virtual uint64_t blockTarget() = 0; uint64_t WOWNERO_WalletManager_blockTarget(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->blockTarget(); + DEBUG_END() } // virtual bool isMining() = 0; bool WOWNERO_WalletManager_isMining(void* wm_ptr) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->isMining(); + DEBUG_END() } // virtual bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) = 0; bool WOWNERO_WalletManager_startMining(void* wm_ptr, const char* address, uint32_t threads, bool backgroundMining, bool ignoreBattery) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->startMining(std::string(address), threads, backgroundMining, ignoreBattery); + DEBUG_END() } // virtual bool stopMining() = 0; bool WOWNERO_WalletManager_stopMining(void* wm_ptr, const char* address) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->stopMining(); + DEBUG_END() } // virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0; const char* WOWNERO_WalletManager_resolveOpenAlias(void* wm_ptr, const char* address, bool dnssec_valid) { + DEBUG_START() Monero::WalletManager *wm = reinterpret_cast(wm_ptr); std::string str = wm->resolveOpenAlias(std::string(address), dnssec_valid); const std::string::size_type size = str.size(); char *buffer = new char[size + 1]; //we need extra char for NUL memcpy(buffer, str.c_str(), size + 1); return buffer; + DEBUG_END() } // WalletManagerFactory void* WOWNERO_WalletManagerFactory_getWalletManager() { + DEBUG_START() Monero::WalletManager *wm = Monero::WalletManagerFactory::getWalletManager(); return reinterpret_cast(wm); + DEBUG_END() } void WOWNERO_WalletManagerFactory_setLogLevel(int level) { + DEBUG_START() Monero::WalletManagerFactory::setLogLevel(level); + DEBUG_END() } void WOWNERO_WalletManagerFactory_setLogCategories(const char* categories) { + DEBUG_START() Monero::WalletManagerFactory::setLogCategories(std::string(categories)); + DEBUG_END() } // DEBUG functions @@ -1759,8 +2273,10 @@ const char* WOWNERO_DEBUG_test5_std() { } bool WOWNERO_DEBUG_isPointerNull(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); return (wallet != NULL); + DEBUG_END() } // cake wallet world @@ -1842,35 +2358,47 @@ struct WOWNERO_cw_WalletListener : Monero::WalletListener }; void* WOWNERO_cw_getWalletListener(void* wallet_ptr) { + DEBUG_START() Monero::Wallet *wallet = reinterpret_cast(wallet_ptr); WOWNERO_cw_WalletListener *listener = new WOWNERO_cw_WalletListener(); wallet->setListener(listener); return reinterpret_cast(listener); + DEBUG_END() } void WOWNERO_cw_WalletListener_resetNeedToRefresh(void* cw_walletListener_ptr) { + DEBUG_START() WOWNERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); listener->cw_resetNeedToRefresh(); + DEBUG_END() } bool WOWNERO_cw_WalletListener_isNeedToRefresh(void* cw_walletListener_ptr) { + DEBUG_START() WOWNERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); return listener->cw_isNeedToRefresh(); + DEBUG_END() }; bool WOWNERO_cw_WalletListener_isNewTransactionExist(void* cw_walletListener_ptr) { + DEBUG_START() WOWNERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); return listener->cw_isNeedToRefresh(); + DEBUG_END() }; void WOWNERO_cw_WalletListener_resetIsNewTransactionExist(void* cw_walletListener_ptr) { + DEBUG_START() WOWNERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); listener->cw_isNeedToRefresh(); + DEBUG_END() }; uint64_t WOWNERO_cw_WalletListener_height(void* cw_walletListener_ptr) { + DEBUG_START() WOWNERO_cw_WalletListener *listener = reinterpret_cast(cw_walletListener_ptr); return listener->cw_isNeedToRefresh(); + DEBUG_END() }; // 14-word polyseed compat @@ -1881,6 +2409,7 @@ uint64_t WOWNERO_cw_WalletListener_height(void* cw_walletListener_ptr) { // https://github.com/cypherstack/flutter_libmonero/blob/2c684cedba6c3d9353c7ea748cadb5a246008027/cw_wownero/ios/Classes/wownero_api.cpp#L240 // this code slightly goes against the way of being simple void* WOWNERO_deprecated_restore14WordSeed(char *path, char *password, char *seed, int32_t networkType) { + DEBUG_START() Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); try { Monero::NetworkType _networkType = static_cast(networkType); @@ -1920,14 +2449,18 @@ void* WOWNERO_deprecated_restore14WordSeed(char *path, char *password, char *see ""); return reinterpret_cast(wallet); } + DEBUG_END() } uint64_t WOWNERO_deprecated_14WordSeedHeight(char *seed) { + DEBUG_START() wownero_seed wow_seed(seed, "wownero"); return wow_seed.blockheight(); + DEBUG_END() } void* WOWNERO_deprecated_create14WordSeed(char *path, char *password, char *language, int32_t networkType) { + DEBUG_START() Monero::NetworkType _networkType = static_cast(networkType); Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager(); @@ -1955,6 +2488,7 @@ void* WOWNERO_deprecated_create14WordSeed(char *path, char *password, char *lang 1); wallet->setCacheAttribute("cake.seed", seed); return reinterpret_cast(wallet); + DEBUG_END() } const char* WOWNERO_checksum_wallet2_api_c_h() { diff --git a/wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h b/wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h index f540930..f58adcd 100644 --- a/wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h +++ b/wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h @@ -601,8 +601,6 @@ extern ADDAPI uint64_t WOWNERO_Wallet_approximateBlockChainHeight(void* wallet_p extern ADDAPI uint64_t WOWNERO_Wallet_estimateBlockChainHeight(void* wallet_ptr); // virtual uint64_t daemonBlockChainHeight() const = 0; extern ADDAPI uint64_t WOWNERO_Wallet_daemonBlockChainHeight(void* wallet_ptr); -extern ADDAPI uint64_t WOWNERO_Wallet_daemonBlockChainHeight_cached(void* wallet_ptr); -extern ADDAPI void WOWNERO_Wallet_daemonBlockChainHeight_runThread(void* wallet_ptr, int seconds); // virtual uint64_t daemonBlockChainTargetHeight() const = 0; extern ADDAPI uint64_t WOWNERO_Wallet_daemonBlockChainTargetHeight(void* wallet_ptr); // virtual bool synchronized() const = 0; diff --git a/wownero_libwallet2_api_c/src/main/cpp/wownero_checksum.h b/wownero_libwallet2_api_c/src/main/cpp/wownero_checksum.h index 40723cc..60a7112 100644 --- a/wownero_libwallet2_api_c/src/main/cpp/wownero_checksum.h +++ b/wownero_libwallet2_api_c/src/main/cpp/wownero_checksum.h @@ -1,6 +1,6 @@ #ifndef MONEROC_CHECKSUMS #define MONEROC_CHECKSUMS -const char * WOWNERO_wallet2_api_c_h_sha256 = "8a8d386dd5d996c89a0586c55b295ef95ca584bf1ffa26255152b291910a0a77"; -const char * WOWNERO_wallet2_api_c_cpp_sha256 = "07d67f34a07869aaa4af6ca04e142dbad2fb1fba0e2ebdefd22bc333fd982e25-e25963cbc11ca0a0fe5eb34b9bd7c72e4f51b795"; -const char * WOWNERO_wallet2_api_c_exp_sha256 = "3673e40e1a7115552276d1d541f6e4d5a0fef47c40fff7b988f49923af84c8a4"; +const char * WOWNERO_wallet2_api_c_h_sha256 = "f99009d1ca1d1c783cc9aa0fb63f680d48753b88124fb5de2079c57b7e34c827"; +const char * WOWNERO_wallet2_api_c_cpp_sha256 = "880add77ec8c77d8054a6f21e996d6d08a37ef84e10df7220630426fd048b43c-dd46a31f3cab67b316e9239b15acf7d5cea60aa9"; +const char * WOWNERO_wallet2_api_c_exp_sha256 = "5f53ea8bbe66a5e5aa6cbc4ca00695900e08589cfd32062e88965a24252d05ba"; #endif diff --git a/wownero_libwallet2_api_c/wownero-seed b/wownero_libwallet2_api_c/wownero-seed new file mode 160000 index 0000000..4a4e5b6 --- /dev/null +++ b/wownero_libwallet2_api_c/wownero-seed @@ -0,0 +1 @@ +Subproject commit 4a4e5b6c54c1710aa185cb0838b087238cbfc9a2 diff --git a/wownero_libwallet2_api_c/wownero_libwallet2_api_c.exp b/wownero_libwallet2_api_c/wownero_libwallet2_api_c.exp index 988e481..10a407f 100644 --- a/wownero_libwallet2_api_c/wownero_libwallet2_api_c.exp +++ b/wownero_libwallet2_api_c/wownero_libwallet2_api_c.exp @@ -163,8 +163,6 @@ _WOWNERO_Wallet_blockChainHeight _WOWNERO_Wallet_approximateBlockChainHeight _WOWNERO_Wallet_estimateBlockChainHeight _WOWNERO_Wallet_daemonBlockChainHeight -_WOWNERO_Wallet_daemonBlockChainHeight_cached -_WOWNERO_Wallet_daemonBlockChainHeight_runThread _WOWNERO_Wallet_daemonBlockChainTargetHeight _WOWNERO_Wallet_synchronized _WOWNERO_Wallet_displayAmount -- cgit v1.2.3