From d04dcf67ec042fca76b48ffc09ea9d2ea4b5106e Mon Sep 17 00:00:00 2001 From: cyan Date: Thu, 17 Oct 2024 10:50:22 +0200 Subject: initial work on docs (#69) * initial work on docs * fix DOCKER_VERSION * update location * add the path **properly** * Update docs workflow --- .github/workflows/docs.yaml | 48 +++++++++++++++ README.md | 87 ++------------------------ docs/.idea/.gitignore | 3 + docs/.idea/docs.iml | 8 +++ docs/.idea/modules.xml | 8 +++ docs/.idea/vcs.xml | 6 ++ docs/Writerside/assets/onefetch.png | Bin 0 -> 304229 bytes docs/Writerside/c.list | 6 ++ docs/Writerside/cfg/buildprofiles.xml | 13 ++++ docs/Writerside/cfg/glossary.xml | 7 +++ docs/Writerside/in.tree | 17 +++++ docs/Writerside/topics/Android.md | 52 ++++++++++++++++ docs/Writerside/topics/Building.md | 99 ++++++++++++++++++++++++++++++ docs/Writerside/topics/Linux.md | 71 +++++++++++++++++++++ docs/Writerside/topics/Using-prebuilds.md | 48 +++++++++++++++ docs/Writerside/topics/Windows.md | 63 +++++++++++++++++++ docs/Writerside/topics/about.md | 59 ++++++++++++++++++ docs/Writerside/topics/macOS.md | 86 ++++++++++++++++++++++++++ docs/Writerside/writerside.cfg | 11 ++++ docs/update_screenshot.sh | 14 +++++ 20 files changed, 623 insertions(+), 83 deletions(-) create mode 100644 .github/workflows/docs.yaml create mode 100644 docs/.idea/.gitignore create mode 100644 docs/.idea/docs.iml create mode 100644 docs/.idea/modules.xml create mode 100644 docs/.idea/vcs.xml create mode 100644 docs/Writerside/assets/onefetch.png create mode 100644 docs/Writerside/c.list create mode 100644 docs/Writerside/cfg/buildprofiles.xml create mode 100644 docs/Writerside/cfg/glossary.xml create mode 100644 docs/Writerside/in.tree create mode 100644 docs/Writerside/topics/Android.md create mode 100644 docs/Writerside/topics/Building.md create mode 100644 docs/Writerside/topics/Linux.md create mode 100644 docs/Writerside/topics/Using-prebuilds.md create mode 100644 docs/Writerside/topics/Windows.md create mode 100644 docs/Writerside/topics/about.md create mode 100644 docs/Writerside/topics/macOS.md create mode 100644 docs/Writerside/writerside.cfg create mode 100755 docs/update_screenshot.sh diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml new file mode 100644 index 0000000..f4d9a53 --- /dev/null +++ b/.github/workflows/docs.yaml @@ -0,0 +1,48 @@ +name: Build documentation + +on: [push] + +env: + # Name of module and id separated by a slash + INSTANCE: Writerside/in + # Replace HI with the ID of the instance in capital letters + ARTIFACT: webHelpIN2-all.zip + # Docker image version + DOCKER_VERSION: "242.21870" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-go@v5 + with: + go-version: '^1.23.2' + - uses: hecrj/setup-rust-action@v2 + with: + rust-version: stable + - name: Update image + run: cd docs && ./update_screenshot.sh + - name: Build Writerside docs using Docker + uses: JetBrains/writerside-github-action@v4 + with: + location: "docs" + instance: ${{ env.INSTANCE }} + artifact: ${{ env.ARTIFACT }} + docker-version: ${{ env.DOCKER_VERSION }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: artifact + path: artifacts/${{ env.ARTIFACT }} + + - name: Release + uses: softprops/action-gh-release@v2 + if: startsWith(github.ref, 'refs/tags/') + with: + files: artifacts/${{ env.ARTIFACT }} + token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} diff --git a/README.md b/README.md index 3a9b1a5..63f3fce 100644 --- a/README.md +++ b/README.md @@ -2,94 +2,15 @@ > Wrapper around wallet2_api.h that can be called using C api. -[![status-badge](https://ci.mrcyjanek.net/api/badges/5/status.svg?branch=rewrite-wip)](https://ci.mrcyjanek.net/repos/5/branches/rewrite-wip) - ## Building -to "clean" everything: +TL;DR: ```bash $ rm -rf monero wownero release $ git submodule update --init --recursive --force +$ for coin in monero wownero; do ./apply_patches $coin; done +$ ./build_single monero $(gcc -dumpmachine) -j$(nproc) ``` -fix ownership (if you build in docker but clone as a local user) - -```bash -$ sudo chown $(whoami) . -R -``` - -patch codebase - -```bash -$ ./apply_patches.sh monero -$ ./apply_patches.sh wownero -``` - -build monero_c - -```bash -$ ./build_single.sh monero x86_64-linux-gnu -j$(nproc) - wownero i686-linux-gnu - aarch64-linux-gnu - x86_64-linux-android - i686-linux-android - aarch64-linux-android - arm-linux-androideabi - i686-w64-mingw32 - x86_64-w64-mingw32 - host-apple-darwin - host-apple-ios -``` - -While building I aim to compile the code at oldest supported release of debian, using default toolchain to ensure that all linux distributions are able to run monero_c libraries, below I present a supported builders for given targets - -| x | builder | notes | -| ---------------------- | -------------------- | ----- | -| x86_64-linux-gnu | debian:buster | | -| i686-linux-gnu | debian:buster | | -| aarch64-linux-gnu | debian:buster | | -| x86_64-linux-android | debian:buster | | -| i686-linux-android | debian:buster | | -| aarch64-linux-android | debian:buster | | -| arm-linux-androideabi | debian:buster | | -| i686-w64-mingw32 | debian:buster | hardcoded DLL paths in build_single.sh | -| x86_64-w64-mingw32 | debian:buster | -"- | -| x86_64-apple-darwin11 | debian:bookworm | extra build step: `${HOST_ABI}-ranlib $PWD/$repo/contrib/depends/${HOST_ABI}/lib/libpolyseed.a` | -| aarch64-apple-darwin11 | debian:bookworm | -"- | -| host-apple-darwin | arm64-apple-darwin23 | dependencies: `brew install unbound boost@1.76 zmq cmake ccache autoconf automake libtool && brew link boost@1.76` | -| host-apple-ios | arm64-apple-darwin23 | | - -## Design - -Functions are as simple as reasonably possible as few steps should be performed to get from the exposed C api to actual wallet2 (or wallet3 in future) api calls. - -The only things passed in and out are: - -- void -- bool -- int -- uint64_t -- void* -- const char* - -All more complex structures are serialized into `const char*`, take look at MONERO_Wallet_createTransaction which uses `splitString(std::string(preferredInputs), std::string(separator));` to convert string into a std::set, so no implementation will need to worry about that. - -Is there more effective way to do that? Probably. Is there more universal way to pass that (JSON, or others?)? Most likely. That being said, I'm against doing that. You can easily join a string in any language, and I like to keep dependency count as low as possible. - -As for function naming `${COIN}_namespaceOrClass_functionName` is being used, currently these cryptocurrencies are supported - -- monero -- wownero - -both using wallet2 api, and both being patched with our secret ingredient(tm) (check patches directory). - -Since monero_c aims to be one-fits-all solution for monero wallets, there are some special things inside, like functions prefixed with `DEBUG_*`, these are not quarenteed to stay in the code, and can be changed, the only reason they are in is because I needed some testing early in the development when bringing support for variety of platforms. - -If you are a wallet developer and you **really** need this one function that doesn't exist, feel free to let me know I'll be happy to implement that. - -Currently there are enterprise resitents in our library: `${COIN}_cw_*` these functions are not guaranteed to stay stable, and are made for cake wallet to implement features that are not used in xmruw nor in stack_wallet (which I need to double-check later?). - -## Contributing - -To contribute you can visit git.mrcyjanek.net/mrcyjanek/monero_c and open a PR, alternatively use any other code mirror or send patches directly. \ No newline at end of file +Broken? Not working? Need help? https://moneroc.mrcyjanek.net/ diff --git a/docs/.idea/.gitignore b/docs/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/docs/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/docs/.idea/docs.iml b/docs/.idea/docs.iml new file mode 100644 index 0000000..6102194 --- /dev/null +++ b/docs/.idea/docs.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/.idea/modules.xml b/docs/.idea/modules.xml new file mode 100644 index 0000000..6049cfe --- /dev/null +++ b/docs/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/.idea/vcs.xml b/docs/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/docs/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/docs/Writerside/assets/onefetch.png b/docs/Writerside/assets/onefetch.png new file mode 100644 index 0000000..4cf1288 Binary files /dev/null and b/docs/Writerside/assets/onefetch.png differ diff --git a/docs/Writerside/c.list b/docs/Writerside/c.list new file mode 100644 index 0000000..c4c77a2 --- /dev/null +++ b/docs/Writerside/c.list @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/docs/Writerside/cfg/buildprofiles.xml b/docs/Writerside/cfg/buildprofiles.xml new file mode 100644 index 0000000..332e47b --- /dev/null +++ b/docs/Writerside/cfg/buildprofiles.xml @@ -0,0 +1,13 @@ + + + + + + + + true + + + + diff --git a/docs/Writerside/cfg/glossary.xml b/docs/Writerside/cfg/glossary.xml new file mode 100644 index 0000000..22bec6b --- /dev/null +++ b/docs/Writerside/cfg/glossary.xml @@ -0,0 +1,7 @@ + + + + + Description of what "foo" is. + + \ No newline at end of file diff --git a/docs/Writerside/in.tree b/docs/Writerside/in.tree new file mode 100644 index 0000000..b803dbb --- /dev/null +++ b/docs/Writerside/in.tree @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/Writerside/topics/Android.md b/docs/Writerside/topics/Android.md new file mode 100644 index 0000000..c02b14f --- /dev/null +++ b/docs/Writerside/topics/Android.md @@ -0,0 +1,52 @@ +# Android + +Building on linux has been tested on +- `ubuntu:22.04` (docker tag) + +## Install dependencies + +```bash +$ apt update +$ apt install -y build-essential pkg-config autoconf libtool \ + ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf \ + unzip python-is-python3 +``` + +## Prepare source + +> If you are running in docker or have not configured git you may need to do the following: +> ```bash +> git config --global --add safe.directory '*' +> git config --global user.email "ci@mrcyjanek.net" +> git config --global user.name "CI mrcyjanek.net" +> ``` + +```bash +$ git clone https://github.com/mrcyjanek/monero_c --recursive +$ cd monero_c +$ ./apply_patches.sh monero +``` + +## Building + + + + +$ ./build_single.sh monero x86_64-linux-android -j$(nproc) + + + + +$ ./build_single.sh monero aarch64-linux-android -j$(nproc) + + + + +$ ./build_single.sh monero armv7a-linux-androideabi -j$(nproc) + + + + +## Known issues + +None. Open an issue if you find something not working. \ No newline at end of file diff --git a/docs/Writerside/topics/Building.md b/docs/Writerside/topics/Building.md new file mode 100644 index 0000000..f44f1f3 --- /dev/null +++ b/docs/Writerside/topics/Building.md @@ -0,0 +1,99 @@ +# Building + +Building monero_c comes down to these simple steps: +```bash +$ git clone https://github.com/mrcyjanek/monero_c --recursive +$ ./apply_patches.sh monero # patch the libraries +$ ./build_single x86_64-linux-gnu -j$(nproc) +``` + +To get detailed information about building please select your host platform (the one that you will use to build the +monero wallet) + +- [I'm scared. How can I use prebuilds?](Using-prebuilds.md) +- [Linux](#linux) +- [macOS](#macos) +- [Windows](#windows) + +## Supported systems + +| Target | Builder | Notes | +|------------------|-----------------------------|----------------------------------------------------------------------------------| +| Windows | Linux, Docker, WSL2 | msys2 builds are not supported, there are hardcoded DLL paths in build_single.sh | +| macOS | Native, Linux, Docker, WSL2 | Native builds are being widely used, but are not endorsed by me personally. | +| iOS | macOS | | +| Android | Linux, Docker, WSL2 | | +| Linux | Native, Docker, WSL2 | | +| Linux/SailfishOS | Native, Docker | Required meego toolchain. | + +All supported systems are built on CI, so for the most up-to-date list of all supported systems, together with proper +dependencies please take a look at [full_check.yml](https://github.com/MrCyjaneK/monero_c/blob/master/.github/workflows/full_check.yaml) +file. + +### Linux + +Linux builds should be built on as old of a distro as possible - targeting `debian:oldoldstable`, main reason for that +is GLIBC compatibility (or lack of it). For some reason software compiled with newer GLIBC won't work on devices with +older GLIBC, so the solution is to simply build on the oldest possible os (or abandon GLIBC and use musl). + +- x86-64-linux-gnu +- i686-linux-gnu - deprecated by monero +- aarch64-linux-gnu + +[More details](Linux.md) + +#### SailfishOS + +Fully supported (except for armv7l devices). + +- i686-meego-linux-gnu +- aarch64-meego-linux-gnu + +More details: TBD + +#### Alpine Linux + +Requires dependency removed by upstream - it is preserved in `external/alpine/libexecinfo`. It could easily +be brought back - but since nobody uses it and nobody complained about it missing it kind of never landed in monero_c +after rewrite fully. That being said it should mostly work. + +- ~~x86_64-alpine-linux-musl~~ +- ~~aarch64-alpine-linux-musl~~ + +More details: TBD + +### Android + +Includes bumped NDK version + +- x86_64-linux-android +- ~~i686-linux-android~~ - unsupported, fails to build +- aarch64-linux-android +- armv7a-linux-androideabi + +[More details](Android.md) + +### Windows + +Msys2 shell is not supported, docker or WSL2 is requried to build + +- i686-w64-mingw32 - deprecated by monero +- x86_64-w64-mingw32 +- aarch64-w64-unknown - unsupported. + Though - if somebody can grab and send me a decent CopilotPC I'll be happy to work on that. As for now using x86_64 + build should be fine (but slower in runtime) solution. + +[More details](Windows.md) + +### macOS + +- x86_64-apple-darwin11 +- aarch64-apple-darwin11 +- host-apple-darwin +- x86_64-host-apple-darwin - alias for host-apple-darwin +- aarch64-host-apple-darwin alias for host-apple-darwin + +[More details](macOS.md) + +### iOS +- host-apple-ios - probably will be renamed to `aarch64-apple-ios` \ No newline at end of file diff --git a/docs/Writerside/topics/Linux.md b/docs/Writerside/topics/Linux.md new file mode 100644 index 0000000..7d3a2f1 --- /dev/null +++ b/docs/Writerside/topics/Linux.md @@ -0,0 +1,71 @@ +# Linux + +Building on linux has been tested on +- `debian:bullseye` (docker tag) + +## Install dependencies + + + + +$ apt update +$ apt install -y build-essential pkg-config autoconf libtool \ + ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf + + + + +$ 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 + + + + +$ apt update +$ apt install -y build-essential pkg-config autoconf libtool \ + ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf \ + gcc-i686-linux-gnu g++-i686-linux-gnu + + + + +## Prepare source + +> If you are running in docker or have not configured git you may need to do the following: +> ```bash +> git config --global --add safe.directory '*' +> git config --global user.email "ci@mrcyjanek.net" +> git config --global user.name "CI mrcyjanek.net" +> ``` + +```bash +$ git clone https://github.com/mrcyjanek/monero_c --recursive +$ cd monero_c +$ ./apply_patches.sh monero +``` + +## Building + + + + +$ ./build_single.sh monero x86_64-linux-gnu -j$(nproc) + + + + +$ ./build_single.sh monero aarch64-linux-gnu -j$(nproc) + + + + +$ ./build_single.sh monero i686-linux-gnu -j$(nproc) + + + + +## Known issues + +None. Open an issue if you find something not working. \ No newline at end of file diff --git a/docs/Writerside/topics/Using-prebuilds.md b/docs/Writerside/topics/Using-prebuilds.md new file mode 100644 index 0000000..f4ff35e --- /dev/null +++ b/docs/Writerside/topics/Using-prebuilds.md @@ -0,0 +1,48 @@ +# Using prebuilds + +Prebuilds are more convenient way of using monero_c in your code (although these builds should **NOT** be used in +production). There are several ways in which you can use prebuilds + +## The monero_c way + +1. Go to https://github.com/MrCyjaneK/monero_c/releases +2. Click on the release that you are interested in +3. Download release-bundle.zip +4. Unzip it in monero_c directory + +That zip file contains all builds that monero_c supports prepared in the exactly same way as you would prepare them if +building on your own. This is the easiest way to get started. + +> Keep in mind that release-bundle.zip contains .xz files inside, so if you want to actually use them you need to use +> them you need to unxz them first +> ```bash +> $ unxz -f -k release/*/*.xz +> ``` + +## The monero_c way (different) + +Alternatively you can go to releases tab and grab whatever you need, there are over 40 libraries. Surely one will fit +your use case. + +## The build_moneroc.sh way + +This method is used by [xmruw](https://github.com/mrcyjanek/unnamed_monero_wallet) and +[monero-tui](https://github.com/Im-Beast/monero-tui). + +It supports both building and downloading prebuilds and putting them in correct location, [have a look at the code +yourself](https://github.com/MrCyjaneK/unnamed_monero_wallet/blob/master-rewrite/build_moneroc.sh) + +```bash +$ ./build_moneroc.sh + --prebuild # allow downloads of prebuilds + --coin # monero/wownero + --tag v0.18.3.3-RC45 # which tag to build / download + --triplet x86_64-linux-android # which triplet to build / download + --location android/.../jniLibs/x86_64 # where to but the libraries +``` + +## The cake wallet way + +There is a simple script in cake_wallet written in `dart` that runs on all platform (including windows) which downloads +all required libraries for selected platforms in one go. +[You can take look at it here](https://github.com/cake-tech/cake_wallet/blob/main/tool/download_moneroc_prebuilds.dart) \ No newline at end of file diff --git a/docs/Writerside/topics/Windows.md b/docs/Writerside/topics/Windows.md new file mode 100644 index 0000000..1cb931c --- /dev/null +++ b/docs/Writerside/topics/Windows.md @@ -0,0 +1,63 @@ +# Windows + +Building on windows has been tested on +- `debian:bullseye` (docker tag) +- `Ubuntu 22.04 WSL` + +## Install dependencies + + + + +$ 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 + + + + +$ apt update +$ apt install -y build-essential pkg-config autoconf libtool \ + ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf \ + gcc-mingw-w64-i686 g++-mingw-w64-i686 + + + + +## Prepare source + +> If you are running in docker or have not configured git you may need to do the following: +> ```bash +> git config --global --add safe.directory '*' +> git config --global user.email "ci@mrcyjanek.net" +> git config --global user.name "CI mrcyjanek.net" +> ``` + +```bash +$ git clone https://github.com/mrcyjanek/monero_c --recursive +$ cd monero_c +$ ./apply_patches.sh monero +``` + +## Building + + + + +$ ./build_single.sh monero x86_64-w64-mingw32 -j$(nproc) + + + + +$ ./build_single.sh monero i686-w64-mingw32 -j$(nproc) + + + + +## Known issues + +### Dynamically loaded dependencies + +There are some dynamically loaded dependencies which are copied over from the OS package repository. All of them are +available in `release/` directory. \ No newline at end of file diff --git a/docs/Writerside/topics/about.md b/docs/Writerside/topics/about.md new file mode 100644 index 0000000..448e70f --- /dev/null +++ b/docs/Writerside/topics/about.md @@ -0,0 +1,59 @@ +# About + +![monero_c onefetch](../assets/onefetch.png) + +## Goal + +The goal of monero_c is simple, at least on the surface +- Build monero for all platforms +- Access monero functions from all languages +- Bring in new features used by wallets but still not merged upstream + +To achieve these tasks some major changes had to happen to monero. + +## Building + +For the most part `contrib/depends` directory of `monero` was used, as it is in perfect shape (that being said it's +terrible to debug when something goes wrong). But it doesn't work with iOS, and it doesn't work on macOS, so some +dependencies had to be pulled as a submodules. But that being said, linux can be used to build for all platforms (except +for iOS - you need macOS for that, and you also need that 100EUR/year license to actually be able to do something with +said library). + +This is especially good because any device can run linux, on WSL2 or under docker. + +## Accessing monero functions + +Though just building the library is not enough, if you just `make wallet_api` in the upstream code you end up in a +place where you technically have the functions in a dynamic library, but you can only use C++ ABI to interact with it. +Which is not ideal. Of course, it is doable, if you want to deal with `__ZNK6Monero10WalletImpl7addressEjj` instead of +`MONERO_Wallet_address`, and make sure that the way you pass in variables is what C++ expects. +I went with the simplest and most standard way of doing that - C ABI (which is what monero should offer in the first +place). Now with any language that supports C interop you can just go ahead and use monero wallet. + +All functionality is simplified to use only the following types: + +- bool +- int +- uint64_t +- void* +- const char* +- void + +## Bring in new features + +There are some extra features inside of the code like +- Polyseed support +- Background sync +- Airgap functions +- Coin control +- UR support +- Ledger support (on android and iSO) +- many fixes + +These features land in the `patches` directory, and are applied to the upstream monero code. + +## Summary + +monero_c extends monero functionality and also simplifies it by a huge extent, if you want to use monero - monero_c is +probably the way to go. +Are you ready to start? Let's see how to build the binary. \ No newline at end of file diff --git a/docs/Writerside/topics/macOS.md b/docs/Writerside/topics/macOS.md new file mode 100644 index 0000000..367893b --- /dev/null +++ b/docs/Writerside/topics/macOS.md @@ -0,0 +1,86 @@ +# macOS + +Building on linux has been tested on +- `macos-14` (github runner) +- `MacOS 15.0.1 Sequoia` (Native) +- `debian:bookworm` (docker tag) + +## Install dependencies + + + + +$ # install xcode 15.4 (or current latest) +$ brew install ccache unbound boost@1.76 zmq autoconf automake libtool +$ brew link boost@1.76 + + + + +$ # install xcode 15.4 (or current latest) +$ brew install ccache unbound boost@1.76 zmq autoconf automake libtool +$ brew link boost@1.76 +$ arch -x86_64 brew install ccache unbound boost@1.76 zmq autoconf automake libtool +$ arch -x86_64 brew link boost@1.76 + + + + +$ apt update +$ apt install -y build-essential pkg-config autoconf libtool \ + ccache make cmake gcc g++ git curl lbzip2 libtinfo5 gperf \ + python-is-python3 + + + + +## Prepare source + +> If you are running in docker or have not configured git you may need to do the following: +> ```bash +> git config --global --add safe.directory '*' +> git config --global user.email "ci@mrcyjanek.net" +> git config --global user.name "CI mrcyjanek.net" +> ``` + +```bash +$ git clone https://github.com/mrcyjanek/monero_c --recursive +$ cd monero_c +$ ./apply_patches.sh monero +``` + +## Building + + + + +$ ./build_single.sh monero x86_64-host-apple-darwin -j$(nproc) + + + + +$ ./build_single.sh monero aarch64-host-apple-darwin -j$(nproc) + + + + +$ arch -x86_64 ./build_single.sh monero x86_64-host-apple-darwin -j$(nproc) + + + + +$ ./build_single.sh monero x86_64-apple-darwin11 -j$(nproc) + + + + +$ ./build_single.sh monero aarch64-apple-darwin-11 -j$(nproc) + + + + +## Known issues + +### Creating fat library + +[Check cake_wallet solution](https://github.com/cake-tech/cake_wallet/blob/main/scripts/macos/build_monero_all.sh) \ No newline at end of file diff --git a/docs/Writerside/writerside.cfg b/docs/Writerside/writerside.cfg new file mode 100644 index 0000000..98e1435 --- /dev/null +++ b/docs/Writerside/writerside.cfg @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/docs/update_screenshot.sh b/docs/update_screenshot.sh new file mode 100755 index 0000000..a404047 --- /dev/null +++ b/docs/update_screenshot.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -x -e +go install github.com/homeport/termshot/cmd/termshot@main +cargo install onefetch +COMMIT_COUNT=$(git log --pretty=format:'' | wc -l | xargs) +echo Commit count: $COMMIT_COUNT +if (( $COMMIT_COUNT % 69 == 0 )) +then + echo "easter egg, yay!" + termshot --show-cmd --filename Writerside/assets/onefetch.png -- "onefetch --number-of-authors 0 -a html | lolcat" +else + echo "normal. Yay." + termshot --show-cmd --filename Writerside/assets/onefetch.png -- "onefetch --number-of-authors 0 -a c++" +fi \ No newline at end of file -- cgit v1.2.3 From 1d8e0fb30b53c28756f23676d5a3e1a99a9b3051 Mon Sep 17 00:00:00 2001 From: cyan Date: Mon, 21 Oct 2024 12:54:58 +0200 Subject: ledger changes (#41) * ledger changes * minor fix * Implement monero queryWalletDevice * update checksums * Remove ledger_flutter_plus dependency --------- Co-authored-by: Konstantin Ullrich --- .github/workflows/full_check.yaml | 1 - impls/monero.dart/lib/monero.dart | 18 ++++++++++++++++++ impls/monero.dart/lib/src/checksum_monero.dart | 4 ++-- .../lib/src/generated_bindings_monero.g.dart | 14 +++++++------- impls/monero.dart/pubspec.yaml | 2 +- impls/monero.ts/checksum_monero.ts | 4 ++-- monero_libwallet2_api_c/src/main/cpp/monero_checksum.h | 4 ++-- monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp | 11 ++++++++--- monero_libwallet2_api_c/src/main/cpp/wallet2_api_c.h | 4 ++-- patches/monero/0016-add-dummy-device-for-ledger.patch | 13 +++++++------ 10 files changed, 49 insertions(+), 26 deletions(-) diff --git a/.github/workflows/full_check.yaml b/.github/workflows/full_check.yaml index eaec860..19f2786 100644 --- a/.github/workflows/full_check.yaml +++ b/.github/workflows/full_check.yaml @@ -467,7 +467,6 @@ jobs: with: name: ios ${{ matrix.coin }} path: release/${{ matrix.coin }} - bulk_lib_release: name: create single release file runs-on: ubuntu-latest diff --git a/impls/monero.dart/lib/monero.dart b/impls/monero.dart/lib/monero.dart index 4fcc970..7b6eac3 100644 --- a/impls/monero.dart/lib/monero.dart +++ b/impls/monero.dart/lib/monero.dart @@ -3607,6 +3607,24 @@ bool WalletManager_verifyWalletPassword( return s; } +int WalletManager_queryWalletDevice( + WalletManager wm_ptr, { + required String keysFileName, + required String password, + required int kdfRounds, + }) { + debugStart?.call('MONERO_WalletManager_queryWalletDevice'); + lib ??= MoneroC(DynamicLibrary.open(libPath)); + final keysFileName_ = keysFileName.toNativeUtf8().cast(); + final password_ = password.toNativeUtf8().cast(); + final s = lib!.MONERO_WalletManager_queryWalletDevice( + wm_ptr, keysFileName_, password_, kdfRounds); + calloc.free(keysFileName_); + calloc.free(password_); + debugEnd?.call('MONERO_WalletManager_queryWalletDevice'); + return s; +} + List WalletManager_findWallets(WalletManager wm_ptr, {required String path}) { debugStart?.call('MONERO_WalletManager_findWallets'); diff --git a/impls/monero.dart/lib/src/checksum_monero.dart b/impls/monero.dart/lib/src/checksum_monero.dart index 4ab72a3..b94fb5a 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 = "e8db0ef0324a153f5e3ecca4c0db23c54f4576e84988f04bd4f11c1142f9d7ad"; -const String wallet2_api_c_cpp_sha256 = "dca52ac9ee009fda9fb5726543a454885e61d8eb74fb33112288029ed625bec5-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; +const String wallet2_api_c_h_sha256 = "198a27486ce4f014b737f52ec2879a90838c3b3ca088de8dc7d55b79a10f4a5b"; +const String wallet2_api_c_cpp_sha256 = "22b0c9dd542fb55edfe9e17da67e97ddf5f80bcdd3e126e1bd64cce54234bde1-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; const String wallet2_api_c_exp_sha256 = "c8913ac41068f67b57c9b0a3c7dd8973e3c1273b66c2ff0aadb0003931da748c"; 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 039fbf9..cd2124b 100644 --- a/impls/monero.dart/lib/src/generated_bindings_monero.g.dart +++ b/impls/monero.dart/lib/src/generated_bindings_monero.g.dart @@ -4911,14 +4911,14 @@ class MoneroC { bool Function(ffi.Pointer, ffi.Pointer, ffi.Pointer, bool, int)>(); - bool MONERO_WalletManager_queryWalletDevice( - int device_type, + int MONERO_WalletManager_queryWalletDevice( + ffi.Pointer wm_ptr, ffi.Pointer keys_file_name, ffi.Pointer password, int kdf_rounds, ) { return _MONERO_WalletManager_queryWalletDevice( - device_type, + wm_ptr, keys_file_name, password, kdf_rounds, @@ -4927,15 +4927,15 @@ class MoneroC { late final _MONERO_WalletManager_queryWalletDevicePtr = _lookup< ffi.NativeFunction< - ffi.Bool Function( - ffi.Int, + ffi.Int Function( + ffi.Pointer, ffi.Pointer, ffi.Pointer, ffi.Uint64)>>('MONERO_WalletManager_queryWalletDevice'); late final _MONERO_WalletManager_queryWalletDevice = _MONERO_WalletManager_queryWalletDevicePtr.asFunction< - bool Function( - int, ffi.Pointer, ffi.Pointer, int)>(); + int Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer, int)>(); ffi.Pointer MONERO_WalletManager_findWallets( ffi.Pointer wm_ptr, diff --git a/impls/monero.dart/pubspec.yaml b/impls/monero.dart/pubspec.yaml index 174a781..2a32efe 100644 --- a/impls/monero.dart/pubspec.yaml +++ b/impls/monero.dart/pubspec.yaml @@ -12,4 +12,4 @@ dependencies: dev_dependencies: lints: ^5.0.0 test: ^1.24.0 - ffigen: ^14.0.0 \ No newline at end of file + ffigen: ^14.0.0 diff --git a/impls/monero.ts/checksum_monero.ts b/impls/monero.ts/checksum_monero.ts index 88406a0..d421d01 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: "e8db0ef0324a153f5e3ecca4c0db23c54f4576e84988f04bd4f11c1142f9d7ad", - wallet2_api_c_cpp_sha256: "dca52ac9ee009fda9fb5726543a454885e61d8eb74fb33112288029ed625bec5-b089f9ee69924882c5d14dd1a6991deb05d9d1cd", + wallet2_api_c_h_sha256: "198a27486ce4f014b737f52ec2879a90838c3b3ca088de8dc7d55b79a10f4a5b", + wallet2_api_c_cpp_sha256: "22b0c9dd542fb55edfe9e17da67e97ddf5f80bcdd3e126e1bd64cce54234bde1-b089f9ee69924882c5d14dd1a6991deb05d9d1cd", wallet2_api_c_exp_sha256: "c8913ac41068f67b57c9b0a3c7dd8973e3c1273b66c2ff0aadb0003931da748c", } 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 20cbeae..5a9d56e 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 = "e8db0ef0324a153f5e3ecca4c0db23c54f4576e84988f04bd4f11c1142f9d7ad"; -const char * MONERO_wallet2_api_c_cpp_sha256 = "dca52ac9ee009fda9fb5726543a454885e61d8eb74fb33112288029ed625bec5-b089f9ee69924882c5d14dd1a6991deb05d9d1cd"; +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 = "c8913ac41068f67b57c9b0a3c7dd8973e3c1273b66c2ff0aadb0003931da748c"; #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 407d700..0cc2f9f 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 @@ -1767,10 +1767,15 @@ bool MONERO_WalletManager_verifyWalletPassword(void* wm_ptr, const char* keys_fi Monero::WalletManager *wm = reinterpret_cast(wm_ptr); return wm->verifyWalletPassword(std::string(keys_file_name), std::string(password), no_spend_key, kdf_rounds); } + // 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 MONERO_WalletManager_queryWalletDevice(int device_type, const char* keys_file_name, const char* password, uint64_t kdf_rounds) { -// return Monero::WalletManagerFactory::getWalletManager()->queryWalletDevice(device_type, std::string(keys_file_name), std::string(password), kdf_rounds); -//} +int MONERO_WalletManager_queryWalletDevice(void* wm_ptr, const char* keys_file_name, const char* password, uint64_t kdf_rounds) { + 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; +} + // virtual std::vector findWallets(const std::string &path) = 0; const char* MONERO_WalletManager_findWallets(void* wm_ptr, const char* path, const char* separator) { Monero::WalletManager *wm = reinterpret_cast(wm_ptr); 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 6e691c8..143da40 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 @@ -948,7 +948,7 @@ extern ADDAPI bool MONERO_WalletManager_walletExists(void* wm_ptr, const char* p // virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const = 0; extern ADDAPI bool MONERO_WalletManager_verifyWalletPassword(void* wm_ptr, const char* keys_file_name, const char* password, bool no_spend_key, uint64_t kdf_rounds); // virtual bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const = 0; -extern ADDAPI bool MONERO_WalletManager_queryWalletDevice(int device_type, const char* keys_file_name, const char* password, uint64_t kdf_rounds); +extern ADDAPI int MONERO_WalletManager_queryWalletDevice(void* wm_ptr, const char* keys_file_name, const char* password, uint64_t kdf_rounds); // virtual std::vector findWallets(const std::string &path) = 0; extern ADDAPI const char* MONERO_WalletManager_findWallets(void* wm_ptr, const char* path, const char* separator); // virtual std::string errorString() const = 0; @@ -1039,4 +1039,4 @@ extern ADDAPI const char* MONERO_checksum_wallet2_api_c_exp(); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/patches/monero/0016-add-dummy-device-for-ledger.patch b/patches/monero/0016-add-dummy-device-for-ledger.patch index d4ab5fe..364d6fd 100644 --- a/patches/monero/0016-add-dummy-device-for-ledger.patch +++ b/patches/monero/0016-add-dummy-device-for-ledger.patch @@ -1,4 +1,4 @@ -From 6b40191d35df998280e1d6e19ff9bf4bce54d5bf Mon Sep 17 00:00:00 2001 +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 @@ -12,10 +12,10 @@ Subject: [PATCH 16/16] add dummy device for ledger src/device/device_io_dummy.hpp | 74 ++++++++++++++++++ src/device/device_ledger.cpp | 6 +- src/device/device_ledger.hpp | 7 +- - src/wallet/api/wallet.cpp | 93 +++++++++++++++++++++++ + src/wallet/api/wallet.cpp | 94 +++++++++++++++++++++++ src/wallet/api/wallet.h | 18 +++++ src/wallet/api/wallet2_api.h | 12 +++ - 11 files changed, 356 insertions(+), 21 deletions(-) + 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 @@ -414,7 +414,7 @@ index 03058c4f1..506f27c4a 100644 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 a307d35a7..3ead385ec 100644 +index 8bdd75a5a..09f91c5e2 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -49,6 +49,9 @@ @@ -427,7 +427,7 @@ index a307d35a7..3ead385ec 100644 using namespace std; using namespace cryptonote; -@@ -3298,4 +3301,94 @@ uint64_t WalletImpl::getBytesSent() +@@ -3299,4 +3302,95 @@ uint64_t WalletImpl::getBytesSent() return m_wallet->get_bytes_sent(); } @@ -502,6 +502,7 @@ index a307d35a7..3ead385ec 100644 + 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; @@ -575,5 +576,5 @@ index 2bbb32c8b..c8d6bb179 100644 /** -- -2.39.2 +2.45.1.windows.1 -- cgit v1.2.3 From dab8a211c18e0060d17e5d2b3dc1e57a314d4f7e Mon Sep 17 00:00:00 2001 From: cyan Date: Fri, 1 Nov 2024 17:21:10 +0000 Subject: GPLv3 -> LGPLv3 --- LICENSE | 827 +++++++++++++--------------------------------------------------- 1 file changed, 159 insertions(+), 668 deletions(-) diff --git a/LICENSE b/LICENSE index f288702..0a04128 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,165 @@ - GNU GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program 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 . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. -- cgit v1.2.3 From 1ef9024e43e774ac2c2976c33bf9024549c9c61b Mon Sep 17 00:00:00 2001 From: julian-CStack <97684800+julian-CStack@users.noreply.github.com> Date: Tue, 5 Nov 2024 10:44:41 -0600 Subject: Ios script fix (#85) * fix: mv will fail on consecutive runs * fix: script cp error --- external/ios/build_boost.sh | 12 ++++++------ external/ios/build_openssl.sh | 10 +++++----- external/ios/build_sodium.sh | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/external/ios/build_boost.sh b/external/ios/build_boost.sh index 4bfbfeb..805fbd0 100755 --- a/external/ios/build_boost.sh +++ b/external/ios/build_boost.sh @@ -16,11 +16,11 @@ 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 + 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 +cd "$BOOST_DIR_PATH" ./boost.sh -ios \ --min-ios-version ${MIN_IOS_VERSION} \ @@ -28,5 +28,5 @@ cd $BOOST_DIR_PATH --boost-version ${BOOST_VERSION} \ --no-framework -mv -f ${BOOST_DIR_PATH}/build/boost/${BOOST_VERSION}/ios/release/prefix/include/* $EXTERNAL_IOS_INCLUDE_DIR -mv -f ${BOOST_DIR_PATH}/build/boost/${BOOST_VERSION}/ios/release/prefix/lib/* $EXTERNAL_IOS_LIB_DIR \ No newline at end of file +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_openssl.sh b/external/ios/build_openssl.sh index 507ca9d..4bf6aa2 100755 --- a/external/ios/build_openssl.sh +++ b/external/ios/build_openssl.sh @@ -16,13 +16,13 @@ if [ -d "$OPEN_SSL_DIR_PATH" ]; then 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 + cp -r "${MONEROC_DIR}/external/OpenSSL-for-iPhone" "$OPEN_SSL_DIR_PATH" fi -cd $OPEN_SSL_DIR_PATH +cd "$OPEN_SSL_DIR_PATH" ./build-libssl.sh --version=1.1.1q --targets="ios-cross-arm64" --deprecated -mv -f ${OPEN_SSL_DIR_PATH}/include/* $EXTERNAL_IOS_INCLUDE_DIR -mv -f ${OPEN_SSL_DIR_PATH}/lib/libcrypto-iOS.a ${EXTERNAL_IOS_LIB_DIR}/libcrypto.a -mv -f ${OPEN_SSL_DIR_PATH}/lib/libssl-iOS.a ${EXTERNAL_IOS_LIB_DIR}/libssl.a \ No newline at end of file +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 index ecb4a5e..d8f8777 100755 --- a/external/ios/build_sodium.sh +++ b/external/ios/build_sodium.sh @@ -16,13 +16,13 @@ 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 + mkdir -p "$SODIUM_PATH" || true + rm -rf "$SODIUM_PATH" + cp -r "${MONEROC_DIR}/external/libsodium" "$SODIUM_PATH" fi -cd $SODIUM_PATH +cd "$SODIUM_PATH" ../../../../libsodium_apple-ios.sh -mv -f ${SODIUM_PATH}/libsodium-apple/ios/include/* $EXTERNAL_IOS_INCLUDE_DIR -mv -f ${SODIUM_PATH}/libsodium-apple/ios/lib/* $EXTERNAL_IOS_LIB_DIR \ No newline at end of file +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 -- cgit v1.2.3 From 8182ca416ef8c6573dd2a4276cd334309076b98c Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Mon, 11 Nov 2024 16:28:04 +0100 Subject: Add MONERO_WalletManager_queryWalletDevice to monero_libwallet2_api_c.exp --- monero_libwallet2_api_c/monero_libwallet2_api_c.exp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monero_libwallet2_api_c/monero_libwallet2_api_c.exp b/monero_libwallet2_api_c/monero_libwallet2_api_c.exp index e468456..3b2931c 100644 --- a/monero_libwallet2_api_c/monero_libwallet2_api_c.exp +++ b/monero_libwallet2_api_c/monero_libwallet2_api_c.exp @@ -273,6 +273,7 @@ _MONERO_WalletManager_createWalletFromPolyseed _MONERO_WalletManager_closeWallet _MONERO_WalletManager_walletExists _MONERO_WalletManager_verifyWalletPassword +_MONERO_WalletManager_queryWalletDevice _MONERO_WalletManager_findWallets _MONERO_WalletManager_errorString _MONERO_WalletManager_setDaemonAddress @@ -306,4 +307,4 @@ _MONERO_cw_WalletListener_height _MONERO_free _MONERO_checksum_wallet2_api_c_h _MONERO_checksum_wallet2_api_c_cpp -_MONERO_checksum_wallet2_api_c_exp \ No newline at end of file +_MONERO_checksum_wallet2_api_c_exp -- cgit v1.2.3 From d47fb528282fd795ec33da79b5bd218840495629 Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Mon, 11 Nov 2024 16:55:28 +0100 Subject: Remove ledger_flutter_plus dependency --- impls/monero.dart/lib/src/checksum_monero.dart | 2 +- impls/monero.ts/checksum_monero.ts | 2 +- monero_libwallet2_api_c/src/main/cpp/monero_checksum.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/impls/monero.dart/lib/src/checksum_monero.dart b/impls/monero.dart/lib/src/checksum_monero.dart index b94fb5a..1092533 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 = "c8913ac41068f67b57c9b0a3c7dd8973e3c1273b66c2ff0aadb0003931da748c"; +const String wallet2_api_c_exp_sha256 = "c1f785d62709fd8b849063ecf4fe8854d9e3b05b568b9716de98d33e7bdaf522"; diff --git a/impls/monero.ts/checksum_monero.ts b/impls/monero.ts/checksum_monero.ts index d421d01..b8e57cb 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: "c8913ac41068f67b57c9b0a3c7dd8973e3c1273b66c2ff0aadb0003931da748c", + wallet2_api_c_exp_sha256: "c1f785d62709fd8b849063ecf4fe8854d9e3b05b568b9716de98d33e7bdaf522", } 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 5a9d56e..4d3d39f 100644 --- a/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h +++ b/monero_libwallet2_api_c/src/main/cpp/monero_checksum.h @@ -2,5 +2,5 @@ #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 = "c8913ac41068f67b57c9b0a3c7dd8973e3c1273b66c2ff0aadb0003931da748c"; +const char * MONERO_wallet2_api_c_exp_sha256 = "c1f785d62709fd8b849063ecf4fe8854d9e3b05b568b9716de98d33e7bdaf522"; #endif -- cgit v1.2.3 From d72c15f4339791a7bbdf17e9d827b7b56ca144e4 Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Wed, 13 Nov 2024 10:44:50 +0100 Subject: use usleep instead of sleep (#88) --- patches/monero/0016-add-dummy-device-for-ledger.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patches/monero/0016-add-dummy-device-for-ledger.patch b/patches/monero/0016-add-dummy-device-for-ledger.patch index 364d6fd..dddb4b1 100644 --- a/patches/monero/0016-add-dummy-device-for-ledger.patch +++ b/patches/monero/0016-add-dummy-device-for-ledger.patch @@ -234,13 +234,13 @@ index 000000000..fb082694e + MDEBUG("exchange(): waitsForDeviceSend"); + // NOTE: waitsForDeviceSend should be changed by external code + while (waitsForDeviceSend) { -+ sleep(1); ++ usleep(1000); + MDEBUG("exchange(): waitsForDeviceSend (still)"); + } + + MDEBUG("exchange(): waitsForDeviceReceive"); + while (waitsForDeviceReceive) { -+ sleep(1); ++ usleep(1000); + MDEBUG("exchange(): waitsForDeviceReceive (still)"); + } + -- cgit v1.2.3 From c41c4dad9aa5003a914cfb2c528c76386f952665 Mon Sep 17 00:00:00 2001 From: cyan Date: Wed, 27 Nov 2024 23:30:18 +0000 Subject: fix queryWalletDevice on corrupted wallets --- ...0020-catch-exception-in-queryWalletDevice.patch | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 patches/monero/0020-catch-exception-in-queryWalletDevice.patch diff --git a/patches/monero/0020-catch-exception-in-queryWalletDevice.patch b/patches/monero/0020-catch-exception-in-queryWalletDevice.patch new file mode 100644 index 0000000..1fb2471 --- /dev/null +++ b/patches/monero/0020-catch-exception-in-queryWalletDevice.patch @@ -0,0 +1,35 @@ +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 + -- cgit v1.2.3 From 40c1a1bda4b6f125c702f5a37ecc48a6ebec24b8 Mon Sep 17 00:00:00 2001 From: Mateusz Franik <47059999+Im-Beast@users.noreply.github.com> Date: Sun, 1 Dec 2024 15:02:20 +0100 Subject: feat!: monero.ts rewrite, integration tests (#80) * feat: move spend/view key symbols to the monero.ts implementation * feat: add integration tests for `0001-polyseed.patch` * feat(monero.ts): add support for backgroundSync and closing the wallet * feat: add integration tests for `0002-wallet-background-sync-with-just-the-view-key.patch` * feat!: require users to provide own node url BREAKING CHANGE: Requires users manual call to `Wallet.initWallet` after wallet creation with preferred node url * feat: add background sync test for `0002-wallet-background-sync-with-just-the-view-key.patch` * ci: add integration tests step * feat(monero.ts): support creating and recovering wallet from polyseed * feat: actually test polyseeds in the integration test * chore: remove legacy comments * fix: uncomment getting moneroC * feat(monero.ts): add support for reading wallet's seed * feat: add seed test for `0009-Add-recoverDeterministicWalletFromSpendKey.patch` * chore: slight refactor * feat(monero.ts): add bindings for `setOffline` and `isOffline` * feat: add integration tests for `0012-WIP-UR-functions.patch` * fix: use correct node depending on the coin * fix: prevent segfaults on wownero * feat(monero.ts): add partial bindings for `Coins` and `CoinsInfo` * feat: add integration tests for `0004-coin-control.patch` * fix coin control * clean up console.logs * chore: comment out the entire block * dev: add devcontainer config for deno * fix(monero.ts): invalid PendingTransactionPtr brand * feat(monero.ts): add bindings for retrieving keys and managing transactions * feat: improve `0012-WIP-UR-functions.patch` tests to follow the airgap doc * fix(monero.ts): make UR methods optional so wownero can load properly * remove flaky balance assertions * tests: add a little bit of delay to make 0002 patch test less flake-y * tests: run wallet transaction tests on ci * enable logging to determine why it segfaults on ci * add delay to every syncBlockchain call * its console logging time * even more console.logs * eep * eep more * dont assert that its not frozen * remove console.logs * fix(monero.ts): type typo becoming a default value * feat(monero.ts): add bindings for `createTransactionMultDest` * feat(monero.ts): support returning multiple values whenever necessary * feat(monero.ts): add missing reexports * feat(monero.ts)!: rewrite bindings BREAKING CHANGES!: - Calls to methods no longer automatically throw errors, you should take care of handling errors yourself - This means the whole sanitizer ordeal is gone, no more sanitize arguments etc. - Some misplaced methods have been moved to their "proper" place, e.g. creating Wallet is now possible using WalletManager instance methods, instead of passing WalletManager instance to Wallet's static method - Return types probably changed in places, methods were inconsitent about returning string or empty string and `string | null`, now its always `string | null` - Every available symbol should now be available in `symbols`, even for the things that are not yet implemented, so you can access them in that case * tests: adapt tests to monero.ts changes * tests: reuse dylib in tests --------- Co-authored-by: cyan --- .devcontainer/Dockerfile | 14 +- .devcontainer/devcontainer.json | 3 +- .github/workflows/full_check.yaml | 33 +- .gitignore | 1 + impls/monero.ts/README.md | 4 +- impls/monero.ts/checksum.ts | 10 +- impls/monero.ts/mod.ts | 5 + impls/monero.ts/src/bindings.ts | 21 +- impls/monero.ts/src/coins.ts | 53 + impls/monero.ts/src/coins_info.ts | 85 + impls/monero.ts/src/pending_transaction.ts | 117 +- impls/monero.ts/src/symbols.ts | 2610 +++++++++++++++++++++++---- impls/monero.ts/src/transaction_history.ts | 33 +- impls/monero.ts/src/transaction_info.ts | 169 +- impls/monero.ts/src/unsigned_transaction.ts | 79 + impls/monero.ts/src/utils.ts | 32 +- impls/monero.ts/src/wallet.ts | 446 ++--- impls/monero.ts/src/wallet_manager.ts | 138 +- tests/compare.ts | 76 +- tests/deno.lock | 4 + tests/integration.test.ts | 632 +++++++ tests/utils.ts | 8 +- 22 files changed, 3766 insertions(+), 807 deletions(-) create mode 100644 impls/monero.ts/src/coins.ts create mode 100644 impls/monero.ts/src/coins_info.ts create mode 100644 impls/monero.ts/src/unsigned_transaction.ts mode change 100755 => 100644 tests/deno.lock create mode 100644 tests/integration.test.ts diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index adebed0..37533b9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,6 @@ FROM ghcr.io/cirruslabs/flutter:3.24.3 # FROM debian:bookworm +SHELL ["/bin/bash", "-c"] # Install dependencies @@ -15,11 +16,18 @@ RUN apt update \ RUN bash -c 'if [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ]] then sudo rm -rf /opt/android-sdk-linux/platform-tools; fi' -# Install zsh and configure it + +ENV DENO_VERSION=v2.0.3 +RUN if [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ]] then export DENO_ARCH=aarch64-unknown-linux-gnu; fi; \ + if [[ "$(uname -m)" == "x86_64" || "$(uname -m)" == "amd64" ]] then export DENO_ARCH=x86_64-unknown-linux-gnu; fi; \ + wget -O /tmp/deno.zip "https://github.com/denoland/deno/releases/download/${DENO_VERSION}/deno-${DENO_ARCH}.zip" && \ + cd /tmp && \ + unzip /tmp/deno.zip && \ + mv deno /usr/local/bin && \ + rm -rf deno deno.zip 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 - -RUN git config --global --add safe.directory '*' \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f6edac1..6342723 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,7 +17,8 @@ }, "extensions": [ "dart-code.flutter", - "dart-code.dart-code" + "dart-code.dart-code", + "denoland.vscode-deno" ] } }, diff --git a/.github/workflows/full_check.yaml b/.github/workflows/full_check.yaml index 19f2786..2beda33 100644 --- a/.github/workflows/full_check.yaml +++ b/.github/workflows/full_check.yaml @@ -609,7 +609,38 @@ jobs: path: release/${{ matrix.coin }} - name: Run regression tests - run: COIN="${{ matrix.coin }}" deno test -A tests/ + run: COIN="${{ matrix.coin }}" deno test -A tests/regression.test.ts + + integration_check: + strategy: + matrix: + coin: [monero, wownero] + needs: [ + lib_linux + ] + runs-on: ubuntu-24.04 + steps: + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - uses: actions/download-artifact@v4 + with: + name: linux ${{ matrix.coin }} + path: release/${{ matrix.coin }} + + - name: Run integration tests + run: COIN="${{ matrix.coin }}" deno test -A tests/integration.test.ts + env: + SECRET_WALLET_PASSWORD: ${{ secrets.SECRET_WALLET_PASSWORD }} + SECRET_WALLET_MNEMONIC: ${{ secrets.SECRET_WALLET_MNEMONIC }} + SECRET_WALLET_RESTORE_HEIGHT: ${{ secrets.SECRET_WALLET_RESTORE_HEIGHT }} + comment_pr: name: comment on pr diff --git a/.gitignore b/.gitignore index 2dbc30e..9159f7d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ release/ build/ tests/monero-cli +tests/wownero-cli tests/libs tests/wallets diff --git a/impls/monero.ts/README.md b/impls/monero.ts/README.md index fe06467..9e3b27a 100644 --- a/impls/monero.ts/README.md +++ b/impls/monero.ts/README.md @@ -23,7 +23,7 @@ There are at least two ways to do so: loadMoneroDylib(); const wm = await WalletManager.new(); - const wallet = await Wallet.create(wm, "./my_wallet", "password"); + const wallet = await wm.createWallet("./my_wallet", "password"); console.log(await wallet.address()); @@ -41,7 +41,7 @@ There are at least two ways to do so: loadMoneroDylib(lib); const wm = await WalletManager.new(); - const wallet = await Wallet.create(wm, "./my_wallet", "password"); + const wallet = await wm.createWallet("./my_wallet", "password"); console.log(await wallet.address()); diff --git a/impls/monero.ts/checksum.ts b/impls/monero.ts/checksum.ts index b0d12b0..81140d3 100644 --- a/impls/monero.ts/checksum.ts +++ b/impls/monero.ts/checksum.ts @@ -1,6 +1,6 @@ import { moneroChecksum } from "./checksum_monero.ts"; -import { getSymbol, readCString } from "./src/utils.ts"; -import { dylib, loadMoneroDylib } from "./src/bindings.ts"; +import { readCString } from "./src/utils.ts"; +import { fns, loadMoneroDylib } from "./src/bindings.ts"; loadMoneroDylib(); @@ -21,7 +21,7 @@ export class ChecksumError extends Error { * @returns {ChecksumError} which contains information about why checksum failed */ export async function validateChecksum(): Promise { - const cppHeaderHash = await readCString(await getSymbol("checksum_wallet2_api_c_h")!(), false); + const cppHeaderHash = await readCString(await fns.checksum_wallet2_api_c_h!(), false); const tsHeaderHash = moneroChecksum.wallet2_api_c_h_sha256; const errors: string[] = []; @@ -32,14 +32,14 @@ export async function validateChecksum(): Promise { errorCode++; } - const cppSourceHash = await readCString(await getSymbol("checksum_wallet2_api_c_cpp")!(), false); + const cppSourceHash = await readCString(await fns.checksum_wallet2_api_c_cpp!(), false); const tsSourceHash = moneroChecksum.wallet2_api_c_cpp_sha256; if (cppSourceHash !== tsSourceHash) { errors.push(`ERR: CPP source file check mismatch ${cppSourceHash} == ${tsSourceHash}`); errorCode++; } - const cppExportHash = await readCString(await getSymbol("checksum_wallet2_api_c_exp")!(), false); + const cppExportHash = await readCString(await fns.checksum_wallet2_api_c_exp!(), false); const tsExportHash = moneroChecksum.wallet2_api_c_exp_sha256; if (cppExportHash !== tsExportHash) { if (Deno.build.os !== "darwin") { diff --git a/impls/monero.ts/mod.ts b/impls/monero.ts/mod.ts index 1eca773..5b9af6c 100644 --- a/impls/monero.ts/mod.ts +++ b/impls/monero.ts/mod.ts @@ -1,6 +1,11 @@ export * from "./src/bindings.ts"; +export * from "./src/coins.ts"; +export * from "./src/coins_info.ts"; export * from "./src/pending_transaction.ts"; +export * from "./src/symbols.ts"; export * from "./src/transaction_history.ts"; export * from "./src/transaction_info.ts"; +export * from "./src/unsigned_transaction.ts"; +export * from "./src/utils.ts"; export * from "./src/wallet.ts"; export * from "./src/wallet_manager.ts"; diff --git a/impls/monero.ts/src/bindings.ts b/impls/monero.ts/src/bindings.ts index 93ce8f9..dc63c03 100644 --- a/impls/monero.ts/src/bindings.ts +++ b/impls/monero.ts/src/bindings.ts @@ -1,8 +1,21 @@ -import { type Dylib, moneroSymbols, type MoneroTsDylib, wowneroSymbols, type WowneroTsDylib } from "./symbols.ts"; +import { type MoneroSymbols, moneroSymbols, type SymbolName, type WowneroSymbols, wowneroSymbols } from "./symbols.ts"; + +export type MoneroDylib = Deno.DynamicLibrary; +export type WowneroDylib = Deno.DynamicLibrary; +export type Dylib = MoneroDylib | WowneroDylib; export let dylib: Dylib; -export function loadMoneroDylib(newDylib?: MoneroTsDylib) { +let dylibPrefix = "MONERO"; +export const fns = new Proxy({} as { [K in SymbolName]: MoneroDylib["symbols"][`MONERO_${K}`] }, { + get(_, symbolName: SymbolName) { + return dylib.symbols[`${dylibPrefix}_${symbolName}` as keyof Dylib["symbols"]]; + }, +}); + +export function loadMoneroDylib(newDylib?: MoneroDylib) { + dylibPrefix = "MONERO"; + if (newDylib) { dylib = newDylib; return; @@ -27,7 +40,9 @@ export function loadMoneroDylib(newDylib?: MoneroTsDylib) { dylib = Deno.dlopen(libPath, moneroSymbols); } -export function loadWowneroDylib(newDylib?: WowneroTsDylib) { +export function loadWowneroDylib(newDylib?: WowneroDylib) { + dylibPrefix = "WOWNERO"; + if (newDylib) { dylib = newDylib; return; diff --git a/impls/monero.ts/src/coins.ts b/impls/monero.ts/src/coins.ts new file mode 100644 index 0000000..45a222f --- /dev/null +++ b/impls/monero.ts/src/coins.ts @@ -0,0 +1,53 @@ +import { CoinsInfo, type CoinsInfoPtr } from "./coins_info.ts"; +import { fns } from "./bindings.ts"; + +export type CoinsPtr = Deno.PointerObject<"coins">; + +export class Coins { + #ptr: CoinsPtr; + + #coins: CoinsInfo[] = []; + + constructor(ptr: CoinsPtr) { + this.#ptr = ptr; + } + + async count(): Promise { + return await fns.Coins_count(this.#ptr); + } + + async coin(index: number): Promise { + if (this.#coins[index]) { + return this.#coins[index]; + } + + const coinPtr = await fns.Coins_coin(this.#ptr, index); + if (!coinPtr) return null; + + return CoinsInfo.new(coinPtr as CoinsInfoPtr); + } + + async setFrozen(index: number) { + return await fns.Coins_setFrozen(this.#ptr, index); + } + + async thaw(index: number) { + return await fns.Coins_thaw(this.#ptr, index); + } + + async getAllSize(): Promise { + return await fns.Coins_getAll_size(this.#ptr); + } + + async getAllByIndex(index: number): Promise { + return await fns.Coins_getAll_byIndex(this.#ptr, index); + } + + async refresh(): Promise { + await fns.Coins_refresh(this.#ptr); + + for (const coin of this.#coins) { + coin.refresh(); + } + } +} diff --git a/impls/monero.ts/src/coins_info.ts b/impls/monero.ts/src/coins_info.ts new file mode 100644 index 0000000..7ea18aa --- /dev/null +++ b/impls/monero.ts/src/coins_info.ts @@ -0,0 +1,85 @@ +import { fns } from "./bindings.ts"; +import { readCString } from "./utils.ts"; + +export type CoinsInfoPtr = Deno.PointerObject<"coinsInfo">; + +export class CoinsInfo { + #ptr: CoinsInfoPtr; + + #hash!: string | null; + #keyImage!: string | null; + #blockHeight!: bigint; + #amount!: bigint; + #spent!: boolean; + #spentHeight!: bigint; + #frozen!: boolean; + #unlocked!: boolean; + + constructor(ptr: CoinsInfoPtr) { + this.#ptr = ptr; + } + + getPointer(): CoinsInfoPtr { + return this.#ptr; + } + + static async new(ptr: CoinsInfoPtr): Promise { + const instance = new CoinsInfo(ptr); + await instance.refresh(); + return instance; + } + + async refresh() { + const [hash, keyImage, blockHeight, amount, spent, spentHeight, frozen, unlocked] = await Promise.all([ + fns.CoinsInfo_hash(this.#ptr).then(readCString), + fns.CoinsInfo_keyImage(this.#ptr).then(readCString), + fns.CoinsInfo_blockHeight(this.#ptr), + fns.CoinsInfo_amount(this.#ptr), + fns.CoinsInfo_spent(this.#ptr), + fns.CoinsInfo_spentHeight(this.#ptr), + fns.CoinsInfo_frozen(this.#ptr), + fns.CoinsInfo_unlocked(this.#ptr), + ]); + + this.#hash = hash; + this.#keyImage = keyImage; + this.#blockHeight = blockHeight; + this.#amount = amount; + this.#spent = spent; + this.#spentHeight = spentHeight; + this.#frozen = frozen; + this.#unlocked = unlocked; + } + + get hash(): string | null { + return this.#hash; + } + + get keyImage(): string | null { + return this.#keyImage; + } + + get blockHeight(): bigint { + return this.#blockHeight; + } + + get amount(): bigint { + return this.#amount; + } + + get spent(): boolean { + return this.#spent; + } + + get spentHeight(): bigint { + return this.#spentHeight; + } + + get frozen(): boolean { + return this.#frozen; + } + + get unlocked(): boolean { + return this.#unlocked; + } +} diff --git a/impls/monero.ts/src/pending_transaction.ts b/impls/monero.ts/src/pending_transaction.ts index 169332f..d80b738 100644 --- a/impls/monero.ts/src/pending_transaction.ts +++ b/impls/monero.ts/src/pending_transaction.ts @@ -1,87 +1,84 @@ -import { CString, getSymbol, readCString, type Sanitizer } from "./utils.ts"; +import { fns } from "./bindings.ts"; +import { C_SEPARATOR, CString, maybeMultipleStrings, readCString } from "./utils.ts"; -export type PendingTransactionPtr = Deno.PointerObject<"transactionInfo">; +export type PendingTransactionPtr = Deno.PointerObject<"pendingTransaction">; -export class PendingTransaction { - #pendingTxPtr: PendingTransactionPtr; - sanitizer?: Sanitizer; +export class PendingTransaction { + #ptr: PendingTransactionPtr; - constructor(pendingTxPtr: PendingTransactionPtr, sanitizer?: Sanitizer) { - this.sanitizer = sanitizer; - this.#pendingTxPtr = pendingTxPtr; - } + #amount!: bigint; + #dust!: bigint; + #fee!: bigint; + #txid!: string | string[] | null; + #txCount!: bigint; - async status(): Promise { - return await getSymbol("PendingTransaction_status")(this.#pendingTxPtr); + constructor(ptr: PendingTransactionPtr) { + this.#ptr = ptr; } - async errorString(): Promise { - if (!await this.status()) return null; + static async new(ptr: PendingTransactionPtr): Promise { + const instance = new PendingTransaction(ptr); - const error = await getSymbol("PendingTransaction_errorString")(this.#pendingTxPtr); - if (!error) return null; + const [amount, dust, fee, txCount, txid] = await Promise.all([ + fns.PendingTransaction_amount(ptr), + fns.PendingTransaction_dust(ptr), + fns.PendingTransaction_fee(ptr), + fns.PendingTransaction_txCount(ptr), + fns.PendingTransaction_txid(ptr, C_SEPARATOR), + ]); - return await readCString(error) || null; - } + instance.#amount = amount; + instance.#dust = dust; + instance.#fee = fee; + instance.#txCount = txCount; + instance.#txid = maybeMultipleStrings(await readCString(txid)); - async throwIfError(sanitize = true): Promise { - const maybeError = await this.errorString(); - if (maybeError) { - if (sanitize) this.sanitizer?.(); - throw new Error(maybeError); - } + return instance; } - async commit(fileName: string, overwrite: boolean, sanitize = true): Promise { - const bool = await getSymbol("PendingTransaction_commit")( - this.#pendingTxPtr, - CString(fileName), - overwrite, - ); - await this.throwIfError(sanitize); - return bool; + get amount(): bigint { + return this.#amount; } - async commitUR(maxFragmentLength: number): Promise { - const commitUR = getSymbol("PendingTransaction_commitUR"); - - if (!commitUR) { - return null; - } + get dust(): bigint { + return this.#dust; + } - const result = await commitUR( - this.#pendingTxPtr, - maxFragmentLength, - ); + get fee(): bigint { + return this.#fee; + } - if (!result) return null; - await this.throwIfError(); - return await readCString(result) || null; + get txCount(): bigint { + return this.#txCount; } - async amount(): Promise { - return await getSymbol("PendingTransaction_amount")(this.#pendingTxPtr); + async commit(fileName: string, overwrite: boolean): Promise { + return await fns.PendingTransaction_commit(this.#ptr, CString(fileName), overwrite); } - async dust(): Promise { - return await getSymbol("PendingTransaction_dust")(this.#pendingTxPtr); + async commitUR(maxFragmentLength: number): Promise { + const commitUR = fns.PendingTransaction_commitUR; + if (!commitUR) return null; + + return await readCString( + await commitUR(this.#ptr, maxFragmentLength), + ); } - async fee(): Promise { - return await getSymbol("PendingTransaction_fee")(this.#pendingTxPtr); + async status(): Promise { + return await fns.PendingTransaction_status(this.#ptr); } - async txid(separator: string, sanitize = true): Promise { - const result = await getSymbol("PendingTransaction_txid")( - this.#pendingTxPtr, - CString(separator), - ); - if (!result) return null; - await this.throwIfError(sanitize); - return await readCString(result) || null; + async errorString(): Promise { + if (!await this.status()) return null; + const error = await fns.PendingTransaction_errorString(this.#ptr); + return await readCString(error); } - async txCount(): Promise { - return await getSymbol("PendingTransaction_txCount")(this.#pendingTxPtr); + async throwIfError(): Promise { + const maybeError = await this.errorString(); + if (maybeError) { + throw new Error(maybeError); + } } } diff --git a/impls/monero.ts/src/symbols.ts b/impls/monero.ts/src/symbols.ts index 38707b8..b2cf965 100644 --- a/impls/monero.ts/src/symbols.ts +++ b/impls/monero.ts/src/symbols.ts @@ -1,553 +1,2457 @@ export const moneroSymbols = { - "MONERO_WalletManagerFactory_getWalletManager": { + MONERO_PendingTransaction_status: { nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_errorString: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_commit: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "bool"] as [ + pendingTx_ptr: "pointer", + filename: "pointer", + overwrite: "bool", + ], + }, + MONERO_PendingTransaction_commitUR: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + pendingTx_ptr: "pointer", + max_fragment_length: "i32", + ], + }, + MONERO_PendingTransaction_amount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_dust: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_fee: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_txid: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_txCount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_subaddrAccount: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_subaddrIndices: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_multisigSignData: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_signMultisigTx: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + pendingTx_ptr: "pointer", + ], + }, + MONERO_PendingTransaction_signersKeys: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_hex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_PendingTransaction_txKey: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + pendingTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_status: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_errorString: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_amount: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_fee: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_mixin: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_confirmationMessage: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_paymentId: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_recipientAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_UnsignedTransaction_minMixinCount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_txCount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + unsignedTx_ptr: "pointer", + ], + }, + MONERO_UnsignedTransaction_sign: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + unsignedTx_ptr: "pointer", + signedFileName: "pointer", + ], + }, + MONERO_UnsignedTransaction_signUR: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + unsignedTx_ptr: "pointer", + max_fragment_length: "i32", + ], + }, + MONERO_TransactionInfo_direction: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_isPending: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_isFailed: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_isCoinbase: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_amount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_fee: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_blockHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_description: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_subaddrIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + txInfo_ptr: "pointer", + separator: "pointer", + ], + }, + MONERO_TransactionInfo_subaddrAccount: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_label: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_confirmations: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_unlockTime: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_hash: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_timestamp: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_paymentId: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_transfers_count: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + txInfo_ptr: "pointer", + ], + }, + MONERO_TransactionInfo_transfers_amount: { + nonblocking: true, + result: "u64", + parameters: ["pointer", "i32"] as [ + txInfo_ptr: "pointer", + index: "i32", + ], + }, + MONERO_TransactionInfo_transfers_address: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + txInfo_ptr: "pointer", + index: "i32", + ], + }, + MONERO_TransactionHistory_count: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + txHistory_ptr: "pointer", + ], + }, + MONERO_TransactionHistory_transaction: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + txHistory_ptr: "pointer", + index: "i32", + ], + }, + MONERO_TransactionHistory_transactionById: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + txHistory_ptr: "pointer", + id: "pointer", + ], + }, + MONERO_TransactionHistory_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + txHistory_ptr: "pointer", + ], + }, + MONERO_TransactionHistory_setTxNote: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer", "pointer"] as [ + txHistory_ptr: "pointer", + txid: "pointer", + note: "pointer", + ], + }, + MONERO_AddressBookRow_extra: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBookRow_getAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBookRow_getDescription: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBookRow_getPaymentId: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBookRow_getRowId: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + addressBookRow_ptr: "pointer", + ], + }, + MONERO_AddressBook_getAll_size: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + addressBook_ptr: "pointer", + ], + }, + MONERO_AddressBook_getAll_byIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + addressBook_ptr: "pointer", + index: "i32", + ], + }, + MONERO_AddressBook_addRow: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer", "pointer"] as [ + addressBook_ptr: "pointer", + dst_addr: "pointer", + payment_id: "pointer", + description: "pointer", + ], + }, + MONERO_AddressBook_deleteRow: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "usize"] as [ + addressBook_ptr: "pointer", + rowId: "usize", + ], + }, + MONERO_AddressBook_setDescription: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "usize", "pointer"] as [ + addressBook_ptr: "pointer", + rowId: "usize", + description: "pointer", + ], + }, + MONERO_AddressBook_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + addressBook_ptr: "pointer", + ], + }, + MONERO_AddressBook_errorString: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + addressBook_ptr: "pointer", + ], + }, + MONERO_AddressBook_errorCode: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + addressBook_ptr: "pointer", + ], + }, + MONERO_AddressBook_lookupPaymentID: { + nonblocking: true, + result: "i32", + parameters: ["pointer", "pointer"] as [ + addressBook_ptr: "pointer", + payment_id: "pointer", + ], + }, + MONERO_CoinsInfo_blockHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_hash: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_internalOutputIndex: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_globalOutputIndex: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_spent: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_frozen: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_spentHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_amount: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_rct: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_keyImageKnown: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_pkIndex: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_subaddrIndex: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_subaddrAccount: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_address: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_addressLabel: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_keyImage: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_unlockTime: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_unlocked: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_pubKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_coinbase: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_CoinsInfo_description: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + coinsInfo_ptr: "pointer", + ], + }, + MONERO_Coins_count: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + coins_ptr: "pointer", + ], + }, + MONERO_Coins_coin: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + coins_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Coins_getAll_size: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + coins_ptr: "pointer", + ], + }, + MONERO_Coins_getAll_byIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + coins_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Coins_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + coins_ptr: "pointer", + ], + }, + MONERO_Coins_setFrozenByPublicKey: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + coins_ptr: "pointer", + public_key: "pointer", + ], + }, + MONERO_Coins_setFrozen: { + nonblocking: true, + result: "void", + parameters: ["pointer", "i32"] as [ + coins_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Coins_thaw: { + nonblocking: true, + result: "void", + parameters: ["pointer", "i32"] as [ + coins_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Coins_thawByPublicKey: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + coins_ptr: "pointer", + public_key: "pointer", + ], + }, + MONERO_Coins_isTransferUnlocked: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "u64", "u64"] as [ + coins_ptr: "pointer", + unlockTime: "u64", + blockHeight: "u64", + ], + }, + MONERO_Coins_setDescription: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer", "pointer"] as [ + coins_ptr: "pointer", + public_key: "pointer", + description: "pointer", + ], + }, + MONERO_SubaddressRow_extra: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressRow_ptr: "pointer", + ], + }, + MONERO_SubaddressRow_getAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressRow_ptr: "pointer", + ], + }, + MONERO_SubaddressRow_getLabel: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressRow_ptr: "pointer", + ], + }, + MONERO_SubaddressRow_getRowId: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + subaddressRow_ptr: "pointer", + ], + }, + MONERO_Subaddress_getAll_size: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + subaddress_ptr: "pointer", + ], + }, + MONERO_Subaddress_getAll_byIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + subaddress_ptr: "pointer", + index: "i32", + ], + }, + MONERO_Subaddress_addRow: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "pointer"] as [ + subaddress_ptr: "pointer", + accountIndex: "u32", + label: "pointer", + ], + }, + MONERO_Subaddress_setLabel: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "u32", "pointer"] as [ + subaddress_ptr: "pointer", + accountIndex: "u32", + addressIndex: "u32", + label: "pointer", + ], + }, + MONERO_Subaddress_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32"] as [ + subaddress_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_SubaddressAccountRow_extra: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getLabel: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getBalance: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getUnlockedBalance: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccountRow_getRowId: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + subaddressAccountRow_ptr: "pointer", + ], + }, + MONERO_SubaddressAccount_getAll_size: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + subaddressAccount_ptr: "pointer", + ], + }, + MONERO_SubaddressAccount_getAll_byIndex: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + subaddressAccount_ptr: "pointer", + index: "i32", + ], + }, + MONERO_SubaddressAccount_addRow: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + subaddressAccount_ptr: "pointer", + label: "pointer", + ], + }, + MONERO_SubaddressAccount_setLabel: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "pointer"] as [ + subaddressAccount_ptr: "pointer", + accountIndex: "u32", + label: "pointer", + ], + }, + MONERO_SubaddressAccount_refresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + subaddressAccount_ptr: "pointer", + ], + }, + MONERO_MultisigState_isMultisig: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + multisigState_ptr: "pointer", + ], + }, + MONERO_MultisigState_isReady: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + multisigState_ptr: "pointer", + ], + }, + MONERO_MultisigState_threshold: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + multisigState_ptr: "pointer", + ], + }, + MONERO_MultisigState_total: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + multisigState_ptr: "pointer", + ], + }, + MONERO_DeviceProgress_progress: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + deviceProgress_ptr: "pointer", + ], + }, + MONERO_DeviceProgress_indeterminate: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + deviceProgress_ptr: "pointer", + ], + }, + MONERO_Wallet_seed: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + seed_offset: "pointer", + ], + }, + MONERO_Wallet_getSeedLanguage: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setSeedLanguage: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + arg: "pointer", + ], + }, + MONERO_Wallet_status: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_errorString: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setPassword: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + password: "pointer", + ], + }, + MONERO_Wallet_getPassword: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setDevicePin: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + pin: "pointer", + ], + }, + MONERO_Wallet_setDevicePassphrase: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + passphrase: "pointer", + ], + }, + MONERO_Wallet_address: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "u64", "u64"] as [ + wallet_ptr: "pointer", + accountIndex: "u64", + addressIndex: "u64", + ], + }, + MONERO_Wallet_path: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_nettype: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_integratedAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + payment_id: "pointer", + ], + }, + MONERO_Wallet_secretViewKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_publicViewKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_secretSpendKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_publicSpendKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_publicMultisigSignerKey: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_stop: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_store: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + path: "pointer", + ], + }, + MONERO_Wallet_filename: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_keysFilename: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_init: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "u64", "pointer", "pointer", "bool", "bool", "pointer"] as [ + wallet_ptr: "pointer", + daemon_address: "pointer", + upper_transaction_size_limit: "u64", + daemon_username: "pointer", + daemon_password: "pointer", + use_ssl: "bool", + lightWallet: "bool", + proxy_address: "pointer", + ], + }, + MONERO_Wallet_createWatchOnly: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + path: "pointer", + password: "pointer", + language: "pointer", + ], + }, + MONERO_Wallet_setRefreshFromBlockHeight: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u64"] as [ + wallet_ptr: "pointer", + refresh_from_block_height: "u64", + ], + }, + MONERO_Wallet_getRefreshFromBlockHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setRecoveringFromSeed: { + nonblocking: true, + result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + recoveringFromSeed: "bool", + ], + }, + MONERO_Wallet_setRecoveringFromDevice: { + nonblocking: true, + result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + recoveringFromDevice: "bool", + ], + }, + MONERO_Wallet_setSubaddressLookahead: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "u32"] as [ + wallet_ptr: "pointer", + major: "u32", + minor: "u32", + ], + }, + MONERO_Wallet_connectToDaemon: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_connected: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setTrustedDaemon: { + nonblocking: true, + result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + arg: "bool", + ], + }, + MONERO_Wallet_trustedDaemon: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setProxy: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + address: "pointer", + ], + }, + MONERO_Wallet_balance: { + nonblocking: true, + result: "u64", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_Wallet_unlockedBalance: { + nonblocking: true, + result: "u64", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_Wallet_viewOnlyBalance: { + nonblocking: true, + result: "u64", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_Wallet_watchOnly: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_isDeterministic: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_blockChainHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_approximateBlockChainHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_estimateBlockChainHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_daemonBlockChainHeight: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + 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", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_synchronized: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_displayAmount: { + nonblocking: true, + result: "pointer", + parameters: ["u64"] as [ + amount: "u64", + ], + }, + MONERO_Wallet_amountFromString: { + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + amount: "pointer", + ], + }, + MONERO_Wallet_amountFromDouble: { + nonblocking: true, + result: "u64", + parameters: ["f64"] as [ + amount: "f64", + ], + }, + MONERO_Wallet_genPaymentId: { + nonblocking: true, + result: "pointer", + parameters: [], + }, + MONERO_Wallet_paymentIdValid: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + paiment_id: "pointer", + ], + }, + MONERO_Wallet_addressValid: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "i32"] as [ + str: "pointer", + nettype: "i32", + ], + }, + MONERO_Wallet_keyValid: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "bool", "i32"] as [ + secret_key_string: "pointer", + address_string: "pointer", + isViewKey: "bool", + nettype: "i32", + ], + }, + MONERO_Wallet_keyValid_error: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "bool", "i32"] as [ + secret_key_string: "pointer", + address_string: "pointer", + isViewKey: "bool", + nettype: "i32", + ], + }, + MONERO_Wallet_paymentIdFromAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "i32"] as [ + strarg: "pointer", + nettype: "i32", + ], + }, + MONERO_Wallet_maximumAllowedAmount: { + nonblocking: true, + result: "u64", parameters: [], - // void* + }, + MONERO_Wallet_init3: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer", "pointer", "pointer", "bool"] as [ + wallet_ptr: "pointer", + argv0: "pointer", + default_log_base_name: "pointer", + log_path: "pointer", + console: "bool", + ], + }, + MONERO_Wallet_getPolyseed: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + passphrase: "pointer", + ], + }, + MONERO_Wallet_createPolyseed: { + nonblocking: true, + result: "pointer", + parameters: ["pointer"] as [ + language: "pointer", + ], + }, + MONERO_Wallet_startRefresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_pauseRefresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_refresh: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_refreshAsync: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_rescanBlockchain: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_rescanBlockchainAsync: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setAutoRefreshInterval: { + nonblocking: true, + result: "void", + parameters: ["pointer", "i32"] as [ + wallet_ptr: "pointer", + millis: "i32", + ], + }, + MONERO_Wallet_autoRefreshInterval: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_addSubaddressAccount: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + label: "pointer", + ], + }, + MONERO_Wallet_numSubaddressAccounts: { + nonblocking: true, + result: "usize", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_numSubaddresses: { + nonblocking: true, + result: "usize", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + ], + }, + MONERO_Wallet_addSubaddress: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "pointer"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + label: "pointer", + ], + }, + MONERO_Wallet_getSubaddressLabel: { + nonblocking: true, result: "pointer", + parameters: ["pointer", "u32", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + addressIndex: "u32", + ], }, - - //#region WalletManager - "MONERO_WalletManager_createWallet": { + MONERO_Wallet_setSubaddressLabel: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32", "u32", "pointer"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + addressIndex: "u32", + label: "pointer", + ], + }, + MONERO_Wallet_multisig: { + optional: true, nonblocking: true, - // void* wm_ptr, const char* path, const char* password, const char* language, int networkType - parameters: ["pointer", "pointer", "pointer", "pointer", "i32"], - // void* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_WalletManager_openWallet": { + MONERO_Wallet_getMultisigInfo: { nonblocking: true, - // void* wm_ptr, const char* path, const char* password, int networkType - "parameters": ["pointer", "pointer", "pointer", "i32"], - // void* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_WalletManager_recoveryWallet": { + MONERO_Wallet_makeMultisig: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "u32"] as [ + wallet_ptr: "pointer", + info: "pointer", + info_separator: "pointer", + threshold: "u32", + ], + }, + MONERO_Wallet_exchangeMultisigKeys: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "bool"] as [ + wallet_ptr: "pointer", + info: "pointer", + info_separator: "pointer", + force_update_use_with_caution: "bool", + ], + }, + MONERO_Wallet_exportMultisigImages: { + optional: true, nonblocking: true, - // void* wm_ptr, const char* path, const char* password, const char* mnemonic, - // int networkType, uint64_t restoreHeight, uint64_t kdfRounds, const char* seedOffset - parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "u64", "pointer"], - // void* result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + separator: "pointer", + ], }, - "MONERO_WalletManager_blockchainHeight": { + MONERO_Wallet_importMultisigImages: { + optional: true, nonblocking: true, - // void* wm_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "usize", + parameters: ["pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + info: "pointer", + info_separator: "pointer", + ], }, - "MONERO_WalletManager_blockchainTargetHeight": { + MONERO_Wallet_hasMultisigPartialKeyImages: { + optional: true, nonblocking: true, - // void* wm_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "usize", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_WalletManager_setDaemonAddress": { + MONERO_Wallet_restoreMultisigTransaction: { + optional: true, nonblocking: true, - // void* wm_ptr, const char* address - parameters: ["pointer", "pointer"], - // void - result: "void", + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + signData: "pointer", + ], }, - //#endregion - - //#region Wallet - "MONERO_Wallet_init": { + MONERO_Wallet_createTransactionMultDest: { nonblocking: true, - // 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 - parameters: ["pointer", "pointer", "u64", "pointer", "pointer", "bool", "bool", "pointer"], - // bool - result: "bool", + result: "pointer", + parameters: [ + "pointer", + "pointer", + "pointer", + "pointer", + "bool", + "pointer", + "pointer", + "u32", + "i32", + "u32", + "pointer", + "pointer", + ] as [ + wallet_ptr: "pointer", + dst_addr_list: "pointer", + dst_addr_list_separator: "pointer", + payment_id: "pointer", + amount_sweep_all: "bool", + amount_list: "pointer", + amount_list_separator: "pointer", + mixin_count: "u32", + pendingTransactionPriority: "i32", + subaddr_account: "u32", + preferredInputs: "pointer", + preferredInputs_separator: "pointer", + ], + }, + MONERO_Wallet_createTransaction: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "u64", "u32", "i32", "u32", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + dst_addr: "pointer", + payment_id: "pointer", + amount: "u64", + mixin_count: "u32", + pendingTransactionPriority: "i32", + subaddr_account: "u32", + preferredInputs: "pointer", + separator: "pointer", + ], + }, + MONERO_Wallet_loadUnsignedTx: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + fileName: "pointer", + ], }, - "MONERO_Wallet_init3": { + MONERO_Wallet_loadUnsignedTxUR: { + optional: true, nonblocking: true, - // void* wallet_ptr, const char* argv0, const char* default_log_base_name, - // const char* log_path, bool console - parameters: ["pointer", "pointer", "pointer", "pointer", "bool"], - // void - result: "void", + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + input: "pointer", + ], }, - "MONERO_Wallet_setTrustedDaemon": { + MONERO_Wallet_submitTransaction: { nonblocking: true, - // void* wallet_ptr, bool arg - parameters: ["pointer", "bool"], - // void - result: "void", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + fileName: "pointer", + ], }, - "MONERO_Wallet_startRefresh": { + MONERO_Wallet_submitTransactionUR: { + optional: true, nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // void - result: "void", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + input: "pointer", + ], }, - "MONERO_Wallet_refreshAsync": { + MONERO_Wallet_hasUnknownKeyImages: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // void - result: "void", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_blockChainHeight": { + MONERO_Wallet_exportKeyImages: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer", "pointer", "bool"] as [ + wallet_ptr: "pointer", + filename: "pointer", + all: "bool", + ], }, - "MONERO_Wallet_daemonBlockChainHeight": { + MONERO_Wallet_exportKeyImagesUR: { + optional: true, nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "pointer", + parameters: ["pointer", "usize", "bool"] as [ + wallet_ptr: "pointer", + max_fragment_length: "usize", + all: "bool", + ], + }, + MONERO_Wallet_importKeyImages: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + filename: "pointer", + ], }, - "MONERO_Wallet_synchronized": { + MONERO_Wallet_importKeyImagesUR: { + optional: true, nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // bool result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + input: "pointer", + ], }, - "MONERO_Wallet_store": { + MONERO_Wallet_exportOutputs: { nonblocking: true, - // void* wallet_ptr, const char* path - parameters: ["pointer", "pointer"], - // bool result: "bool", + parameters: ["pointer", "pointer", "bool"] as [ + wallet_ptr: "pointer", + filename: "pointer", + all: "bool", + ], }, - "MONERO_Wallet_address": { + MONERO_Wallet_exportOutputsUR: { + optional: true, nonblocking: true, - // void* wallet_ptr, uint64_t accountIndex, uint64_t addressIndex - parameters: ["pointer", "u64", "u64"], - // char* result: "pointer", + parameters: ["pointer", "usize", "bool"] as [ + wallet_ptr: "pointer", + max_fragment_length: "usize", + all: "bool", + ], }, - "MONERO_Wallet_balance": { + MONERO_Wallet_importOutputs: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex - parameters: ["pointer", "u32"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + filename: "pointer", + ], }, - "MONERO_Wallet_unlockedBalance": { + MONERO_Wallet_importOutputsUR: { + optional: true, nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex - parameters: ["pointer", "u32"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + input: "pointer", + ], }, - "MONERO_Wallet_addSubaddressAccount": { + MONERO_Wallet_setupBackgroundSync: { nonblocking: true, - // void* wallet_ptr, const char* label - parameters: ["pointer", "pointer"], - // void - result: "void", + result: "bool", + parameters: ["pointer", "i32", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + background_sync_type: "i32", + wallet_password: "pointer", + background_cache_password: "pointer", + ], }, - "MONERO_Wallet_numSubaddressAccounts": { + MONERO_Wallet_getBackgroundSyncType: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // size_t - result: "usize", + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_addSubaddress": { + MONERO_Wallet_startBackgroundSync: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex, const char* label - parameters: ["pointer", "u32", "pointer"], - // void - result: "void", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_numSubaddresses": { + MONERO_Wallet_stopBackgroundSync: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex - parameters: ["pointer", "u32"], - // size_t - result: "usize", + result: "bool", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + wallet_password: "pointer", + ], + }, + MONERO_Wallet_isBackgroundSyncing: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_getSubaddressLabel": { + MONERO_Wallet_isBackgroundWallet: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_history: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex - parameters: ["pointer", "u32", "u32"], - // const char* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_setSubaddressLabel": { + MONERO_Wallet_addressBook: { nonblocking: true, - // void* wallet_ptr, uint32_t accountIndex, uint32_t addressIndex, const char* label - parameters: ["pointer", "u32", "u32", "pointer"], - // void - result: "void", + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_status": { + MONERO_Wallet_coins: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // int - result: "i32", + result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_errorString": { + MONERO_Wallet_subaddress: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // char* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_Wallet_history": { + MONERO_Wallet_subaddressAccount: { nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // void* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_defaultMixin: { + nonblocking: true, + result: "u32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setDefaultMixin: { + nonblocking: true, + result: "void", + parameters: ["pointer", "u32"] as [ + wallet_ptr: "pointer", + arg: "u32", + ], + }, + MONERO_Wallet_setCacheAttribute: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + key: "pointer", + val: "pointer", + ], }, - "MONERO_Wallet_createTransaction": { + MONERO_Wallet_getCacheAttribute: { nonblocking: true, - // void* wallet_ptr, const char* dst_addr, const char* payment_id - // uint64_t amount, uint32_t mixin_count, int pendingTransactionPriority, - // uint32_t subaddr_account, const char* preferredInputs, const char* separator - parameters: ["pointer", "pointer", "pointer", "u64", "u32", "i32", "u32", "pointer", "pointer"], - // void* result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + key: "pointer", + ], }, - "MONERO_Wallet_amountFromString": { + MONERO_Wallet_setUserNote: { nonblocking: true, - // const char* amount - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + txid: "pointer", + note: "pointer", + ], }, - //#endregion - - //#region TransactionHistory - "MONERO_TransactionHistory_count": { + MONERO_Wallet_getUserNote: { nonblocking: true, - // void* txHistory_ptr - parameters: ["pointer"], - // int - result: "i32", + result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + txid: "pointer", + ], }, - "MONERO_TransactionHistory_transaction": { + MONERO_Wallet_getTxKey: { nonblocking: true, - // void* txHistory_ptr, int index - parameters: ["pointer", "i32"], - // void* result: "pointer", + parameters: ["pointer", "pointer"] as [ + wallet_ptr: "pointer", + txid: "pointer", + ], }, - "MONERO_TransactionHistory_transactionById": { + MONERO_Wallet_signMessage: { nonblocking: true, - // void* txHistory_ptr, const char* id - parameters: ["pointer", "pointer"], - // void* result: "pointer", + parameters: ["pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + message: "pointer", + address: "pointer", + ], + }, + MONERO_Wallet_verifySignedMessage: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer", "pointer"] as [ + wallet_ptr: "pointer", + message: "pointer", + address: "pointer", + signature: "pointer", + ], }, - "MONERO_TransactionHistory_refresh": { + MONERO_Wallet_rescanSpent: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_setOffline: { nonblocking: true, - // void* txHistory_ptr - parameters: ["pointer"], - // void result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + offline: "bool", + ], + }, + MONERO_Wallet_isOffline: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionHistory_setTxNote": { + MONERO_Wallet_segregatePreForkOutputs: { nonblocking: true, - // void* txHistory_ptr, const char* txid, const char* note - parameters: ["pointer", "pointer", "pointer"], - // void result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + segregate: "bool", + ], }, - //#endregion - - //#region TransactionInfo - "MONERO_TransactionInfo_direction": { + MONERO_Wallet_segregationHeight: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // int - result: "i32", + result: "void", + parameters: ["pointer", "u64"] as [ + wallet_ptr: "pointer", + height: "u64", + ], + }, + MONERO_Wallet_keyReuseMitigation2: { + nonblocking: true, + result: "void", + parameters: ["pointer", "bool"] as [ + wallet_ptr: "pointer", + mitigation: "bool", + ], }, - "MONERO_TransactionInfo_isPending": { + MONERO_Wallet_lockKeysFile: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // bool result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_isFailed": { + MONERO_Wallet_unlockKeysFile: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // bool result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_isCoinbase": { + MONERO_Wallet_isKeysFileLocked: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // bool result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_amount": { + MONERO_Wallet_getDeviceType: { + nonblocking: true, + result: "i32", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_Wallet_coldKeyImageSync: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer", "u64", "u64"] as [ + wallet_ptr: "pointer", + spent: "u64", + unspent: "u64", + ], + }, + MONERO_Wallet_deviceShowAddress: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "u32", "u32"] as [ + wallet_ptr: "pointer", + accountIndex: "u32", + addressIndex: "u32", + ], + }, + MONERO_Wallet_reconnectDevice: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_fee": { + MONERO_Wallet_getBytesReceived: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_blockHeight": { + MONERO_Wallet_getBytesSent: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_description": { + MONERO_Wallet_getStateIsConnected: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* - result: "pointer", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_subaddrIndex": { + MONERO_Wallet_getSendToDevice: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_subaddrAccount": { + MONERO_Wallet_getSendToDeviceLength: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint32_t - result: "u32", + result: "usize", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_label": { + MONERO_Wallet_getReceivedFromDevice: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_confirmations": { + MONERO_Wallet_getReceivedFromDeviceLength: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "usize", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_unlockTime": { + MONERO_Wallet_getWaitsForDeviceSend: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_hash": { + MONERO_Wallet_getWaitsForDeviceReceive: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* - result: "pointer", + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], }, - "MONERO_TransactionInfo_timestamp": { + MONERO_Wallet_setDeviceReceivedData: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // uint64_t - result: "u64", + result: "void", + parameters: ["pointer", "pointer", "usize"] as [ + wallet_ptr: "pointer", + data: "pointer", + len: "usize", + ], }, - "MONERO_TransactionInfo_paymentId": { + MONERO_Wallet_setDeviceSendData: { + optional: true, nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // const char* - result: "pointer", + result: "void", + parameters: ["pointer", "pointer", "usize"] as [ + wallet_ptr: "pointer", + data: "pointer", + len: "usize", + ], }, - "MONERO_TransactionInfo_transfers_count": { + MONERO_WalletManager_createWallet: { nonblocking: true, - // void* txInfo_ptr - parameters: ["pointer"], - // int - result: "i32", + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "pointer", "i32"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + language: "pointer", + networkType: "i32", + ], }, - "MONERO_TransactionInfo_transfers_amount": { + MONERO_WalletManager_openWallet: { nonblocking: true, - // void* txInfo_ptr, int index - parameters: ["pointer", "i32"], - // uint64_t - result: "u64", + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "i32"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + networkType: "i32", + ], }, - "MONERO_TransactionInfo_transfers_address": { + MONERO_WalletManager_recoveryWallet: { nonblocking: true, - // void* txInfo_ptr, int index - parameters: ["pointer", "i32"], - // const char* result: "pointer", - }, - //#endregion - - //#region PendingTransaction - "MONERO_PendingTransaction_status": { + parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "u64", "pointer"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + mnemonic: "pointer", + networkType: "i32", + restoreHeight: "u64", + kdfRounds: "u64", + seedOffset: "pointer", + ], + }, + MONERO_WalletManager_createWalletFromKeys: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // int - result: "i32", - }, - "MONERO_PendingTransaction_errorString": { + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "pointer", "pointer", "pointer", "u64"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + language: "pointer", + nettype: "i32", + restoreHeight: "u64", + addressString: "pointer", + viewKeyString: "pointer", + spendKeyString: "pointer", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_createWalletFromDevice: { + optional: true, + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "i32", "pointer", "u64", "pointer", "pointer", "pointer", "u64"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + nettype: "i32", + deviceName: "pointer", + restoreHeight: "u64", + subaddressLookahead: "pointer", + viewKeyString: "pointer", + spendKeyString: "pointer", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_createDeterministicWalletFromSpendKey: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: ["pointer", "pointer", "pointer", "pointer", "i32", "u64", "pointer", "u64"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + language: "pointer", + nettype: "i32", + restoreHeight: "u64", + spendKeyString: "pointer", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_createWalletFromPolyseed: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer", "i32", "pointer", "pointer", "bool", "u64", "u64"] as [ + wm_ptr: "pointer", + path: "pointer", + password: "pointer", + nettype: "i32", + mnemonic: "pointer", + passphrase: "pointer", + newWallet: "bool", + restore_height: "u64", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_closeWallet: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "bool"] as [ + wm_ptr: "pointer", + wallet_ptr: "pointer", + store: "bool", + ], }, - "MONERO_PendingTransaction_commit": { + MONERO_WalletManager_walletExists: { nonblocking: true, - // void* pendingTx_ptr, const char* filename, bool overwrite - parameters: ["pointer", "pointer", "bool"], - // bool result: "bool", + parameters: ["pointer", "pointer"] as [ + wm_ptr: "pointer", + path: "pointer", + ], }, - "MONERO_PendingTransaction_commitUR": { + MONERO_WalletManager_verifyWalletPassword: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "pointer", "bool", "u64"] as [ + wm_ptr: "pointer", + keys_file_name: "pointer", + password: "pointer", + no_spend_key: "bool", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_queryWalletDevice: { optional: true, nonblocking: true, - // void* pendingTx_ptr, int max_fragment_length - parameters: ["pointer", "i32"], - // const char* + result: "i32", + parameters: ["pointer", "pointer", "pointer", "u64"] as [ + wm_ptr: "pointer", + keys_file_name: "pointer", + password: "pointer", + kdf_rounds: "u64", + ], + }, + MONERO_WalletManager_findWallets: { + nonblocking: true, + result: "pointer", + parameters: ["pointer", "pointer", "pointer"] as [ + wm_ptr: "pointer", + path: "pointer", + separator: "pointer", + ], + }, + MONERO_WalletManager_errorString: { + nonblocking: true, result: "pointer", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], + }, + MONERO_WalletManager_setDaemonAddress: { + nonblocking: true, + result: "void", + parameters: ["pointer", "pointer"] as [ + wm_ptr: "pointer", + address: "pointer", + ], + }, + MONERO_WalletManager_setProxy: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wm_ptr: "pointer", + address: "pointer", + ], }, - "MONERO_PendingTransaction_amount": { + MONERO_WalletManager_blockchainHeight: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_dust": { + MONERO_WalletManager_blockchainTargetHeight: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_fee": { + MONERO_WalletManager_networkDifficulty: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_txid": { + MONERO_WalletManager_miningHashRate: { nonblocking: true, - // void* pendingTx_ptr, const char* separator - parameters: ["pointer", "pointer"], - // const char* - result: "pointer", + result: "f64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_txCount": { + MONERO_WalletManager_blockTarget: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // uint64_t result: "u64", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], }, - "MONERO_PendingTransaction_subaddrAccount": { + MONERO_WalletManager_isMining: { nonblocking: true, - // void* pendingTx_ptr, const char* separator - parameters: ["pointer", "pointer"], - // const char* - result: "pointer", + result: "bool", + parameters: ["pointer"] as [ + wm_ptr: "pointer", + ], + }, + MONERO_WalletManager_startMining: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer", "u32", "bool", "bool"] as [ + wm_ptr: "pointer", + address: "pointer", + threads: "u32", + backgroundMining: "bool", + ignoreBattery: "bool", + ], }, - "MONERO_PendingTransaction_subaddrIndices": { + MONERO_WalletManager_stopMining: { + nonblocking: true, + result: "bool", + parameters: ["pointer", "pointer"] as [ + wm_ptr: "pointer", + address: "pointer", + ], + }, + MONERO_WalletManager_resolveOpenAlias: { nonblocking: true, - // void* pendingTx_ptr, const char* separator - parameters: ["pointer", "pointer"], - // const char* result: "pointer", + parameters: ["pointer", "pointer", "bool"] as [ + wm_ptr: "pointer", + address: "pointer", + dnssec_valid: "bool", + ], }, - "MONERO_PendingTransaction_multisigSignData": { + MONERO_WalletManagerFactory_getWalletManager: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: [], + }, + MONERO_WalletManagerFactory_setLogLevel: { + nonblocking: true, + result: "void", + parameters: ["i32"] as [ + level: "i32", + ], + }, + MONERO_WalletManagerFactory_setLogCategories: { + optional: true, + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + categories: "pointer", + ], }, - "MONERO_PendingTransaction_signMultisigTx": { + MONERO_DEBUG_test0: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // void result: "void", + parameters: [], + }, + MONERO_DEBUG_test1: { + nonblocking: true, + result: "bool", + parameters: ["bool"] as [ + x: "bool", + ], + }, + MONERO_DEBUG_test2: { + nonblocking: true, + result: "i32", + parameters: ["i32"] as [ + x: "i32", + ], + }, + MONERO_DEBUG_test3: { + nonblocking: true, + result: "u64", + parameters: ["u64"] as [ + x: "u64", + ], }, - "MONERO_PendingTransaction_signersKeys": { + MONERO_DEBUG_test4: { nonblocking: true, - // void* pendingTx_ptr - parameters: ["pointer"], - // const char* result: "pointer", + parameters: ["u64"] as [ + x: "u64", + ], }, - "MONERO_PendingTransaction_hex": { + MONERO_DEBUG_test5: { nonblocking: true, - // void* pendingTx_ptr, const char* separator - parameters: ["pointer", "pointer"], - // const char* result: "pointer", + parameters: [], }, - //#endregion - - //#region Checksum - "MONERO_checksum_wallet2_api_c_h": { - optional: true, + MONERO_DEBUG_test5_std: { nonblocking: true, + result: "pointer", parameters: [], - // const char* + }, + MONERO_DEBUG_isPointerNull: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_cw_getWalletListener: { + nonblocking: true, result: "pointer", + parameters: ["pointer"] as [ + wallet_ptr: "pointer", + ], + }, + MONERO_cw_WalletListener_resetNeedToRefresh: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], + }, + MONERO_cw_WalletListener_isNeedToRefresh: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], + }, + MONERO_cw_WalletListener_isNewTransactionExist: { + nonblocking: true, + result: "bool", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], + }, + MONERO_cw_WalletListener_resetIsNewTransactionExist: { + nonblocking: true, + result: "void", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], }, - "MONERO_checksum_wallet2_api_c_cpp": { + MONERO_cw_WalletListener_height: { + optional: true, + nonblocking: true, + result: "u64", + parameters: ["pointer"] as [ + cw_walletListener_ptr: "pointer", + ], + }, + MONERO_checksum_wallet2_api_c_h: { optional: true, nonblocking: true, - parameters: [], - // const char* result: "pointer", + parameters: [], }, - "MONERO_checksum_wallet2_api_c_exp": { + MONERO_checksum_wallet2_api_c_cpp: { optional: true, nonblocking: true, + result: "pointer", parameters: [], - // const char* + }, + MONERO_checksum_wallet2_api_c_exp: { + optional: true, + nonblocking: true, result: "pointer", + parameters: [], }, - //#endregion - - "MONERO_free": { + MONERO_free: { nonblocking: true, - // void* ptr - parameters: ["pointer"], - // void result: "void", + parameters: ["pointer"] as [ + ptr: "pointer", + ], }, } as const; -type MoneroSymbols = typeof moneroSymbols; -export type MoneroTsDylib = Deno.DynamicLibrary; +export type MoneroSymbols = typeof moneroSymbols; type ReplaceMonero = T extends `MONERO${infer Y}` ? `WOWNERO${Y}` : never; +export type WowneroSymbols = { [Key in keyof MoneroSymbols as ReplaceMonero]: MoneroSymbols[Key] }; -type WowneroSymbols = { [Key in keyof MoneroSymbols as ReplaceMonero]: MoneroSymbols[Key] }; +export type SymbolName = keyof MoneroSymbols extends `MONERO_${infer SymbolName}` ? SymbolName : never; export const wowneroSymbols = Object.fromEntries( Object.entries(moneroSymbols).map(([key, value]) => [key.replace("MONERO", "WOWNERO"), value]), ) as WowneroSymbols; - -export type WowneroTsDylib = Deno.DynamicLibrary; - -export type Dylib = MoneroTsDylib | WowneroTsDylib; diff --git a/impls/monero.ts/src/transaction_history.ts b/impls/monero.ts/src/transaction_history.ts index aab64da..cec3fde 100644 --- a/impls/monero.ts/src/transaction_history.ts +++ b/impls/monero.ts/src/transaction_history.ts @@ -1,34 +1,41 @@ +import { fns } from "./bindings.ts"; import { TransactionInfo, TransactionInfoPtr } from "./transaction_info.ts"; -import { CString, getSymbol } from "./utils.ts"; +import { CString } from "./utils.ts"; export type TransactionHistoryPtr = Deno.PointerObject<"transactionHistory">; export class TransactionHistory { - #txHistoryPtr: TransactionHistoryPtr; + #ptr: TransactionHistoryPtr; - constructor(txHistoryPtr: TransactionHistoryPtr) { - this.#txHistoryPtr = txHistoryPtr; + #count!: number; + + constructor(ptr: TransactionHistoryPtr) { + this.#ptr = ptr; + } + + static async new(ptr: TransactionHistoryPtr) { + const instance = new TransactionHistory(ptr); + instance.#count = await fns.TransactionHistory_count(ptr); + return instance; } - async count(): Promise { - return await getSymbol("TransactionHistory_count")(this.#txHistoryPtr); + get count(): number { + return this.#count; } async transaction(index: number): Promise { - return new TransactionInfo( - ( - await getSymbol("TransactionHistory_transaction")(this.#txHistoryPtr, index) - ) as TransactionInfoPtr, + return TransactionInfo.new( + await fns.TransactionHistory_transaction(this.#ptr, index) as TransactionInfoPtr, ); } async refresh(): Promise { - await getSymbol("TransactionHistory_refresh")(this.#txHistoryPtr); + await fns.TransactionHistory_refresh(this.#ptr); } async setTxNote(transactionId: string, note: string): Promise { - await getSymbol("TransactionHistory_setTxNote")( - this.#txHistoryPtr, + await fns.TransactionHistory_setTxNote( + this.#ptr, CString(transactionId), CString(note), ); diff --git a/impls/monero.ts/src/transaction_info.ts b/impls/monero.ts/src/transaction_info.ts index 22ea0e7..9becfb9 100644 --- a/impls/monero.ts/src/transaction_info.ts +++ b/impls/monero.ts/src/transaction_info.ts @@ -1,104 +1,147 @@ -import { dylib } from "./bindings.ts"; -import { getSymbol, readCString, Sanitizer } from "./utils.ts"; +import { fns } from "./bindings.ts"; +import { C_SEPARATOR, CString, maybeMultipleStrings, readCString, SEPARATOR } from "./utils.ts"; -export type TransactionInfoPtr = Deno.PointerObject<"transactionInfo">; +export type TransactionInfoPtr = Deno.PointerObject<"pendingTransaction">; -export class TransactionInfo { - #txInfoPtr: TransactionInfoPtr; - sanitizer?: Sanitizer; - - constructor(txInfoPtr: TransactionInfoPtr, sanitizer?: Sanitizer) { - this.#txInfoPtr = txInfoPtr; - this.sanitizer = sanitizer; - } +export interface TransferData { + address: string | null; + amount: bigint; +} - async direction(): Promise<"in" | "out"> { - switch (await getSymbol("TransactionInfo_direction")(this.#txInfoPtr)) { - case 0: - return "in"; - case 1: - return "out"; - default: - await this.sanitizer?.(); - throw new Error("Invalid TransactionInfo direction"); +export class TransactionInfo { + #ptr: TransactionInfoPtr; + + #amount!: bigint; + #fee!: bigint; + #timestamp!: bigint; + #transfersCount!: number; + #paymentId!: string | null; + #hash!: string | null; + + #subaddrAccount!: number; + #subaddrIndex!: string | null; + + #transfers!: readonly TransferData[]; + + constructor(ptr: TransactionInfoPtr) { + this.#ptr = ptr; + } + + static async new(ptr: TransactionInfoPtr): Promise { + const instance = new TransactionInfo(ptr); + + const [amount, paymentId, fee, hash, subaddrIndex, subaddrAccount, timestamp, transfersCount] = await Promise.all([ + fns.TransactionInfo_amount(ptr), + fns.TransactionInfo_paymentId(ptr).then(readCString), + fns.TransactionInfo_fee(ptr), + fns.TransactionInfo_hash(ptr).then(readCString), + fns.TransactionInfo_subaddrIndex(ptr, C_SEPARATOR).then(readCString), + fns.TransactionInfo_subaddrAccount(ptr), + fns.TransactionInfo_timestamp(ptr), + fns.TransactionInfo_transfers_count(ptr), + ]); + + instance.#amount = amount; + instance.#fee = fee; + instance.#timestamp = timestamp; + instance.#transfersCount = transfersCount; + instance.#paymentId = paymentId; + instance.#hash = hash; + + instance.#subaddrAccount = subaddrAccount; + instance.#subaddrIndex = subaddrIndex; + + const transfers = []; + for (let i = 0; i < transfersCount; ++i) { + const [amount, address] = await Promise.all([ + fns.TransactionInfo_transfers_amount(ptr, i), + fns.TransactionInfo_transfers_address(ptr, i).then(readCString), + ]); + + transfers.push({ amount, address }); } - } + Object.freeze(transfers); + instance.#transfers = transfers; - async isPending(): Promise { - return await getSymbol("TransactionInfo_isPending")(this.#txInfoPtr); + return instance; } - async isFailed(): Promise { - return await getSymbol("TransactionInfo_isFailed")(this.#txInfoPtr); + get amount(): bigint { + return this.#amount; } - async isCoinbase(): Promise { - return await getSymbol("TransactionInfo_isCoinbase")(this.#txInfoPtr); + get fee(): bigint { + return this.#fee; } - async amount(): Promise { - return await getSymbol("TransactionInfo_amount")(this.#txInfoPtr); + get timestamp(): bigint { + return this.#timestamp; } - async fee(): Promise { - return await getSymbol("TransactionInfo_fee")(this.#txInfoPtr); + get transfersCount(): number { + return this.#transfersCount; } - async blockHeight(): Promise { - return await getSymbol("TransactionInfo_blockHeight")(this.#txInfoPtr); + get paymentId(): string | null { + return this.#paymentId; } - async description(): Promise { - const description = await getSymbol("TransactionInfo_description")(this.#txInfoPtr); - return await readCString(description) || ""; + get hash(): string | null { + return this.#hash; } - async subaddrIndex(): Promise { - const subaddrIndex = await getSymbol("TransactionInfo_subaddrIndex")(this.#txInfoPtr); - return await readCString(subaddrIndex) || ""; + get subaddrAccount(): number { + return this.#subaddrAccount; } - async subaddrAccount(): Promise { - return await getSymbol("TransactionInfo_subaddrAccount")(this.#txInfoPtr); + get subaddrIndex(): string | null { + return this.#subaddrIndex; } - async label(): Promise { - const label = await getSymbol("TransactionInfo_label")(this.#txInfoPtr); - return await readCString(label) || ""; + get transfers(): readonly TransferData[] { + return this.#transfers; } - async confirmations(): Promise { - return await getSymbol("TransactionInfo_confirmations")(this.#txInfoPtr); + async direction(): Promise<"in" | "out"> { + switch (await fns.TransactionInfo_direction(this.#ptr)) { + case 0: + return "in"; + case 1: + return "out"; + default: + throw new Error("Invalid TransactionInfo direction"); + } } - async unlockTime(): Promise { - return await getSymbol("TransactionInfo_unlockTime")(this.#txInfoPtr); + async description(): Promise { + return await readCString( + await fns.TransactionInfo_description(this.#ptr), + ); } - async hash(): Promise { - const hash = await getSymbol("TransactionInfo_hash")(this.#txInfoPtr); - return await readCString(hash) || ""; + async label(): Promise { + return await readCString( + await fns.TransactionInfo_label(this.#ptr), + ); } - async timestamp(): Promise { - return await getSymbol("TransactionInfo_timestamp")(this.#txInfoPtr); + async confirmations(): Promise { + return await fns.TransactionInfo_confirmations(this.#ptr); } - async paymentId(): Promise { - const paymentId = await getSymbol("TransactionInfo_paymentId")(this.#txInfoPtr); - return await readCString(paymentId) || ""; + async unlockTime(): Promise { + return await fns.TransactionInfo_unlockTime(this.#ptr); } - async transfersCount(): Promise { - return await getSymbol("TransactionInfo_transfers_count")(this.#txInfoPtr); + async isPending(): Promise { + return await fns.TransactionInfo_isPending(this.#ptr); } - async transfersAmount(index: number): Promise { - return await getSymbol("TransactionInfo_transfers_amount")(this.#txInfoPtr, index); + async isFailed(): Promise { + return await fns.TransactionInfo_isFailed(this.#ptr); } - async transfersAddress(index: number): Promise { - const transfersAddress = await getSymbol("TransactionInfo_transfers_address")(this.#txInfoPtr, index); - return await readCString(transfersAddress) || ""; + async isCoinbase(): Promise { + return await fns.TransactionInfo_isCoinbase(this.#ptr); } } diff --git a/impls/monero.ts/src/unsigned_transaction.ts b/impls/monero.ts/src/unsigned_transaction.ts new file mode 100644 index 0000000..c5d8ed7 --- /dev/null +++ b/impls/monero.ts/src/unsigned_transaction.ts @@ -0,0 +1,79 @@ +import { fns } from "./bindings.ts"; +import { C_SEPARATOR, CString, maybeMultipleStrings, readCString } from "./utils.ts"; + +export type UnsignedTransactionPtr = Deno.PointerObject<"pendingTransaction">; + +export class UnsignedTransaction { + #ptr: UnsignedTransactionPtr; + + #amount!: string | string[] | null; + #fee!: string | string[] | null; + #txCount!: bigint; + #paymentId!: string | null; + #recipientAddress!: string | string[] | null; + + constructor(ptr: UnsignedTransactionPtr) { + this.#ptr = ptr; + } + + async status(): Promise { + return await fns.UnsignedTransaction_status(this.#ptr); + } + + async errorString(): Promise { + return await readCString(await fns.UnsignedTransaction_errorString(this.#ptr)); + } + + static async new(ptr: UnsignedTransactionPtr): Promise { + const instance = new UnsignedTransaction(ptr); + + const [amount, paymentId, fee, txCount, recipientAddress] = await Promise.all([ + fns.UnsignedTransaction_amount(ptr, C_SEPARATOR).then(readCString), + fns.UnsignedTransaction_paymentId(ptr, C_SEPARATOR).then(readCString), + fns.UnsignedTransaction_fee(ptr, C_SEPARATOR).then(readCString), + fns.UnsignedTransaction_txCount(ptr), + fns.UnsignedTransaction_recipientAddress(ptr, C_SEPARATOR).then(readCString), + ]); + + instance.#amount = maybeMultipleStrings(amount); + instance.#fee = maybeMultipleStrings(fee); + instance.#recipientAddress = maybeMultipleStrings(recipientAddress); + instance.#txCount = txCount; + instance.#paymentId = paymentId; + + return instance; + } + + get amount(): string | string[] | null { + return this.#amount; + } + + get fee(): string | string[] | null { + return this.#fee; + } + + get txCount(): bigint { + return this.#txCount; + } + + get paymentId(): string | null { + return this.#paymentId; + } + + get recipientAddress(): string | string[] | null { + return this.#recipientAddress; + } + + async sign(signedFileName: string): Promise { + return await fns.UnsignedTransaction_sign(this.#ptr, CString(signedFileName)); + } + + async signUR(maxFragmentLength: number): Promise { + const signUR = fns.UnsignedTransaction_signUR; + if (!signUR) return null; + + return await readCString( + await signUR(this.#ptr, maxFragmentLength), + ); + } +} diff --git a/impls/monero.ts/src/utils.ts b/impls/monero.ts/src/utils.ts index e88ddcd..4323b72 100644 --- a/impls/monero.ts/src/utils.ts +++ b/impls/monero.ts/src/utils.ts @@ -1,22 +1,19 @@ -import { dylib } from "../mod.ts"; -import type { moneroSymbols, MoneroTsDylib, WowneroTsDylib } from "./symbols.ts"; - -export type Sanitizer = () => void | PromiseLike; +import { fns } from "./bindings.ts"; const textEncoder = new TextEncoder(); -export function CString(string: string): Deno.PointerValue { - return Deno.UnsafePointer.of(textEncoder.encode(`${string}\x00`)); +export const SEPARATOR = ","; +export const C_SEPARATOR = CString(SEPARATOR); + +export function maybeMultipleStrings(input: string): string | string[]; +export function maybeMultipleStrings(input: null | string): null | string | string[]; +export function maybeMultipleStrings(input: null | string): null | string | string[] { + if (!input) return null; + const multiple = input.split(SEPARATOR); + return multiple.length === 1 ? multiple[0] : multiple; } -type SymbolWithoutPrefix = keyof typeof moneroSymbols extends `MONERO_${infer DylibSymbol}` ? DylibSymbol : never; -export function getSymbol( - symbol: S, -): MoneroTsDylib["symbols"][`MONERO_${S}`] | WowneroTsDylib["symbols"][`WOWNERO_${S}`] { - if ("MONERO_free" in dylib.symbols) { - return dylib.symbols[`MONERO_${symbol}` as const]; - } else { - return dylib.symbols[`WOWNERO_${symbol}` as const]; - } +export function CString(string: string): Deno.PointerValue { + return Deno.UnsafePointer.of(textEncoder.encode(`${string}\x00`)); } /** @@ -29,9 +26,8 @@ export async function readCString(pointer: Deno.PointerObject, free?: boolean): export async function readCString(pointer: Deno.PointerValue, free?: boolean): Promise; export async function readCString(pointer: Deno.PointerValue, free = true): Promise { if (!pointer) return null; + const string = new Deno.UnsafePointerView(pointer).getCString(); - if (free) { - await getSymbol("free")(pointer); - } + if (string && free) await fns.free(pointer); return string; } diff --git a/impls/monero.ts/src/wallet.ts b/impls/monero.ts/src/wallet.ts index ea25f21..673ccab 100644 --- a/impls/monero.ts/src/wallet.ts +++ b/impls/monero.ts/src/wallet.ts @@ -1,294 +1,268 @@ -import { dylib } from "./bindings.ts"; -import { CString, getSymbol, readCString, Sanitizer } from "./utils.ts"; +import { WalletManager } from "./wallet_manager.ts"; -import { WalletManager, type WalletManagerPtr } from "./wallet_manager.ts"; -import { TransactionHistory, TransactionHistoryPtr } from "./transaction_history.ts"; -import { PendingTransaction } from "./pending_transaction.ts"; -import { PendingTransactionPtr } from "./pending_transaction.ts"; +import { C_SEPARATOR, CString, readCString, SEPARATOR } from "./utils.ts"; +import { PendingTransaction, PendingTransactionPtr } from "./pending_transaction.ts"; +import { UnsignedTransaction, UnsignedTransactionPtr } from "./unsigned_transaction.ts"; +import { Coins, CoinsPtr } from "./coins.ts"; +import { fns } from "./bindings.ts"; export type WalletPtr = Deno.PointerObject<"walletManager">; +interface DaemonInfo { + address?: string; + username?: string; + password?: string; + lightWallet?: boolean; + proxyAddress?: string; +} + export class Wallet { - #walletManagerPtr: WalletManagerPtr; - #walletPtr: WalletPtr; - sanitizer?: Sanitizer; + #walletManager: WalletManager; + #ptr: WalletPtr; - constructor(walletManagerPtr: WalletManager, walletPtr: WalletPtr, sanitizer?: Sanitizer) { - this.#walletPtr = walletPtr; - this.#walletManagerPtr = walletManagerPtr.getPointer(); - this.sanitizer = sanitizer; + constructor(walletManager: WalletManager, ptr: WalletPtr) { + this.#walletManager = walletManager; + this.#ptr = ptr; } - getPointer(): WalletPtr { - return this.#walletPtr; + getPointer() { + return this.#ptr; } - async store(path = ""): Promise { - const bool = await getSymbol("Wallet_store")(this.#walletPtr, CString(path)); - await this.throwIfError(); - return bool; - } + async init(daemonInfo: DaemonInfo, log = false): Promise { + const success = await fns.Wallet_init( + this.#ptr, + CString(daemonInfo.address ?? ""), + 0n, + CString(daemonInfo.username ?? ""), + CString(daemonInfo.password ?? ""), + false, + daemonInfo.lightWallet ?? false, + CString(daemonInfo.proxyAddress ?? ""), + ); + + if (log) { + await fns.Wallet_init3( + this.#ptr, + CString(""), + CString(""), + CString(""), + true, + ); + } - async initWallet(daemonAddress = "http://nodex.monerujo.io:18081"): Promise { - await this.init(); await this.setTrustedDaemon(true); - await this.setDaemonAddress(daemonAddress); await this.startRefresh(); await this.refreshAsync(); - await this.throwIfError(); + + return success; } - async setDaemonAddress(address: string): Promise { - await getSymbol("WalletManager_setDaemonAddress")( - this.#walletManagerPtr, - CString(address), - ); + async setTrustedDaemon(value: boolean): Promise { + return await fns.Wallet_setTrustedDaemon(this.#ptr, value); } async startRefresh(): Promise { - await getSymbol("Wallet_startRefresh")(this.#walletPtr); - await this.throwIfError(); + return await fns.Wallet_startRefresh(this.#ptr); } async refreshAsync(): Promise { - await getSymbol("Wallet_refreshAsync")(this.#walletPtr); - await this.throwIfError(); - } - - async init(): Promise { - const bool = await getSymbol("Wallet_init")( - this.#walletPtr, - CString("http://nodex.monerujo.io:18081"), - 0n, - CString(""), - CString(""), - false, - false, - CString(""), + return await fns.Wallet_refreshAsync(this.#ptr); + } + + async setupBackgroundSync( + backgroundSyncType: number, + walletPassword: string, + backgroundCachePassword: string, + ): Promise { + return await fns.Wallet_setupBackgroundSync( + this.#ptr, + backgroundSyncType, + CString(walletPassword), + CString(backgroundCachePassword), ); - await this.throwIfError(); - return bool; } - async setTrustedDaemon(value: boolean): Promise { - await getSymbol("Wallet_setTrustedDaemon")(this.#walletPtr, value); - } - - static async create( - walletManager: WalletManager, - path: string, - password: string, - sanitizeError = true, - ): Promise { - // We assign holder of the pointer in Wallet constructor - const walletManagerPtr = walletManager.getPointer(); - - const walletPtr = await getSymbol("WalletManager_createWallet")( - walletManagerPtr, - CString(path), - CString(password), - CString("English"), - 0, - ); + async startBackgroundSync(): Promise { + return await fns.Wallet_startBackgroundSync(this.#ptr); + } - const wallet = new Wallet(walletManager, walletPtr as WalletPtr, walletManager.sanitizer); - await wallet.throwIfError(sanitizeError); - await wallet.initWallet(); + async stopBackgroundSync(walletPassword: string): Promise { + return await fns.Wallet_stopBackgroundSync(this.#ptr, CString(walletPassword)); + } - return wallet; + async store(path = ""): Promise { + return await fns.Wallet_store(this.#ptr, CString(path)); } - static async open( - walletManager: WalletManager, - path: string, - password: string, - sanitizeError = true, - ): Promise { - // We assign holder of the pointer in Wallet constructor - const walletManagerPtr = walletManager.getPointer(); + async close(store: boolean): Promise { + return await fns.WalletManager_closeWallet(this.#walletManager.getPointer(), this.#ptr, store); + } - const walletPtr = await getSymbol("WalletManager_openWallet")( - walletManagerPtr, - CString(path), - CString(password), - 0, + async seed(offset = ""): Promise { + return await readCString( + await fns.Wallet_seed(this.#ptr, CString(offset)), ); + } - const wallet = new Wallet(walletManager, walletPtr as WalletPtr, walletManager.sanitizer); - await wallet.throwIfError(sanitizeError); - await wallet.initWallet(); - - return wallet; - } - - static async recover( - walletManager: WalletManager, - path: string, - password: string, - mnemonic: string, - restoreHeight: bigint, - seedOffset: string = "", - sanitizeError = true, - ): Promise { - // We assign holder of the pointer in Wallet constructor - const walletManagerPtr = walletManager.getPointer(); - - const walletPtr = await getSymbol("WalletManager_recoveryWallet")( - walletManagerPtr, - CString(path), - CString(password), - CString(mnemonic), - 0, - restoreHeight, - 1n, - CString(seedOffset), + async address(accountIndex = 0n, addressIndex = 0n): Promise { + return await readCString( + await fns.Wallet_address(this.#ptr, accountIndex, addressIndex), ); + } - const wallet = new Wallet(walletManager, walletPtr as WalletPtr, walletManager.sanitizer); - await wallet.throwIfError(sanitizeError); - await wallet.initWallet(); + async balance(accountIndex = 0): Promise { + return await fns.Wallet_balance(this.#ptr, accountIndex); + } - return wallet; + async unlockedBalance(accountIndex = 0): Promise { + return await fns.Wallet_unlockedBalance(this.#ptr, accountIndex); } - async address(accountIndex = 0n, addressIndex = 0n): Promise { - const address = await getSymbol("Wallet_address")(this.#walletPtr, accountIndex, addressIndex); - if (!address) { - const error = await this.errorString(); - throw new Error(`Failed getting address from a wallet: ${error ?? ""}`); - } - return await readCString(address); + async synchronized(): Promise { + return await fns.Wallet_synchronized(this.#ptr); } - async balance(accountIndex = 0): Promise { - return await getSymbol("Wallet_balance")(this.#walletPtr, accountIndex); + async blockChainHeight(): Promise { + return await fns.Wallet_blockChainHeight(this.#ptr); } - async unlockedBalance(accountIndex = 0): Promise { - return await getSymbol("Wallet_unlockedBalance")(this.#walletPtr, accountIndex); + async daemonBlockChainHeight(): Promise { + return await fns.Wallet_daemonBlockChainHeight(this.#ptr); } - status(): Promise { - return getSymbol("Wallet_status")(this.#walletPtr); + async addSubaddressAccount(label: string): Promise { + return await fns.Wallet_addSubaddressAccount(this.#ptr, CString(label)); } - async errorString(): Promise { - if (!await this.status()) return null; + async numSubaddressAccounts(): Promise { + return await fns.Wallet_numSubaddressAccounts(this.#ptr); + } - const error = await getSymbol("Wallet_errorString")(this.#walletPtr); - if (!error) return null; + async addSubaddress(accountIndex: number, label: string): Promise { + return await fns.Wallet_addSubaddress( + this.#ptr, + accountIndex, + CString(label), + ); + } - return await readCString(error) || null; + async numSubaddresses(accountIndex: number): Promise { + return await fns.Wallet_numSubaddresses( + this.#ptr, + accountIndex, + ); } - async throwIfError(sanitize = true): Promise { - const maybeError = await this.errorString(); - if (maybeError) { - if (sanitize) this.sanitizer?.(); - throw new Error(maybeError); - } + async getSubaddressLabel(accountIndex: number, addressIndex: number): Promise { + return await readCString( + await fns.Wallet_getSubaddressLabel(this.#ptr, accountIndex, addressIndex), + ); } - async synchronized(): Promise { - const synchronized = await getSymbol("Wallet_synchronized")(this.#walletPtr); - await this.throwIfError(); - return synchronized; + async setSubaddressLabel(accountIndex: number, addressIndex: number, label: string): Promise { + return await fns.Wallet_setSubaddressLabel(this.#ptr, accountIndex, addressIndex, CString(label)); } - async blockChainHeight(): Promise { - const height = await getSymbol("Wallet_blockChainHeight")(this.#walletPtr); - await this.throwIfError(); - return height; + async isOffline(): Promise { + return await fns.Wallet_isOffline(this.#ptr); } - async daemonBlockChainHeight(): Promise { - const height = await getSymbol("Wallet_daemonBlockChainHeight")(this.#walletPtr); - await this.throwIfError(); - return height; + async setOffline(offline: boolean): Promise { + return await fns.Wallet_setOffline(this.#ptr, offline); } - async managerBlockChainHeight(): Promise { - const height = await getSymbol("WalletManager_blockchainHeight")(this.#walletManagerPtr); - await this.throwIfError(); - return height; + async publicViewKey(): Promise { + return await readCString(await fns.Wallet_publicViewKey(this.#ptr)); } - async managerTargetBlockChainHeight(): Promise { - const height = await getSymbol("WalletManager_blockchainTargetHeight")(this.#walletManagerPtr); - await this.throwIfError(); - return height; + async secretViewKey(): Promise { + return await readCString(await fns.Wallet_secretViewKey(this.#ptr)); } - async addSubaddressAccount(label: string): Promise { - await getSymbol("Wallet_addSubaddressAccount")( - this.#walletPtr, - CString(label), - ); - await this.throwIfError(); + async publicSpendKey(): Promise { + return await readCString(await fns.Wallet_publicSpendKey(this.#ptr)); } - async numSubaddressAccounts(): Promise { - const accountsLen = await getSymbol("Wallet_numSubaddressAccounts")(this.#walletPtr); - await this.throwIfError(); - return accountsLen; + async secretSpendKey(): Promise { + return await readCString(await fns.Wallet_secretSpendKey(this.#ptr)); } - async addSubaddress(accountIndex: number, label: string): Promise { - await getSymbol("Wallet_addSubaddress")( - this.#walletPtr, - accountIndex, - CString(label), - ); - await this.throwIfError(); + async exportOutputs(fileName: string, all: boolean): Promise { + return await fns.Wallet_exportOutputs(this.#ptr, CString(fileName), all); } - async numSubaddresses(accountIndex: number): Promise { - const address = await getSymbol("Wallet_numSubaddresses")( - this.#walletPtr, - accountIndex, + async exportOutputsUR(maxFragmentLength: bigint, all: boolean): Promise { + const exportOutputsUR = fns.Wallet_exportOutputsUR; + if (!exportOutputsUR) return null; + + return await readCString( + await exportOutputsUR(this.#ptr, maxFragmentLength, all), ); - await this.throwIfError(); - return address; } - async getSubaddressLabel(accountIndex: number, addressIndex: number): Promise { - const label = await getSymbol("Wallet_getSubaddressLabel")(this.#walletPtr, accountIndex, addressIndex); - if (!label) { - const error = await this.errorString(); - throw new Error(`Failed getting subaddress label from a wallet: ${error ?? ""}`); - } - return await readCString(label); + async importOutputs(fileName: string): Promise { + return await fns.Wallet_importOutputs(this.#ptr, CString(fileName)); } - async setSubaddressLabel(accountIndex: number, addressIndex: number, label: string): Promise { - await getSymbol("Wallet_setSubaddressLabel")( - this.#walletPtr, - accountIndex, - addressIndex, - CString(label), + async importOutputsUR(input: string): Promise { + const importOutputsUR = fns.Wallet_importOutputsUR; + if (!importOutputsUR) return null; + + return await importOutputsUR(this.#ptr, CString(input)); + } + + async exportKeyImages(fileName: string, all: boolean): Promise { + return await fns.Wallet_exportKeyImages(this.#ptr, CString(fileName), all); + } + + async exportKeyImagesUR(maxFragmentLength: bigint, all: boolean): Promise { + const exportKeyImagesUR = fns.Wallet_exportKeyImagesUR; + if (!exportKeyImagesUR) return null; + + return await readCString( + await exportKeyImagesUR(this.#ptr, maxFragmentLength, all), ); - await this.throwIfError(); } - async getHistory(): Promise { - const transactionHistoryPointer = await getSymbol("Wallet_history")(this.#walletPtr); - await this.throwIfError(); - return new TransactionHistory(transactionHistoryPointer as TransactionHistoryPtr); + async importKeyImages(fileName: string): Promise { + return await fns.Wallet_importKeyImages(this.#ptr, CString(fileName)); + } + + async importKeyImagesUR(input: string): Promise { + const importKeyImagesUR = fns.Wallet_importKeyImagesUR; + if (!importKeyImagesUR) return null; + + return await importKeyImagesUR(this.#ptr, CString(input)); + } + + async loadUnsignedTx(fileName: string): Promise { + const pendingTxPtr = await fns.Wallet_loadUnsignedTx(this.#ptr, CString(fileName)); + return UnsignedTransaction.new(pendingTxPtr as UnsignedTransactionPtr); + } + + async loadUnsignedTxUR(input: string): Promise { + const loadUnsignedTxUR = fns.Wallet_loadUnsignedTxUR; + if (!loadUnsignedTxUR) return null; + + const pendingTxPtr = await loadUnsignedTxUR(this.#ptr, CString(input)); + if (await this.status()) { + throw this.errorString(); + } + return UnsignedTransaction.new(pendingTxPtr as UnsignedTransactionPtr); } async createTransaction( destinationAddress: string, amount: bigint, - pendingTransactionPriority = 0 | 1 | 2 | 3, + pendingTransactionPriority: 0 | 1 | 2 | 3, subaddressAccount: number, - sanitize = true, prefferedInputs = "", mixinCount = 0, paymentId = "", - separator = ",", - ): Promise { - const pendingTxPtr = await getSymbol("Wallet_createTransaction")( - this.#walletPtr, + ): Promise { + const pendingTxPtr = await fns.Wallet_createTransaction( + this.#ptr, CString(destinationAddress), CString(paymentId), amount, @@ -296,13 +270,61 @@ export class Wallet { pendingTransactionPriority, subaddressAccount, CString(prefferedInputs), - CString(separator), + C_SEPARATOR, + ); + + if (!pendingTxPtr) return null; + return PendingTransaction.new(pendingTxPtr as PendingTransactionPtr); + } + + async createTransactionMultDest( + destinationAddresses: string[], + amounts: bigint[], + amountSweepAll: boolean, + pendingTransactionPriority: 0 | 1 | 2 | 3, + subaddressAccount: number, + preferredInputs: string[] = [], + mixinCount = 0, + paymentId = "", + ): Promise { + const pendingTxPtr = await fns.Wallet_createTransactionMultDest( + this.#ptr, + CString(destinationAddresses.join(SEPARATOR)), + C_SEPARATOR, + CString(paymentId), + amountSweepAll, + CString(amounts.join(SEPARATOR)), + C_SEPARATOR, + mixinCount, + pendingTransactionPriority, + subaddressAccount, + CString(preferredInputs.join(SEPARATOR)), + C_SEPARATOR, ); - await this.throwIfError(sanitize); - return new PendingTransaction(pendingTxPtr as PendingTransactionPtr); + return PendingTransaction.new(pendingTxPtr as PendingTransactionPtr); + } + + async coins(): Promise { + const coinsPtr = await fns.Wallet_coins(this.#ptr); + if (!coinsPtr) return null; + + return new Coins(coinsPtr as CoinsPtr); + } + + async status(): Promise { + return await fns.Wallet_status(this.#ptr); + } + + async errorString(): Promise { + if (!await this.status()) return null; + const error = await fns.Wallet_errorString(this.#ptr); + return await readCString(error); } - async amountFromString(amount: string): Promise { - return await getSymbol("Wallet_amountFromString")(CString(amount)); + async throwIfError(): Promise { + const maybeError = await this.errorString(); + if (maybeError) { + throw new Error(maybeError); + } } } diff --git a/impls/monero.ts/src/wallet_manager.ts b/impls/monero.ts/src/wallet_manager.ts index b1fafec..45fd0c9 100644 --- a/impls/monero.ts/src/wallet_manager.ts +++ b/impls/monero.ts/src/wallet_manager.ts @@ -1,26 +1,148 @@ -import { getSymbol, Sanitizer } from "./utils.ts"; +import { fns } from "./bindings.ts"; +import { CString } from "./utils.ts"; +import { Wallet, WalletPtr } from "./wallet.ts"; export type WalletManagerPtr = Deno.PointerObject<"walletManager">; export class WalletManager { #ptr: WalletManagerPtr; - sanitizer?: Sanitizer; - constructor(walletManagerPtr: WalletManagerPtr, sanitizer?: Sanitizer) { + constructor(walletManagerPtr: WalletManagerPtr) { this.#ptr = walletManagerPtr; - this.sanitizer = sanitizer; } getPointer(): WalletManagerPtr { return this.#ptr; } - static async new(sanitizer?: Sanitizer) { - const ptr = await getSymbol("WalletManagerFactory_getWalletManager")(); + static async new() { + const ptr = await fns.WalletManagerFactory_getWalletManager(); if (!ptr) { - sanitizer?.(); throw new Error("Failed retrieving wallet manager"); } - return new WalletManager(ptr as WalletManagerPtr, sanitizer); + + return new WalletManager(ptr as WalletManagerPtr); + } + + async setDaemonAddress(address: string): Promise { + return await fns.WalletManager_setDaemonAddress(this.#ptr, CString(address)); + } + + async createWallet(path: string, password: string): Promise { + const walletPtr = await fns.WalletManager_createWallet( + this.#ptr, + CString(path), + CString(password), + CString("English"), + 0, + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; + } + + async openWallet(path: string, password: string): Promise { + const walletPtr = await fns.WalletManager_openWallet( + this.#ptr, + CString(path), + CString(password), + 0, + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; + } + + async recoverWallet( + path: string, + password: string, + mnemonic: string, + restoreHeight: bigint, + seedOffset: string = "", + ): Promise { + const walletPtr = await fns.WalletManager_recoveryWallet( + this.#ptr, + CString(path), + CString(password), + CString(mnemonic), + 0, + restoreHeight, + 1n, + CString(seedOffset), + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; + } + + async recoverFromPolyseed( + path: string, + password: string, + mnemonic: string, + restoreHeight: bigint, + passphrase = "", + ): Promise { + return await this.createFromPolyseed( + path, + password, + mnemonic, + restoreHeight, + passphrase, + false, + ); + } + + async createFromPolyseed( + path: string, + password: string, + mnemonic: string, + restoreHeight: bigint, + passphrase = "", + newWallet = true, + ): Promise { + const walletPtr = await fns.WalletManager_createWalletFromPolyseed( + this.#ptr, + CString(path), + CString(password), + 0, + CString(mnemonic), + CString(passphrase), + newWallet, + restoreHeight, + 1n, + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; + } + + async recoverFromKeys( + path: string, + password: string, + restoreHeight: bigint, + address: string, + viewKey: string, + spendKey: string, + ): Promise { + const walletPtr = await fns.WalletManager_createWalletFromKeys( + this.#ptr, + CString(path), + CString(password), + CString("English"), + 0, + restoreHeight, + CString(address), + CString(viewKey), + CString(spendKey), + 0n, + ); + + const wallet = new Wallet(this, walletPtr as WalletPtr); + await wallet.throwIfError(); + return wallet; } } diff --git a/tests/compare.ts b/tests/compare.ts index d09bdd9..2fd27b8 100755 --- a/tests/compare.ts +++ b/tests/compare.ts @@ -1,80 +1,34 @@ -import { moneroSymbols as symbols, type MoneroTsDylib, type WowneroTsDylib } from "../impls/monero.ts/src/symbols.ts"; -import { loadMoneroDylib, loadWowneroDylib } from "../impls/monero.ts/src/bindings.ts"; -import { Wallet, WalletManager } from "../impls/monero.ts/mod.ts"; -import { readCString } from "../impls/monero.ts/src/utils.ts"; import { assertEquals } from "jsr:@std/assert"; +import { + loadMoneroDylib, + loadWowneroDylib, + moneroSymbols, + WalletManager, + wowneroSymbols, +} from "../impls/monero.ts/mod.ts"; + const coin = Deno.args[0] as "monero" | "wownero"; const version = Deno.args[1]; const walletInfo = JSON.parse(Deno.args[2]); -const moneroSymbols = { - ...symbols, - - "MONERO_Wallet_secretViewKey": { - nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // const char* - result: "pointer", - }, - "MONERO_Wallet_publicViewKey": { - nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // const char* - result: "pointer", - }, - - "MONERO_Wallet_secretSpendKey": { - nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // const char* - result: "pointer", - }, - "MONERO_Wallet_publicSpendKey": { - nonblocking: true, - // void* wallet_ptr - parameters: ["pointer"], - // const char* - result: "pointer", - }, -} as const; - -type ReplaceMonero = T extends `MONERO${infer Y}` ? `WOWNERO${Y}` : never; -type WowneroSymbols = { [Key in keyof typeof moneroSymbols as ReplaceMonero]: (typeof moneroSymbols)[Key] }; -const wowneroSymbols = Object.fromEntries( - Object.entries(moneroSymbols).map(([key, value]) => [key.replace("MONERO", "WOWNERO"), value]), -) as WowneroSymbols; - -let getKey: (wallet: Wallet, type: `${"secret" | "public"}${"Spend" | "View"}Key`) => Promise; - if (coin === "monero") { const dylib = Deno.dlopen(`tests/libs/${version}/monero_libwallet2_api_c.so`, moneroSymbols); - loadMoneroDylib(dylib as MoneroTsDylib); - - getKey = async (wallet, type) => - await readCString(await dylib.symbols[`MONERO_Wallet_${type}` as const](wallet.getPointer())); + loadMoneroDylib(dylib); } else { const dylib = Deno.dlopen(`tests/libs/${version}/wownero_libwallet2_api_c.so`, wowneroSymbols); - loadWowneroDylib(dylib as WowneroTsDylib); - - getKey = async (wallet, type) => - await readCString( - await dylib.symbols[`WOWNERO_Wallet_${type}` as const](wallet.getPointer()), - ); + loadWowneroDylib(dylib); } const walletManager = await WalletManager.new(); -const wallet = await Wallet.open(walletManager, walletInfo.path, walletInfo.password); +const wallet = await walletManager.openWallet(walletInfo.path, walletInfo.password); assertEquals(await wallet.address(), walletInfo.address); -assertEquals(await getKey(wallet, "publicSpendKey"), walletInfo.publicSpendKey); -assertEquals(await getKey(wallet, "secretSpendKey"), walletInfo.secretSpendKey); +assertEquals(await wallet.publicSpendKey(), walletInfo.publicSpendKey); +assertEquals(await wallet.secretSpendKey(), walletInfo.secretSpendKey); -assertEquals(await getKey(wallet, "publicViewKey"), walletInfo.publicViewKey); -assertEquals(await getKey(wallet, "secretViewKey"), walletInfo.secretViewKey); +assertEquals(await wallet.publicViewKey(), walletInfo.publicViewKey); +assertEquals(await wallet.secretViewKey(), walletInfo.secretViewKey); await wallet.store(walletInfo.path); diff --git a/tests/deno.lock b/tests/deno.lock old mode 100755 new mode 100644 index 02d189f..b67d77d --- a/tests/deno.lock +++ b/tests/deno.lock @@ -12,6 +12,7 @@ "jsr:@std/fs@1": "1.0.4", "jsr:@std/io@0.221": "0.221.0", "jsr:@std/path@1": "1.0.6", + "jsr:@std/path@1.0.8": "1.0.8", "jsr:@std/path@^1.0.6": "1.0.6", "jsr:@std/streams@0.221": "0.221.0", "jsr:@std/streams@^1.0.7": "1.0.7", @@ -71,6 +72,9 @@ "@std/path@1.0.6": { "integrity": "ab2c55f902b380cf28e0eec501b4906e4c1960d13f00e11cfbcd21de15f18fed" }, + "@std/path@1.0.8": { + "integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be" + }, "@std/streams@0.221.0": { "integrity": "47f2f74634b47449277c0ee79fe878da4424b66bd8975c032e3afdca88986e61", "dependencies": [ diff --git a/tests/integration.test.ts b/tests/integration.test.ts new file mode 100644 index 0000000..1a65009 --- /dev/null +++ b/tests/integration.test.ts @@ -0,0 +1,632 @@ +import { + CoinsInfo, + type Dylib, + loadMoneroDylib, + loadWowneroDylib, + moneroSymbols, + Wallet, + WalletManager, + wowneroSymbols, +} from "../impls/monero.ts/mod.ts"; + +import { assert, assertEquals } from "jsr:@std/assert"; +import { $, downloadCli, getMoneroC } from "./utils.ts"; + +const coin = Deno.env.get("COIN"); +if (coin !== "monero" && coin !== "wownero") { + throw new Error("COIN env var invalid or missing"); +} + +async function syncBlockchain(wallet: Wallet): Promise { + // Wait for blockchain to sync + const blockHeight = await new Promise((resolve) => { + let timeout: number; + + const poll = async () => { + const blockChainHeight = await wallet.blockChainHeight(); + const daemonBlockchainHeight = await wallet.daemonBlockChainHeight(); + // console.log("Blockchain height:", blockChainHeight, "Daemon blockchain height:", daemonBlockchainHeight, "Remains:", daemonBlockchainHeight - blockChainHeight); + if (blockChainHeight === daemonBlockchainHeight) { + clearTimeout(timeout); + resolve(blockChainHeight); + } else { + setTimeout(poll, 500); + } + }; + + poll(); + }); + await new Promise((r) => setTimeout(r, 1500)); // wait for it to sync + return blockHeight; +} + +// TODO: Change for custom address on CI +const WOWNERO_NODE_URL = "https://node3.monerodevs.org:34568"; +const MONERO_NODE_URL = "https://nodes.hashvault.pro:18081"; +const NODE_URL = coin === "monero" ? MONERO_NODE_URL : WOWNERO_NODE_URL; + +const WOWNERO_DESTINATION_ADDRESS = + "WW3Zetw4Gg5Rk88ViCm8H8Ft8BqgAQ5DbTLZC1whv8GNFJPSoGfLViW3dAAb4Bcqpz2M1y31pZykd4ZKd8GH1UyF1fwEFg5mS"; +const MONERO_DESTINATION_ADDRESS = + "89BoVWjqdGVe68wdxbYurXR8sXaEb96eWKYRPxdT6wSCfZYK6XSHoj5ZRXQLtd7GzL2B2PD7Lb7GSKupkXMWjQVFAEb1CK8"; +const DESTINATION_ADDRESS = coin === "monero" ? MONERO_DESTINATION_ADDRESS : WOWNERO_DESTINATION_ADDRESS; + +const BILLION = 10n ** 9n; + +await getMoneroC(coin, "next"); + +interface WalletInfo { + name: string; + password: string; + seed: string; + offset?: string; + address: string; + restoreHeight: bigint; + + publicSpendKey: string; + secretSpendKey: string; + publicViewKey: string; + secretViewKey: string; +} + +async function clearWallets() { + await Deno.remove("tests/wallets/", { recursive: true }).catch(() => {}); + await Deno.mkdir("tests/wallets/"); +} + +let dylib: Dylib; +if (coin === "monero") { + dylib = Deno.dlopen(`tests/libs/next/monero_libwallet2_api_c.so`, moneroSymbols); + loadMoneroDylib(dylib); +} else { + dylib = Deno.dlopen(`tests/libs/next/wownero_libwallet2_api_c.so`, wowneroSymbols); + loadWowneroDylib(dylib); +} + +Deno.test("0001-polyseed.patch", async (t) => { + const WALLETS: Record<"monero" | "wownero", WalletInfo[]> = { + monero: [ + //#region Cake wallet, no offset + { + name: "English Wallet", + password: "englishwallet", + seed: + "tortoise winter play argue laptop diary tell library travel cupboard view river embark rubber plunge student", + restoreHeight: 3254619n, + address: "49PL6qHMkc4Hw3dWT5wy5NbbVd2xda8zw3tLx2BoQsNZUWDQYYpwMEKjB9BLbEKSo3S3z34bncFw6ijToTwfiEJJ5m8aefx", + publicSpendKey: "ccd6846ab69fdd653a8d092d89590dced40aa2862f3c24113fedfcd6469162a4", + secretSpendKey: "37fd2e3e933c8468beb407e5350789e23bed5df33eeeb35d3b119401988e6709", + publicViewKey: "6f0de7385aafd4fc259cbd0abb069295c5d3824b7e1b81f97ffcf8cccde6c72a", + secretViewKey: "b8095208d61fc22e4ee3a79347a889e4872cdcf1cceff991542834cef5375907", + }, + { + name: "Chinese Traditional Wallet", + password: "chinesetraditionalwallet", + seed: "旗 铁 革 酯 紧 毅 饱 应 第 兄 植 隙 点 吐 童 赞", + restoreHeight: 3254619n, + address: "4AR8YW51Ga3DR4a47F5J8rXaqyBa8pnnF557pTyt52ZqNMFa3gfxvi13R7sbt5zHfjbF5aKsLFZQrRod3qcr5MQj4f91rLh", + publicSpendKey: "e80bab7b3e2d384a393b825cf4f2abb6d8d08f1742d87c1856c064a609a0647f", + secretSpendKey: "c7de78f9819db6755e14d2e1411f1591c2d0b3a6ee19049c30e270f81eb50401", + publicViewKey: "a366527c3a6d160e717ebaac11d08eccb95586991a4c87944ad750331adac020", + secretViewKey: "aab2ea0dc6fa2745c8c7113399242c03300664a21cac9202c315ad789b67d004", + }, + { + name: "Chinese Simplified Wallet", + password: "chinesesimplifiedwallet", + seed: "纹 触 集 驶 朋 辨 你 版 是 益 驳 修 偏 汽 录 吨", + restoreHeight: 3254619n, + address: "47E7p1mFGNj59QNfjpXcopP1YZuGdn7NhYJv25xbPKdicnThww5DUv2aNMH7oPWsKZjQQmXMkBzUze2T6gAaXafLAF9E4Dz", + publicSpendKey: "93dec0155d30b818c7d64a0b0c3a678395e3e28bdd736abb2ef7c360b38449d5", + secretSpendKey: "60084b4ff3d99d6ca38876078721c13692635429c74ef5d71f03310cf2e0690b", + publicViewKey: "eff74761051a2fc77eac4d7ed1b564fd83c0e3e924199bdd5ba66b4bf4ecf351", + secretViewKey: "3bde0d3a1bd2877f75fc9f6ea331c976d26a62e469db1d11ddaef2cce9405d02", + }, + { + name: "Japanese Wallet", + password: "japanesewallet", + seed: + "きほん ことば そうび きどう なまえ ひさしぶり ごうけい ふひょう ぎゅうにく しはらい きびしい はんとし ととのえる たかい とかい るりがわら", + restoreHeight: 3254619n, + address: "45YYsW5do7NjGSaRZPAkPQ9mi4HQsMv3w86HFddtkZi4cCbqtFiVoqJjdFobCtCwpBPZWSnUtmrU2G9fLpEE7vsQU3aZyeE", + publicSpendKey: "677ac034b7c3e9fcb178370e3df067346ff5703db8273e2a65010862a85935d2", + secretSpendKey: "1fe9110cc46c58ed0f5cb6c6e189e246f6c3cfbf459de2ed1565838b6e08780e", + publicViewKey: "7276d8537e0cd9fed6b9327177ed9086e159f7d63b9fb35a92693cdbf322ffef", + secretViewKey: "f3c8f4121cdf07616453f005e7f2cf0474cf3608ec632f4e81165ec1c5b4aa0c", + }, + { + name: "Korean Wallet", + password: "koreanwallet", + seed: "단골 운전 일대 제작 구역 보자기 대한민국 답장 쇼핑 논문 편견 대전 충돌 강당 형제 볼펜", + restoreHeight: 3254619n, + address: "42DNYppLMXki8j3urYuXaTU1S9EBrSJ71aVNbzr4fKfnXryMwJ2rFt6Y5eCP9vpej9AdrZqNFXDFB1VmkjzjX5e5URJ9q8c", + publicSpendKey: "0f96caeac7cd4ff5eb55810b4eb87ca1779637e949d138c837fe9a776b3c51b8", + secretSpendKey: "d86c40f13694e7511499cdb22db4e96ee2990cfffde52274248bb8818a36c406", + publicViewKey: "826438a35b0f63b9d0b877269a0468399b3f60513e079602f73c4b0f7cd0e6f3", + secretViewKey: "3773b61c0cac3a2e15d03b989461704c6e90916561c6b98d035bd3d2caf88c05", + }, + { + name: "Portugese Wallet", + password: "portugesewallet", + seed: + "inscrito raposa vermelho medusa apetite bacharel quantia usado poupar pilotar sigilo ideia robalo ignorado desgaste intimar", + restoreHeight: 3254619n, + address: "43xw29tpLnU5VaPZ8Nuzz7DqrnUip5tmWBn3aukkZAyzgHTscJHvESy6pmYutLebQAB9TJgGhoCAWhPK6WJ39CpiD29fbwj", + publicSpendKey: "3dcbbde593acc11adc291eeeec8bc44cc7943192d54fb1406de435b4d7b73dea", + secretSpendKey: "9b94f57038b07b8280f27cedbed6e53472b48fe4683de15cc9e034fdddfcbb05", + publicViewKey: "dce02ef2ca595e22d1242a53b5235f3ca8508e22386d03f171bc77ef856e1b6a", + secretViewKey: "27bdd486a74f9b7c7a79c39c8ff7438a3fc862bb1ec3f6ef035a8acedf876705", + }, + { + name: "Czech Wallet", + password: "czechwallet", + seed: + "ulice louskat odmlka parodie dominant slanina sukno vodivost zimnice vykonat sundat kalibr dobro moucha kometa legenda", + restoreHeight: 3254619n, + address: "499LrJgGPkFA1BPvF4xqr5bwZfKyCZah1C2CEhvqpW7YGQddwWYyR2L1F1TJhqyxxwa4TXKYZM4bb8ukq3kein1cPMNLLi4", + publicSpendKey: "c6796799947e0c35d3720af264d6a6d0e5a574cea1cde441e343b828a00c875c", + secretSpendKey: "61f9c86a71744a101c36b5628c1c4324e49e84861f136cbd2d0f1477ba5ace0b", + publicViewKey: "1d75ed535e3dfd0171a4a714aef368c5a6862aaf9972422f49cd76d232f88fc6", + secretViewKey: "47fd8eadc1848a09b343815a74185835a0b2cc8567e61022322dd52f6aaf2004", + }, + { + name: "Spanish Wallet", + password: "spanishwallet", + seed: + "rehén torpedo remo existir fuente dama culpa riqueza cebolla supremo vereda odio novio sumar espía margen", + restoreHeight: 3254619n, + address: "41xeBwVJEpVVrJntrvUXafJR4mk9x3tfW3zXxamh8G951SM9BbBLgJSgzwdCywLRrbZvipLL2Azu9jKbu4Y9Hey3MUF6f19", + publicSpendKey: "08e31c09ad35beac7bcb9e4a689e40681df1b42cd2686511e344d02606bfc802", + secretSpendKey: "19ae689bdd594fde5d06b66e4c7a89d9783c02694ae615e33928b38f0c52120c", + publicViewKey: "9cdf4adfd9c7c7ef236c9084a0ceb4c4da6017b9fd1b6cfd04e02527ea6f06b5", + secretViewKey: "40ae98266cadfbf546e861cac2b2e93ce921f9db2e02f072d86aea274a182006", + }, + { + name: "Italian Wallet", + password: "italianwallet", + seed: + "fuso rinvenire astenuto camicia erboso icona bollito esito spettrale abisso dogma appunto prefisso gracile podismo araldica", + restoreHeight: 3254619n, + address: "46Kkbh8jLorHRKJEV7C4hNczZknHeB6w4GHpLWkpfThMdPLeeU8MFRmDfMqioYyacCTAG9wZ9y9UHZDNhehEssDVHePj3Ja", + publicSpendKey: "7c0b928d9d8349622a07673a9721e9d72f60ccfc8e83935b699c379999104cd9", + secretSpendKey: "673b61bdb369cc61a022e0eab47e5e41cf86e99a80979f75ebcda1219ee0ab01", + publicViewKey: "8858e908f756c44bb286d5b657824d9c660386c7db0b0ec0974d268a7432bc93", + secretViewKey: "5012dbfa9e1cd2a30c2a18654dc9c8bee128af8fe7246c46e5f4def76705fe0f", + }, + //#endregion + //#region Feather wallet, offset + { + name: "English Wallet (offset)", + password: "englishwallet", + seed: "loud fix cattle broken right main web rather write aunt left nation broken ship program ten", + offset: "englishoffset", + restoreHeight: 3263855n, + address: "4B2QGWy9as7bwwLNq2DQ26Q3woahpTLbR7d8vJE1uKL5gobU9iMydFqbVrYa9ixfrnAvnuwT9BXpkBx1APocbJfb2drFuQi", + publicSpendKey: "f817ca86625d1ed0ef81ccb8a4e82b89cfc3345512c7b82798ba2f5982b7daed", + secretSpendKey: "39ae15e92e08a0903652b4b0f187d740d2a5bf08e77879babb345b9a78ca6504", + publicViewKey: "f7fb585b9a288cce3f3b1a5f0ca6873b5a2fef8afb3bd94174ac31cbed53620e", + secretViewKey: "d5676e49438b0cd38c6a699ab783c11f21e1a7ebc1c9174121e37456a97f380d", + }, + //#endregion + ], + wownero: [ + { + name: "English Wowlet", + password: "englishwallet", + seed: "fragile proud oven shove trend visit oxygen dove pledge entire pencil exist throw type large chase", + restoreHeight: 0n, + address: "Wo4ExnCfajHZcVY9Q4XgjTYHu9GwyT9E7dZQuoFhY7HNWr2X6iD2wuB1asHQ1DVEtNYSLjqiCzJVDg5ZKeWnbKDe1LD9Wwy91", + publicSpendKey: "88c53568fd38c2f957f229a7d4dabb142e4fcfd7c128da922b63e4f4df25b26e", + secretSpendKey: "b131442ee0aecd410947a74bf622e2833397f372ee843fc3d291cb16a343e308", + publicViewKey: "ea7a909bc832037db14c6a537357bbf2eedee84b7d00e9a2b1d718d92fe52693", + secretViewKey: "96e03a70a4956656be6cc1fa2252f159ae0b2d2fdc21f761fc7e8d0316931708", + }, + // TODO: Add other localized wallets for Wownero + ], + }; + + await clearWallets(); + + for (const walletInfo of WALLETS[coin]) { + await t.step(walletInfo.name, async () => { + const walletManager = await WalletManager.new(); + const path = `tests/wallets/${walletInfo.name}`; + + const wallet = await walletManager.recoverFromPolyseed( + path, + walletInfo.password, + walletInfo.seed, + walletInfo.restoreHeight, + walletInfo.offset, + ); + + await wallet.init({}); // empty daemon address for offline test + + assertEquals(await wallet.address(), walletInfo.address); + + assertEquals(await wallet.publicSpendKey(), walletInfo.publicSpendKey); + assertEquals(await wallet.secretSpendKey(), walletInfo.secretSpendKey); + + assertEquals(await wallet.publicViewKey(), walletInfo.publicViewKey); + assertEquals(await wallet.secretViewKey(), walletInfo.secretViewKey); + + await wallet.close(true); + }); + } +}); + +Deno.test("0002-wallet-background-sync-with-just-the-view-key.patch", async () => { + await clearWallets(); + + const walletManager = await WalletManager.new(); + const wallet = await walletManager.createWallet("tests/wallets/squirrel", "belka"); + await wallet.init({ + address: NODE_URL, + }); + + const walletInfo = { + address: await wallet.address(), + publicSpendKey: await wallet.publicSpendKey(), + secretSpendKey: await wallet.secretSpendKey(), + publicViewKey: await wallet.publicViewKey(), + secretViewKey: await wallet.secretViewKey(), + }; + + await wallet.setupBackgroundSync(2, "belka", "background-belka"); + await wallet.startBackgroundSync(); + await wallet.close(true); + + const backgroundWallet = await walletManager.openWallet( + "tests/wallets/squirrel.background", + "background-belka", + ); + await backgroundWallet.init({ address: NODE_URL }); + + const blockChainHeight = await syncBlockchain(backgroundWallet); + await backgroundWallet.refreshAsync(); + + await backgroundWallet.close(true); + + const reopenedWallet = await walletManager.openWallet("tests/wallets/squirrel", "belka"); + await reopenedWallet.throwIfError(); + await reopenedWallet.refreshAsync(); + + assertEquals(await reopenedWallet.blockChainHeight(), blockChainHeight); + assertEquals( + walletInfo, + { + address: await reopenedWallet.address(), + publicSpendKey: await reopenedWallet.publicSpendKey(), + secretSpendKey: await reopenedWallet.secretSpendKey(), + publicViewKey: await reopenedWallet.publicViewKey(), + secretViewKey: await reopenedWallet.secretViewKey(), + }, + ); + + await reopenedWallet.close(true); +}); + +Deno.test("0004-coin-control.patch", { + ignore: coin === "wownero" || !( + Deno.env.get("SECRET_WALLET_PASSWORD") && + Deno.env.get("SECRET_WALLET_MNEMONIC") && + Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT") + ), +}, async (t) => { + await clearWallets(); + + const walletManager = await WalletManager.new(); + const wallet = await walletManager.recoverFromPolyseed( + "tests/wallets/secret-wallet", + Deno.env.get("SECRET_WALLET_PASSWORD")!, + Deno.env.get("SECRET_WALLET_MNEMONIC")!, + BigInt(Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT")!), + ); + + assertEquals( + await wallet.address(), + "434dZdLzhymcoNyGSBUJAqhDCLtBECN6698CGRMYByuEAYtpxXdbiibQb3t4qX3SiZi9vDWkxeiEF8kmDGmEoEZ4VMG8Nvh", + ); + + await wallet.init({ address: NODE_URL }); + await wallet.refreshAsync(); + + // Wait for blockchain to sync + await syncBlockchain(wallet); + + await wallet.refreshAsync(); + await wallet.store(); + await wallet.refreshAsync(); + + const coins = (await wallet.coins())!; + await coins.refresh(); + + // COINS: + // 5x 0.001XMR 1x 0.005XMR (in no particular order) + await t.step("preffered_inputs", async (t) => { + const coinsCount = await coins.count(); + + const availableCoinsData: Record = { + ["0.001"]: [], + ["0.005"]: [], + }; + + const freezeAll = async () => { + for (const [_, coinsData] of Object.entries(availableCoinsData)) { + for (const coinData of coinsData) { + await coins.setFrozen(coinData.index); + } + } + await coins.refresh(); + }; + + const thawAll = async () => { + for (const [_, coinsData] of Object.entries(availableCoinsData)) { + for (const coinData of coinsData) { + await coins.thaw(coinData.index); + } + } + await coins.refresh(); + }; + + let availableCoinsCount = 0; + let totalAvailableAmount = 0n; + for (let i = 0; i < coinsCount; ++i) { + const coin = (await coins.coin(i))!; + if (coin.spent) { + continue; + } + + let humanReadableAmount: string; + if (coin.amount === BILLION) { + humanReadableAmount = "0.001"; + } else if (coin.amount === 5n * BILLION) { + humanReadableAmount = "0.005"; + } else { + throw new Error("Invalid coin amount! Only 5x0.01XMR coins and 1x0.05XMR coin should be available"); + } + + availableCoinsData[humanReadableAmount].push({ + index: i, + coin, + keyImage: coin.keyImage, + amount: coin.amount, + }); + + totalAvailableAmount += coin.amount; + availableCoinsCount += 1; + + await coins.thaw(i); + } + + await coins.refresh(); + + assertEquals(availableCoinsCount, 6); + assertEquals(totalAvailableAmount, 10n * BILLION); + + await t.step("Try to spend 0.002XMR by using only one 0.001XMR coin", async () => { + const transaction = await wallet.createTransaction( + DESTINATION_ADDRESS, + 2n * BILLION, + 0, + 0, + availableCoinsData["0.001"][0].keyImage!, + ); + + if (!transaction) { + throw new Error("Failed creating a transaction"); + } + + assertEquals(await transaction.status(), 1); + }); + + await t.step("Try to spend 0.002XMR with only 0.001XMR unlocked balance", async () => { + await freezeAll(); + await coins.thaw(availableCoinsData["0.001"][0].index); + + const transaction = await wallet.createTransaction(DESTINATION_ADDRESS, 2n * BILLION, 0, 0); + + if (!transaction) { + throw new Error("Failed creating a transaction: " + await wallet.errorString()); + } + + assertEquals(await transaction.status(), 1); + assert((await transaction.errorString())?.includes("not enough money to transfer")); + + await thawAll(); + }); + + await t.step("Try to spend 0.002XMR + fee with only 0.002XMR unlocked balance", async () => { + await freezeAll(); + await coins.thaw(availableCoinsData["0.001"][0].index); + await coins.thaw(availableCoinsData["0.001"][1].index); + + const transaction = await wallet.createTransaction( + DESTINATION_ADDRESS, + 2n * BILLION, + 0, + 0, + availableCoinsData["0.001"][0].keyImage!, + ); + + if (!transaction) { + throw new Error("Failed creating a transaction: " + await wallet.errorString()); + } + + assertEquals(await transaction.status(), 1); + assertEquals( + await transaction.errorString(), + "not enough money to transfer, overall balance only 0.002000000000, sent amount 0.002000000000", + ); + }); + + await thawAll(); + }); + + await t.step("spend more than unfrozen balance", async () => { + const unlockedBalance = await wallet.unlockedBalance(); + const transaction = await wallet.createTransaction(DESTINATION_ADDRESS, unlockedBalance + 1n, 0, 0); + + if (!transaction) { + throw new Error("Failed creating a transaction: " + await wallet.errorString()); + } + + assertEquals(await transaction.status(), 1); + assert( + await transaction.errorString(), + "not enough money to transfer, overall balance only 0.001000000000, sent amount 0.001000000001", + ); + }); + + await wallet.close(true); +}); + +Deno.test("0009-Add-recoverDeterministicWalletFromSpendKey.patch", async () => { + await Promise.all([ + downloadCli(coin), + clearWallets(), + ]); + + const walletManager = await WalletManager.new(); + const wallet = await walletManager.createWallet("tests/wallets/stoat", "gornostay"); + const moneroCSeed = await wallet.seed(); + await wallet.close(true); + + await Deno.remove("./tests/wallets/stoat"); + + const cliPath = `./tests/${coin}-cli/${coin}-wallet-cli`; + const moneroCliSeed = (await $.raw`${cliPath} --wallet-file ./tests/wallets/stoat --password gornostay --command seed` + .stdinText(`gornostay\n`) + .lines()).slice(-3).join(" "); + + assertEquals(moneroCSeed, moneroCliSeed); +}); + +Deno.test("0012-WIP-UR-functions.patch", { + ignore: coin === "wownero" || !( + Deno.env.get("SECRET_WALLET_PASSWORD") && + Deno.env.get("SECRET_WALLET_MNEMONIC") && + Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT") + ), +}, async (t) => { + for (const method of ["UR", "file"] as const) { + await clearWallets(); + + const walletManager = await WalletManager.new(); + + const airgap = await walletManager.recoverFromPolyseed( + "tests/wallets/secret-wallet", + Deno.env.get("SECRET_WALLET_PASSWORD")!, + Deno.env.get("SECRET_WALLET_MNEMONIC")!, + BigInt(Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT")!), + ); + await airgap.init({ address: "" }); + + const online = await walletManager.recoverFromKeys( + "tests/wallets/horse-online", + "loshad-online", + BigInt(Deno.env.get("SECRET_WALLET_RESTORE_HEIGHT")!) - 2000n, + (await airgap.address())!, + (await airgap.secretViewKey())!, + "", + ); + await online.init({ address: NODE_URL }); + await online.refreshAsync(); + + await syncBlockchain(online); + + await online.refreshAsync(); + await online.store(); + await online.refreshAsync(); + + if (method === "UR") { + await t.step({ + name: "Sync wallets (UR)", + ignore: coin === "wownero", // Wownero doesn't have UR methods + fn: async () => { + try { + const outputs = await online.exportOutputsUR(130n, false); + await airgap.importOutputsUR(outputs!); + + const keyImages = await airgap.exportKeyImagesUR(130n, false); + await online.importKeyImagesUR(keyImages!); + } catch { + const outputs = await online.exportOutputsUR(130n, true); + await airgap.importOutputsUR(outputs!); + + const keyImages = await airgap.exportKeyImagesUR(130n, true); + await online.importKeyImagesUR(keyImages!); + } + }, + }); + + await t.step({ + name: "Transaction (UR)", + ignore: coin === "wownero", + fn: async () => { + const transaction = await online.createTransaction(DESTINATION_ADDRESS, 1n * BILLION, 0, 0); + if (!transaction) { + throw new Error("Failed creating online transaction: " + await online.errorString()); + } + + const input = await transaction.commitUR(130); + + const unsignedTx = (await airgap.loadUnsignedTxUR(input!))!; + if (!unsignedTx) { + throw new Error("Failed creating unsigned transaction: " + await online.errorString()); + } + + assertEquals(await unsignedTx.status(), 0); + assertEquals(unsignedTx.recipientAddress, DESTINATION_ADDRESS); + assert(!isNaN(Number(unsignedTx.fee))); + assertEquals(unsignedTx.amount, "1000000000"); + + await unsignedTx.signUR(130); + assertEquals(await unsignedTx.status(), 0, (await unsignedTx.errorString())!); + }, + }); + } else { + await t.step("Sync wallets (File)", async () => { + try { + await online.exportOutputs("tests/wallets/outputs", false); + await airgap.importOutputs("tests/wallets/outputs"); + + await airgap.exportKeyImages("tests/wallets/keyImages", false); + await online.importKeyImages("tests/wallets/keyImages"); + } catch { + await online.exportOutputs("tests/wallets/outputs", true); + await airgap.importOutputs("tests/wallets/outputs"); + + await airgap.exportKeyImages("tests/wallets/keyImages", true); + await online.importKeyImages("tests/wallets/keyImages"); + } + }); + + await t.step("Transaction (File)", async () => { + const transaction = await online.createTransaction(DESTINATION_ADDRESS, 1n * BILLION, 0, 0); + if (!transaction) { + throw new Error("Failed creating online transaction: " + await online.errorString()); + } + + await transaction.commit("tests/wallets/transaction", false); + + const unsignedTx = await airgap.loadUnsignedTx("tests/wallets/transaction"); + if (!unsignedTx) { + throw new Error("Failed creating unsigned transaction: " + await online.errorString()); + } + + assertEquals(await unsignedTx.status(), 0); + assertEquals(unsignedTx.amount, "1000000000"); + assertEquals(unsignedTx.recipientAddress, DESTINATION_ADDRESS); + assert(!isNaN(Number(unsignedTx.fee))); + + await unsignedTx.sign("tests/wallets/signed-transaction"); + assertEquals(await unsignedTx.status(), 0); + }); + } + } +}); diff --git a/tests/utils.ts b/tests/utils.ts index 028e0ff..cd05232 100755 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -14,9 +14,9 @@ export async function downloadMoneroCli() { const MONERO_CLI_FILE_NAME = "monero-linux-x64-v0.18.3.4"; const MONERO_WALLET_CLI_URL = `https://downloads.getmonero.org/cli/${MONERO_CLI_FILE_NAME}.tar.bz2`; - await $`wget ${MONERO_WALLET_CLI_URL}`; + await $`wget -q -o /dev/null ${MONERO_WALLET_CLI_URL}`; await $ - .raw`tar -xvf ${MONERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=monero-cli --strip-components=1 -C tests`; + .raw`tar -xf ${MONERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=monero-cli --strip-components=1 -C tests`; await $.raw`rm ${MONERO_CLI_FILE_NAME}.tar.bz2`; } @@ -25,9 +25,9 @@ export async function downloadWowneroCli() { const WOWNERO_WALLET_CLI_URL = `https://codeberg.org/wownero/wownero/releases/download/v0.11.2.0/wownero-x86_64-linux-gnu-59db3fe8d.tar.bz2`; - await $`wget ${WOWNERO_WALLET_CLI_URL}`; + await $`wget -q -o /dev/null ${WOWNERO_WALLET_CLI_URL}`; await $ - .raw`tar -xvf ${WOWNERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=wownero-cli --strip-components=1 -C tests`; + .raw`tar -xf ${WOWNERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=wownero-cli --strip-components=1 -C tests`; await $.raw`rm ${WOWNERO_CLI_FILE_NAME}.tar.bz2`; } -- cgit v1.2.3 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 From 65950f46f40561f594114064009670d96a47301c Mon Sep 17 00:00:00 2001 From: cyan Date: Wed, 11 Dec 2024 12:21:44 -0500 Subject: Fix coin control error message (#94) * Fix coin control error message * fix string format --- ...0021-fix-error-messages-with-coin-control.patch | 53 ++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 patches/monero/0021-fix-error-messages-with-coin-control.patch diff --git a/patches/monero/0021-fix-error-messages-with-coin-control.patch b/patches/monero/0021-fix-error-messages-with-coin-control.patch new file mode 100644 index 0000000..b4e3625 --- /dev/null +++ b/patches/monero/0021-fix-error-messages-with-coin-control.patch @@ -0,0 +1,53 @@ +From 6259ee6acea965c2bc0bc2e4a24a73e543b062cb Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Mon, 9 Dec 2024 11:21:06 -0500 +Subject: [PATCH] fix error messages with coin control + +--- + src/wallet/api/wallet.cpp | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp +index de507b3..731055a 100644 +--- a/src/wallet/api/wallet.cpp ++++ b/src/wallet/api/wallet.cpp +@@ -2156,8 +2156,9 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector preferred_input_list; ++ uint64_t max_coin_control_input = 0; + if (!preferred_inputs.empty()) { +- LOG_ERROR("empty"); ++ LOG_ERROR("not empty"); + + for (const auto &public_key : preferred_inputs) { + crypto::key_image keyImage; +@@ -2173,6 +2174,13 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorget_num_transfer_details(); ++i) { ++ const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i); ++ if (td.m_key_image == keyImage) { ++ max_coin_control_input += td.amount(); ++ } ++ } ++ + preferred_input_list.push_back(keyImage); + } + } else { +@@ -2252,6 +2260,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector Date: Wed, 11 Dec 2024 20:48:13 -0500 Subject: detailed error messages (#95) --- ...0021-fix-error-messages-with-coin-control.patch | 57 +++++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/patches/monero/0021-fix-error-messages-with-coin-control.patch b/patches/monero/0021-fix-error-messages-with-coin-control.patch index b4e3625..1caf15c 100644 --- a/patches/monero/0021-fix-error-messages-with-coin-control.patch +++ b/patches/monero/0021-fix-error-messages-with-coin-control.patch @@ -1,28 +1,29 @@ -From 6259ee6acea965c2bc0bc2e4a24a73e543b062cb Mon Sep 17 00:00:00 2001 +From 1737ff03788db38d198955dab136d0388d674ba2 Mon Sep 17 00:00:00 2001 From: Czarek Nakamoto Date: Mon, 9 Dec 2024 11:21:06 -0500 Subject: [PATCH] fix error messages with coin control --- - src/wallet/api/wallet.cpp | 15 ++++++++++++++- - 1 file changed, 14 insertions(+), 1 deletion(-) + src/wallet/api/wallet.cpp | 44 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index de507b3..731055a 100644 +index de507b3de..3e33a318c 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp -@@ -2156,8 +2156,9 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector preferred_input_list; + uint64_t max_coin_control_input = 0; ++ uint64_t max_frozen_input = 0; if (!preferred_inputs.empty()) { - LOG_ERROR("empty"); + LOG_ERROR("not empty"); for (const auto &public_key : preferred_inputs) { crypto::key_image keyImage; -@@ -2173,6 +2174,13 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector Date: Mon, 30 Dec 2024 10:39:28 +0100 Subject: tests: run integration and regression tests on other platforms (#93) * tests: add script to download test dependencies from fallback mirrors * tests: use the new download_deps script, run tests on macos * ci: download proper artifact for macos * chore: make download_deps script download everything to dir named `monero_c` when ran directly * tests: await downloading deps * tests download proper monero_c version in prepareMoneroC * tests: fix typos * tests: add file data for more targets * tests: print why retrieving tags failed * chore: change mirror url endpoint from `monero_c` to `download_mirror` * tests: use cached releases endpoint to prevent ratelimits * ci: remove brew@1.76 dependency * tests: fix macos dylib path * feat!(monero.ts): make `createTransactionMultDest` optionally return `null` * feat(monero.ts): make `Wallet_reconnectDevice` symbol optional * tests: don't try to extract file if out already exists * tests: remove unnecesary directory rm calls * ci: set regression tests to use canary --- .github/workflows/full_check.yaml | 67 ++++++++++- .gitignore | 4 +- docs/Writerside/topics/macOS.md | 12 +- impls/monero.ts/src/symbols.ts | 2 + impls/monero.ts/src/wallet.ts | 6 +- tests/compare.ts | 17 +-- tests/deno.lock | 32 ++---- tests/download_deps.ts | 235 ++++++++++++++++++++++++++++++++++++++ tests/integration.test.ts | 28 +---- tests/regression.test.ts | 9 +- tests/utils.ts | 164 ++++++++++++++++++-------- 11 files changed, 451 insertions(+), 125 deletions(-) create mode 100644 tests/download_deps.ts diff --git a/.github/workflows/full_check.yaml b/.github/workflows/full_check.yaml index 2f73515..e083dd6 100644 --- a/.github/workflows/full_check.yaml +++ b/.github/workflows/full_check.yaml @@ -271,7 +271,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 unbound zmq autoconf automake libtool - name: Patch sources run: | git config --global --add safe.directory '*' @@ -404,7 +404,8 @@ jobs: cd impls/monero.ts deno run --unstable-ffi --allow-ffi checksum.ts - regression_check: + regression_tests_linux: + name: linux regression tests strategy: fail-fast: false matrix: @@ -431,7 +432,35 @@ jobs: - name: Run regression tests run: COIN="${{ matrix.coin }}" deno test -A tests/regression.test.ts - integration_check: + regression_tests_macos: + name: macos regression tests + strategy: + matrix: + coin: [monero, wownero] + needs: [ + lib_macos + ] + runs-on: macos-14 + steps: + - uses: denoland/setup-deno@v2 + with: + deno-version: canary + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - uses: actions/download-artifact@v4 + with: + name: macos ${{ matrix.coin }} + path: release/${{ matrix.coin }} + + - name: Run regression tests + run: COIN="${{ matrix.coin }}" deno test -A tests/regression.test.ts + + integration_tests_linux: + name: linux integration tests strategy: matrix: coin: [monero, wownero] @@ -462,6 +491,38 @@ jobs: SECRET_WALLET_RESTORE_HEIGHT: ${{ secrets.SECRET_WALLET_RESTORE_HEIGHT }} + integration_tests_macos: + name: macos integration tests + strategy: + matrix: + coin: [monero, wownero] + needs: [ + lib_macos + ] + runs-on: macos-14 + steps: + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - uses: actions/download-artifact@v4 + with: + name: macos ${{ matrix.coin }} + path: release/${{ matrix.coin }} + + - name: Run integration tests + run: COIN="${{ matrix.coin }}" deno test -A tests/integration.test.ts + env: + SECRET_WALLET_PASSWORD: ${{ secrets.SECRET_WALLET_PASSWORD }} + SECRET_WALLET_MNEMONIC: ${{ secrets.SECRET_WALLET_MNEMONIC }} + SECRET_WALLET_RESTORE_HEIGHT: ${{ secrets.SECRET_WALLET_RESTORE_HEIGHT }} + + comment_pr: name: comment on pr runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 9159f7d..7dc5983 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ release/ build/ -tests/monero-cli -tests/wownero-cli -tests/libs +tests/dependencies tests/wallets diff --git a/docs/Writerside/topics/macOS.md b/docs/Writerside/topics/macOS.md index 367893b..52b6be8 100644 --- a/docs/Writerside/topics/macOS.md +++ b/docs/Writerside/topics/macOS.md @@ -11,17 +11,15 @@ Building on linux has been tested on $ # install xcode 15.4 (or current latest) -$ brew install ccache unbound boost@1.76 zmq autoconf automake libtool -$ brew link boost@1.76 +$ brew install ccache unbound zmq autoconf automake libtool $ # install xcode 15.4 (or current latest) -$ brew install ccache unbound boost@1.76 zmq autoconf automake libtool -$ brew link boost@1.76 -$ arch -x86_64 brew install ccache unbound boost@1.76 zmq autoconf automake libtool -$ arch -x86_64 brew link boost@1.76 +$ brew install ccache unbound zmq autoconf automake libtool +$ arch -x86_64 brew install ccache unbound zmq autoconf automake libtool +$ arch -x86_64 brew link @@ -83,4 +81,4 @@ $ ./build_single.sh monero aarch64-apple-darwin-11 -j$(nproc) ### Creating fat library -[Check cake_wallet solution](https://github.com/cake-tech/cake_wallet/blob/main/scripts/macos/build_monero_all.sh) \ No newline at end of file +[Check cake_wallet solution](https://github.com/cake-tech/cake_wallet/blob/main/scripts/macos/build_monero_all.sh) diff --git a/impls/monero.ts/src/symbols.ts b/impls/monero.ts/src/symbols.ts index 2c34a6e..91d95b2 100644 --- a/impls/monero.ts/src/symbols.ts +++ b/impls/monero.ts/src/symbols.ts @@ -1548,6 +1548,7 @@ export const moneroSymbols = { ], }, MONERO_Wallet_createTransactionMultDest: { + optional: true, nonblocking: true, result: "pointer", parameters: [ @@ -1956,6 +1957,7 @@ export const moneroSymbols = { ], }, MONERO_Wallet_reconnectDevice: { + optional: true, nonblocking: true, result: "bool", parameters: ["pointer"] as [ diff --git a/impls/monero.ts/src/wallet.ts b/impls/monero.ts/src/wallet.ts index 673ccab..92832da 100644 --- a/impls/monero.ts/src/wallet.ts +++ b/impls/monero.ts/src/wallet.ts @@ -286,8 +286,8 @@ export class Wallet { preferredInputs: string[] = [], mixinCount = 0, paymentId = "", - ): Promise { - const pendingTxPtr = await fns.Wallet_createTransactionMultDest( + ): Promise { + const pendingTxPtr = await fns.Wallet_createTransactionMultDest?.( this.#ptr, CString(destinationAddresses.join(SEPARATOR)), C_SEPARATOR, @@ -301,6 +301,8 @@ export class Wallet { CString(preferredInputs.join(SEPARATOR)), C_SEPARATOR, ); + + if (!pendingTxPtr) return null; return PendingTransaction.new(pendingTxPtr as PendingTransactionPtr); } diff --git a/tests/compare.ts b/tests/compare.ts index 2fd27b8..8c13fc5 100755 --- a/tests/compare.ts +++ b/tests/compare.ts @@ -1,24 +1,13 @@ import { assertEquals } from "jsr:@std/assert"; -import { - loadMoneroDylib, - loadWowneroDylib, - moneroSymbols, - WalletManager, - wowneroSymbols, -} from "../impls/monero.ts/mod.ts"; +import { WalletManager } from "../impls/monero.ts/mod.ts"; +import { loadDylib } from "./utils.ts"; const coin = Deno.args[0] as "monero" | "wownero"; const version = Deno.args[1]; const walletInfo = JSON.parse(Deno.args[2]); -if (coin === "monero") { - const dylib = Deno.dlopen(`tests/libs/${version}/monero_libwallet2_api_c.so`, moneroSymbols); - loadMoneroDylib(dylib); -} else { - const dylib = Deno.dlopen(`tests/libs/${version}/wownero_libwallet2_api_c.so`, wowneroSymbols); - loadWowneroDylib(dylib); -} +loadDylib(coin, version); const walletManager = await WalletManager.new(); const wallet = await walletManager.openWallet(walletInfo.path, walletInfo.password); diff --git a/tests/deno.lock b/tests/deno.lock index b67d77d..5ed1a7a 100644 --- a/tests/deno.lock +++ b/tests/deno.lock @@ -9,14 +9,12 @@ "jsr:@std/bytes@0.221": "0.221.0", "jsr:@std/fmt@0.221": "0.221.0", "jsr:@std/fmt@1": "1.0.2", - "jsr:@std/fs@1": "1.0.4", + "jsr:@std/fs@1": "1.0.5", "jsr:@std/io@0.221": "0.221.0", - "jsr:@std/path@1": "1.0.6", - "jsr:@std/path@1.0.8": "1.0.8", - "jsr:@std/path@^1.0.6": "1.0.6", - "jsr:@std/streams@0.221": "0.221.0", - "jsr:@std/streams@^1.0.7": "1.0.7", - "jsr:@std/tar@*": "0.1.2" + "jsr:@std/path@*": "1.0.8", + "jsr:@std/path@1": "1.0.8", + "jsr:@std/path@^1.0.7": "1.0.8", + "jsr:@std/streams@0.221": "0.221.0" }, "jsr": { "@david/dax@0.42.0": { @@ -28,7 +26,7 @@ "jsr:@std/fs", "jsr:@std/io", "jsr:@std/path@1", - "jsr:@std/streams@0.221" + "jsr:@std/streams" ] }, "@david/path@0.2.0": { @@ -56,10 +54,10 @@ "@std/fmt@1.0.2": { "integrity": "87e9dfcdd3ca7c066e0c3c657c1f987c82888eb8103a3a3baa62684ffeb0f7a7" }, - "@std/fs@1.0.4": { - "integrity": "2907d32d8d1d9e540588fd5fe0ec21ee638134bd51df327ad4e443aaef07123c", + "@std/fs@1.0.5": { + "integrity": "41806ad6823d0b5f275f9849a2640d87e4ef67c51ee1b8fb02426f55e02fd44e", "dependencies": [ - "jsr:@std/path@^1.0.6" + "jsr:@std/path@^1.0.7" ] }, "@std/io@0.221.0": { @@ -69,9 +67,6 @@ "jsr:@std/bytes" ] }, - "@std/path@1.0.6": { - "integrity": "ab2c55f902b380cf28e0eec501b4906e4c1960d13f00e11cfbcd21de15f18fed" - }, "@std/path@1.0.8": { "integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be" }, @@ -80,15 +75,6 @@ "dependencies": [ "jsr:@std/io" ] - }, - "@std/streams@1.0.7": { - "integrity": "1a93917ca0c58c01b2bfb93647189229b1702677f169b6fb61ad6241cd2e499b" - }, - "@std/tar@0.1.2": { - "integrity": "98183102395decd6268253996177804f818580ef547a25b81da0e7cc334db708", - "dependencies": [ - "jsr:@std/streams@^1.0.7" - ] } } } diff --git a/tests/download_deps.ts b/tests/download_deps.ts new file mode 100644 index 0000000..808640e --- /dev/null +++ b/tests/download_deps.ts @@ -0,0 +1,235 @@ +import { join, resolve } from "jsr:@std/path"; +import { Coin, getMoneroCTags } from "./utils.ts"; + +export type Target = `${typeof Deno["build"]["os"]}_${typeof Deno["build"]["arch"]}`; + +interface FileInfo { + overrideMirrors?: string[]; + name: string; + sha256?: string; +} + +interface DownloadInfo { + mirrors: string[]; + file: + | FileInfo + | { [os in Target]?: FileInfo }; + outDir?: string; +} + +export function getFileInfo( + downloadInfo: DownloadInfo, + target: Target = `${Deno.build.os}_${Deno.build.arch}`, +): FileInfo { + const fileInfo = "name" in downloadInfo.file ? downloadInfo.file : downloadInfo.file[target]; + if (!fileInfo) { + throw new Error(`No fileInfo set for target: ${target}`); + } + return fileInfo; +} + +async function sha256(buffer: Uint8Array): Promise { + const hashed = new Uint8Array(await crypto.subtle.digest("SHA-256", buffer)); + return Array.from(hashed).map((i) => i.toString(16).padStart(2, "0")).join(""); +} + +export async function downloadDependencies(...infos: DownloadInfo[]): Promise { + return await downloadFiles("./tests/dependencies", `${Deno.build.os}_${Deno.build.arch}`, ...infos); +} + +export async function downloadFiles(outDir: string, target: Target, ...infos: DownloadInfo[]): Promise { + try { + await Deno.mkdir(outDir, { recursive: true }); + } catch (error) { + if (!(error instanceof Deno.errors.AlreadyExists)) { + throw error; + } + } + + for (const info of infos) { + const fileInfo = getFileInfo(info, target); + const fileName = fileInfo.name; + const filePath = join(outDir, info.outDir ?? "", fileName); + + file_might_exist: try { + const fileBuffer = await Deno.readFile(filePath); + + // File exists, make sure checksum matches + if (fileInfo.sha256) { + const fileChecksum = await sha256(fileBuffer); + if (fileChecksum !== fileInfo.sha256) { + console.log( + `File ${fileName} already exists, but checksum is mismatched (${fileChecksum} != ${fileInfo.sha256}), redownloading`, + ); + await Deno.remove(filePath); + break file_might_exist; + } + } + + console.log(`File ${fileName} already exists, skipping`); + continue; + } catch { /**/ } + + let buffer: Uint8Array | undefined; + + for (const mirror of fileInfo.overrideMirrors ?? info.mirrors) { + const url = `${mirror}/${fileName}`; + + const response = await fetch(url); + if (!response.ok) { + console.warn(`Could not reach file ${fileName} on mirror: ${mirror}`); + await response.body?.cancel(); + continue; + } + + const responseBuffer = await response.bytes(); + + if (fileInfo.sha256) { + const responseChecksum = await sha256(responseBuffer); + if (responseChecksum !== fileInfo.sha256) { + console.warn( + `Checksum mismatch on file ${fileName} on mirror: ${mirror} (${responseChecksum} != ${fileInfo.sha256})`, + ); + continue; + } + } + + buffer = responseBuffer; + } + + if (!buffer) { + throw new Error(`None of the mirrors for ${fileName} are available`); + } + + await Deno.mkdir(resolve(filePath, ".."), { + recursive: true, + }).catch(() => {}); + + await Deno.writeFile(filePath, buffer); + console.info("Downloaded file", fileInfo.name); + } +} + +export const wowneroCliInfo: DownloadInfo = { + mirrors: [ + "https://static.mrcyjanek.net/download_mirror/", + "https://codeberg.org/wownero/wownero/releases/download/v0.11.2.0/", + ], + file: { + linux_aarch64: { + name: "wownero-aarch64-linux-gnu-59db3fe8d.tar.bz2", + sha256: "07ce678302c07a6e79d90be65cbda243d843d414fbadb30f972d6c226575cfa7", + }, + linux_x86_64: { + name: "wownero-x86_64-linux-gnu-59db3fe8d.tar.bz2", + sha256: "03880967c70cc86558d962b8a281868c3934238ea457a36174ba72b99d70107e", + }, + + darwin_aarch64: { + name: "wownero-aarch64-apple-darwin11-59db3fe8d.tar.bz2", + sha256: "25ff454a92b1cf036df5f28cdd2c63dcaf4b03da7da9403087371f868827c957", + }, + darwin_x86_64: { + name: "wownero-x86_64-apple-darwin11-59db3fe8d.tar.bz2", + sha256: "7e9b6a84a560ed7a9ed7117c6f07fb228d77a06afac863d0ea1dbf833c4eddf6", + }, + + windows_x86_64: { + name: "wownero-x86_64-w64-mingw32-59db3fe8d.zip", + sha256: "7e0ed84afa51e3b403d635c706042859094eb6850de21c9e82cb0a104425510e", + }, + + android_aarch64: { + overrideMirrors: [ + "https://static.mrcyjanek.net/download_mirror/", + "https://codeberg.org/wownero/wownero/releases/download/v0.11.1.0/", + ], + name: "wownero-aarch64-linux-android-v0.11.1.0.tar.bz2", + sha256: "236188f8d8e7fad2ff35973f8c2417afffa8855d1a57b4c682fff5b199ea40f5", + }, + }, +}; + +export const moneroCliInfo: DownloadInfo = { + mirrors: [ + "https://static.mrcyjanek.net/download_mirror/", + "https://downloads.getmonero.org/cli/", + ], + file: { + linux_aarch64: { + name: "monero-linux-armv8-v0.18.3.4.tar.bz2", + sha256: "33ca2f0055529d225b61314c56370e35606b40edad61c91c859f873ed67a1ea7", + }, + linux_x86_64: { + name: "monero-linux-x64-v0.18.3.4.tar.bz2", + sha256: "51ba03928d189c1c11b5379cab17dd9ae8d2230056dc05c872d0f8dba4a87f1d", + }, + + darwin_aarch64: { + name: "monero-mac-armv8-v0.18.3.4.tar.bz2", + sha256: "44520cb3a05c2518ca9aeae1b2e3080fe2bba1e3596d014ceff1090dfcba8ab4", + }, + darwin_x86_64: { + name: "monero-mac-x64-v0.18.3.4.tar.bz2", + sha256: "32c449f562216d3d83154e708471236d07db7477d6b67f1936a0a85a5005f2b8", + }, + + windows_x86_64: { + name: "monero-win-x64-v0.18.3.4.zip", + sha256: "54a66db6c892b2a0999754841f4ca68511741b88ea3ab20c7cd504a027f465f5", + }, + + android_aarch64: { + name: "monero-android-armv8-v0.18.3.4.tar.bz2", + sha256: "d9c9249d1408822ce36b346c6b9fb6b896cda16714d62117fb1c588a5201763c", + }, + }, +}; + +export const dylibInfos: Record = { + monero: [], + wownero: [], +}; + +for (const tag of await getMoneroCTags()) { + for (const coin of ["monero", "wownero"] as const) { + dylibInfos[coin].push({ + mirrors: [ + `https://static.mrcyjanek.net/download_mirror/libs/${tag}/`, + `https://github.com/MrCyjaneK/monero_c/releases/download/${tag}/`, + ], + file: { + linux_aarch64: { name: `${coin}_aarch64-linux-gnu_libwallet2_api_c.so.xz` }, + linux_x86_64: { name: `${coin}_x86_64-linux-gnu_libwallet2_api_c.so.xz` }, + darwin_aarch64: { name: `${coin}_aarch64-apple-darwin11_libwallet2_api_c.dylib.xz` }, + darwin_x86_64: { name: `${coin}_x86_64-apple-darwin11_libwallet2_api_c.dylib.xz` }, + windows_x86_64: { name: `${coin}_x86_64-w64-mingw32_libwallet2_api_c.dll.xz` }, + android_aarch64: { name: `${coin}_aarch64-linux-android_libwallet2_api_c.so.xz` }, + }, + outDir: `libs/${tag}`, + }); + } +} + +// Download files to the download_mirror folder +// (used on mirror to keep files up to date) +if (import.meta.main) { + const supportedTargets: Target[] = [ + "linux_x86_64", + "linux_aarch64", + "darwin_x86_64", + "darwin_aarch64", + "windows_x86_64", + "android_aarch64", + ]; + + for (const target of supportedTargets) { + await downloadFiles( + "./download_mirror", + target, + moneroCliInfo, + wowneroCliInfo, + ...Object.values(dylibInfos).flat(), + ); + } +} diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 1a65009..100bd43 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,16 +1,7 @@ -import { - CoinsInfo, - type Dylib, - loadMoneroDylib, - loadWowneroDylib, - moneroSymbols, - Wallet, - WalletManager, - wowneroSymbols, -} from "../impls/monero.ts/mod.ts"; +import { CoinsInfo, Wallet, WalletManager } from "../impls/monero.ts/mod.ts"; import { assert, assertEquals } from "jsr:@std/assert"; -import { $, downloadCli, getMoneroC } from "./utils.ts"; +import { $, loadDylib, prepareCli, prepareMoneroC } from "./utils.ts"; const coin = Deno.env.get("COIN"); if (coin !== "monero" && coin !== "wownero") { @@ -53,7 +44,7 @@ const DESTINATION_ADDRESS = coin === "monero" ? MONERO_DESTINATION_ADDRESS : WOW const BILLION = 10n ** 9n; -await getMoneroC(coin, "next"); +await prepareMoneroC(coin, "next"); interface WalletInfo { name: string; @@ -74,14 +65,7 @@ async function clearWallets() { await Deno.mkdir("tests/wallets/"); } -let dylib: Dylib; -if (coin === "monero") { - dylib = Deno.dlopen(`tests/libs/next/monero_libwallet2_api_c.so`, moneroSymbols); - loadMoneroDylib(dylib); -} else { - dylib = Deno.dlopen(`tests/libs/next/wownero_libwallet2_api_c.so`, wowneroSymbols); - loadWowneroDylib(dylib); -} +loadDylib(coin, "next"); Deno.test("0001-polyseed.patch", async (t) => { const WALLETS: Record<"monero" | "wownero", WalletInfo[]> = { @@ -487,7 +471,7 @@ Deno.test("0004-coin-control.patch", { Deno.test("0009-Add-recoverDeterministicWalletFromSpendKey.patch", async () => { await Promise.all([ - downloadCli(coin), + prepareCli(coin), clearWallets(), ]); @@ -498,7 +482,7 @@ Deno.test("0009-Add-recoverDeterministicWalletFromSpendKey.patch", async () => { await Deno.remove("./tests/wallets/stoat"); - const cliPath = `./tests/${coin}-cli/${coin}-wallet-cli`; + const cliPath = `./tests/dependencies/${coin}-cli/${coin}-wallet-cli`; const moneroCliSeed = (await $.raw`${cliPath} --wallet-file ./tests/wallets/stoat --password gornostay --command seed` .stdinText(`gornostay\n`) .lines()).slice(-3).join(" "); diff --git a/tests/regression.test.ts b/tests/regression.test.ts index 82a9f95..797720f 100755 --- a/tests/regression.test.ts +++ b/tests/regression.test.ts @@ -1,4 +1,4 @@ -import { $, createWalletViaCli, downloadCli, getMoneroC, getMoneroCTags } from "./utils.ts"; +import { $, createWalletViaCli, getMoneroCTags, prepareCli, prepareMoneroC } from "./utils.ts"; const coin = Deno.env.get("COIN"); if (coin !== "monero" && coin !== "wownero") { @@ -11,7 +11,7 @@ Deno.test(`Regression tests (${coin})`, async (t) => { const tags = await getMoneroCTags(); const latestTag = tags[0]; - await Promise.all([getMoneroC(coin, "next"), await getMoneroC(coin, latestTag), downloadCli(coin)]); + await Promise.all([prepareMoneroC(coin, "next"), await prepareMoneroC(coin, latestTag), prepareCli(coin)]); await t.step("Simple (next, latest, next)", async () => { const walletInfo = await createWalletViaCli(coin, "dog", "sobaka"); @@ -27,13 +27,10 @@ Deno.test(`Regression tests (${coin})`, async (t) => { const walletInfo = await createWalletViaCli(coin, "cat", "koshka"); for (const version of tags.toReversed()) { - if (version !== "next" && version !== tags[0]) await getMoneroC(coin, version); + if (version !== "next" && version !== tags[0]) await prepareMoneroC(coin, version); await $`deno run -A ./tests/compare.ts ${coin} ${version} ${JSON.stringify(walletInfo)}`; } - - await Deno.remove("./tests/wallets", { recursive: true }).catch(() => {}); }); await Deno.remove("./tests/wallets", { recursive: true }).catch(() => {}); - await Deno.remove("./tests/libs", { recursive: true }).catch(() => {}); }); diff --git a/tests/utils.ts b/tests/utils.ts index cd05232..86501a8 100755 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -1,4 +1,18 @@ import { build$, CommandBuilder } from "jsr:@david/dax"; +import { dirname, join } from "jsr:@std/path"; +import { + downloadDependencies, + dylibInfos, + getFileInfo, + moneroCliInfo, + Target, + wowneroCliInfo, +} from "./download_deps.ts"; +import { loadMoneroDylib, loadWowneroDylib, moneroSymbols, wowneroSymbols } from "../impls/monero.ts/mod.ts"; + +export type Coin = "monero" | "wownero"; + +const target = `${Deno.build.os}_${Deno.build.arch}` as const; export const $ = build$({ commandBuilder: new CommandBuilder() @@ -8,34 +22,83 @@ export const $ = build$({ .stderr("inherit"), }); -type Coin = "monero" | "wownero"; +export const dylibNames = (coin: Coin, version: MoneroCVersion) => ({ + linux_x86_64: `${coin}_x86_64-linux-gnu_libwallet2_api_c.so`, + darwin_aarch64: version === "next" + ? `${coin}_aarch64-apple-darwin_libwallet2_api_c.dylib` + : `${coin}_aarch64-apple-darwin11_libwallet2_api_c.dylib`, + windows_x86_64: `${coin}_x86_64-w64-mingw32_libwallet2_api_c.dll`, +} as Partial>); + +export const moneroTsDylibNames = (coin: Coin) => ({ + linux_x86_64: `${coin}_libwallet2_api_c.so`, + darwin_aarch64: `${coin}_aarch64-apple-darwin11_libwallet2_api_c.dylib`, + windows_x86_64: `${coin}_libwallet2_api_c.dll`, +} as Partial>); + +export function loadDylib(coin: Coin, version: MoneroCVersion) { + const dylibName = moneroTsDylibNames(coin)[target]!; + + if (coin === "monero") { + const dylib = Deno.dlopen(`tests/dependencies/libs/${version}/${dylibName}`, moneroSymbols); + loadMoneroDylib(dylib); + return dylib; + } else { + const dylib = Deno.dlopen(`tests/dependencies/libs/${version}/${dylibName}`, wowneroSymbols); + loadWowneroDylib(dylib); + return dylib; + } +} -export async function downloadMoneroCli() { - const MONERO_CLI_FILE_NAME = "monero-linux-x64-v0.18.3.4"; - const MONERO_WALLET_CLI_URL = `https://downloads.getmonero.org/cli/${MONERO_CLI_FILE_NAME}.tar.bz2`; +async function exists(path: string): Promise { + try { + await Deno.stat(path); + return true; + } catch (error) { + if (error instanceof Deno.errors.NotFound) { + return false; + } + throw error; + } +} - await $`wget -q -o /dev/null ${MONERO_WALLET_CLI_URL}`; - await $ - .raw`tar -xf ${MONERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=monero-cli --strip-components=1 -C tests`; - await $.raw`rm ${MONERO_CLI_FILE_NAME}.tar.bz2`; +export async function extract(path: string, out: string) { + const outDir = out.endsWith("/") ? out : dirname(out); + await Deno.mkdir(outDir, { recursive: true }); + + if (path.endsWith(".tar.bz2")) { + let args = `-C ${dirname(out)}`; + if (outDir === out) { + args = `-C ${out} --strip-components=1`; + } + await $.raw`tar -xf ${path} ${args}`; + } else if (path.endsWith(".zip")) { + await $.raw`unzip ${path} -nu -d ${outDir}`; + } else if (path.endsWith(".xz")) { + await $.raw`xz -kd ${path}`; + await Deno.rename(path.slice(0, -3), out); + } else { + throw new Error("Unsupported archive file for:" + path); + } } -export async function downloadWowneroCli() { - const WOWNERO_CLI_FILE_NAME = "wownero-x86_64-linux-gnu-59db3fe8d"; - const WOWNERO_WALLET_CLI_URL = - `https://codeberg.org/wownero/wownero/releases/download/v0.11.2.0/wownero-x86_64-linux-gnu-59db3fe8d.tar.bz2`; +export async function prepareMoneroCli() { + await downloadDependencies(moneroCliInfo); + const path = join("./tests/dependencies", moneroCliInfo.outDir ?? "", getFileInfo(moneroCliInfo).name); + await extract(path, "./tests/dependencies/monero-cli/"); +} - await $`wget -q -o /dev/null ${WOWNERO_WALLET_CLI_URL}`; - await $ - .raw`tar -xf ${WOWNERO_CLI_FILE_NAME}.tar.bz2 --one-top-level=wownero-cli --strip-components=1 -C tests`; - await $.raw`rm ${WOWNERO_CLI_FILE_NAME}.tar.bz2`; +export async function prepareWowneroCli() { + await downloadDependencies(wowneroCliInfo); + const path = join("./tests/dependencies", wowneroCliInfo.outDir ?? "", getFileInfo(wowneroCliInfo).name); + await extract(path, "./tests/dependencies/wownero-cli/"); } -export function downloadCli(coin: Coin) { +export function prepareCli(coin: Coin) { if (coin === "wownero") { - return downloadWowneroCli(); + return prepareWowneroCli(); } - return downloadMoneroCli(); + return prepareMoneroCli(); } interface WalletInfo { @@ -54,7 +117,7 @@ export async function createWalletViaCli( password: string, ): Promise { const path = `./tests/wallets/${name}`; - const cliPath = `./tests/${coin}-cli/${coin}-wallet-cli`; + const cliPath = `./tests/dependencies/${coin}-cli/${coin}-wallet-cli`; await $ .raw`${cliPath} --generate-new-wallet ${path} --password ${password} --mnemonic-language English --command exit` @@ -97,35 +160,46 @@ export async function createWalletViaCli( export type MoneroCVersion = "next" | (string & {}); export async function getMoneroCTags(): Promise { - return (( - await (await fetch( - "https://api.github.com/repos/MrCyjanek/monero_c/releases", - )).json() - ) as { tag_name: string }[]) - .map(({ tag_name }) => tag_name); + const response = await fetch("https://static.mrcyjanek.net/monero_c/release.php"); + + if (!response.ok) { + throw new Error(`Could not receive monero_c release tags: ${await response.text()}`); + } + + const json = await response.json() as { tag_name: string }[]; + return json.map(({ tag_name }) => tag_name); } -export async function getMoneroC(coin: Coin, version: MoneroCVersion) { - const dylibName = `${coin}_x86_64-linux-gnu_libwallet2_api_c.so`; - const endpointDylibName = `${coin}_libwallet2_api_c.so`; + +export async function prepareMoneroC(coin: Coin, version: MoneroCVersion) { + const dylibName = dylibNames(coin, version)[target]; + const moneroTsDylibName = moneroTsDylibNames(coin)[target]; + + if (!dylibName || !moneroTsDylibName) { + throw new Error(`Missing dylib name value for target: ${target}`); + } + const releaseDylibName = dylibName.slice(`${coin}_`.length); if (version === "next") { - await $.raw`xz -kd release/${coin}/${releaseDylibName}.xz`; - await $`mkdir -p tests/libs/next`; - await $`mv release/${coin}/${releaseDylibName} tests/libs/next/${endpointDylibName}`; + const outFileDir = `./tests/dependencies/libs/${version}/${moneroTsDylibName}`; + + if (await exists(outFileDir)) { + return; + } + + await extract(`./release/${coin}/${releaseDylibName}.xz`, outFileDir); } else { - const downloadUrl = `https://github.com/MrCyjaneK/monero_c/releases/download/${version}/${dylibName}.xz`; - - const file = await Deno.open(`./tests/${dylibName}.xz`, { - create: true, - write: true, - }); - file.write(await (await fetch(downloadUrl)).bytes()); - file.close(); - - await $.raw`xz -d ./tests/${dylibName}.xz`; - await $.raw`mkdir -p ./tests/libs/${version}`; - await $ - .raw`mv ./tests/${dylibName} ./tests/libs/${version}/${endpointDylibName}`; + const outFileDir = `./tests/dependencies/libs/${version}/${moneroTsDylibName}`; + + if (await exists(outFileDir)) { + return; + } + + const downloadInfo = dylibInfos[coin].find((info) => info.outDir?.endsWith(version)); + if (downloadInfo) { + await downloadDependencies(downloadInfo); + } + + await extract(`./tests/dependencies/libs/${version}/${dylibName}.xz`, outFileDir); } } -- cgit v1.2.3 From 58267fe5bd07b0a22f533f0139de013ee9087c2f Mon Sep 17 00:00:00 2001 From: cyan Date: Mon, 30 Dec 2024 13:49:32 +0000 Subject: update coin-control patch --- patches/monero/0009-coin-control.patch | 105 ++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 21 deletions(-) diff --git a/patches/monero/0009-coin-control.patch b/patches/monero/0009-coin-control.patch index 1aac12a..4c4b842 100644 --- a/patches/monero/0009-coin-control.patch +++ b/patches/monero/0009-coin-control.patch @@ -1,7 +1,7 @@ -From 4d897d9ee1d24710500f4d58e9ccd79fb48cf1d2 Mon Sep 17 00:00:00 2001 +From d15a18cac55cb06d5421ecfef1118e439d0cd572 Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 11:07:57 +0100 -Subject: [PATCH 09/14] coin control +Subject: [PATCH 10/15] coin control --- src/simplewallet/simplewallet.cpp | 2 +- @@ -10,19 +10,19 @@ Subject: [PATCH 09/14] coin control 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.cpp | 106 ++++++++++++++++- 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(-) + 11 files changed, 635 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 +index 2c51337ef..645bd37e2 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 @@ -35,7 +35,7 @@ index 2c51337..645bd37 100644 if (ptx_vector.empty()) { diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt -index af7948d..bb740e2 100644 +index af7948d8a..bb740e2ac 100644 --- a/src/wallet/api/CMakeLists.txt +++ b/src/wallet/api/CMakeLists.txt @@ -40,7 +40,9 @@ set(wallet_api_sources @@ -62,7 +62,7 @@ index af7948d..bb740e2 100644 ${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 +index 000000000..ef12141cf --- /dev/null +++ b/src/wallet/api/coins.cpp @@ -0,0 +1,186 @@ @@ -254,7 +254,7 @@ index 0000000..ef12141 +} // namespace diff --git a/src/wallet/api/coins.h b/src/wallet/api/coins.h new file mode 100644 -index 0000000..b7a0a86 +index 000000000..b7a0a8642 --- /dev/null +++ b/src/wallet/api/coins.h @@ -0,0 +1,40 @@ @@ -300,7 +300,7 @@ index 0000000..b7a0a86 +#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 +index 000000000..5f2c4e1e4 --- /dev/null +++ b/src/wallet/api/coins_info.cpp @@ -0,0 +1,122 @@ @@ -428,7 +428,7 @@ index 0000000..5f2c4e1 +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 +index 000000000..c43e45abd --- /dev/null +++ b/src/wallet/api/coins_info.h @@ -0,0 +1,71 @@ @@ -504,7 +504,7 @@ index 0000000..c43e45a + +#endif //FEATHER_COINS_INFO_H diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 67ac90a..6bb3a21 100644 +index 67ac90a46..a76d773ba 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -35,6 +35,7 @@ @@ -548,7 +548,7 @@ index 67ac90a..6bb3a21 100644 de.is_subaddress = info.is_subaddress; de.is_integrated = info.has_payment_id; dsts.push_back(de); -@@ -2115,6 +2119,51 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector preferred_input_list; ++ uint64_t max_coin_control_input = 0; ++ uint64_t max_frozen_input = 0; + if (!preferred_inputs.empty()) { -+ LOG_ERROR("empty"); ++ LOG_ERROR("not empty"); + + for (const auto &public_key : preferred_inputs) { + crypto::key_image keyImage; @@ -576,6 +578,16 @@ index 67ac90a..6bb3a21 100644 + break; + } + ++ 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); ++ if (td.m_key_image == keyImage) { ++ max_coin_control_input += td.amount(); ++ } ++ if (td.m_frozen) { ++ max_frozen_input += td.amount(); ++ } ++ } ++ + preferred_input_list.push_back(keyImage); + } + } else { @@ -600,7 +612,7 @@ index 67ac90a..6bb3a21 100644 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, @@ -614,7 +626,58 @@ index 67ac90a..6bb3a21 100644 } pendingTxPostProcess(transaction); -@@ -2214,10 +2263,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector amount, uint32_t mixin_count, @@ -627,7 +690,7 @@ index 67ac90a..6bb3a21 100644 } PendingTransaction *WalletImpl::createSweepUnmixableTransaction() -@@ -2342,6 +2391,11 @@ AddressBook *WalletImpl::addressBook() +@@ -2342,6 +2433,11 @@ AddressBook *WalletImpl::addressBook() return m_addressBook.get(); } @@ -640,7 +703,7 @@ index 67ac90a..6bb3a21 100644 { return m_subaddress.get(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h -index 32e1228..a82f270 100644 +index 32e12284b..a82f270e4 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -46,6 +46,7 @@ class PendingTransactionImpl; @@ -693,7 +756,7 @@ index 32e1228..a82f270 100644 // multi-threaded refresh stuff diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h -index be1c370..013b5bc 100644 +index be1c3704e..013b5bcba 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -263,6 +263,51 @@ struct AddressBook @@ -777,7 +840,7 @@ index be1c370..013b5bc 100644 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 +index fa346a96e..d060bf95b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2094,12 +2094,21 @@ bool wallet2::frozen(const multisig_tx_set& txs) const @@ -924,7 +987,7 @@ index fa346a9..d060bf9 100644 { 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 +index 91cf2a376..bc16d528c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1209,8 +1209,8 @@ private: @@ -975,5 +1038,5 @@ index 91cf2a3..bc16d52 100644 void set_unspent(size_t idx); bool is_spent(const transfer_details &td, bool strict = true) const; -- -2.39.5 (Apple Git-154) +2.43.0 -- cgit v1.2.3 From 785aca81ea4aa0081a92198e19d680d9661cba9d Mon Sep 17 00:00:00 2001 From: cyan Date: Mon, 30 Dec 2024 13:50:10 +0000 Subject: remove patch --- ...0021-fix-error-messages-with-coin-control.patch | 96 ---------------------- 1 file changed, 96 deletions(-) delete mode 100644 patches/monero/0021-fix-error-messages-with-coin-control.patch diff --git a/patches/monero/0021-fix-error-messages-with-coin-control.patch b/patches/monero/0021-fix-error-messages-with-coin-control.patch deleted file mode 100644 index 1caf15c..0000000 --- a/patches/monero/0021-fix-error-messages-with-coin-control.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 1737ff03788db38d198955dab136d0388d674ba2 Mon Sep 17 00:00:00 2001 -From: Czarek Nakamoto -Date: Mon, 9 Dec 2024 11:21:06 -0500 -Subject: [PATCH] fix error messages with coin control - ---- - src/wallet/api/wallet.cpp | 44 ++++++++++++++++++++++++++++++++++++++- - 1 file changed, 43 insertions(+), 1 deletion(-) - -diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index de507b3de..3e33a318c 100644 ---- a/src/wallet/api/wallet.cpp -+++ b/src/wallet/api/wallet.cpp -@@ -2156,8 +2156,10 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector preferred_input_list; -+ uint64_t max_coin_control_input = 0; -+ uint64_t max_frozen_input = 0; - if (!preferred_inputs.empty()) { -- LOG_ERROR("empty"); -+ LOG_ERROR("not empty"); - - for (const auto &public_key : preferred_inputs) { - crypto::key_image keyImage; -@@ -2173,6 +2175,16 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorget_num_transfer_details(); ++i) { -+ const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i); -+ if (td.m_key_image == keyImage) { -+ max_coin_control_input += td.amount(); -+ } -+ if (td.m_frozen) { -+ max_frozen_input += td.amount(); -+ } -+ } -+ - preferred_input_list.push_back(keyImage); - } - } else { -@@ -2236,6 +2248,16 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector Date: Mon, 30 Dec 2024 16:15:33 +0000 Subject: fix wownero crashing issue --- impls/monero.dart/lib/src/checksum_wownero.dart | 2 +- impls/monero.ts/checksum_wownero.ts | 2 +- wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp | 8 ++++++-- wownero_libwallet2_api_c/src/main/cpp/wownero_checksum.h | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/impls/monero.dart/lib/src/checksum_wownero.dart b/impls/monero.dart/lib/src/checksum_wownero.dart index 615a989..bada1f4 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 = "f99009d1ca1d1c783cc9aa0fb63f680d48753b88124fb5de2079c57b7e34c827"; -const String wallet2_api_c_cpp_sha256 = "880add77ec8c77d8054a6f21e996d6d08a37ef84e10df7220630426fd048b43c-dd46a31f3cab67b316e9239b15acf7d5cea60aa9"; +const String wallet2_api_c_cpp_sha256 = "70b39a0bef660cb9ba0397117eb1590e18461ace89ab18141690658a2a537d5d-dd46a31f3cab67b316e9239b15acf7d5cea60aa9"; const String wallet2_api_c_exp_sha256 = "5f53ea8bbe66a5e5aa6cbc4ca00695900e08589cfd32062e88965a24252d05ba"; diff --git a/impls/monero.ts/checksum_wownero.ts b/impls/monero.ts/checksum_wownero.ts index 2a6007f..7e0c8cd 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: "f99009d1ca1d1c783cc9aa0fb63f680d48753b88124fb5de2079c57b7e34c827", - wallet2_api_c_cpp_sha256: "880add77ec8c77d8054a6f21e996d6d08a37ef84e10df7220630426fd048b43c-dd46a31f3cab67b316e9239b15acf7d5cea60aa9", + wallet2_api_c_cpp_sha256: "70b39a0bef660cb9ba0397117eb1590e18461ace89ab18141690658a2a537d5d-dd46a31f3cab67b316e9239b15acf7d5cea60aa9", wallet2_api_c_exp_sha256: "5f53ea8bbe66a5e5aa6cbc4ca00695900e08589cfd32062e88965a24252d05ba", } 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 0d53cb5..44887f6 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 @@ -2454,8 +2454,12 @@ void* WOWNERO_deprecated_restore14WordSeed(char *path, char *password, char *see uint64_t WOWNERO_deprecated_14WordSeedHeight(char *seed) { DEBUG_START() - wownero_seed wow_seed(seed, "wownero"); - return wow_seed.blockheight(); + try { + wownero_seed wow_seed(seed, "wownero"); + return wow_seed.blockheight(); + } catch(...) { + return 1; + } DEBUG_END() } 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 60a7112..e366e07 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 = "f99009d1ca1d1c783cc9aa0fb63f680d48753b88124fb5de2079c57b7e34c827"; -const char * WOWNERO_wallet2_api_c_cpp_sha256 = "880add77ec8c77d8054a6f21e996d6d08a37ef84e10df7220630426fd048b43c-dd46a31f3cab67b316e9239b15acf7d5cea60aa9"; +const char * WOWNERO_wallet2_api_c_cpp_sha256 = "70b39a0bef660cb9ba0397117eb1590e18461ace89ab18141690658a2a537d5d-dd46a31f3cab67b316e9239b15acf7d5cea60aa9"; const char * WOWNERO_wallet2_api_c_exp_sha256 = "5f53ea8bbe66a5e5aa6cbc4ca00695900e08589cfd32062e88965a24252d05ba"; #endif -- cgit v1.2.3 From 073b85ad010044fa62ed7a21bd52febef522c35a Mon Sep 17 00:00:00 2001 From: cyan Date: Fri, 3 Jan 2025 15:46:03 +0100 Subject: wrap 14WordSeedHeight (#97) * wrap 14WordSeedHeight * update checksums --- impls/monero.dart/lib/src/checksum_wownero.dart | 2 +- impls/monero.ts/checksum_wownero.ts | 2 +- wownero_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp | 8 ++++++-- wownero_libwallet2_api_c/src/main/cpp/wownero_checksum.h | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/impls/monero.dart/lib/src/checksum_wownero.dart b/impls/monero.dart/lib/src/checksum_wownero.dart index 7d501c6..dee3b0e 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_cpp_sha256 = "64a0ea40b62cce04987c7efb8a6e6deceac8d91d4bc564a1356711c000155ffd-e25963cbc11ca0a0fe5eb34b9bd7c72e4f51b795"; const String wallet2_api_c_exp_sha256 = "3673e40e1a7115552276d1d541f6e4d5a0fef47c40fff7b988f49923af84c8a4"; diff --git a/impls/monero.ts/checksum_wownero.ts b/impls/monero.ts/checksum_wownero.ts index 8b2899c..b7df828 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_cpp_sha256: "64a0ea40b62cce04987c7efb8a6e6deceac8d91d4bc564a1356711c000155ffd-e25963cbc11ca0a0fe5eb34b9bd7c72e4f51b795", wallet2_api_c_exp_sha256: "3673e40e1a7115552276d1d541f6e4d5a0fef47c40fff7b988f49923af84c8a4", } 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..af062b6 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 @@ -1923,8 +1923,12 @@ void* WOWNERO_deprecated_restore14WordSeed(char *path, char *password, char *see } uint64_t WOWNERO_deprecated_14WordSeedHeight(char *seed) { - wownero_seed wow_seed(seed, "wownero"); - return wow_seed.blockheight(); + try { + wownero_seed wow_seed(seed, "wownero"); + return wow_seed.blockheight(); + } catch (...) { + return 2; + } } void* WOWNERO_deprecated_create14WordSeed(char *path, char *password, char *language, int32_t networkType) { 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..376f377 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_cpp_sha256 = "64a0ea40b62cce04987c7efb8a6e6deceac8d91d4bc564a1356711c000155ffd-e25963cbc11ca0a0fe5eb34b9bd7c72e4f51b795"; const char * WOWNERO_wallet2_api_c_exp_sha256 = "3673e40e1a7115552276d1d541f6e4d5a0fef47c40fff7b988f49923af84c8a4"; #endif -- cgit v1.2.3 From fcc2924f31e6ecc1ad787ab6c704188c393318aa Mon Sep 17 00:00:00 2001 From: cyan Date: Sat, 4 Jan 2025 09:02:44 +0100 Subject: initial zano commit (#83) * initial zano commit * update checksum, fix zano patches on CI * fix monero builds * fix cmake command * fix: devcontainer on x64 ffigen: add zano zano: add missing free dart: implement zano * update boost filenames * unboost the cmakelists * fix zano boost issues * added patch into proper location * fix various build issues * [skip ci] update tor-connect * fix zano builds for ios * fix apply patches and don't fail-fast * uncomment build depends for monero * build_single.sh fix for macos native builds * disable qemu on arm64 builders from buildjet * fix boost, fix missing symbols (maybe) * fix ordering of crypto and ssl libraries * fix wownero mingw * fetch zano releases to release-bulk * build things 'the zano way' * proper cmake config * Zano.. yeah... * Update zano release to 2.0.1.367 * update zano patches * update zano builds * update zano build * fix zano build * move zlibstatic to the top (this shouldn't matter anyway) * fix patch location, update tor-connect * update ci runner * fix zano build on the CI * enable zano for other targets * nvm * don't use darwin in single release file * Increase max password length * build contrib/depends offline * zano support for macos * Update dependencies to work on multithread via rosetta2 * different way of adding .patch-applied * Improve performance of incremental builds * remove unnecessary patches * update coin-control patch * fix test * remove contrib/depends patches in wownero * chore: support fallback names in the download_deps util --------- Co-authored-by: Im-Beast --- .devcontainer/Dockerfile | 5 +- .github/workflows/full_check.yaml | 34 +- .gitmodules | 4 + README.md | 3 +- apply_patches.sh | 27 +- build_single.sh | 21 +- contrib/depends/packages/android_ndk.mk | 2 +- contrib/depends/packages/native_ccache.mk | 4 +- contrib/depends/packages/sodium.mk | 4 +- contrib/depends/toolchain.cmake.in | 1 + generate_checksum.sh | 2 +- impls/monero.dart/ffigen_zano.yaml | 11 + impls/monero.dart/lib/src/checksum_zano.dart | 4 + .../lib/src/generated_bindings_zano.g.dart | 527 ++++++++++++++++ impls/monero.dart/lib/zano.dart | 688 +++++++++++++++++++++ impls/monero.dart/update_bindings.sh | 1 + impls/monero.ts/checksum_zano.ts | 5 + monero_libwallet2_api_c/CMakeLists.txt | 60 +- monero_libwallet2_api_c/src/main/cpp/helpers.hpp | 1 + patches/monero/0009-coin-control.patch | 211 ++++--- patches/zano/0001-add-missing-include.patch | 64 ++ patches/zano/0002-fix-build-issues.patch | 39 ++ patches/zano/0003-fix-mingw-build-issues.patch | 61 ++ patches/zano/0004-update-tor-connect.patch | 33 + patches/zano/0005-fix-ios-builds.patch | 43 ++ ...006-use-boost-filesystem-instead-of-stdfs.patch | 80 +++ ...ade-cmake-version-so-LIB_DEPENDS-shows-up.patch | 22 + patches/zano/0008-increase-max-password.patch | 25 + tests/.DS_Store | Bin 0 -> 6148 bytes tests/download_deps.ts | 171 +++-- tests/integration.test.ts | 2 +- tests/utils.ts | 4 +- zano | 1 + zano_libwallet2_api_c/CMakeLists.txt | 1 + zano_libwallet2_api_c/src/main/cpp/helpers.cpp | 1 + zano_libwallet2_api_c/src/main/cpp/helpers.hpp | 1 + .../src/main/cpp/wallet2_api_c.cpp | 322 ++++++++++ zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.h | 67 ++ zano_libwallet2_api_c/src/main/cpp/zano_checksum.h | 6 + zano_libwallet2_api_c/zano_libwallet2_api_c.exp | 33 + 40 files changed, 2411 insertions(+), 180 deletions(-) create mode 100644 impls/monero.dart/ffigen_zano.yaml create mode 100644 impls/monero.dart/lib/src/checksum_zano.dart create mode 100644 impls/monero.dart/lib/src/generated_bindings_zano.g.dart create mode 100644 impls/monero.dart/lib/zano.dart create mode 100644 impls/monero.ts/checksum_zano.ts create mode 100644 patches/zano/0001-add-missing-include.patch create mode 100644 patches/zano/0002-fix-build-issues.patch create mode 100644 patches/zano/0003-fix-mingw-build-issues.patch create mode 100644 patches/zano/0004-update-tor-connect.patch create mode 100644 patches/zano/0005-fix-ios-builds.patch create mode 100644 patches/zano/0006-use-boost-filesystem-instead-of-stdfs.patch create mode 100644 patches/zano/0007-downgrade-cmake-version-so-LIB_DEPENDS-shows-up.patch create mode 100644 patches/zano/0008-increase-max-password.patch create mode 100644 tests/.DS_Store create mode 160000 zano create mode 120000 zano_libwallet2_api_c/CMakeLists.txt create mode 120000 zano_libwallet2_api_c/src/main/cpp/helpers.cpp create mode 120000 zano_libwallet2_api_c/src/main/cpp/helpers.hpp create mode 100644 zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp create mode 100644 zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.h create mode 100644 zano_libwallet2_api_c/src/main/cpp/zano_checksum.h create mode 100644 zano_libwallet2_api_c/zano_libwallet2_api_c.exp diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 6dc59c4..ed16f21 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,7 +6,10 @@ SHELL ["/bin/bash", "-c"] # lintinfo5 -RUN wget http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb -O libtinfo5.deb \ +SHELL ["/bin/bash", "-c"] + +RUN [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ]] && exit 0 \ + || wget http://mirrors.kernel.org/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb -O libtinfo5.deb \ && apt install ./libtinfo5.deb \ && rm libtinfo5.deb diff --git a/.github/workflows/full_check.yaml b/.github/workflows/full_check.yaml index e083dd6..20dcbc5 100644 --- a/.github/workflows/full_check.yaml +++ b/.github/workflows/full_check.yaml @@ -64,8 +64,8 @@ jobs: strategy: fail-fast: false matrix: - coin: [monero, wownero] - runs-on: ubuntu-24.04 + coin: [monero, wownero, zano] + runs-on: ubuntu-22.04 # container: # image: debian:bookworm steps: @@ -137,7 +137,7 @@ jobs: strategy: fail-fast: false matrix: - coin: [monero, wownero] + coin: [monero, wownero, zano] runs-on: ubuntu-latest container: image: debian:bullseye @@ -195,7 +195,7 @@ jobs: strategy: fail-fast: false matrix: - coin: [monero, wownero] + coin: [monero, wownero, zano] name: macos build runs-on: macos-14 steps: @@ -210,7 +210,7 @@ jobs: xcode-version: '15.4' - name: install dependencies run: | - brew install ccache binutils pigz autoconf automake libtool + brew install ccache binutils pigz autoconf automake libtool pkg-config - name: Patch sources run: | git config --global --add safe.directory '*' @@ -256,9 +256,9 @@ jobs: strategy: fail-fast: false matrix: - coin: [monero, wownero] + coin: [monero, wownero, zano] name: ios build - runs-on: macos-14 + runs-on: macos-15 steps: - name: Checkout monero_c repo uses: actions/checkout@v4 @@ -268,10 +268,10 @@ jobs: submodules: recursive - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.4' + xcode-version: '16.1' - name: install dependencies run: | - brew install ccache unbound zmq autoconf automake libtool + brew install ccache cmake autoconf automake libtool - name: Patch sources run: | git config --global --add safe.directory '*' @@ -325,6 +325,10 @@ jobs: with: name: android wownero path: release/wownero + - uses: actions/download-artifact@v4 + with: + name: android zano + path: release/zano - uses: actions/download-artifact@v4 with: name: ios monero @@ -333,6 +337,10 @@ jobs: with: name: ios wownero path: release/wownero + - uses: actions/download-artifact@v4 + with: + name: ios zano + path: release/zano - uses: actions/download-artifact@v4 with: name: linux monero @@ -341,6 +349,10 @@ jobs: with: name: linux wownero path: release/wownero + - uses: actions/download-artifact@v4 + with: + name: linux zano + path: release/zano - uses: actions/download-artifact@v4 with: name: macos monero @@ -349,6 +361,10 @@ jobs: with: name: macos wownero path: release/wownero + - uses: actions/download-artifact@v4 + with: + name: macos zano + path: release/zano - uses: actions/download-artifact@v4 with: name: mingw monero diff --git a/.gitmodules b/.gitmodules index ecf86b2..04af1e1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,10 @@ path = wownero url = https://codeberg.org/wownero/wownero shallow = true +[submodule "zano"] + path = zano + url = https://github.com/hyle-team/zano + shallow = true [submodule "wownero_libwallet2_api_c/wownero-seed"] path = wownero_libwallet2_api_c/wownero-seed url = https://github.com/MrCyjaneK/wownero-seed diff --git a/README.md b/README.md index 4036888..5ed7564 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ TL;DR: ```bash $ rm -rf monero wownero release $ git submodule update --init --recursive --force -$ for coin in monero wownero; do ./apply_patches.sh $coin; done -$ ./build_single monero $(gcc -dumpmachine) -j$(nproc) +$ for coin in monero wownero zano; do ./apply_patches.sh $coin; done ``` Broken? Not working? Need help? https://moneroc.mrcyjanek.net/ diff --git a/apply_patches.sh b/apply_patches.sh index 22c3bba..8013c9d 100755 --- a/apply_patches.sh +++ b/apply_patches.sh @@ -38,7 +38,30 @@ then git remote set-url origin https://github.com/mrcyjanek/randomwow.git popd fi +if [[ "$repo" == "zano" ]]; +then + pushd contrib/tor-connect + git remote set-url origin https://github.com/mrcyjanek/tor-connect.git + popd +fi git submodule init git submodule update --init --recursive --force -touch .patch-applied -echo "you are good to go!" \ No newline at end of file +git am -3 < +Date: Fri, 20 Dec 2024 09:18:08 +0100 +Subject: [PATCH] add .patch-applied + +--- + .patch-applied | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + create mode 100644 .patch-applied + +diff --git a/.patch-applied b/.patch-applied +new file mode 100644 +index 000000000..e69de29bb +-- +2.39.5 (Apple Git-154) +EOF + +echo "you are good to go!" diff --git a/build_single.sh b/build_single.sh index f868f98..0309a28 100755 --- a/build_single.sh +++ b/build_single.sh @@ -20,14 +20,14 @@ set -e repo=$1 if [[ "x$repo" == "x" ]]; then - echo "Usage: $0 monero/wownero $(gcc -dumpmachine) -j$proccount" + echo "Usage: $0 monero/wownero/zano $(gcc -dumpmachine) -j$proccount" exit 1 fi -if [[ "x$repo" != "xwownero" && "x$repo" != "xmonero" ]]; +if [[ "x$repo" != "xwownero" && "x$repo" != "xmonero" && "x$repo" != "xzano" ]]; then - echo "Usage: $0 monero/wownero $(gcc -dumpmachine) -j$proccount" - echo "Invalid target given, only monero and wownero are supported targets" + echo "Usage: $0 monero/wownero/zano $(gcc -dumpmachine) -j$proccount" + echo "Invalid target given" exit 1 fi @@ -55,7 +55,12 @@ fi cd $(dirname $0) WDIR=$PWD pushd contrib/depends - env -i PATH="$PATH" CC=gcc CXX=g++ make "$NPROC" HOST="$HOST_ABI" + if [[ -d $HOST_ABI ]]; + then + echo "Not building depends, directory exists" + else + env -i PATH="$PATH" CC=gcc CXX=g++ make "$NPROC" HOST="$HOST_ABI" + fi popd buildType=Debug @@ -63,8 +68,12 @@ buildType=Debug pushd ${repo}_libwallet2_api_c rm -rf build/${HOST_ABI} || true mkdir -p build/${HOST_ABI} -p + if [[ "$repo" == "zano" ]]; + then + EXTRA_CMAKE_FLAGS="-DCAKEWALLET=ON" + fi pushd build/${HOST_ABI} - 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} ../.. + cmake -DCMAKE_TOOLCHAIN_FILE=$PWD/../../../contrib/depends/${HOST_ABI}/share/toolchain.cmake $EXTRA_CMAKE_FLAGS -DUSE_DEVICE_TREZOR=OFF -DMONERO_FLAVOR=$repo -DCMAKE_BUILD_TYPE=Debug -DHOST_ABI=${HOST_ABI} ../.. make $NPROC popd popd diff --git a/contrib/depends/packages/android_ndk.mk b/contrib/depends/packages/android_ndk.mk index 2c2914e..89db323 100644 --- a/contrib/depends/packages/android_ndk.mk +++ b/contrib/depends/packages/android_ndk.mk @@ -15,7 +15,7 @@ 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) + echo "A" | unzip -q $($(1)_source_dir)/$($(package)_file_name) endef # arm-linux-androideabi-ar - openssl workaround diff --git a/contrib/depends/packages/native_ccache.mk b/contrib/depends/packages/native_ccache.mk index 6821454..1523660 100644 --- a/contrib/depends/packages/native_ccache.mk +++ b/contrib/depends/packages/native_ccache.mk @@ -5,7 +5,7 @@ $(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" +$(package)_config_opts=-DCMAKE_INSTALL_PREFIX="$(host_prefix)/native" -DENABLE_TESTING=OFF endef define $(package)_config_cmds @@ -17,7 +17,7 @@ define $(package)_build_cmds endef define $(package)_stage_cmds - cd build && $(MAKE) DESTDIR=$($(package)_staging_dir) install + cd build && $(MAKE) -j1 DESTDIR=$($(package)_staging_dir) install endef define $(package)_postprocess_cmds diff --git a/contrib/depends/packages/sodium.mk b/contrib/depends/packages/sodium.mk index 8e85a4e..0050bd3 100644 --- a/contrib/depends/packages/sodium.mk +++ b/contrib/depends/packages/sodium.mk @@ -21,11 +21,11 @@ define $(package)_config_cmds endef define $(package)_build_cmds - $(MAKE) -j$(NUM_CORES) + $(MAKE) endef define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) install + $(MAKE) -j1 DESTDIR=$($(package)_staging_dir) install endef define $(package)_postprocess_cmds diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in index 2e0ba52..216741e 100644 --- a/contrib/depends/toolchain.cmake.in +++ b/contrib/depends/toolchain.cmake.in @@ -197,6 +197,7 @@ include_directories(@prefix@/include) include_directories(@prefix@/include/wownero_seed) add_definitions(-DPOLYSEED_STATIC=ON) +add_definitions(-DMOBILE_WALLET_BUILD) #Create a new global cmake flag that indicates building with depends set (DEPENDS true) \ No newline at end of file diff --git a/generate_checksum.sh b/generate_checksum.sh index 4b82e53..ea59961 100755 --- a/generate_checksum.sh +++ b/generate_checksum.sh @@ -6,7 +6,7 @@ then function sha256sum() { shasum -a 256 "$@" ; } && export -f sha256sum fi -for coin in monero wownero; +for coin in monero wownero zano; do submodule_hash=$(git ls-tree HEAD ${coin} | xargs | awk '{ print $3 }') COIN=$(echo "$coin" | tr a-z A-Z) diff --git a/impls/monero.dart/ffigen_zano.yaml b/impls/monero.dart/ffigen_zano.yaml new file mode 100644 index 0000000..2c0f6a0 --- /dev/null +++ b/impls/monero.dart/ffigen_zano.yaml @@ -0,0 +1,11 @@ +name: ZanoC +description: monero_c bindings +output: 'lib/src/generated_bindings_zano.g.dart' +headers: + entry-points: + - '../../zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.h' + +exclude-all-by-default: true +functions: + include: + - "ZANO_.+" \ No newline at end of file diff --git a/impls/monero.dart/lib/src/checksum_zano.dart b/impls/monero.dart/lib/src/checksum_zano.dart new file mode 100644 index 0000000..b8b879a --- /dev/null +++ b/impls/monero.dart/lib/src/checksum_zano.dart @@ -0,0 +1,4 @@ +// ignore_for_file: constant_identifier_names +const String wallet2_api_c_h_sha256 = "8acaa95513b85a984c08e05cc3f2ac7530bb8f32946eeeb45357bd846aef33dd"; +const String wallet2_api_c_cpp_sha256 = "4efacd3812d53dd268b6869cc0a9560e7320574d96e09136cf067f796edfeba6-2817090c8ac7639d6f697d00fc8bcba2b3681d90"; +const String wallet2_api_c_exp_sha256 = "66f3ff655bbfd11ad28c318ab707090b5a93276f436b06f7b1c0f329dba3c9c2"; diff --git a/impls/monero.dart/lib/src/generated_bindings_zano.g.dart b/impls/monero.dart/lib/src/generated_bindings_zano.g.dart new file mode 100644 index 0000000..1ffdc3e --- /dev/null +++ b/impls/monero.dart/lib/src/generated_bindings_zano.g.dart @@ -0,0 +1,527 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint +import 'dart:ffi' as ffi; + +/// monero_c bindings +class ZanoC { + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + ZanoC(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + ZanoC.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + ffi.Pointer ZANO_PlainWallet_init( + ffi.Pointer address, + ffi.Pointer working_dir, + int log_level, + ) { + return _ZANO_PlainWallet_init( + address, + working_dir, + log_level, + ); + } + + late final _ZANO_PlainWallet_initPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Int)>>('ZANO_PlainWallet_init'); + late final _ZANO_PlainWallet_init = _ZANO_PlainWallet_initPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer, int)>(); + + ffi.Pointer ZANO_PlainWallet_init2( + ffi.Pointer ip, + ffi.Pointer port, + ffi.Pointer working_dir, + int log_level, + ) { + return _ZANO_PlainWallet_init2( + ip, + port, + working_dir, + log_level, + ); + } + + late final _ZANO_PlainWallet_init2Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Int)>>('ZANO_PlainWallet_init2'); + late final _ZANO_PlainWallet_init2 = _ZANO_PlainWallet_init2Ptr.asFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer, ffi.Pointer, int)>(); + + ffi.Pointer ZANO_PlainWallet_reset() { + return _ZANO_PlainWallet_reset(); + } + + late final _ZANO_PlainWallet_resetPtr = + _lookup Function()>>( + 'ZANO_PlainWallet_reset'); + late final _ZANO_PlainWallet_reset = + _ZANO_PlainWallet_resetPtr.asFunction Function()>(); + + ffi.Pointer ZANO_PlainWallet_setLogLevel( + int log_level, + ) { + return _ZANO_PlainWallet_setLogLevel( + log_level, + ); + } + + late final _ZANO_PlainWallet_setLogLevelPtr = + _lookup Function(ffi.Int)>>( + 'ZANO_PlainWallet_setLogLevel'); + late final _ZANO_PlainWallet_setLogLevel = _ZANO_PlainWallet_setLogLevelPtr + .asFunction Function(int)>(); + + ffi.Pointer ZANO_PlainWallet_getVersion() { + return _ZANO_PlainWallet_getVersion(); + } + + late final _ZANO_PlainWallet_getVersionPtr = + _lookup Function()>>( + 'ZANO_PlainWallet_getVersion'); + late final _ZANO_PlainWallet_getVersion = _ZANO_PlainWallet_getVersionPtr + .asFunction Function()>(); + + ffi.Pointer ZANO_PlainWallet_getWalletFiles() { + return _ZANO_PlainWallet_getWalletFiles(); + } + + late final _ZANO_PlainWallet_getWalletFilesPtr = + _lookup Function()>>( + 'ZANO_PlainWallet_getWalletFiles'); + late final _ZANO_PlainWallet_getWalletFiles = + _ZANO_PlainWallet_getWalletFilesPtr.asFunction< + ffi.Pointer Function()>(); + + ffi.Pointer ZANO_PlainWallet_getExportPrivateInfo( + ffi.Pointer target_dir, + ) { + return _ZANO_PlainWallet_getExportPrivateInfo( + target_dir, + ); + } + + late final _ZANO_PlainWallet_getExportPrivateInfoPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('ZANO_PlainWallet_getExportPrivateInfo'); + late final _ZANO_PlainWallet_getExportPrivateInfo = + _ZANO_PlainWallet_getExportPrivateInfoPtr.asFunction< + ffi.Pointer Function(ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_deleteWallet( + ffi.Pointer file_name, + ) { + return _ZANO_PlainWallet_deleteWallet( + file_name, + ); + } + + late final _ZANO_PlainWallet_deleteWalletPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('ZANO_PlainWallet_deleteWallet'); + late final _ZANO_PlainWallet_deleteWallet = _ZANO_PlainWallet_deleteWalletPtr + .asFunction Function(ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_getAddressInfo( + ffi.Pointer addr, + ) { + return _ZANO_PlainWallet_getAddressInfo( + addr, + ); + } + + late final _ZANO_PlainWallet_getAddressInfoPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('ZANO_PlainWallet_getAddressInfo'); + late final _ZANO_PlainWallet_getAddressInfo = + _ZANO_PlainWallet_getAddressInfoPtr.asFunction< + ffi.Pointer Function(ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_getAppconfig( + ffi.Pointer encryption_key, + ) { + return _ZANO_PlainWallet_getAppconfig( + encryption_key, + ); + } + + late final _ZANO_PlainWallet_getAppconfigPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('ZANO_PlainWallet_getAppconfig'); + late final _ZANO_PlainWallet_getAppconfig = _ZANO_PlainWallet_getAppconfigPtr + .asFunction Function(ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_setAppconfig( + ffi.Pointer conf_str, + ffi.Pointer encryption_key, + ) { + return _ZANO_PlainWallet_setAppconfig( + conf_str, + encryption_key, + ); + } + + late final _ZANO_PlainWallet_setAppconfigPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer)>>('ZANO_PlainWallet_setAppconfig'); + late final _ZANO_PlainWallet_setAppconfig = + _ZANO_PlainWallet_setAppconfigPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_generateRandomKey( + int lenght, + ) { + return _ZANO_PlainWallet_generateRandomKey( + lenght, + ); + } + + late final _ZANO_PlainWallet_generateRandomKeyPtr = + _lookup Function(ffi.Uint64)>>( + 'ZANO_PlainWallet_generateRandomKey'); + late final _ZANO_PlainWallet_generateRandomKey = + _ZANO_PlainWallet_generateRandomKeyPtr.asFunction< + ffi.Pointer Function(int)>(); + + ffi.Pointer ZANO_PlainWallet_getLogsBuffer() { + return _ZANO_PlainWallet_getLogsBuffer(); + } + + late final _ZANO_PlainWallet_getLogsBufferPtr = + _lookup Function()>>( + 'ZANO_PlainWallet_getLogsBuffer'); + late final _ZANO_PlainWallet_getLogsBuffer = + _ZANO_PlainWallet_getLogsBufferPtr.asFunction< + ffi.Pointer Function()>(); + + ffi.Pointer ZANO_PlainWallet_truncateLog() { + return _ZANO_PlainWallet_truncateLog(); + } + + late final _ZANO_PlainWallet_truncateLogPtr = + _lookup Function()>>( + 'ZANO_PlainWallet_truncateLog'); + late final _ZANO_PlainWallet_truncateLog = _ZANO_PlainWallet_truncateLogPtr + .asFunction Function()>(); + + ffi.Pointer ZANO_PlainWallet_getConnectivityStatus() { + return _ZANO_PlainWallet_getConnectivityStatus(); + } + + late final _ZANO_PlainWallet_getConnectivityStatusPtr = + _lookup Function()>>( + 'ZANO_PlainWallet_getConnectivityStatus'); + late final _ZANO_PlainWallet_getConnectivityStatus = + _ZANO_PlainWallet_getConnectivityStatusPtr.asFunction< + ffi.Pointer Function()>(); + + ffi.Pointer ZANO_PlainWallet_open( + ffi.Pointer path, + ffi.Pointer password, + ) { + return _ZANO_PlainWallet_open( + path, + password, + ); + } + + late final _ZANO_PlainWallet_openPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer)>>('ZANO_PlainWallet_open'); + late final _ZANO_PlainWallet_open = _ZANO_PlainWallet_openPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_restore( + ffi.Pointer seed, + ffi.Pointer path, + ffi.Pointer password, + ffi.Pointer seed_password, + ) { + return _ZANO_PlainWallet_restore( + seed, + path, + password, + seed_password, + ); + } + + late final _ZANO_PlainWallet_restorePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>('ZANO_PlainWallet_restore'); + late final _ZANO_PlainWallet_restore = + _ZANO_PlainWallet_restorePtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_generate( + ffi.Pointer path, + ffi.Pointer password, + ) { + return _ZANO_PlainWallet_generate( + path, + password, + ); + } + + late final _ZANO_PlainWallet_generatePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, + ffi.Pointer)>>('ZANO_PlainWallet_generate'); + late final _ZANO_PlainWallet_generate = + _ZANO_PlainWallet_generatePtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_getOpenWallets() { + return _ZANO_PlainWallet_getOpenWallets(); + } + + late final _ZANO_PlainWallet_getOpenWalletsPtr = + _lookup Function()>>( + 'ZANO_PlainWallet_getOpenWallets'); + late final _ZANO_PlainWallet_getOpenWallets = + _ZANO_PlainWallet_getOpenWalletsPtr.asFunction< + ffi.Pointer Function()>(); + + ffi.Pointer ZANO_PlainWallet_getWalletStatus( + int h, + ) { + return _ZANO_PlainWallet_getWalletStatus( + h, + ); + } + + late final _ZANO_PlainWallet_getWalletStatusPtr = + _lookup Function(ffi.Int64)>>( + 'ZANO_PlainWallet_getWalletStatus'); + late final _ZANO_PlainWallet_getWalletStatus = + _ZANO_PlainWallet_getWalletStatusPtr.asFunction< + ffi.Pointer Function(int)>(); + + ffi.Pointer ZANO_PlainWallet_closeWallet( + int h, + ) { + return _ZANO_PlainWallet_closeWallet( + h, + ); + } + + late final _ZANO_PlainWallet_closeWalletPtr = + _lookup Function(ffi.Int64)>>( + 'ZANO_PlainWallet_closeWallet'); + late final _ZANO_PlainWallet_closeWallet = _ZANO_PlainWallet_closeWalletPtr + .asFunction Function(int)>(); + + ffi.Pointer ZANO_PlainWallet_invoke( + int h, + ffi.Pointer params, + ) { + return _ZANO_PlainWallet_invoke( + h, + params, + ); + } + + late final _ZANO_PlainWallet_invokePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Int64, ffi.Pointer)>>('ZANO_PlainWallet_invoke'); + late final _ZANO_PlainWallet_invoke = _ZANO_PlainWallet_invokePtr.asFunction< + ffi.Pointer Function(int, ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_asyncCall( + ffi.Pointer method_name, + int instance_id, + ffi.Pointer params, + ) { + return _ZANO_PlainWallet_asyncCall( + method_name, + instance_id, + params, + ); + } + + late final _ZANO_PlainWallet_asyncCallPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, ffi.Uint64, + ffi.Pointer)>>('ZANO_PlainWallet_asyncCall'); + late final _ZANO_PlainWallet_asyncCall = + _ZANO_PlainWallet_asyncCallPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, int, ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_tryPullResult( + int instance_id, + ) { + return _ZANO_PlainWallet_tryPullResult( + instance_id, + ); + } + + late final _ZANO_PlainWallet_tryPullResultPtr = + _lookup Function(ffi.Uint64)>>( + 'ZANO_PlainWallet_tryPullResult'); + late final _ZANO_PlainWallet_tryPullResult = + _ZANO_PlainWallet_tryPullResultPtr.asFunction< + ffi.Pointer Function(int)>(); + + ffi.Pointer ZANO_PlainWallet_syncCall( + ffi.Pointer method_name, + int instance_id, + ffi.Pointer params, + ) { + return _ZANO_PlainWallet_syncCall( + method_name, + instance_id, + params, + ); + } + + late final _ZANO_PlainWallet_syncCallPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer, ffi.Uint64, + ffi.Pointer)>>('ZANO_PlainWallet_syncCall'); + late final _ZANO_PlainWallet_syncCall = + _ZANO_PlainWallet_syncCallPtr.asFunction< + ffi.Pointer Function( + ffi.Pointer, int, ffi.Pointer)>(); + + bool ZANO_PlainWallet_isWalletExist( + ffi.Pointer path, + ) { + return _ZANO_PlainWallet_isWalletExist( + path, + ); + } + + late final _ZANO_PlainWallet_isWalletExistPtr = + _lookup)>>( + 'ZANO_PlainWallet_isWalletExist'); + late final _ZANO_PlainWallet_isWalletExist = + _ZANO_PlainWallet_isWalletExistPtr.asFunction< + bool Function(ffi.Pointer)>(); + + ffi.Pointer ZANO_PlainWallet_getWalletInfo( + int h, + ) { + return _ZANO_PlainWallet_getWalletInfo( + h, + ); + } + + late final _ZANO_PlainWallet_getWalletInfoPtr = + _lookup Function(ffi.Int64)>>( + 'ZANO_PlainWallet_getWalletInfo'); + late final _ZANO_PlainWallet_getWalletInfo = + _ZANO_PlainWallet_getWalletInfoPtr.asFunction< + ffi.Pointer Function(int)>(); + + ffi.Pointer ZANO_PlainWallet_resetWalletPassword( + int h, + ffi.Pointer password, + ) { + return _ZANO_PlainWallet_resetWalletPassword( + h, + password, + ); + } + + late final _ZANO_PlainWallet_resetWalletPasswordPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Int64, + ffi.Pointer)>>('ZANO_PlainWallet_resetWalletPassword'); + late final _ZANO_PlainWallet_resetWalletPassword = + _ZANO_PlainWallet_resetWalletPasswordPtr.asFunction< + ffi.Pointer Function(int, ffi.Pointer)>(); + + int ZANO_PlainWallet_getCurrentTxFee( + int priority, + ) { + return _ZANO_PlainWallet_getCurrentTxFee( + priority, + ); + } + + late final _ZANO_PlainWallet_getCurrentTxFeePtr = + _lookup>( + 'ZANO_PlainWallet_getCurrentTxFee'); + late final _ZANO_PlainWallet_getCurrentTxFee = + _ZANO_PlainWallet_getCurrentTxFeePtr.asFunction(); + + void ZANO_free( + ffi.Pointer ptr, + ) { + return _ZANO_free( + ptr, + ); + } + + late final _ZANO_freePtr = + _lookup)>>( + 'ZANO_free'); + late final _ZANO_free = + _ZANO_freePtr.asFunction)>(); + + ffi.Pointer ZANO_checksum_wallet2_api_c_h() { + return _ZANO_checksum_wallet2_api_c_h(); + } + + late final _ZANO_checksum_wallet2_api_c_hPtr = + _lookup Function()>>( + 'ZANO_checksum_wallet2_api_c_h'); + late final _ZANO_checksum_wallet2_api_c_h = _ZANO_checksum_wallet2_api_c_hPtr + .asFunction Function()>(); + + ffi.Pointer ZANO_checksum_wallet2_api_c_cpp() { + return _ZANO_checksum_wallet2_api_c_cpp(); + } + + late final _ZANO_checksum_wallet2_api_c_cppPtr = + _lookup Function()>>( + 'ZANO_checksum_wallet2_api_c_cpp'); + late final _ZANO_checksum_wallet2_api_c_cpp = + _ZANO_checksum_wallet2_api_c_cppPtr.asFunction< + ffi.Pointer Function()>(); + + ffi.Pointer ZANO_checksum_wallet2_api_c_exp() { + return _ZANO_checksum_wallet2_api_c_exp(); + } + + late final _ZANO_checksum_wallet2_api_c_expPtr = + _lookup Function()>>( + 'ZANO_checksum_wallet2_api_c_exp'); + late final _ZANO_checksum_wallet2_api_c_exp = + _ZANO_checksum_wallet2_api_c_expPtr.asFunction< + ffi.Pointer Function()>(); +} diff --git a/impls/monero.dart/lib/zano.dart b/impls/monero.dart/lib/zano.dart new file mode 100644 index 0000000..f17346a --- /dev/null +++ b/impls/monero.dart/lib/zano.dart @@ -0,0 +1,688 @@ + +// ignore_for_file: non_constant_identifier_names, camel_case_types + +import 'dart:ffi'; +import 'dart:io'; + +import 'package:ffi/ffi.dart'; +import 'package:monero/src/generated_bindings_zano.g.dart'; + +export 'src/checksum_monero.dart'; + +typedef PendingTransaction = Pointer; + +ZanoC? lib; +String libPath = (() { + if (Platform.isWindows) return 'zano_libwallet2_api_c.dll'; + if (Platform.isMacOS) return 'zano_libwallet2_api_c.dylib'; + if (Platform.isIOS) return 'ZanoWallet.framework/ZanoWallet'; + if (Platform.isAndroid) return 'libzano_libwallet2_api_c.so'; + return 'zano_libwallet2_api_c.so'; +})(); + +Map> debugCallLength = {}; + +final defaultSeparatorStr = ";"; +final defaultSeparator = defaultSeparatorStr.toNativeUtf8().cast(); +/* we don't call .free here, this comment serves one purpose - so the numbers match :) */ + +final Stopwatch sw = Stopwatch()..start(); + +bool printStarts = false; + +void Function(String call)? debugStart = (call) { + try { + if (printStarts) print("MONERO: $call"); + debugCallLength[call] ??= []; + debugCallLength[call]!.add(sw.elapsedMicroseconds); + } catch (e) {} +}; +void debugChores() { + for (var key in debugCallLength.keys) { + if (debugCallLength[key]!.length > 1000000) { + final elm = + debugCallLength[key]!.reduce((value, element) => value + element); + debugCallLength[key]!.clear(); + debugCallLength["${key}_1M"] ??= []; + debugCallLength["${key}_1M"]!.add(elm); + } + } +} + +int debugCount = 0; + +void Function(String call)? debugEnd = (call) { + try { + final id = debugCallLength[call]!.length - 1; + if (++debugCount > 1000000) { + debugCount = 0; + debugChores(); + } + debugCallLength[call]![id] = + sw.elapsedMicroseconds - debugCallLength[call]![id]; + } catch (e) {} +}; +void Function(String call, dynamic error)? errorHandler = (call, error) { + print("$call: $error"); +}; + +// extern ADDAPI const char* ZANO_PlainWallet_init(const char* address, const char* working_dir, int log_level); +String PlainWallet_init(String address, String working_dir, int log_level) { + debugStart?.call('ZANO_PlainWallet_init'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final address_ = address.toNativeUtf8(); + final working_dir_ = working_dir.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_init(address_.cast(), working_dir_.cast(), log_level); + calloc.free(address_); + calloc.free(working_dir_); + debugEnd?.call('ZANO_PlainWallet_init'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_init'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_init', e); + debugEnd?.call('ZANO_PlainWallet_init'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_init2(const char* ip, const char* port, const char* working_dir, int log_level); +String PlainWallet_init2(String ip, String port, String working_dir, int log_level) { + debugStart?.call('ZANO_PlainWallet_init2'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final ip_ = ip.toNativeUtf8(); + final port_ = port.toNativeUtf8(); + final working_dir_ = working_dir.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_init2(ip_.cast(), port_.cast(), working_dir_.cast(), log_level); + calloc.free(ip_); + calloc.free(port_); + calloc.free(working_dir_); + debugEnd?.call('ZANO_PlainWallet_init2'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_init2'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_init2', e); + debugEnd?.call('ZANO_PlainWallet_init2'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_reset(); +String PlainWallet_reset() { + debugStart?.call('ZANO_PlainWallet_reset'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_reset(); + debugEnd?.call('ZANO_PlainWallet_reset'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_reset'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_reset', e); + debugEnd?.call('ZANO_PlainWallet_reset'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_setLogLevel(int log_level); +String PlainWallet_setLogLevel(int log_level) { + debugStart?.call('ZANO_PlainWallet_setLogLevel'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_setLogLevel(log_level); + debugEnd?.call('ZANO_PlainWallet_setLogLevel'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_setLogLevel'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_setLogLevel', e); + debugEnd?.call('ZANO_PlainWallet_setLogLevel'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_getVersion(); +String PlainWallet_getVersion() { + debugStart?.call('ZANO_PlainWallet_getVersion'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_getVersion(); + debugEnd?.call('ZANO_PlainWallet_getVersion'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getVersion'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getVersion', e); + debugEnd?.call('ZANO_PlainWallet_getVersion'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_getWalletFiles(); +String PlainWallet_getWalletFiles() { + debugStart?.call('ZANO_PlainWallet_getWalletFiles'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_getWalletFiles(); + debugEnd?.call('ZANO_PlainWallet_getWalletFiles'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getWalletFiles'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getWalletFiles', e); + debugEnd?.call('ZANO_PlainWallet_getWalletFiles'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_getExportPrivateInfo(const char* target_dir); +String PlainWallet_getExportPrivateInfo(String target_dir) { + debugStart?.call('ZANO_PlainWallet_getExportPrivateInfo'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final target_dir_ = target_dir.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_getExportPrivateInfo(target_dir_.cast()); + calloc.free(target_dir_); + debugEnd?.call('ZANO_PlainWallet_getExportPrivateInfo'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getExportPrivateInfo'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getExportPrivateInfo', e); + debugEnd?.call('ZANO_PlainWallet_getExportPrivateInfo'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_deleteWallet(const char* file_name); +String PlainWallet_deleteWallet(String file_name) { + debugStart?.call('ZANO_PlainWallet_deleteWallet'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final file_name_ = file_name.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_deleteWallet(file_name_.cast()); + calloc.free(file_name_); + debugEnd?.call('ZANO_PlainWallet_deleteWallet'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_deleteWallet'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_deleteWallet', e); + debugEnd?.call('ZANO_PlainWallet_deleteWallet'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_getAddressInfo(const char* addr); +String PlainWallet_getAddressInfo(String addr) { + debugStart?.call('ZANO_PlainWallet_getAddressInfo'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final addr_ = addr.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_getAddressInfo(addr_.cast()); + calloc.free(addr_); + debugEnd?.call('ZANO_PlainWallet_getAddressInfo'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getAddressInfo'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getAddressInfo', e); + debugEnd?.call('ZANO_PlainWallet_getAddressInfo'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_getAppconfig(const char* encryption_key); +String PlainWallet_getAppconfig(String encryption_key) { + debugStart?.call('ZANO_PlainWallet_getAppconfig'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final encryption_key_ = encryption_key.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_getAppconfig(encryption_key_.cast()); + calloc.free(encryption_key_); + debugEnd?.call('ZANO_PlainWallet_getAppconfig'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getAppconfig'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getAppconfig', e); + debugEnd?.call('ZANO_PlainWallet_getAppconfig'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_setAppconfig(const char* conf_str, const char* encryption_key); +String PlainWallet_setAppconfig(String conf_str, String encryption_key) { + debugStart?.call('ZANO_PlainWallet_setAppconfig'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final conf_str_ = conf_str.toNativeUtf8(); + final encryption_key_ = encryption_key.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_setAppconfig(conf_str_.cast(), encryption_key_.cast()); + calloc.free(conf_str_); + calloc.free(encryption_key_); + debugEnd?.call('ZANO_PlainWallet_setAppconfig'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_setAppconfig'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_setAppconfig', e); + debugEnd?.call('ZANO_PlainWallet_setAppconfig'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_generateRandomKey(uint64_t lenght); +String PlainWallet_generateRandomKey(int length) { + debugStart?.call('ZANO_PlainWallet_generateRandomKey'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_generateRandomKey(length); + debugEnd?.call('ZANO_PlainWallet_generateRandomKey'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_generateRandomKey'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_generateRandomKey', e); + debugEnd?.call('ZANO_PlainWallet_generateRandomKey'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_getLogsBuffer(); +String PlainWallet_getLogsBuffer() { + debugStart?.call('ZANO_PlainWallet_getLogsBuffer'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_getLogsBuffer(); + debugEnd?.call('ZANO_PlainWallet_getLogsBuffer'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getLogsBuffer'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getLogsBuffer', e); + debugEnd?.call('ZANO_PlainWallet_getLogsBuffer'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_truncateLog(); +String PlainWallet_truncateLog() { + debugStart?.call('ZANO_PlainWallet_truncateLog'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_truncateLog(); + debugEnd?.call('ZANO_PlainWallet_truncateLog'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_truncateLog'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_truncateLog', e); + debugEnd?.call('ZANO_PlainWallet_truncateLog'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_getConnectivityStatus(); +String PlainWallet_getConnectivityStatus() { + debugStart?.call('ZANO_PlainWallet_getConnectivityStatus'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_getConnectivityStatus(); + debugEnd?.call('ZANO_PlainWallet_getConnectivityStatus'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getConnectivityStatus'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getConnectivityStatus', e); + debugEnd?.call('ZANO_PlainWallet_getConnectivityStatus'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_open(const char* path, const char* password); +String PlainWallet_open(String path, String password) { + debugStart?.call('ZANO_PlainWallet_open'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final path_ = path.toNativeUtf8(); + final password_ = password.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_open(path_.cast(), password_.cast()); + calloc.free(path_); + calloc.free(password_); + debugEnd?.call('ZANO_PlainWallet_open'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_open'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_open', e); + debugEnd?.call('ZANO_PlainWallet_open'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_restore(const char* seed, const char* path, const char* password, const char* seed_password); +String PlainWallet_restore(String seed, String path, String password, String seed_password) { + debugStart?.call('ZANO_PlainWallet_restore'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final seed_ = seed.toNativeUtf8(); + final path_ = path.toNativeUtf8(); + final password_ = password.toNativeUtf8(); + final seed_password_ = seed_password.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_restore(seed_.cast(), path_.cast(), password_.cast(), seed_password_.cast()); + calloc.free(seed_); + calloc.free(path_); + calloc.free(password_); + calloc.free(seed_password_); + debugEnd?.call('ZANO_PlainWallet_restore'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_restore'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_restore', e); + debugEnd?.call('ZANO_PlainWallet_restore'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_generate(const char* path, const char* password); +String PlainWallet_generate(String path, String password) { + debugStart?.call('ZANO_PlainWallet_generate'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final path_ = path.toNativeUtf8(); + final password_ = password.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_generate(path_.cast(), password_.cast()); + calloc.free(path_); + calloc.free(password_); + debugEnd?.call('ZANO_PlainWallet_generate'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_generate'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_generate', e); + debugEnd?.call('ZANO_PlainWallet_generate'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_getOpenWallets(); +String PlainWallet_getOpenWallets() { + debugStart?.call('ZANO_PlainWallet_getOpenWallets'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_getOpenWallets(); + debugEnd?.call('ZANO_PlainWallet_getOpenWallets'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getOpenWallets'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getOpenWallets', e); + debugEnd?.call('ZANO_PlainWallet_getOpenWallets'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_getWalletStatus(int64_t h); +String PlainWallet_getWalletStatus(int h) { + debugStart?.call('ZANO_PlainWallet_getWalletStatus'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_getWalletStatus(h); + debugEnd?.call('ZANO_PlainWallet_getWalletStatus'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getWalletStatus'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getWalletStatus', e); + debugEnd?.call('ZANO_PlainWallet_getWalletStatus'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_closeWallet(int64_t h); +String PlainWallet_closeWallet(int h) { + debugStart?.call('ZANO_PlainWallet_closeWallet'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_closeWallet(h); + debugEnd?.call('ZANO_PlainWallet_closeWallet'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_closeWallet'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_closeWallet', e); + debugEnd?.call('ZANO_PlainWallet_closeWallet'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_invoke(int64_t h, const char* params); +String PlainWallet_invoke(int h, String params) { + debugStart?.call('ZANO_PlainWallet_invoke'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final params_ = params.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_invoke(h, params_.cast()); + calloc.free(params_); + debugEnd?.call('ZANO_PlainWallet_invoke'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_invoke'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_invoke', e); + debugEnd?.call('ZANO_PlainWallet_invoke'); + return ""; + } +} + +// extern ADDAPI const char* ZANO_PlainWallet_asyncCall(const char* method_name, uint64_t instance_id, const char* params); +String PlainWallet_asyncCall(String method_name, int h, String params) { + debugStart?.call('ZANO_PlainWallet_asyncCall'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final params_ = params.toNativeUtf8(); + final method_name_ = method_name.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_asyncCall(method_name_.cast(), h, params_.cast()); + calloc.free(params_); + calloc.free(method_name_); + debugEnd?.call('ZANO_PlainWallet_asyncCall'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_asyncCall'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_asyncCall', e); + debugEnd?.call('ZANO_PlainWallet_asyncCall'); + return ""; + } +} + +// extern ADDAPI const char* ZANO_PlainWallet_tryPullResult(uint64_t instance_id); +String PlainWallet_tryPullResult(int instance_id) { + debugStart?.call('ZANO_PlainWallet_tryPullResult'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_tryPullResult(instance_id); + debugEnd?.call('ZANO_PlainWallet_tryPullResult'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_tryPullResult'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_tryPullResult', e); + debugEnd?.call('ZANO_PlainWallet_tryPullResult'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_syncCall(const char* method_name, uint64_t instance_id, const char* params); +String PlainWallet_syncCall(String method_name, int instance_id, String params) { + debugStart?.call('ZANO_PlainWallet_syncCall'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final method_name_ = method_name.toNativeUtf8(); + final params_ = params.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_syncCall(method_name_.cast(), instance_id, params_.cast()); + calloc.free(method_name_); + calloc.free(params_); + debugEnd?.call('ZANO_PlainWallet_syncCall'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_syncCall'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_syncCall', e); + debugEnd?.call('ZANO_PlainWallet_syncCall'); + return ""; + } +} +// extern ADDAPI bool ZANO_PlainWallet_isWalletExist(const char* path); +bool PlainWallet_isWalletExist(String path) { + debugStart?.call('ZANO_PlainWallet_isWalletExist'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final path_ = path.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_isWalletExist(path_.cast()); + calloc.free(path_); + debugEnd?.call('ZANO_PlainWallet_isWalletExist'); + return txid; +} +// extern ADDAPI const char* ZANO_PlainWallet_getWalletInfo(int64_t h); +String PlainWallet_getWalletInfo(int h) { + debugStart?.call('ZANO_PlainWallet_getWalletInfo'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_getWalletInfo(h); + debugEnd?.call('ZANO_PlainWallet_getWalletInfo'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_getWalletInfo'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_getWalletInfo', e); + debugEnd?.call('ZANO_PlainWallet_getWalletInfo'); + return ""; + } +} +// extern ADDAPI const char* ZANO_PlainWallet_resetWalletPassword(int64_t h, const char* password); +String PlainWallet_resetWalletPassword(int h, String password) { + debugStart?.call('ZANO_PlainWallet_resetWalletPassword'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final password_ = password.toNativeUtf8(); + final txid = lib!.ZANO_PlainWallet_resetWalletPassword(h, password_.cast()); + calloc.free(password_); + debugEnd?.call('ZANO_PlainWallet_resetWalletPassword'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_PlainWallet_resetWalletPassword'); + return str; + } catch (e) { + errorHandler?.call('ZANO_PlainWallet_resetWalletPassword', e); + debugEnd?.call('ZANO_PlainWallet_resetWalletPassword'); + return ""; + } +} +// extern ADDAPI uint64_t ZANO_PlainWallet_getCurrentTxFee(uint64_t priority); +int PlainWallet_getCurrentTxFee(int priority) { + debugStart?.call('ZANO_PlainWallet_getCurrentTxFee'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_PlainWallet_getCurrentTxFee(priority); + debugEnd?.call('ZANO_PlainWallet_getCurrentTxFee'); + return txid; +} + +void ZANO_free(Pointer wlptr) { + debugStart?.call('ZANO_free'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + + final s = lib!.ZANO_free(wlptr); + debugEnd?.call('ZANO_free'); + return s; +} + +// extern ADDAPI const char* ZANO_checksum_wallet2_api_c_h(); +String checksum_wallet2_api_c_h() { + debugStart?.call('ZANO_checksum_wallet2_api_c_h'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_checksum_wallet2_api_c_h(); + debugEnd?.call('ZANO_checksum_wallet2_api_c_h'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_checksum_wallet2_api_c_h'); + return str; + } catch (e) { + errorHandler?.call('ZANO_checksum_wallet2_api_c_h', e); + debugEnd?.call('ZANO_checksum_wallet2_api_c_h'); + return ""; + } +} +// extern ADDAPI const char* ZANO_checksum_wallet2_api_c_cpp(); +String checksum_wallet2_api_c_cpp() { + debugStart?.call('ZANO_checksum_wallet2_api_c_cpp'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_checksum_wallet2_api_c_cpp(); + debugEnd?.call('ZANO_checksum_wallet2_api_c_cpp'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_checksum_wallet2_api_c_cpp'); + return str; + } catch (e) { + errorHandler?.call('ZANO_checksum_wallet2_api_c_cpp', e); + debugEnd?.call('ZANO_checksum_wallet2_api_c_cpp'); + return ""; + } +} +// extern ADDAPI const char* ZANO_checksum_wallet2_api_c_exp(); +String checksum_wallet2_api_c_exp() { + debugStart?.call('ZANO_checksum_wallet2_api_c_exp'); + lib ??= ZanoC(DynamicLibrary.open(libPath)); + final txid = lib!.ZANO_checksum_wallet2_api_c_exp(); + debugEnd?.call('ZANO_checksum_wallet2_api_c_exp'); + try { + final strPtr = txid.cast(); + final str = strPtr.toDartString(); + ZANO_free(strPtr.cast()); + debugEnd?.call('ZANO_checksum_wallet2_api_c_exp'); + return str; + } catch (e) { + errorHandler?.call('ZANO_checksum_wallet2_api_c_exp', e); + debugEnd?.call('ZANO_checksum_wallet2_api_c_exp'); + return ""; + } +} \ No newline at end of file diff --git a/impls/monero.dart/update_bindings.sh b/impls/monero.dart/update_bindings.sh index 8ac3cab..f1ba024 100755 --- a/impls/monero.dart/update_bindings.sh +++ b/impls/monero.dart/update_bindings.sh @@ -6,3 +6,4 @@ cd "$(realpath $(dirname $0))" dart run ffigen --config ffigen_wownero.yaml dart run ffigen --config ffigen_monero.yaml +dart run ffigen --config ffigen_zano.yaml diff --git a/impls/monero.ts/checksum_zano.ts b/impls/monero.ts/checksum_zano.ts new file mode 100644 index 0000000..2af89bd --- /dev/null +++ b/impls/monero.ts/checksum_zano.ts @@ -0,0 +1,5 @@ +export const zanoChecksum = { + wallet2_api_c_h_sha256: "8acaa95513b85a984c08e05cc3f2ac7530bb8f32946eeeb45357bd846aef33dd", + wallet2_api_c_cpp_sha256: "4efacd3812d53dd268b6869cc0a9560e7320574d96e09136cf067f796edfeba6-2817090c8ac7639d6f697d00fc8bcba2b3681d90", + wallet2_api_c_exp_sha256: "66f3ff655bbfd11ad28c318ab707090b5a93276f436b06f7b1c0f329dba3c9c2", +} diff --git a/monero_libwallet2_api_c/CMakeLists.txt b/monero_libwallet2_api_c/CMakeLists.txt index dadafc6..6790333 100644 --- a/monero_libwallet2_api_c/CMakeLists.txt +++ b/monero_libwallet2_api_c/CMakeLists.txt @@ -1,8 +1,11 @@ -cmake_minimum_required(VERSION 3.4.1) +cmake_minimum_required(VERSION 3.5) project(wallet2_api_c) message(STATUS ABI_INFO = ${HOST_ABI}) -set (CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-lto") if(${HOST_ABI} STREQUAL "x86_64-w64-mingw32") set(CMAKE_SYSTEM_NAME Windows) @@ -34,7 +37,7 @@ if (${HOST_ABI} STREQUAL "host-apple-darwin" OR endif() endif() -if(${HOST_ABI} STREQUAL "x86_64-linux-android" OR +if(${HOST_ABI} STREQUAL "" OR ${HOST_ABI} STREQUAL "i686-linux-android" OR ${HOST_ABI} STREQUAL "aarch64-linux-android" OR ${HOST_ABI} STREQUAL "armv7a-linux-androideabi") @@ -76,26 +79,60 @@ if(${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR ${HOST_ABI} STREQUAL "aarch64 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) +elseif(${MONERO_FLAVOR} STREQUAL "zano") + target_compile_definitions(wallet2_api_c PRIVATE FLAVOR_ZANO) +endif() + 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() -add_subdirectory("${CMAKE_SOURCE_DIR}/../${MONERO_FLAVOR}" ${CMAKE_BINARY_DIR}/${MONERO_FLAVOR}_build EXCLUDE_FROM_ALL) - - +if (${MONERO_FLAVOR} STREQUAL "zano") + include_directories( + ${CMAKE_SOURCE_DIR}/build/${HOST_ABI}/zano_build/contrib/zlib + ) +endif() +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(${HOST_ABI} STREQUAL "x86_64-apple-darwin11" OR ${HOST_ABI} STREQUAL "aarch64-apple-darwin11" OR ${HOST_ABI} STREQUAL "x86_64-apple-darwin" OR ${HOST_ABI} STREQUAL "aarch64-apple-darwin" 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") set(EXPORTED_SYMBOLS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/wownero_libwallet2_api_c.exp) + elseif(${MONERO_FLAVOR} STREQUAL "zano") + set(EXPORTED_SYMBOLS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/zano_libwallet2_api_c.exp) endif() set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -exported_symbols_list ${EXPORTED_SYMBOLS_FILE}") set_target_properties(${TARGET} PROPERTIES LINK_DEPENDS ${EXPORTED_SYMBOLS_FILE}) endif() +if (${MONERO_FLAVOR} STREQUAL "monero") + set(WALLET_TARGETS wallet_api ${wallet_api_LIB_DEPENDS}) # wallet_api_LIB_DEPENDS +elseif(${MONERO_FLAVOR} STREQUAL "wownero") + set(WALLET_TARGETS wallet_api ${wallet_api_LIB_DEPENDS}) # wallet_api_LIB_DEPENDS +elseif(${MONERO_FLAVOR} STREQUAL "zano") + find_package(Boost 1.71 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options) + find_package(OpenSSL REQUIRED) + set(WALLET_TARGETS + wallet + general + tor-connect + crypto + currency_core + common + zlibstatic + + ${Boost_LIBRARIES} + ${OpenSSL_LIBRARIES}) +endif() + if(${MONERO_FLAVOR} STREQUAL "wownero") add_subdirectory(wownero-seed EXCLUDE_FROM_ALL) set(EXTRA_LIBS_WOWNEROSEED wownero-seed) @@ -106,11 +143,14 @@ endif() #foreach (_variableName ${_variableNames}) # message(STATUS "${_variableName}=${${_variableName}}") #endforeach() +#message(SEND_ERROR "${Boost_LIBRARIES}") +#message(SEND_ERROR "${WALLET_TARGETS} +# ${EXTRA_LIBS_WOWNEROSEED} +# ${EXTRA_LIBS_ANDROID}") target_link_libraries( wallet2_api_c - wallet_api + ${WALLET_TARGETS} ${EXTRA_LIBS_WOWNEROSEED} ${EXTRA_LIBS_ANDROID} - ${wallet_api_LIB_DEPENDS} - ) \ 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 2c64394..83cf33b 100644 --- a/monero_libwallet2_api_c/src/main/cpp/helpers.hpp +++ b/monero_libwallet2_api_c/src/main/cpp/helpers.hpp @@ -3,6 +3,7 @@ #include #include #include +#include // Debug macros #define DEBUG_START() \ diff --git a/patches/monero/0009-coin-control.patch b/patches/monero/0009-coin-control.patch index 4c4b842..5ce3669 100644 --- a/patches/monero/0009-coin-control.patch +++ b/patches/monero/0009-coin-control.patch @@ -1,4 +1,4 @@ -From d15a18cac55cb06d5421ecfef1118e439d0cd572 Mon Sep 17 00:00:00 2001 +From 5b7e0a2085f1e9804b0c4ae97f17d84419a1199b Mon Sep 17 00:00:00 2001 From: tobtoht Date: Tue, 12 Mar 2024 11:07:57 +0100 Subject: [PATCH 10/15] coin control @@ -10,12 +10,12 @@ Subject: [PATCH 10/15] coin control 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 | 106 ++++++++++++++++- + src/wallet/api/wallet.cpp | 170 +++++++++++++++++++++------ 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, 635 insertions(+), 19 deletions(-) + 11 files changed, 667 insertions(+), 51 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 @@ -504,7 +504,7 @@ index 000000000..c43e45abd + +#endif //FEATHER_COINS_INFO_H diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp -index 67ac90a46..a76d773ba 100644 +index 67ac90a46..06837ab61 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -35,6 +35,7 @@ @@ -532,87 +532,144 @@ index 67ac90a46..a76d773ba 100644 { 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::vectornettype(), dst_addr[i])) { +- // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982 +- setStatusError(tr("Invalid destination address")); +- error = true; +- break; +- } +- if (info.has_payment_id) { +- if (!extra_nonce.empty()) { +- setStatusError(tr("a single transaction cannot use more than one payment id")); ++ uint64_t max_coin_control_input = 0; ++ uint64_t max_frozen_input = 0; ++ try { ++ bool error = false; ++ uint64_t amountSum = 0; ++ for (size_t i = 0; i < dst_addr.size() && !error; i++) { ++ if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr[i])) { ++ // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982 ++ setStatusError(tr("Invalid destination address")); + error = true; + break; } - } - } -+ // uint64_t maxAllowedSpend = m_wallet->unlocked_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; -+ uint64_t max_coin_control_input = 0; -+ uint64_t max_frozen_input = 0; -+ if (!preferred_inputs.empty()) { -+ LOG_ERROR("not 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; -+ } -+ -+ 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); -+ if (td.m_key_image == keyImage) { -+ max_coin_control_input += td.amount(); -+ } -+ if (td.m_frozen) { -+ max_frozen_input += td.amount(); +- set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id); ++ if (info.has_payment_id) { ++ if (!extra_nonce.empty()) { ++ setStatusError(tr("a single transaction cannot use more than one payment id")); ++ error = true; ++ break; ++ } ++ set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id); + } -+ } + -+ preferred_input_list.push_back(keyImage); -+ } -+ } else { ++ if (amount) { ++ cryptonote::tx_destination_entry de; ++ de.original = dst_addr[i]; ++ de.addr = info.address; ++ de.amount = (*amount)[i]; ++ amountSum += (*amount)[i]; ++ de.is_subaddress = info.is_subaddress; ++ de.is_integrated = info.has_payment_id; ++ dsts.push_back(de); ++ } else { ++ if (subaddr_indices.empty()) { ++ for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(subaddr_account); ++index) ++ subaddr_indices.insert(index); ++ } ++ } + } ++ // uint64_t maxAllowedSpend = m_wallet->unlocked_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("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 &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; ++ } + +- if (amount) { +- cryptonote::tx_destination_entry de; +- de.original = dst_addr[i]; +- de.addr = info.address; +- de.amount = (*amount)[i]; +- de.is_subaddress = info.is_subaddress; +- de.is_integrated = info.has_payment_id; +- dsts.push_back(de); ++ 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); ++ if (td.m_key_image == keyImage) { ++ max_coin_control_input += td.amount(); ++ } ++ if (td.m_frozen) { ++ max_frozen_input += td.amount(); ++ } + } ++ ++ preferred_input_list.push_back(keyImage); + } -+ } -+ for (const auto &de : preferred_input_list) { -+ LOG_ERROR("preferred input: " << de); -+ } - if (error) { - break; - } -@@ -2129,11 +2190,11 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vectorget_num_subaddresses(subaddr_account); ++index) +- subaddr_indices.insert(index); ++ 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); ++ } + } + } +- } +- if (error) { +- break; +- } +- if (!extra_nonce.empty() && !add_extra_nonce_to_tx_extra(extra, extra_nonce)) { +- setStatusError(tr("failed to set up payment id, though it was decoded correctly")); +- break; +- } +- try { ++ for (const auto &de : preferred_input_list) { ++ LOG_ERROR("preferred input: " << de); ++ } ++ if (error) { ++ break; ++ } ++ if (!extra_nonce.empty() && !add_extra_nonce_to_tx_extra(extra, extra_nonce)) { ++ setStatusError(tr("failed to set up payment id, though it was decoded correctly")); ++ break; ++ } + size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin(); + fake_outs_count = m_wallet->adjust_mixin(mixin_count); + if (amount) { transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, adjusted_priority, @@ -1038,5 +1095,5 @@ index 91cf2a376..bc16d528c 100644 void set_unspent(size_t idx); bool is_spent(const transfer_details &td, bool strict = true) const; -- -2.43.0 +2.39.5 (Apple Git-154) diff --git a/patches/zano/0001-add-missing-include.patch b/patches/zano/0001-add-missing-include.patch new file mode 100644 index 0000000..cd95528 --- /dev/null +++ b/patches/zano/0001-add-missing-include.patch @@ -0,0 +1,64 @@ +From a49fa9a64aebf69f15832291e70888ff18ed6040 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Thu, 31 Oct 2024 13:07:20 +0000 +Subject: [PATCH 1/5] add missing #include + +--- + src/currency_core/genesis.cpp | 1 + + src/currency_core/genesis.h | 2 +- + src/wallet/plain_wallet_api.cpp | 1 + + src/wallet/plain_wallet_api.h | 1 + + 4 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/currency_core/genesis.cpp b/src/currency_core/genesis.cpp +index a58dcb4e..c34b0285 100644 +--- a/src/currency_core/genesis.cpp ++++ b/src/currency_core/genesis.cpp +@@ -4,6 +4,7 @@ + // file COPYING or http://www.opensource.org/licenses/mit-license.php. + + #include "genesis.h" ++#include + + namespace currency + { +diff --git a/src/currency_core/genesis.h b/src/currency_core/genesis.h +index 8ea77d89..d78cec97 100644 +--- a/src/currency_core/genesis.h ++++ b/src/currency_core/genesis.h +@@ -3,7 +3,7 @@ + // Copyright (c) 2014-2018 The Louisdor Project + // Distributed under the MIT/X11 software license, see the accompanying + // file COPYING or http://www.opensource.org/licenses/mit-license.php. +- ++#include + #pragma once + #include + namespace currency +diff --git a/src/wallet/plain_wallet_api.cpp b/src/wallet/plain_wallet_api.cpp +index bb322481..dccff76c 100644 +--- a/src/wallet/plain_wallet_api.cpp ++++ b/src/wallet/plain_wallet_api.cpp +@@ -6,6 +6,7 @@ + #ifdef ANDROID_BUILD + #include + #endif ++#include + #include "plain_wallet_api.h" + #include "plain_wallet_api_defs.h" + #include "currency_core/currency_config.h" +diff --git a/src/wallet/plain_wallet_api.h b/src/wallet/plain_wallet_api.h +index f12eba03..177b8173 100644 +--- a/src/wallet/plain_wallet_api.h ++++ b/src/wallet/plain_wallet_api.h +@@ -6,6 +6,7 @@ + #pragma once + + #include ++#include + #include "../common/error_codes.h" + + namespace plain_wallet +-- +2.39.5 (Apple Git-154) + diff --git a/patches/zano/0002-fix-build-issues.patch b/patches/zano/0002-fix-build-issues.patch new file mode 100644 index 0000000..9ff3f85 --- /dev/null +++ b/patches/zano/0002-fix-build-issues.patch @@ -0,0 +1,39 @@ +From 627750b0f1471c5d34755ca446d452429945d5d6 Mon Sep 17 00:00:00 2001 +From: cyan +Date: Sat, 2 Nov 2024 20:50:26 +0000 +Subject: [PATCH 2/5] fix build issues + +--- + contrib/db/libmdbx/CMakeLists.txt | 2 +- + contrib/db/libmdbx/packages/rpm/CMakeLists.txt | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/contrib/db/libmdbx/CMakeLists.txt b/contrib/db/libmdbx/CMakeLists.txt +index 75e9b3b0..01a5915c 100644 +--- a/contrib/db/libmdbx/CMakeLists.txt ++++ b/contrib/db/libmdbx/CMakeLists.txt +@@ -80,7 +80,7 @@ macro(add_mdbx_option NAME DESCRIPTION DEFAULT) + endmacro() + + # only for compatibility testing +-# set(CMAKE_CXX_STANDARD 14) ++set(CMAKE_CXX_STANDARD 17) + + if(NOT "$ENV{TEAMCITY_PROCESS_FLOW_ID}" STREQUAL "") + set(CI TEAMCITY) +diff --git a/contrib/db/libmdbx/packages/rpm/CMakeLists.txt b/contrib/db/libmdbx/packages/rpm/CMakeLists.txt +index 5949e9f0..e7b677bd 100644 +--- a/contrib/db/libmdbx/packages/rpm/CMakeLists.txt ++++ b/contrib/db/libmdbx/packages/rpm/CMakeLists.txt +@@ -12,7 +12,7 @@ set(MDBX_VERSION_STRING ${MDBX_VERSION_MAJOR}.${MDBX_VERSION_MINOR}.${MDBX_VERSI + enable_language(C) + enable_language(CXX) + +-set(CMAKE_CXX_STANDARD 11) ++set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED on) + + add_definitions(-DNDEBUG=1 -DMDBX_DEBUG=0 -DLIBMDBX_EXPORTS=1 -D_GNU_SOURCE=1) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/zano/0003-fix-mingw-build-issues.patch b/patches/zano/0003-fix-mingw-build-issues.patch new file mode 100644 index 0000000..48b4d65 --- /dev/null +++ b/patches/zano/0003-fix-mingw-build-issues.patch @@ -0,0 +1,61 @@ +From 1f7604d5b661f4490a1757e5ed1d6ed367ffb739 Mon Sep 17 00:00:00 2001 +From: cyan +Date: Sun, 3 Nov 2024 08:59:22 +0000 +Subject: [PATCH 3/5] fix mingw build issues + +--- + contrib/epee/include/misc_os_dependent.h | 4 ++-- + src/common/callstack_helper.cpp | 2 +- + src/crypto/ecrypt-config.h | 2 +- + 3 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h +index c06e5d94..a2a979b9 100644 +--- a/contrib/epee/include/misc_os_dependent.h ++++ b/contrib/epee/include/misc_os_dependent.h +@@ -110,14 +110,14 @@ namespace misc_utils + } + + +-#if defined(__GNUC__) && !defined(__ANDROID__) ++#if defined(__GNUC__) && !defined(__ANDROID__) && !defined(_WIN32) + #include + #include + #endif + inline std::string print_trace_default() + { + std::stringstream ss; +-#if defined(__GNUC__) && !defined(__ANDROID__) ++#if defined(__GNUC__) && !defined(__ANDROID__) && !defined(_WIN32) + ss << std::endl << "STACK" << std::endl; + const size_t max_depth = 100; + size_t stack_depth; +diff --git a/src/common/callstack_helper.cpp b/src/common/callstack_helper.cpp +index b84fe5a8..c9eae839 100644 +--- a/src/common/callstack_helper.cpp ++++ b/src/common/callstack_helper.cpp +@@ -9,7 +9,7 @@ + #define NOMINMAX + #endif + #include +-#include ++#include + #pragma comment(lib, "psapi.lib") + #pragma comment(lib, "dbghelp.lib") + +diff --git a/src/crypto/ecrypt-config.h b/src/crypto/ecrypt-config.h +index 9176de17..8b488135 100644 +--- a/src/crypto/ecrypt-config.h ++++ b/src/crypto/ecrypt-config.h +@@ -257,7 +257,7 @@ + + #ifdef _UI64_MAX + +-#if (_UI64_MAX / 0xFFFFFFFFui64 > 0xFFFFFFFFui64) ++#if (_UI64_MAX / 0xFFFFFFFF > 0xFFFFFFFF) + #ifndef I64T + #define I64T __int64 + #define U64C(v) (v##ui64) +-- +2.39.5 (Apple Git-154) + diff --git a/patches/zano/0004-update-tor-connect.patch b/patches/zano/0004-update-tor-connect.patch new file mode 100644 index 0000000..76cc59d --- /dev/null +++ b/patches/zano/0004-update-tor-connect.patch @@ -0,0 +1,33 @@ +From 7d05e6a8d62ad3302bd1bb1818db36bde5885bab Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Tue, 5 Nov 2024 10:35:03 -0500 +Subject: [PATCH 4/5] update tor-connect + +--- + .gitmodules | 2 +- + contrib/tor-connect | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.gitmodules b/.gitmodules +index 57896bbb..4df859f8 100644 +--- a/.gitmodules ++++ b/.gitmodules +@@ -7,7 +7,7 @@ + branch = main + [submodule "contrib/tor-connect"] + path = contrib/tor-connect +- url = https://github.com/hyle-team/tor-connect.git ++ url = https://github.com/MrCyjaneK/tor-connect.git + branch = main + [submodule "contrib/jwt-cpp"] + path = contrib/jwt-cpp +diff --git a/contrib/tor-connect b/contrib/tor-connect +index b589edb1..cc445b2f 160000 +--- a/contrib/tor-connect ++++ b/contrib/tor-connect +@@ -1 +1 @@ +-Subproject commit b589edb1906dccb387cfeded6ed12286c5f0405f ++Subproject commit cd7f0c4b583488a7fe9b50de8e533a9853b6a5c7 +-- +2.39.5 (Apple Git-154) + diff --git a/patches/zano/0005-fix-ios-builds.patch b/patches/zano/0005-fix-ios-builds.patch new file mode 100644 index 0000000..f68244f --- /dev/null +++ b/patches/zano/0005-fix-ios-builds.patch @@ -0,0 +1,43 @@ +From 20975fed75b7255f6cb6df74a17f1923f7159c09 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Tue, 5 Nov 2024 16:52:23 +0100 +Subject: [PATCH 5/5] fix ios builds + +--- + CMakeLists.txt | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c480300f..7087d796 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -136,6 +136,8 @@ else() + set(ARCH default CACHE STRING "CPU to build for: -march value or default") + if("${ARCH}" STREQUAL "default") + set(ARCH_FLAG "") ++ elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") ++ set(ARCH_FLAG "") + else() + set(ARCH_FLAG "-march=${ARCH}") + endif() +@@ -207,7 +209,7 @@ else() + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${RELEASE_FLAGS}") + if(STATIC) + if(APPLE) +- message(SEND_ERROR "Static build is not supported on MacOS X") ++ message("Static build is not supported on MacOS X") + else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") + endif() +@@ -260,8 +262,6 @@ else() + find_package(Boost 1.70 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options locale log) + endif() + +- +- + message(STATUS "Boost: ${Boost_VERSION} from ${Boost_LIBRARY_DIRS}") + + +-- +2.39.5 (Apple Git-154) + diff --git a/patches/zano/0006-use-boost-filesystem-instead-of-stdfs.patch b/patches/zano/0006-use-boost-filesystem-instead-of-stdfs.patch new file mode 100644 index 0000000..64b7f4c --- /dev/null +++ b/patches/zano/0006-use-boost-filesystem-instead-of-stdfs.patch @@ -0,0 +1,80 @@ +From 033d71f4a8623dee3508c493431402bbbe5a8b2d Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Wed, 4 Dec 2024 17:21:44 -0600 +Subject: [PATCH] use boost::filesystem instead of stdfs + +--- + CMakeLists.txt | 23 ++++------------------- + contrib/epee/include/file_io_utils.h | 6 +++--- + 2 files changed, 7 insertions(+), 22 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 7087d796..6ded9711 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -232,24 +232,9 @@ if(STATIC) + endif() + + message("CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") +-if(CMAKE_SYSTEM_NAME STREQUAL "iOS") +- set(CMAKE_OSX_DEPLOYMENT_TARGET 12.00) +- if(NOT DEFINED SKIP_BOOST_FATLIB_LIB OR NOT SKIP_BOOST_FATLIB_LIB) +- message("Ios: libboost.a included as library") +- set(Boost_LIBRARIES "libboost.a") +- else() +- message("Ios: libboost.a not included as library") +- endif() +- #workaround for new XCode 12 policy for builds(now it includes a slice for the "arm64" when builds for simulator) +- set(__iphoneos_archs "arm64") +- #set(__iphonesimulator_archs "arm64,x86_64") +- set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "${__iphoneos_archs}") +- set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "${__iphoneos_archs}") +- #set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}") +- #set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "${__iphonesimulator_archs}") +-elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") ++if(CMAKE_SYSTEM_NAME STREQUAL "Android") + if(CAKEWALLET) +- find_package(Boost 1.71 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options locale) ++ find_package(Boost 1.71 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options) + else() + set(Boost_LIBRARY_DIRS "${Boost_LIBRARY_DIRS}/${CMAKE_ANDROID_ARCH_ABI}/") + set(Boost_LIBRARIES "${Boost_LIBRARY_DIRS}libboost_system.a;${Boost_LIBRARY_DIRS}libboost_filesystem.a;${Boost_LIBRARY_DIRS}libboost_thread.a;${Boost_LIBRARY_DIRS}libboost_timer.a;${Boost_LIBRARY_DIRS}libboost_date_time.a;${Boost_LIBRARY_DIRS}libboost_chrono.a;${Boost_LIBRARY_DIRS}libboost_regex.a;${Boost_LIBRARY_DIRS}libboost_serialization.a;${Boost_LIBRARY_DIRS}libboost_atomic.a;${Boost_LIBRARY_DIRS}libboost_program_options.a") +@@ -257,7 +242,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fPIC") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fPIC") + elseif(APPLE) +- find_package(Boost 1.71 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options locale) ++ find_package(Boost 1.71 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options) + else() + find_package(Boost 1.70 REQUIRED COMPONENTS system filesystem thread timer date_time chrono regex serialization atomic program_options locale log) + endif() +@@ -302,7 +287,7 @@ else() + find_package(Git QUIET) + if(Git_FOUND OR GIT_FOUND) + message(STATUS "Found Git: ${GIT_EXECUTABLE}") +- add_custom_target(version ALL "${CMAKE_COMMAND}" "-D" "VERSION=${VERSION}" "-D" "GIT=${GIT_EXECUTABLE}" "-D" "TO=${CMAKE_BINARY_DIR}/version/version.h" "-P" "src/version.cmake" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") ++ add_custom_target(version ALL "${CMAKE_COMMAND}" "-D" "VERSION=${VERSION}" "-D" "GIT=${GIT_EXECUTABLE}" "-D" "TO=${CMAKE_BINARY_DIR}/version/version.h" "-P" "src/version.cmake" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + else() + message(STATUS "WARNING: Git was not found!") + set(VERSION "${VERSION}-unknown") +diff --git a/contrib/epee/include/file_io_utils.h b/contrib/epee/include/file_io_utils.h +index bb21ae99..ced01092 100644 +--- a/contrib/epee/include/file_io_utils.h ++++ b/contrib/epee/include/file_io_utils.h +@@ -574,10 +574,10 @@ namespace file_io_utils + try + { + +- stdfs::directory_iterator end_itr; // default construction yields past-the-end +- for (stdfs::directory_iterator itr( epee::string_encoding::utf8_to_wstring(path) ); itr != end_itr; ++itr ) ++ boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end ++ for ( boost::filesystem::directory_iterator itr( epee::string_encoding::utf8_to_wstring(path) ); itr != end_itr; ++itr ) + { +- if ( only_files && stdfs::is_directory(itr->status()) ) ++ if ( only_files && boost::filesystem::is_directory(itr->status()) ) + { + continue; + } +-- +2.39.5 (Apple Git-154) + diff --git a/patches/zano/0007-downgrade-cmake-version-so-LIB_DEPENDS-shows-up.patch b/patches/zano/0007-downgrade-cmake-version-so-LIB_DEPENDS-shows-up.patch new file mode 100644 index 0000000..0ac7e50 --- /dev/null +++ b/patches/zano/0007-downgrade-cmake-version-so-LIB_DEPENDS-shows-up.patch @@ -0,0 +1,22 @@ +From e6201106cd09416f96c8d83270c94565583cd356 Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Thu, 5 Dec 2024 09:36:34 -0600 +Subject: [PATCH] downgrade cmake version so LIB_DEPENDS shows up + +--- + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 6ded9711..47d24a81 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,4 +1,4 @@ +-cmake_minimum_required(VERSION 3.16) ++cmake_minimum_required(VERSION 3.5) + + PROJECT(Zano) + +-- +2.39.5 (Apple Git-154) + diff --git a/patches/zano/0008-increase-max-password.patch b/patches/zano/0008-increase-max-password.patch new file mode 100644 index 0000000..0ce87c7 --- /dev/null +++ b/patches/zano/0008-increase-max-password.patch @@ -0,0 +1,25 @@ +From 6812b5de7e0a52e9341e54dbe2fc0b790dc6de5a Mon Sep 17 00:00:00 2001 +From: Czarek Nakamoto +Date: Thu, 12 Dec 2024 09:00:57 -0500 +Subject: [PATCH] increase max password + +--- + src/currency_core/currency_format_utils.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp +index 96d55b4..0c3d947 100644 +--- a/src/currency_core/currency_format_utils.cpp ++++ b/src/currency_core/currency_format_utils.cpp +@@ -3632,7 +3632,7 @@ namespace currency + return true; + } + //------------------------------------------------------------------ +- #define PASSWORD_REGEXP R"([A-Za-z0-9~!?@#$%^&*_+|{}\[\]()<>:;"'\-=/.,]{0,40})" ++ #define PASSWORD_REGEXP R"([A-Za-z0-9~!?@#$%^&*_+|{}\[\]()<>:;"'\-=/.,]{0,2048})" + bool validate_password(const std::string& password) + { + // OLD: static const std::string allowed_password_symbols = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!?@#$%^&*_+|{}[]()<>:;\"'-=\\/.,"; +-- +2.39.5 (Apple Git-154) + diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000..6914114 Binary files /dev/null and b/tests/.DS_Store differ diff --git a/tests/download_deps.ts b/tests/download_deps.ts index 808640e..8871fa5 100644 --- a/tests/download_deps.ts +++ b/tests/download_deps.ts @@ -4,8 +4,11 @@ import { Coin, getMoneroCTags } from "./utils.ts"; export type Target = `${typeof Deno["build"]["os"]}_${typeof Deno["build"]["arch"]}`; interface FileInfo { - overrideMirrors?: string[]; - name: string; + // List of mirrors that override DownloadInfo mirrors for this FileInfo + overrideMirrors?: [mainFilePath: string, ...fallbacks: string[]]; + // List of file names that fallback if previous failed + // If first name failed and got fallbacked by another, it will get renamed to the first entry + names: string[]; sha256?: string; } @@ -21,7 +24,7 @@ export function getFileInfo( downloadInfo: DownloadInfo, target: Target = `${Deno.build.os}_${Deno.build.arch}`, ): FileInfo { - const fileInfo = "name" in downloadInfo.file ? downloadInfo.file : downloadInfo.file[target]; + const fileInfo = "names" in downloadInfo.file ? downloadInfo.file : downloadInfo.file[target]; if (!fileInfo) { throw new Error(`No fileInfo set for target: ${target}`); } @@ -37,6 +40,61 @@ export async function downloadDependencies(...infos: DownloadInfo[]): Promise { + const url = `${mirror}/${fileName}`; + + const response = await fetch(url); + if (!response.ok) { + console.warn(`Could not reach file ${fileName} on mirror: ${mirror}`); + await response.body?.cancel(); + return; + } + + const responseBuffer = await response.bytes(); + + if (fileInfo.sha256) { + const responseChecksum = await sha256(responseBuffer); + if (responseChecksum !== fileInfo.sha256) { + console.warn( + `Checksum mismatch on file ${fileName} on mirror: ${mirror} (${responseChecksum} != ${fileInfo.sha256})`, + ); + return; + } + } + + return responseBuffer; +} + +async function validFileExists(filePath: string, fileInfo: FileInfo): Promise { + const [mainFileName] = fileInfo.names; + + let fileBuffer: Uint8Array; + try { + fileBuffer = await Deno.readFile(filePath); + } catch { + return false; + } + + // File exists, make sure checksum matches + if (fileInfo.sha256) { + const fileChecksum = await sha256(fileBuffer); + if (fileChecksum !== fileInfo.sha256) { + console.log( + `File ${mainFileName} already exists, but checksum is mismatched (${fileChecksum} != ${fileInfo.sha256}), redownloading`, + ); + await Deno.remove(filePath); + return false; + } + } + + console.log(`File ${mainFileName} already exists, skipping`); + return true; +} + export async function downloadFiles(outDir: string, target: Target, ...infos: DownloadInfo[]): Promise { try { await Deno.mkdir(outDir, { recursive: true }); @@ -48,65 +106,40 @@ export async function downloadFiles(outDir: string, target: Target, ...infos: Do for (const info of infos) { const fileInfo = getFileInfo(info, target); - const fileName = fileInfo.name; - const filePath = join(outDir, info.outDir ?? "", fileName); - - file_might_exist: try { - const fileBuffer = await Deno.readFile(filePath); - - // File exists, make sure checksum matches - if (fileInfo.sha256) { - const fileChecksum = await sha256(fileBuffer); - if (fileChecksum !== fileInfo.sha256) { - console.log( - `File ${fileName} already exists, but checksum is mismatched (${fileChecksum} != ${fileInfo.sha256}), redownloading`, - ); - await Deno.remove(filePath); - break file_might_exist; - } - } - - console.log(`File ${fileName} already exists, skipping`); + const [mainFileName] = fileInfo.names; + + if ( + await validFileExists( + join(outDir, info.outDir || ""), + fileInfo, + ) + ) { continue; - } catch { /**/ } + } let buffer: Uint8Array | undefined; + outer: for (const mirror of fileInfo.overrideMirrors ?? info.mirrors) { + for (const fileName of fileInfo.names) { + buffer = await tryToDownloadFile(mirror, fileInfo, fileName); - for (const mirror of fileInfo.overrideMirrors ?? info.mirrors) { - const url = `${mirror}/${fileName}`; - - const response = await fetch(url); - if (!response.ok) { - console.warn(`Could not reach file ${fileName} on mirror: ${mirror}`); - await response.body?.cancel(); - continue; - } - - const responseBuffer = await response.bytes(); - - if (fileInfo.sha256) { - const responseChecksum = await sha256(responseBuffer); - if (responseChecksum !== fileInfo.sha256) { - console.warn( - `Checksum mismatch on file ${fileName} on mirror: ${mirror} (${responseChecksum} != ${fileInfo.sha256})`, - ); - continue; + if (buffer) { + break outer; } } - - buffer = responseBuffer; } if (!buffer) { - throw new Error(`None of the mirrors for ${fileName} are available`); + throw new Error(`None of the mirrors for ${fileInfo.names} are available`); } + const filePath = join(outDir, info.outDir ?? "", mainFileName); + await Deno.mkdir(resolve(filePath, ".."), { recursive: true, }).catch(() => {}); await Deno.writeFile(filePath, buffer); - console.info("Downloaded file", fileInfo.name); + console.info("Downloaded file", filePath); } } @@ -117,25 +150,25 @@ export const wowneroCliInfo: DownloadInfo = { ], file: { linux_aarch64: { - name: "wownero-aarch64-linux-gnu-59db3fe8d.tar.bz2", + names: ["wownero-aarch64-linux-gnu-59db3fe8d.tar.bz2"], sha256: "07ce678302c07a6e79d90be65cbda243d843d414fbadb30f972d6c226575cfa7", }, linux_x86_64: { - name: "wownero-x86_64-linux-gnu-59db3fe8d.tar.bz2", + names: ["wownero-x86_64-linux-gnu-59db3fe8d.tar.bz2"], sha256: "03880967c70cc86558d962b8a281868c3934238ea457a36174ba72b99d70107e", }, darwin_aarch64: { - name: "wownero-aarch64-apple-darwin11-59db3fe8d.tar.bz2", + names: ["wownero-aarch64-apple-darwin11-59db3fe8d.tar.bz2"], sha256: "25ff454a92b1cf036df5f28cdd2c63dcaf4b03da7da9403087371f868827c957", }, darwin_x86_64: { - name: "wownero-x86_64-apple-darwin11-59db3fe8d.tar.bz2", + names: ["wownero-x86_64-apple-darwin11-59db3fe8d.tar.bz2"], sha256: "7e9b6a84a560ed7a9ed7117c6f07fb228d77a06afac863d0ea1dbf833c4eddf6", }, windows_x86_64: { - name: "wownero-x86_64-w64-mingw32-59db3fe8d.zip", + names: ["wownero-x86_64-w64-mingw32-59db3fe8d.zip"], sha256: "7e0ed84afa51e3b403d635c706042859094eb6850de21c9e82cb0a104425510e", }, @@ -144,7 +177,7 @@ export const wowneroCliInfo: DownloadInfo = { "https://static.mrcyjanek.net/download_mirror/", "https://codeberg.org/wownero/wownero/releases/download/v0.11.1.0/", ], - name: "wownero-aarch64-linux-android-v0.11.1.0.tar.bz2", + names: ["wownero-aarch64-linux-android-v0.11.1.0.tar.bz2"], sha256: "236188f8d8e7fad2ff35973f8c2417afffa8855d1a57b4c682fff5b199ea40f5", }, }, @@ -157,30 +190,30 @@ export const moneroCliInfo: DownloadInfo = { ], file: { linux_aarch64: { - name: "monero-linux-armv8-v0.18.3.4.tar.bz2", + names: ["monero-linux-armv8-v0.18.3.4.tar.bz2"], sha256: "33ca2f0055529d225b61314c56370e35606b40edad61c91c859f873ed67a1ea7", }, linux_x86_64: { - name: "monero-linux-x64-v0.18.3.4.tar.bz2", + names: ["monero-linux-x64-v0.18.3.4.tar.bz2"], sha256: "51ba03928d189c1c11b5379cab17dd9ae8d2230056dc05c872d0f8dba4a87f1d", }, darwin_aarch64: { - name: "monero-mac-armv8-v0.18.3.4.tar.bz2", + names: ["monero-mac-armv8-v0.18.3.4.tar.bz2"], sha256: "44520cb3a05c2518ca9aeae1b2e3080fe2bba1e3596d014ceff1090dfcba8ab4", }, darwin_x86_64: { - name: "monero-mac-x64-v0.18.3.4.tar.bz2", + names: ["monero-mac-x64-v0.18.3.4.tar.bz2"], sha256: "32c449f562216d3d83154e708471236d07db7477d6b67f1936a0a85a5005f2b8", }, windows_x86_64: { - name: "monero-win-x64-v0.18.3.4.zip", + names: ["monero-win-x64-v0.18.3.4.zip"], sha256: "54a66db6c892b2a0999754841f4ca68511741b88ea3ab20c7cd504a027f465f5", }, android_aarch64: { - name: "monero-android-armv8-v0.18.3.4.tar.bz2", + names: ["monero-android-armv8-v0.18.3.4.tar.bz2"], sha256: "d9c9249d1408822ce36b346c6b9fb6b896cda16714d62117fb1c588a5201763c", }, }, @@ -199,12 +232,22 @@ for (const tag of await getMoneroCTags()) { `https://github.com/MrCyjaneK/monero_c/releases/download/${tag}/`, ], file: { - linux_aarch64: { name: `${coin}_aarch64-linux-gnu_libwallet2_api_c.so.xz` }, - linux_x86_64: { name: `${coin}_x86_64-linux-gnu_libwallet2_api_c.so.xz` }, - darwin_aarch64: { name: `${coin}_aarch64-apple-darwin11_libwallet2_api_c.dylib.xz` }, - darwin_x86_64: { name: `${coin}_x86_64-apple-darwin11_libwallet2_api_c.dylib.xz` }, - windows_x86_64: { name: `${coin}_x86_64-w64-mingw32_libwallet2_api_c.dll.xz` }, - android_aarch64: { name: `${coin}_aarch64-linux-android_libwallet2_api_c.so.xz` }, + linux_aarch64: { names: [`${coin}_aarch64-linux-gnu_libwallet2_api_c.so.xz`] }, + linux_x86_64: { names: [`${coin}_x86_64-linux-gnu_libwallet2_api_c.so.xz`] }, + darwin_aarch64: { + names: [ + `${coin}_aarch64-apple-darwin11_libwallet2_api_c.dylib.xz`, + `${coin}_aarch64-apple-darwin_libwallet2_api_c.dylib.xz`, + ], + }, + darwin_x86_64: { + names: [ + `${coin}_x86_64-apple-darwin11_libwallet2_api_c.dylib.xz`, + `${coin}_x86_64-apple-darwin_libwallet2_api_c.dylib.xz`, + ], + }, + windows_x86_64: { names: [`${coin}_x86_64-w64-mingw32_libwallet2_api_c.dll.xz`] }, + android_aarch64: { names: [`${coin}_aarch64-linux-android_libwallet2_api_c.so.xz`] }, }, outDir: `libs/${tag}`, }); diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 100bd43..2570874 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -443,7 +443,7 @@ Deno.test("0004-coin-control.patch", { assertEquals(await transaction.status(), 1); assertEquals( - await transaction.errorString(), + (await transaction.errorString())?.split("\n")[0], "not enough money to transfer, overall balance only 0.002000000000, sent amount 0.002000000000", ); }); diff --git a/tests/utils.ts b/tests/utils.ts index 86501a8..9408f54 100755 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -84,13 +84,13 @@ export async function extract(path: string, out: string) { export async function prepareMoneroCli() { await downloadDependencies(moneroCliInfo); - const path = join("./tests/dependencies", moneroCliInfo.outDir ?? "", getFileInfo(moneroCliInfo).name); + const path = join("./tests/dependencies", moneroCliInfo.outDir ?? "", getFileInfo(moneroCliInfo).names[0]); await extract(path, "./tests/dependencies/monero-cli/"); } export async function prepareWowneroCli() { await downloadDependencies(wowneroCliInfo); - const path = join("./tests/dependencies", wowneroCliInfo.outDir ?? "", getFileInfo(wowneroCliInfo).name); + const path = join("./tests/dependencies", wowneroCliInfo.outDir ?? "", getFileInfo(wowneroCliInfo).names[0]); await extract(path, "./tests/dependencies/wownero-cli/"); } diff --git a/zano b/zano new file mode 160000 index 0000000..2817090 --- /dev/null +++ b/zano @@ -0,0 +1 @@ +Subproject commit 2817090c8ac7639d6f697d00fc8bcba2b3681d90 diff --git a/zano_libwallet2_api_c/CMakeLists.txt b/zano_libwallet2_api_c/CMakeLists.txt new file mode 120000 index 0000000..73b40ff --- /dev/null +++ b/zano_libwallet2_api_c/CMakeLists.txt @@ -0,0 +1 @@ +../monero_libwallet2_api_c/CMakeLists.txt \ No newline at end of file diff --git a/zano_libwallet2_api_c/src/main/cpp/helpers.cpp b/zano_libwallet2_api_c/src/main/cpp/helpers.cpp new file mode 120000 index 0000000..524768b --- /dev/null +++ b/zano_libwallet2_api_c/src/main/cpp/helpers.cpp @@ -0,0 +1 @@ +../../../../monero_libwallet2_api_c/src/main/cpp/helpers.cpp \ No newline at end of file diff --git a/zano_libwallet2_api_c/src/main/cpp/helpers.hpp b/zano_libwallet2_api_c/src/main/cpp/helpers.hpp new file mode 120000 index 0000000..99d2733 --- /dev/null +++ b/zano_libwallet2_api_c/src/main/cpp/helpers.hpp @@ -0,0 +1 @@ +../../../../monero_libwallet2_api_c/src/main/cpp/helpers.hpp \ No newline at end of file diff --git a/zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp b/zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp new file mode 100644 index 0000000..11acc0a --- /dev/null +++ b/zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.cpp @@ -0,0 +1,322 @@ +#include +#include "wallet2_api_c.h" +#include +#include +#include +#include "zano_checksum.h" +#include "helpers.hpp" +#include "../../../../zano/src/wallet/plain_wallet_api.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +// namespace plain_wallet +// { +// typedef int64_t hwallet; +// std::string init(const std::string& address, const std::string& working_dir, int log_level); +const char* ZANO_PlainWallet_init(const char* address, const char* working_dir, int log_level) { + DEBUG_START() + std::string str = plain_wallet::init(std::string(address), std::string(working_dir), log_level); + 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 init(const std::string& ip, const std::string& port, const std::string& working_dir, int log_level); +const char* ZANO_PlainWallet_init2(const char* ip, const char* port, const char* working_dir, int log_level) { + DEBUG_START() + std::string str = plain_wallet::init(std::string(ip), std::string(port), std::string(working_dir), log_level); + 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 reset(); +const char* ZANO_PlainWallet_reset() { + DEBUG_START() + std::string str = plain_wallet::reset(); + 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 set_log_level(int log_level); +const char* ZANO_PlainWallet_setLogLevel(int log_level) { + DEBUG_START() + std::string str = plain_wallet::set_log_level(log_level); + 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 get_version(); +const char* ZANO_PlainWallet_getVersion() { + DEBUG_START() + std::string str = plain_wallet::get_version(); + 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 get_wallet_files(); +const char* ZANO_PlainWallet_getWalletFiles() { + DEBUG_START() + std::string str = plain_wallet::get_wallet_files(); + 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 get_export_private_info(const std::string& target_dir); +const char* ZANO_PlainWallet_getExportPrivateInfo(const char* target_dir) { + DEBUG_START() + std::string str = plain_wallet::get_export_private_info(std::string(target_dir)); + 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 delete_wallet(const std::string& file_name); +const char* ZANO_PlainWallet_deleteWallet(const char* file_name) { + DEBUG_START() + std::string str = plain_wallet::delete_wallet(std::string(file_name)); + 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 get_address_info(const std::string& addr); +const char* ZANO_PlainWallet_getAddressInfo(const char* addr) { + DEBUG_START() + std::string str = plain_wallet::get_address_info(std::string(addr)); + 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 get_appconfig(const std::string& encryption_key); +const char* ZANO_PlainWallet_getAppconfig(const char* encryption_key) { + DEBUG_START() + std::string str = plain_wallet::get_appconfig(std::string(encryption_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() +} +// std::string set_appconfig(const std::string& conf_str, const std::string& encryption_key); +const char* ZANO_PlainWallet_setAppconfig(const char* conf_str, const char* encryption_key) { + DEBUG_START() + std::string str = plain_wallet::set_appconfig(std::string(conf_str), std::string(encryption_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() +} +// std::string generate_random_key(uint64_t lenght); +const char* ZANO_PlainWallet_generateRandomKey(uint64_t lenght) { + DEBUG_START() + std::string str = plain_wallet::generate_random_key(lenght); + 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 get_logs_buffer(); +const char* ZANO_PlainWallet_getLogsBuffer() { + DEBUG_START() + std::string str = plain_wallet::get_logs_buffer(); + 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 truncate_log(); +const char* ZANO_PlainWallet_truncateLog() { + DEBUG_START() + std::string str = plain_wallet::truncate_log(); + 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 get_connectivity_status(); +const char* ZANO_PlainWallet_getConnectivityStatus() { + DEBUG_START() + std::string str = plain_wallet::get_connectivity_status(); + 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 open(const std::string& path, const std::string& password); +const char* ZANO_PlainWallet_open(const char* path, const char* password) { + DEBUG_START() + std::string str = plain_wallet::open(std::string(path), std::string(password)); + 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 restore(const std::string& seed, const std::string& path, const std::string& password, const std::string& seed_password); +const char* ZANO_PlainWallet_restore(const char* seed, const char* path, const char* password, const char* seed_password) { + DEBUG_START() + std::string str = plain_wallet::restore(std::string(seed), std::string(path), std::string(password), std::string(seed_password)); + 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 generate(const std::string& path, const std::string& password); +const char* ZANO_PlainWallet_generate(const char* path, const char* password) { + DEBUG_START() + std::string str = plain_wallet::generate(std::string(path), std::string(password)); + 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 get_opened_wallets(); +const char* ZANO_PlainWallet_getOpenWallets() { + DEBUG_START() + std::string str = plain_wallet::get_opened_wallets(); + 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 get_wallet_status(hwallet h); +const char* ZANO_PlainWallet_getWalletStatus(int64_t h) { + DEBUG_START() + std::string str = plain_wallet::get_wallet_status(h); + 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 close_wallet(hwallet h); +const char* ZANO_PlainWallet_closeWallet(int64_t h) { + DEBUG_START() + std::string str = plain_wallet::close_wallet(h); + 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 invoke(hwallet h, const std::string& params); +const char* ZANO_PlainWallet_invoke(int64_t h, const char* params) { + DEBUG_START() + std::string str = plain_wallet::invoke(h, std::string(params)); + 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() +} +// //async api +// std::string async_call(const std::string& method_name, uint64_t instance_id, const std::string& params); +const char* ZANO_PlainWallet_asyncCall(const char* method_name, uint64_t instance_id, const char* params) { + DEBUG_START() + std::string str = plain_wallet::async_call(std::string(method_name), instance_id, std::string(params)); + 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 try_pull_result(uint64_t); +const char* ZANO_PlainWallet_tryPullResult(uint64_t instance_id) { + DEBUG_START() + std::string str = plain_wallet::try_pull_result(instance_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() +} +// std::string sync_call(const std::string& method_name, uint64_t instance_id, const std::string& params); +const char* ZANO_PlainWallet_syncCall(const char* method_name, uint64_t instance_id, const char* params) { + DEBUG_START() + std::string str = plain_wallet::sync_call(std::string(method_name), instance_id, std::string(params)); + 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() +} +// //cake wallet api extension +// bool is_wallet_exist(const std::string& path); +bool ZANO_PlainWallet_isWalletExist(const char* path) { + DEBUG_START() + return plain_wallet::is_wallet_exist(std::string(path)); + DEBUG_END() +} +// std::string get_wallet_info(hwallet h); +const char* ZANO_PlainWallet_getWalletInfo(int64_t h) { + DEBUG_START() + std::string str = plain_wallet::get_wallet_info(h); + 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 reset_wallet_password(hwallet h, const std::string& password); +const char* ZANO_PlainWallet_resetWalletPassword(int64_t h, const char* password) { + DEBUG_START() + std::string str = plain_wallet::reset_wallet_password(h, std::string(password)); + 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 get_current_tx_fee(uint64_t priority); // 0 (default), 1 (unimportant), 2 (normal), 3 (elevated), 4 (priority) +uint64_t ZANO_PlainWallet_getCurrentTxFee(uint64_t priority) { + DEBUG_START() + return plain_wallet::get_current_tx_fee(priority); + DEBUG_END() +} +// } + +void ZANO_free(void* ptr) { + free(ptr); +} + +const char* ZANO_checksum_wallet2_api_c_h() { + return ZANO_wallet2_api_c_h_sha256; +} +const char* ZANO_checksum_wallet2_api_c_cpp() { + return ZANO_wallet2_api_c_cpp_sha256; +} +const char* ZANO_checksum_wallet2_api_c_exp() { + return ZANO_wallet2_api_c_exp_sha256; +} + +#ifdef __cplusplus +} +#endif diff --git a/zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.h b/zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.h new file mode 100644 index 0000000..df6d3ef --- /dev/null +++ b/zano_libwallet2_api_c/src/main/cpp/wallet2_api_c.h @@ -0,0 +1,67 @@ +/* +#include + +#define LOG_TAG "[NDK]" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) +*/ +#include +#include +#include +#include +#include "zano_checksum.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __MINGW32__ + #define ADDAPI __declspec(dllexport) +#else + #define ADDAPI __attribute__((__visibility__("default"))) +#endif + + +extern ADDAPI const char* ZANO_PlainWallet_init(const char* address, const char* working_dir, int log_level); +extern ADDAPI const char* ZANO_PlainWallet_init2(const char* ip, const char* port, const char* working_dir, int log_level); +extern ADDAPI const char* ZANO_PlainWallet_reset(); +extern ADDAPI const char* ZANO_PlainWallet_setLogLevel(int log_level); +extern ADDAPI const char* ZANO_PlainWallet_getVersion(); +extern ADDAPI const char* ZANO_PlainWallet_getWalletFiles(); +extern ADDAPI const char* ZANO_PlainWallet_getExportPrivateInfo(const char* target_dir); +extern ADDAPI const char* ZANO_PlainWallet_deleteWallet(const char* file_name); +extern ADDAPI const char* ZANO_PlainWallet_getAddressInfo(const char* addr); +extern ADDAPI const char* ZANO_PlainWallet_getAppconfig(const char* encryption_key); +extern ADDAPI const char* ZANO_PlainWallet_setAppconfig(const char* conf_str, const char* encryption_key); +extern ADDAPI const char* ZANO_PlainWallet_generateRandomKey(uint64_t lenght); +extern ADDAPI const char* ZANO_PlainWallet_getLogsBuffer(); +extern ADDAPI const char* ZANO_PlainWallet_truncateLog(); +extern ADDAPI const char* ZANO_PlainWallet_getConnectivityStatus(); +extern ADDAPI const char* ZANO_PlainWallet_open(const char* path, const char* password); +extern ADDAPI const char* ZANO_PlainWallet_restore(const char* seed, const char* path, const char* password, const char* seed_password); +extern ADDAPI const char* ZANO_PlainWallet_generate(const char* path, const char* password); +extern ADDAPI const char* ZANO_PlainWallet_getOpenWallets(); +extern ADDAPI const char* ZANO_PlainWallet_getWalletStatus(int64_t h); +extern ADDAPI const char* ZANO_PlainWallet_closeWallet(int64_t h); +extern ADDAPI const char* ZANO_PlainWallet_invoke(int64_t h, const char* params); + + +extern ADDAPI const char* ZANO_PlainWallet_asyncCall(const char* method_name, uint64_t instance_id, const char* params); +extern ADDAPI const char* ZANO_PlainWallet_tryPullResult(uint64_t instance_id); +extern ADDAPI const char* ZANO_PlainWallet_syncCall(const char* method_name, uint64_t instance_id, const char* params); +extern ADDAPI bool ZANO_PlainWallet_isWalletExist(const char* path); +extern ADDAPI const char* ZANO_PlainWallet_getWalletInfo(int64_t h); +extern ADDAPI const char* ZANO_PlainWallet_resetWalletPassword(int64_t h, const char* password); +extern ADDAPI uint64_t ZANO_PlainWallet_getCurrentTxFee(uint64_t priority); + +extern ADDAPI void ZANO_free(void* ptr); + +extern ADDAPI const char* ZANO_checksum_wallet2_api_c_h(); +extern ADDAPI const char* ZANO_checksum_wallet2_api_c_cpp(); +extern ADDAPI const char* ZANO_checksum_wallet2_api_c_exp(); + +#ifdef __cplusplus +} +#endif diff --git a/zano_libwallet2_api_c/src/main/cpp/zano_checksum.h b/zano_libwallet2_api_c/src/main/cpp/zano_checksum.h new file mode 100644 index 0000000..7f8d845 --- /dev/null +++ b/zano_libwallet2_api_c/src/main/cpp/zano_checksum.h @@ -0,0 +1,6 @@ +#ifndef MONEROC_CHECKSUMS +#define MONEROC_CHECKSUMS +const char * ZANO_wallet2_api_c_h_sha256 = "8acaa95513b85a984c08e05cc3f2ac7530bb8f32946eeeb45357bd846aef33dd"; +const char * ZANO_wallet2_api_c_cpp_sha256 = "4efacd3812d53dd268b6869cc0a9560e7320574d96e09136cf067f796edfeba6-2817090c8ac7639d6f697d00fc8bcba2b3681d90"; +const char * ZANO_wallet2_api_c_exp_sha256 = "66f3ff655bbfd11ad28c318ab707090b5a93276f436b06f7b1c0f329dba3c9c2"; +#endif diff --git a/zano_libwallet2_api_c/zano_libwallet2_api_c.exp b/zano_libwallet2_api_c/zano_libwallet2_api_c.exp new file mode 100644 index 0000000..423922a --- /dev/null +++ b/zano_libwallet2_api_c/zano_libwallet2_api_c.exp @@ -0,0 +1,33 @@ +_ZANO_PlainWallet_init +_ZANO_PlainWallet_init2 +_ZANO_PlainWallet_reset +_ZANO_PlainWallet_setLogLevel +_ZANO_PlainWallet_getVersion +_ZANO_PlainWallet_getWalletFiles +_ZANO_PlainWallet_getExportPrivateInfo +_ZANO_PlainWallet_deleteWallet +_ZANO_PlainWallet_getAddressInfo +_ZANO_PlainWallet_getAppconfig +_ZANO_PlainWallet_setAppconfig +_ZANO_PlainWallet_generateRandomKey +_ZANO_PlainWallet_getLogsBuffer +_ZANO_PlainWallet_truncateLog +_ZANO_PlainWallet_getConnectivityStatus +_ZANO_PlainWallet_open +_ZANO_PlainWallet_restore +_ZANO_PlainWallet_generate +_ZANO_PlainWallet_getOpenWallets +_ZANO_PlainWallet_getWalletStatus +_ZANO_PlainWallet_closeWallet +_ZANO_PlainWallet_invoke +_ZANO_PlainWallet_asyncCall +_ZANO_PlainWallet_tryPullResult +_ZANO_PlainWallet_syncCall +_ZANO_PlainWallet_isWalletExist +_ZANO_PlainWallet_getWalletInfo +_ZANO_PlainWallet_resetWalletPassword +_ZANO_PlainWallet_getCurrentTxFee +_ZANO_free +_ZANO_checksum_wallet2_api_c_h +_ZANO_checksum_wallet2_api_c_cpp +_ZANO_checksum_wallet2_api_c_exp \ No newline at end of file -- cgit v1.2.3