commit 0f612f0bbe592c750d66985caab81a8245d03993 Author: Czarek Nakamoto Date: Tue Dec 26 21:43:14 2023 +0100 Initial commit diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..a5ffce5 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,289 @@ +name: Android arm64-v8a Release +run-name: Building Anonero for android arm64-v8a +on: + push: + tags: + - '*' + +# cat envs.txt | sed 's/ //g' | sed 's/: /=/g' | grep -v '#' > .env +# export $(cat .env | xargs) +env: + WORKDIR: /opt/android + ANDROID_SDK_ROOT: /opt/android/tools + ANDROID_NDK_ROOT: /opt/android/android-ndk-r17c + PREFIX: /opt/android/prefix + TOOLCHAIN_DIR: /opt/android/toolchain + NPROC: 2 + CLEAN_PATH: /usr/cmake-3.14.6-Linux-x86_64/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + + PATH: /usr/cmake-3.14.6-Linux-x86_64/bin:/opt/android/toolchain/aarch64-linux-android/bin:/opt/android/toolchain/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + # CMake + CMAKE_VERSION: '3.14.6' + CMAKE_HASH: '82e08e50ba921035efa82b859c74c5fbe27d3e49a4003020e3c77618a4e912cd' + # Boost + BOOST_VERSION: '1_70_0' + BOOST_VERSION_DOT: '1.70.0' + BOOST_HASH: '430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778' + # iconv + ICONV_VERSION: '1.16' + ICONV_HASH: 'e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04' + # zlib + ZLIB_VERSION: '1.3' + ZLIB_HASH: 'ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e' + # OpenSSL + OPENSSL_VERSION: '3.0.5' + OPENSSL_HASH: 'aa7d8d9bef71ad6525c55ba11e5f4397889ce49c2c9349dcea6d3e4f0b024a7a' + # ZMQ + # NOTE: aarch64-linux-android-clang instead of clang + # has to be used - because ./configure fails to detect the compiler. + ZMQ_VERSION: 'v4.3.2' + ZMQ_HASH: 'a84ffa12b2eb3569ced199660bac5ad128bff1f0' + # libsodium + SODIUM_VERSION: '1.0.18' + SODIUM_HASH: '4f5e89fa84ce1d178a6765b8b46f2b6f91216677' + # libexpat + LIBEXPAT_VERSION: 'R_2_4_8' + LIBEXPAT_HASH: '3bab6c09bbe8bf42d84b81563ddbcf4cca4be838' + # libunbound + LIBUNBOUND_VERSION: 'branch-1.16.1' + LIBUNBOUND_HASH: '903538c76e1d8eb30d0814bb55c3ef1ea28164e8' + # polyseed + POLYSEED_HASH: 'b7c35bb3c6b91e481ecb04fc235eaff69c507fa1' + # utf8proc + UTF8PROC_HASH: '1cb28a66ca79a0845e99433fd1056257456cef8b' + # (finally) monero + MONERO_GIT_SOURCE: https://github.com/monero-project/monero + MONERO_TAG: 'v0.18.3.1' + + +jobs: + build-arm64v8a: + runs-on: androidndk-r17c + steps: + - name: Checkout repository code + uses: actions/checkout@v3 + - name: Download monero + run: | + cd ${{ github.workspace }} \ + && git clone ${MONERO_GIT_SOURCE} --depth=1 --branch ${MONERO_TAG} monero \ + && cd monero \ + && git submodule init && git submodule update + - name: Install CMake + run: | + cd ${{ github.workspace }} \ + && cd /usr \ + && curl -L -O https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ + && echo "${CMAKE_HASH} cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz" | sha256sum -c \ + && tar -xzf /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ + && rm -f /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz + - name: Download Boost + run: | + cd ${{ github.workspace }} \ + && curl -L -o boost_${BOOST_VERSION}.tar.bz2 https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION_DOT}/source/boost_${BOOST_VERSION}.tar.bz2 \ + && echo "${BOOST_HASH} boost_${BOOST_VERSION}.tar.bz2" | sha256sum -c \ + && tar -xf boost_${BOOST_VERSION}.tar.bz2 \ + && rm -f boost_${BOOST_VERSION}.tar.bz2 + - name: Download iconv + run: | + cd ${{ github.workspace }} \ + && curl -O http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz \ + && echo "${ICONV_HASH} libiconv-${ICONV_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf libiconv-${ICONV_VERSION}.tar.gz \ + && rm -f libiconv-${ICONV_VERSION}.tar.gz + - name: Download zlib + run: | + cd ${{ github.workspace }} \ + && curl -O https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \ + && echo "${ZLIB_HASH} zlib-${ZLIB_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf zlib-${ZLIB_VERSION}.tar.gz \ + && rm zlib-${ZLIB_VERSION}.tar.gz \ + && mv zlib-${ZLIB_VERSION} zlib + - name: Download OpenSSL + run: | + cd ${{ github.workspace }} \ + && curl -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \ + && echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \ + && rm openssl-${OPENSSL_VERSION}.tar.gz + - name: Download ZMQ + run: | + cd ${{ github.workspace }} \ + && git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} --depth=1 \ + && cd libzmq \ + && test `git rev-parse HEAD` = ${ZMQ_HASH} || exit 1 + - name: Download libsodium + run: | + cd ${{ github.workspace }} \ + && git clone https://github.com/jedisct1/libsodium.git -b ${SODIUM_VERSION} --depth=1 \ + && cd libsodium \ + && test `git rev-parse HEAD` = ${SODIUM_HASH} || exit 1 + - name: Download libexpat + run: | + cd ${{ github.workspace }} \ + && git clone https://github.com/libexpat/libexpat.git -b ${LIBEXPAT_VERSION} --depth=1 \ + && cd libexpat/expat \ + && test `git rev-parse HEAD` = ${LIBEXPAT_HASH} || exit 1 \ + - name: Download libunbound + run: | + cd ${{ github.workspace }} \ + && git clone https://github.com/NLnetLabs/unbound.git -b ${LIBUNBOUND_VERSION} --depth=1 \ + && cd unbound \ + && test `git rev-parse HEAD` = ${LIBUNBOUND_HASH} || exit 1 + - name: Download polyseed + run: | + cd ${{ github.workspace }} \ + && git clone https://github.com/tevador/polyseed.git \ + && cd polyseed \ + && git reset --hard ${POLYSEED_HASH} + - name: Download utf8proc + run: | + cd ${{ github.workspace }} \ + && git clone https://github.com/JuliaStrings/utf8proc -b v2.8.0 --depth=1 \ + && cd utf8proc \ + && git reset --hard ${UTF8PROC_HASH} + - name: Make standalone toolchain + run: | + ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py \ + --arch arm64 \ + --api 21 \ + --install-dir ${TOOLCHAIN_DIR} \ + --stl=libc++ + - name: Build boost (1/2) + run: | + cd ${{ github.workspace }} \ + && cd boost_${BOOST_VERSION} \ + && PATH=$CLEAN_PATH ./bootstrap.sh --prefix=${PREFIX} + # iconv: libtool: warning: remember to run 'libtool --finish /opt/android/prefix/lib' + - name: Build iconv (for boost) + run: | + cd ${{ github.workspace }} \ + && cd libiconv-${ICONV_VERSION} \ + && CC=clang CXX=clang++ ./configure --build=x86_64-linux-gnu --host=aarch64-linux-android --prefix=${PREFIX} --disable-rpath \ + && make -j${NPROC} \ + && make install + - name: Build boost (2/2) + run: | + cd ${{ github.workspace }} \ + && cd boost_${BOOST_VERSION} \ + && ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --with-locale --build-dir=android --stagedir=android toolset=clang threading=multi threadapi=pthread target-os=android -sICONV_PATH=${PREFIX} install -j${NPROC} &>/tmp/boost_log.txt || cat /tmp/boost_log.txt + - name: Build zlib + run: | + cd ${{ github.workspace }} \ + && cd zlib \ + && CC=clang CXX=clang++ ./configure --static \ + && make -j${NPROC} + - name: Build OpenSSL + run: | + cd ${{ github.workspace }} \ + && cd openssl-${OPENSSL_VERSION} \ + && export PATH=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH \ + && ./Configure android-arm64 \ + -D__ANDROID_API__=21 \ + -static \ + no-shared no-tests \ + --with-zlib-include=${WORKDIR}/zlib/include --with-zlib-lib=${WORKDIR}/zlib/lib \ + --prefix=${PREFIX} --openssldir=${PREFIX} \ + && make -j${NPROC} \ + && make install_sw + - name: Build ZMQ + run: | + cd ${{ github.workspace }} \ + && cd libzmq \ + && ./autogen.sh \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install + - name: Build Sodium + run: | + cd ${{ github.workspace }} \ + && cd libsodium \ + && ./autogen.sh \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install + - name: Build libexpat + run: | + cd ${{ github.workspace }} \ + && cd libexpat/expat \ + && ./buildconf.sh \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install + - name: Build libunbound + run: | + cd ${{ github.workspace }} \ + && cd unbound \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared --disable-flto --with-ssl=${PREFIX} --with-libexpat=${PREFIX} \ + && make -j${NPROC} \ + && make install + - name: Build polyseed + run: | + cd ${{ github.workspace }} \ + && cd polyseed \ + && CC=clang CXX=clang++ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} . \ + && make -j${NPROC} \ + && make install + - name: Build utf8proc + run: | + cd ${{ github.workspace }} \ + && cd utf8proc \ + && mkdir build \ + && cd build \ + && rm -rf ../CMakeCache.txt ../CMakeFiles/ \ + && CC=clang CXX=clang++ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} .. \ + && make -j${NPROC} \ + && make install + - name: Build monero (1/2) configure + run: | + cd ${{ github.workspace }} \ + && cd monero \ + && export CMAKE_INCLUDE_PATH="${PREFIX}/include" \ + CMAKE_LIBRARY_PATH="${PREFIX}/lib" \ + ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \ + USE_SINGLE_BUILDDIR=1 \ + PATH=${CLEAN_PATH} \ + && mkdir -p build/release \ + && cd build/release \ + && CC=clang CXX=clang++ cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-armv8" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="arm64-v8a" ../.. + - name: Build monero (2/2) build + run: | + cd ${{ github.workspace }} \ + && cd monero \ + && cd build/release \ + && export CMAKE_INCLUDE_PATH="${PREFIX}/include" \ + CMAKE_LIBRARY_PATH="${PREFIX}/lib" \ + ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \ + USE_SINGLE_BUILDDIR=1 \ + PATH=${CLEAN_PATH} \ + && make wallet_api -j${NPROC} + # && ln -s /src/build/release/lib/ /opt/android/monero \ + - name: Copy prefix from $PREFIX to local + run: | + cp -a /opt/android/prefix ${{ github.workspace }}/prefix + - name: Build libbridge (1/2) configure + run: | + cd ${{ github.workspace }} \ + && cd libbridge \ + && mkdir build \ + && cd build \ + && env CC=clang CXX=clang++ cmake -DANDROID_ABI=arm64-v8a .. \ + && make -j${NPROC} + - name: Build libbridge (2/2) make + run: | + cd ${{ github.workspace }} \ + && cd libbridge \ + && cd build \ + && make -j${NPROC} + - name: Rename libwallet2_api_c.so + run: | + cd ${{ github.workspace }}/libbridge/build/ + mv libwallet2_api_c.so android_arm64-v8a_libwallet2_api_c.so + - name: Create release + uses: akkuman/github-release-action@v1 + with: + body: | + This is a **preview** release to be used by testers. + It was build from **${{ github.ref }}** commit `${{ github.sha }}` + Grab corresponding [wallet2_api_c.h](https://git.mrcyjanek.net/mrcyjanek/monero_c/src/commit/${{ github.sha }}/libbridge/src/main/cpp/wallet2_api_c.h) + files: |- + ${{ github.workspace }}/libbridge/build/android_arm64-v8a_libwallet2_api_c.so \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e75f9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/arm64-v8a +/armeabi-v7a +/x86 +/x86_64 +monero* \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c9fb3b6 --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +FLAVOUR = monero + +all: arm64-v8a x86 x86_64 armeabi-v7a include/wallet2_api.h VERSION + +arm64-v8a: $(FLAVOUR) android64.Dockerfile + -rm -rf arm64-v8a + -docker container rm $(FLAVOUR)-android-arm64 -f + docker build -f android64.Dockerfile -t $(FLAVOUR)-android-arm64 . + docker create -it --name $(FLAVOUR)-android-arm64 $(FLAVOUR)-android-arm64 bash + # docker cp $(FLAVOUR)-android-arm64:/opt/android/prefix/lib/. arm64-v8a/ + # docker cp $(FLAVOUR)-android-arm64:/src/build/release/lib/. arm64-v8a/$(FLAVOUR) + mkdir arm64-v8a + docker cp $(FLAVOUR)-android-arm64:/opt/android/libbridge/build/libmonerujo.so arm64-v8a/libmonerujo.so + docker cp $(FLAVOUR)-android-arm64:/opt/android/toolchain/aarch64-linux-android/lib/libc++_shared.so arm64-v8a/libc++_shared.so + +include/wallet2_api.h: $(FLAVOUR) include $(FLAVOUR)/src/wallet/api/wallet2_api.h + cp $(FLAVOUR)/src/wallet/api/wallet2_api.h include/wallet2_api.h + +include: + mkdir include + +VERSION: + echo MONERUJO_$(FLAVOUR) `git -C . branch | grep "^\*" | sed 's/^..//'` with $(FLAVOUR) `git -C $(FLAVOUR) branch | grep "^\*" | sed 's/^..//'` `git -C $(FLAVOUR) rev-parse --short=12 HEAD` > VERSION + +clean: + -rm -rf arm64-v8a + -rm -rf armeabi-v7a + -rm -rf x86_64 + -rm -rf x86 + -rm -rf include + +distclean: clean + -docker container rm $(FLAVOUR)-android-arm64 -f + -docker container rm $(FLAVOUR)-android-arm32 -f + -docker container rm $(FLAVOUR)-android-x86_64 -f + -docker container rm $(FLAVOUR)-android-x86 -f + -docker image rm $(FLAVOUR)-android-arm64 -f + -docker image rm $(FLAVOUR)-android-arm32 -f + -docker image rm $(FLAVOUR)-android-x86_64 -f + -docker image rm $(FLAVOUR)-android-x86 -f + + -rm $(FLAVOUR) + +$(FLAVOUR): + $(error Please ln -s $(FLAVOUR)) + +.PHONY: all clean distclean VERSION include/wallet2_api.h arm64-v8a diff --git a/README.md b/README.md new file mode 100644 index 0000000..b65f4f2 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# wallet2_api.h (but this time C compatible) + +Wrapper around wallet2_api.h that can be called using C api. \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..fde13a8 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +MONERUJO_monero with monero release-v0.18.2.2-monerujo cfd9e4f661cf diff --git a/android64.Dockerfile b/android64.Dockerfile new file mode 100644 index 0000000..6911188 --- /dev/null +++ b/android64.Dockerfile @@ -0,0 +1,178 @@ +FROM registry.mrcyjanek.net/androidndk:r17c + +RUN set -x \ + && ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py \ + --arch arm64 \ + --api 21 \ + --install-dir ${TOOLCHAIN_DIR} \ + --stl=libc++ + +#INSTALL cmake +ARG CMAKE_VERSION=3.14.6 +ARG CMAKE_HASH=82e08e50ba921035efa82b859c74c5fbe27d3e49a4003020e3c77618a4e912cd +RUN set -x \ + && cd /usr \ + && curl -L -O https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ + && echo "${CMAKE_HASH} cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz" | sha256sum -c \ + && tar -xzf /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz \ + && rm -f /usr/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz +ENV PATH /usr/cmake-${CMAKE_VERSION}-Linux-x86_64/bin:$PATH + +## Boost +ARG BOOST_VERSION=1_70_0 +ARG BOOST_VERSION_DOT=1.70.0 +ARG BOOST_HASH=430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778 +RUN set -x \ + && curl -L -o boost_${BOOST_VERSION}.tar.bz2 https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION_DOT}/source/boost_${BOOST_VERSION}.tar.bz2 \ + && echo "${BOOST_HASH} boost_${BOOST_VERSION}.tar.bz2" | sha256sum -c \ + && tar -xvf boost_${BOOST_VERSION}.tar.bz2 \ + && rm -f boost_${BOOST_VERSION}.tar.bz2 \ + && cd boost_${BOOST_VERSION} \ + && ./bootstrap.sh --prefix=${PREFIX} + +ENV HOST_PATH $PATH +ENV PATH $TOOLCHAIN_DIR/aarch64-linux-android/bin:$TOOLCHAIN_DIR/bin:$PATH + +ARG NPROC=4 + +# Build iconv for lib boost locale +ENV ICONV_VERSION 1.16 +ENV ICONV_HASH e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04 +RUN set -x \ + && curl -O http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz \ + && echo "${ICONV_HASH} libiconv-${ICONV_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf libiconv-${ICONV_VERSION}.tar.gz \ + && rm -f libiconv-${ICONV_VERSION}.tar.gz \ + && cd libiconv-${ICONV_VERSION} \ + && CC=clang CXX=clang++ ./configure --build=x86_64-linux-gnu --host=aarch64-linux-android --prefix=${PREFIX} --disable-rpath \ + && make -j${NPROC} && make install + +## Build BOOST +RUN set -x \ + && cd boost_${BOOST_VERSION} \ + && ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --with-locale --build-dir=android --stagedir=android toolset=clang threading=multi threadapi=pthread target-os=android -sICONV_PATH=${PREFIX} install -j${NPROC} + +# download, configure and make Zlib +ENV ZLIB_VERSION 1.3 +ENV ZLIB_HASH ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e +RUN set -x \ + && curl -O https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \ + && echo "${ZLIB_HASH} zlib-${ZLIB_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf zlib-${ZLIB_VERSION}.tar.gz \ + && rm zlib-${ZLIB_VERSION}.tar.gz \ + && mv zlib-${ZLIB_VERSION} zlib \ + && cd zlib && CC=clang CXX=clang++ ./configure --static \ + && make -j${NPROC} + +# open ssl +ARG OPENSSL_VERSION=3.0.5 +ARG OPENSSL_HASH=aa7d8d9bef71ad6525c55ba11e5f4397889ce49c2c9349dcea6d3e4f0b024a7a +# openssl explicitly demands to be built by a clang that has a "/prebuilt/" somewhere along its path, so use the prebuilt version, but make sure to specify the target android api +RUN set -x \ + && curl -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \ + && echo "${OPENSSL_HASH} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c \ + && tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \ + && rm openssl-${OPENSSL_VERSION}.tar.gz \ + && cd openssl-${OPENSSL_VERSION} \ + && export not=needed PATH=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH \ + && ./Configure android-arm64 \ + -D__ANDROID_API__=21 \ + -static \ + no-shared no-tests \ + --with-zlib-include=${WORKDIR}/zlib/include --with-zlib-lib=${WORKDIR}/zlib/lib \ + --prefix=${PREFIX} --openssldir=${PREFIX} \ + && make -j${NPROC} \ + && make install_sw + +# ZMQ +ARG ZMQ_VERSION=v4.3.2 +ARG ZMQ_HASH=a84ffa12b2eb3569ced199660bac5ad128bff1f0 +RUN set -x \ + && git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} \ + && cd libzmq \ + && test `git rev-parse HEAD` = ${ZMQ_HASH} || exit 1 \ + && ./autogen.sh \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install + +# Sodium +ARG SODIUM_VERSION=1.0.18 +ARG SODIUM_HASH=4f5e89fa84ce1d178a6765b8b46f2b6f91216677 +RUN set -x \ + && git clone https://github.com/jedisct1/libsodium.git -b ${SODIUM_VERSION} \ + && cd libsodium \ + && test `git rev-parse HEAD` = ${SODIUM_HASH} || exit 1 \ + && ./autogen.sh \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install + +# libexpat (required by libunbound) +ARG LIBEXPAT_VERSION=R_2_4_8 +ARG LIBEXPAT_HASH=3bab6c09bbe8bf42d84b81563ddbcf4cca4be838 +RUN set -x \ + && git clone https://github.com/libexpat/libexpat.git -b ${LIBEXPAT_VERSION} \ + && cd libexpat/expat \ + && test `git rev-parse HEAD` = ${LIBEXPAT_HASH} || exit 1 \ + && ./buildconf.sh \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \ + && make -j${NPROC} \ + && make install + +# libunbound +ARG LIBUNBOUND_VERSION=branch-1.16.1 +ARG LIBUNBOUND_HASH=903538c76e1d8eb30d0814bb55c3ef1ea28164e8 +RUN git clone https://github.com/NLnetLabs/unbound.git -b ${LIBUNBOUND_VERSION} +RUN set -x \ + && cd unbound \ + && test `git rev-parse HEAD` = ${LIBUNBOUND_HASH} || exit 1 \ + && CC=clang CXX=clang++ ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared --disable-flto --with-ssl=${PREFIX} --with-libexpat=${PREFIX} \ + && make -j${NPROC} \ + && make install + +# polyseed +RUN git clone https://github.com/tevador/polyseed.git +RUN set -x \ + && cd polyseed \ + && git reset --hard b7c35bb3c6b91e481ecb04fc235eaff69c507fa1 \ + && CC=clang CXX=clang++ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} . \ + && make \ + && make install + +# utf8proc +RUN git clone https://github.com/JuliaStrings/utf8proc -b v2.8.0 +RUN set -x \ + && cd utf8proc \ + && git reset --hard 1cb28a66ca79a0845e99433fd1056257456cef8b \ + && mkdir build \ + && cd build \ + && rm -rf ../CMakeCache.txt ../CMakeFiles/ \ + && CC=clang CXX=clang++ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} .. \ + && make \ + && make install + +COPY monero/ /src + +RUN cd /src \ + && export CMAKE_INCLUDE_PATH="${PREFIX}/include" \ + CMAKE_LIBRARY_PATH="${PREFIX}/lib" \ + ANDROID_STANDALONE_TOOLCHAIN_PATH=${TOOLCHAIN_DIR} \ + USE_SINGLE_BUILDDIR=1 \ + PATH=${HOST_PATH} \ + && mkdir -p build/release \ + && cd build/release \ + && CC=clang CXX=clang++ cmake -D USE_DEVICE_TREZOR=OFF -D BUILD_GUI_DEPS=1 -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android-armv8" -D CMAKE_SYSTEM_NAME="Android" -D CMAKE_ANDROID_STANDALONE_TOOLCHAIN="${ANDROID_STANDALONE_TOOLCHAIN_PATH}" -D CMAKE_ANDROID_ARCH_ABI="arm64-v8a" ../.. \ + && make wallet_api -j ${NPROC} + +RUN set -x \ + && cd /src/build/release \ + && find . -path ./lib -prune -o -name '*.a' -exec cp '{}' lib \; + +COPY ./libbridge /opt/android/libbridge +RUN cd libbridge/ \ + && ln -s /src/build/release/lib/ /opt/android/monero \ + && mkdir build \ + && cd build \ + && env CC=clang CXX=clang++ cmake -DANDROID_ABI=arm64-v8a .. \ + && make -j${NPROC} \ No newline at end of file diff --git a/libbridge/.dockerignore b/libbridge/.dockerignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/libbridge/.dockerignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/libbridge/.gitignore b/libbridge/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/libbridge/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/libbridge/CMakeLists.txt b/libbridge/CMakeLists.txt new file mode 100644 index 0000000..8c5160f --- /dev/null +++ b/libbridge/CMakeLists.txt @@ -0,0 +1,245 @@ +cmake_minimum_required(VERSION 3.4.1) +project(wallet2_api_c) +message(STATUS ABI_INFO = ${ANDROID_ABI}) + +set (CMAKE_CXX_STANDARD 11) + +add_library( wallet2_api_c + SHARED + src/main/cpp/wallet2_api_c.cpp ) + +set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/..) + +############ +# libsodium +############ + +add_library(sodium STATIC IMPORTED) +set_target_properties(sodium PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libsodium.a) + +############ +# OpenSSL +############ + +add_library(crypto STATIC IMPORTED) +set_target_properties(crypto PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libcrypto.a) + +add_library(ssl STATIC IMPORTED) +set_target_properties(ssl PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libssl.a) + +############ +# Boost +############ + +add_library(boost_chrono STATIC IMPORTED) +set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libboost_chrono.a) + +add_library(boost_date_time STATIC IMPORTED) +set_target_properties(boost_date_time PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libboost_date_time.a) + +add_library(boost_filesystem STATIC IMPORTED) +set_target_properties(boost_filesystem PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libboost_filesystem.a) + +add_library(boost_program_options STATIC IMPORTED) +set_target_properties(boost_program_options PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libboost_program_options.a) + +add_library(boost_regex STATIC IMPORTED) +set_target_properties(boost_regex PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libboost_regex.a) + +add_library(boost_serialization STATIC IMPORTED) +set_target_properties(boost_serialization PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libboost_serialization.a) + +add_library(boost_system STATIC IMPORTED) +set_target_properties(boost_system PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libboost_system.a) + +add_library(boost_thread STATIC IMPORTED) +set_target_properties(boost_thread PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libboost_thread.a) + +add_library(boost_wserialization STATIC IMPORTED) +set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libboost_wserialization.a) + +############# +# Monero +############# + +add_library(wallet_api STATIC IMPORTED) +set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/lib/libwallet_api.a) + +add_library(wallet STATIC IMPORTED) +set_target_properties(wallet PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/lib/libwallet.a) + +add_library(cryptonote_core STATIC IMPORTED) +set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/cryptonote_core/libcryptonote_core.a) + +add_library(cryptonote_basic STATIC IMPORTED) +set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/cryptonote_basic/libcryptonote_basic.a) + +add_library(mnemonics STATIC IMPORTED) +set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/mnemonics/libmnemonics.a) + +add_library(common STATIC IMPORTED) +set_target_properties(common PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/common/libcommon.a) + +add_library(cncrypto STATIC IMPORTED) +set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/crypto/libcncrypto.a) + +add_library(ringct STATIC IMPORTED) +set_target_properties(ringct PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/ringct/libringct.a) + +add_library(ringct_basic STATIC IMPORTED) +set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/ringct/libringct_basic.a) + +add_library(blockchain_db STATIC IMPORTED) +set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/blockchain_db/libblockchain_db.a) + +add_library(lmdb STATIC IMPORTED) +set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/external/db_drivers/liblmdb/liblmdb.a) + +add_library(easylogging STATIC IMPORTED) +set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/external/easylogging++/libeasylogging.a) + +add_library(unbound STATIC IMPORTED) +set_target_properties(unbound PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/prefix/lib/libunbound.a) + +add_library(epee STATIC IMPORTED) +set_target_properties(epee PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/contrib/epee/src/libepee.a) + +add_library(blocks STATIC IMPORTED) +set_target_properties(blocks PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/blocks/libblocks.a) + +add_library(checkpoints STATIC IMPORTED) +set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/checkpoints/libcheckpoints.a) + +add_library(device STATIC IMPORTED) +set_target_properties(device PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/device/libdevice.a) + +add_library(device_trezor STATIC IMPORTED) +set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/device_trezor/libdevice_trezor.a) + +add_library(multisig STATIC IMPORTED) +set_target_properties(multisig PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/multisig/libmultisig.a) + +add_library(version STATIC IMPORTED) +set_target_properties(version PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/libversion.a) + +add_library(net STATIC IMPORTED) +set_target_properties(net PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/net/libnet.a) + +add_library(hardforks STATIC IMPORTED) +set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/hardforks/libhardforks.a) + +add_library(randomx STATIC IMPORTED) +set_target_properties(randomx PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/external/randomx/librandomx.a) + +add_library(rpc_base STATIC IMPORTED) +set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/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 + ${EXTERNAL_LIBS_DIR}/monero/libwallet-crypto.a) + +add_library(cryptonote_format_utils_basic STATIC IMPORTED) +set_target_properties(cryptonote_format_utils_basic PROPERTIES IMPORTED_LOCATION + ${EXTERNAL_LIBS_DIR}/monero/build/release/src/cryptonote_basic/libcryptonote_format_utils_basic.a) + +############# +# System +############# + +find_library( log-lib log ) + +include_directories( ${EXTERNAL_LIBS_DIR}/include ) + +message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR}) + +if(${ANDROID_ABI} STREQUAL "x86_64") + set(EXTRA_LIBS "wallet-crypto") +else() + set(EXTRA_LIBS "") +endif() + +# target_compile_options(wallet2_api_c PRIVATE -static-libstdc++) + +target_link_libraries( wallet2_api_c + + 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} + + boost_chrono + boost_date_time + boost_filesystem + boost_program_options + boost_regex + boost_serialization + boost_system + boost_thread + boost_wserialization + + ssl + crypto + + sodium + + log + ) diff --git a/libbridge/src/main/cpp/wallet2_api.h b/libbridge/src/main/cpp/wallet2_api.h new file mode 100644 index 0000000..646e585 --- /dev/null +++ b/libbridge/src/main/cpp/wallet2_api.h @@ -0,0 +1,1419 @@ +// Copyright (c) 2014-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. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#pragma once + + +#include +#include +#include +#include +#include +#include +#include + +// Public interface for libwallet library +namespace Monero { + +enum NetworkType : uint8_t { + MAINNET = 0, + TESTNET, + STAGENET +}; + + namespace Utils { + bool isAddressLocal(const std::string &hostaddr); + void onStartup(); + } + + template + class optional { + public: + optional(): set(false) {} + optional(const T &t): t(t), set(true) {} + const T &operator*() const { return t; } + T &operator*() { return t; } + operator bool() const { return set; } + private: + T t; + bool set; + }; + +/** + * @brief Transaction-like interface for sending money + */ +struct PendingTransaction +{ + enum Status { + Status_Ok, + Status_Error, + Status_Critical + }; + + enum Priority { + Priority_Default = 0, + Priority_Low = 1, + Priority_Medium = 2, + Priority_High = 3, + Priority_Last + }; + + virtual ~PendingTransaction() = 0; + virtual int status() const = 0; + 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 uint64_t amount() const = 0; + virtual uint64_t dust() const = 0; + virtual uint64_t fee() const = 0; + virtual std::vector txid() const = 0; + /*! + * \brief txCount - number of transactions current transaction will be splitted to + * \return + */ + virtual uint64_t txCount() const = 0; + virtual std::vector subaddrAccount() const = 0; + virtual std::vector> subaddrIndices() const = 0; + + /** + * @brief multisigSignData + * @return encoded multisig transaction with signers' keys. + * Transfer this data to another wallet participant to sign it. + * Assumed use case is: + * 1. Initiator: + * auto data = pendingTransaction->multisigSignData(); + * 2. Signer1: + * pendingTransaction = wallet->restoreMultisigTransaction(data); + * pendingTransaction->signMultisigTx(); + * auto signed = pendingTransaction->multisigSignData(); + * 3. Signer2: + * pendingTransaction = wallet->restoreMultisigTransaction(signed); + * pendingTransaction->signMultisigTx(); + * pendingTransaction->commit(); + */ + virtual std::string multisigSignData() = 0; + virtual void signMultisigTx() = 0; + /** + * @brief signersKeys + * @return vector of base58-encoded signers' public keys + */ + virtual std::vector signersKeys() const = 0; +}; + +/** + * @brief Transaction-like interface for sending money + */ +struct UnsignedTransaction +{ + enum Status { + Status_Ok, + Status_Error, + Status_Critical + }; + + virtual ~UnsignedTransaction() = 0; + virtual int status() const = 0; + virtual std::string errorString() const = 0; + virtual std::vector amount() const = 0; + virtual std::vector fee() const = 0; + virtual std::vector mixin() const = 0; + // returns a string with information about all transactions. + virtual std::string confirmationMessage() const = 0; + virtual std::vector paymentId() const = 0; + virtual std::vector recipientAddress() const = 0; + virtual uint64_t minMixinCount() const = 0; + /*! + * \brief txCount - number of transactions current transaction will be splitted to + * \return + */ + virtual uint64_t txCount() const = 0; + /*! + * @brief sign - Sign txs and saves to file + * @param signedFileName + * return - true on success + */ + virtual bool sign(const std::string &signedFileName) = 0; +}; + +/** + * @brief The TransactionInfo - interface for displaying transaction information + */ +struct TransactionInfo +{ + enum Direction { + Direction_In, + Direction_Out + }; + + struct Transfer { + Transfer(uint64_t _amount, const std::string &address); + const uint64_t amount; + const std::string address; + }; + + virtual ~TransactionInfo() = 0; + virtual int direction() const = 0; + virtual bool isPending() const = 0; + virtual bool isFailed() const = 0; + virtual bool isCoinbase() const = 0; + virtual uint64_t amount() const = 0; + virtual uint64_t fee() const = 0; + virtual uint64_t blockHeight() const = 0; + virtual std::string description() const = 0; + virtual std::set subaddrIndex() const = 0; + virtual uint32_t subaddrAccount() const = 0; + virtual std::string label() const = 0; + virtual uint64_t confirmations() const = 0; + virtual uint64_t unlockTime() const = 0; + //! transaction_id + virtual std::string hash() const = 0; + virtual std::time_t timestamp() const = 0; + virtual std::string paymentId() const = 0; + //! only applicable for output transactions + virtual const std::vector & transfers() const = 0; +}; +/** + * @brief The TransactionHistory - interface for displaying transaction history + */ +struct TransactionHistory +{ + virtual ~TransactionHistory() = 0; + virtual int count() const = 0; + virtual TransactionInfo * transaction(int index) const = 0; + virtual TransactionInfo * transaction(const std::string &id) const = 0; + virtual std::vector getAll() const = 0; + virtual void refresh() = 0; + virtual void setTxNote(const std::string &txid, const std::string ¬e) = 0; +}; + +/** + * @brief AddressBookRow - provides functions to manage address book + */ +struct AddressBookRow { +public: + AddressBookRow(std::size_t _rowId, const std::string &_address, const std::string &_paymentId, const std::string &_description): + m_rowId(_rowId), + m_address(_address), + m_paymentId(_paymentId), + m_description(_description) {} + +private: + std::size_t m_rowId; + std::string m_address; + std::string m_paymentId; + std::string m_description; +public: + std::string extra; + std::string getAddress() const {return m_address;} + std::string getDescription() const {return m_description;} + std::string getPaymentId() const {return m_paymentId;} + std::size_t getRowId() const {return m_rowId;} +}; + +/** + * @brief The AddressBook - interface for +Book + */ +struct AddressBook +{ + enum ErrorCode { + Status_Ok, + General_Error, + Invalid_Address, + Invalid_Payment_Id + }; + virtual ~AddressBook() = 0; + virtual std::vector getAll() const = 0; + virtual bool addRow(const std::string &dst_addr , const std::string &payment_id, const std::string &description) = 0; + virtual bool deleteRow(std::size_t rowId) = 0; + virtual bool setDescription(std::size_t index, const std::string &description) = 0; + virtual void refresh() = 0; + virtual std::string errorString() const = 0; + virtual int errorCode() const = 0; + 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; +}; + +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(int index) = 0; + virtual void thaw(int index) = 0; + virtual bool isTransferUnlocked(uint64_t unlockTime, uint64_t blockHeight) = 0; +}; + +struct SubaddressRow { +public: + SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label): + m_rowId(_rowId), + m_address(_address), + m_label(_label) {} + +private: + std::size_t m_rowId; + std::string m_address; + std::string m_label; +public: + std::string extra; + std::string getAddress() const {return m_address;} + std::string getLabel() const {return m_label;} + std::size_t getRowId() const {return m_rowId;} +}; + +struct Subaddress +{ + virtual ~Subaddress() = 0; + virtual std::vector getAll() const = 0; + virtual void addRow(uint32_t accountIndex, const std::string &label) = 0; + virtual void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0; + virtual void refresh(uint32_t accountIndex) = 0; +}; + +struct SubaddressAccountRow { +public: + SubaddressAccountRow(std::size_t _rowId, const std::string &_address, const std::string &_label, const std::string &_balance, const std::string &_unlockedBalance): + m_rowId(_rowId), + m_address(_address), + m_label(_label), + m_balance(_balance), + m_unlockedBalance(_unlockedBalance) {} + +private: + std::size_t m_rowId; + std::string m_address; + std::string m_label; + std::string m_balance; + std::string m_unlockedBalance; +public: + std::string extra; + std::string getAddress() const {return m_address;} + std::string getLabel() const {return m_label;} + std::string getBalance() const {return m_balance;} + std::string getUnlockedBalance() const {return m_unlockedBalance;} + std::size_t getRowId() const {return m_rowId;} +}; + +struct SubaddressAccount +{ + virtual ~SubaddressAccount() = 0; + virtual std::vector getAll() const = 0; + virtual void addRow(const std::string &label) = 0; + virtual void setLabel(uint32_t accountIndex, const std::string &label) = 0; + virtual void refresh() = 0; +}; + +struct MultisigState { + MultisigState() : isMultisig(false), isReady(false), threshold(0), total(0) {} + + bool isMultisig; + bool isReady; + uint32_t threshold; + uint32_t total; +}; + + +struct DeviceProgress { + DeviceProgress(): m_progress(0), m_indeterminate(false) {} + DeviceProgress(double progress, bool indeterminate=false): m_progress(progress), m_indeterminate(indeterminate) {} + + virtual double progress() const { return m_progress; } + virtual bool indeterminate() const { return m_indeterminate; } + +protected: + double m_progress; + bool m_indeterminate; +}; + +struct Wallet; +struct WalletListener +{ + virtual ~WalletListener() = 0; + /** + * @brief moneySpent - called when money spent + * @param txId - transaction id + * @param amount - amount + */ + virtual void moneySpent(const std::string &txId, uint64_t amount) = 0; + + /** + * @brief moneyReceived - called when money received + * @param txId - transaction id + * @param amount - amount + */ + virtual void moneyReceived(const std::string &txId, uint64_t amount) = 0; + + /** + * @brief unconfirmedMoneyReceived - called when payment arrived in tx pool + * @param txId - transaction id + * @param amount - amount + */ + virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) = 0; + + /** + * @brief newBlock - called when new block received + * @param height - block height + */ + virtual void newBlock(uint64_t height) = 0; + + /** + * @brief updated - generic callback, called when any event (sent/received/block reveived/etc) happened with the wallet; + */ + virtual void updated() = 0; + + + /** + * @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously + */ + virtual void refreshed() = 0; + + /** + * @brief called by device if the action is required + */ + virtual void onDeviceButtonRequest(uint64_t code) { (void)code; } + + /** + * @brief called by device if the button was pressed + */ + virtual void onDeviceButtonPressed() { } + + /** + * @brief called by device when PIN is needed + */ + virtual optional onDevicePinRequest() { + throw std::runtime_error("Not supported"); + } + + /** + * @brief called by device when passphrase entry is needed + */ + virtual optional onDevicePassphraseRequest(bool & on_device) { + on_device = true; + return optional(); + } + + /** + * @brief Signalizes device operation progress + */ + virtual void onDeviceProgress(const DeviceProgress & event) { (void)event; }; + + /** + * @brief If the listener is created before the wallet this enables to set created wallet object + */ + virtual void onSetWallet(Wallet * wallet) { (void)wallet; }; +}; + + +/** + * @brief Interface for wallet operations. + */ +struct Wallet +{ + enum Device { + Device_Software = 0, + Device_Ledger = 1, + Device_Trezor = 2 + }; + + enum Status { + Status_Ok, + Status_Error, + Status_Critical + }; + + enum ConnectionStatus { + ConnectionStatus_Disconnected, + ConnectionStatus_Connected, + ConnectionStatus_WrongVersion + }; + + virtual ~Wallet() = 0; + virtual std::string seed(const std::string& seed_offset = "") const = 0; + virtual std::string getSeedLanguage() const = 0; + virtual void setSeedLanguage(const std::string &arg) = 0; + //! returns wallet status (Status_Ok | Status_Error) + virtual int status() const = 0; //deprecated: use safe alternative statusWithErrorString + //! in case error status, returns error string + virtual std::string errorString() const = 0; //deprecated: use safe alternative statusWithErrorString + //! returns both error and error string atomically. suggested to use in instead of status() and errorString() + virtual void statusWithErrorString(int& status, std::string& errorString) const = 0; + virtual bool setPassword(const std::string &password) = 0; + virtual const std::string& getPassword() const = 0; + virtual bool setDevicePin(const std::string &pin) { (void)pin; return false; }; + virtual bool setDevicePassphrase(const std::string &passphrase) { (void)passphrase; return false; }; + virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0; + std::string mainAddress() const { return address(0, 0); } + virtual std::string path() const = 0; + virtual NetworkType nettype() const = 0; + bool mainnet() const { return nettype() == MAINNET; } + bool testnet() const { return nettype() == TESTNET; } + bool stagenet() const { return nettype() == STAGENET; } + //! returns current hard fork info + virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; + //! check if hard fork rules should be used + virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; + /*! + * \brief integratedAddress - returns integrated address for current wallet address and given payment_id. + * if passed "payment_id" param is an empty string or not-valid payment id string + * (16 characters hexadecimal string) - random payment_id will be generated + * + * \param payment_id - 16 characters hexadecimal string or empty string if new random payment id needs to be + * generated + * \return - 106 characters string representing integrated address + */ + virtual std::string integratedAddress(const std::string &payment_id) const = 0; + + /*! + * \brief secretViewKey - returns secret view key + * \return - secret view key + */ + virtual std::string secretViewKey() const = 0; + + /*! + * \brief publicViewKey - returns public view key + * \return - public view key + */ + virtual std::string publicViewKey() const = 0; + + /*! + * \brief secretSpendKey - returns secret spend key + * \return - secret spend key + */ + virtual std::string secretSpendKey() const = 0; + + /*! + * \brief publicSpendKey - returns public spend key + * \return - public spend key + */ + virtual std::string publicSpendKey() const = 0; + + /*! + * \brief publicMultisigSignerKey - returns public signer key + * \return - public multisignature signer key or empty string if wallet is not multisig + */ + virtual std::string publicMultisigSignerKey() const = 0; + + /*! + * \brief stop - interrupts wallet refresh() loop once (doesn't stop background refresh thread) + */ + virtual void stop() = 0; + + /*! + * \brief store - stores wallet to file. + * \param path - main filename to store wallet to. additionally stores address file and keys file. + * to store to the same file - just pass empty string; + * \return + */ + virtual bool store(const std::string &path) = 0; + /*! + * \brief filename - returns wallet filename + * \return + */ + virtual std::string filename() const = 0; + /*! + * \brief keysFilename - returns keys filename. usually this formed as "wallet_filename".keys + * \return + */ + virtual std::string keysFilename() const = 0; + /*! + * \brief init - initializes wallet with daemon connection params. + * if daemon_address is local address, "trusted daemon" will be set to true forcibly + * startRefresh() should be called when wallet is initialized. + * + * \param daemon_address - daemon address in "hostname:port" format + * \param upper_transaction_size_limit + * \param daemon_username + * \param daemon_password + * \param lightWallet - start wallet in light mode, connect to a openmonero compatible server. + * \param proxy_address - set proxy address, empty string to disable + * \return - true on success + */ + 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; + + /*! + * \brief createWatchOnly - Creates a watch only wallet + * \param path - where to store the wallet + * \param password + * \param language + * \return - true if created successfully + */ + virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0; + + /*! + * \brief setRefreshFromBlockHeight - start refresh from block height on recover + * + * \param refresh_from_block_height - blockchain start height + */ + virtual void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) = 0; + + /*! + * \brief getRestoreHeight - get wallet creation height + * + */ + virtual uint64_t getRefreshFromBlockHeight() const = 0; + + /*! + * \brief setRecoveringFromSeed - set state recover form seed + * + * \param recoveringFromSeed - true/false + */ + virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0; + + /*! + * \brief setRecoveringFromDevice - set state to recovering from device + * + * \param recoveringFromDevice - true/false + */ + virtual void setRecoveringFromDevice(bool recoveringFromDevice) = 0; + + /*! + * \brief setSubaddressLookahead - set size of subaddress lookahead + * + * \param major - size fot the major index + * \param minor - size fot the minor index + */ + virtual void setSubaddressLookahead(uint32_t major, uint32_t minor) = 0; + + /** + * @brief connectToDaemon - connects to the daemon. TODO: check if it can be removed + * @return + */ + virtual bool connectToDaemon() = 0; + + /** + * @brief connected - checks if the wallet connected to the daemon + * @return - true if connected + */ + virtual ConnectionStatus connected() const = 0; + virtual void setTrustedDaemon(bool arg) = 0; + virtual bool trustedDaemon() const = 0; + virtual bool setProxy(const std::string &address) = 0; + virtual uint64_t balance(uint32_t accountIndex = 0) const = 0; + uint64_t balanceAll() const { + uint64_t result = 0; + for (uint32_t i = 0; i < numSubaddressAccounts(); ++i) + result += balance(i); + return result; + } + virtual uint64_t unlockedBalance(uint32_t accountIndex = 0) const = 0; + uint64_t unlockedBalanceAll() const { + uint64_t result = 0; + for (uint32_t i = 0; i < numSubaddressAccounts(); ++i) + result += unlockedBalance(i); + return result; + } + + /** + * @brief watchOnly - checks if wallet is watch only + * @return - true if watch only + */ + virtual bool watchOnly() const = 0; + + /** + * @brief isDeterministic - checks if wallet keys are deterministic + * @return - true if deterministic + */ + virtual bool isDeterministic() const = 0; + + /** + * @brief blockChainHeight - returns current blockchain height + * @return + */ + virtual uint64_t blockChainHeight() const = 0; + + /** + * @brief approximateBlockChainHeight - returns approximate blockchain height calculated from date/time + * @return + */ + virtual uint64_t approximateBlockChainHeight() const = 0; + + /** + * @brief estimateBlockChainHeight - returns estimate blockchain height. More accurate than approximateBlockChainHeight, + * uses daemon height and falls back to calculation from date/time + * @return + **/ + virtual uint64_t estimateBlockChainHeight() const = 0; + /** + * @brief daemonBlockChainHeight - returns daemon blockchain height + * @return 0 - in case error communicating with the daemon. + * status() will return Status_Error and errorString() will return verbose error description + */ + virtual uint64_t daemonBlockChainHeight() const = 0; + + /** + * @brief daemonBlockChainTargetHeight - returns daemon blockchain target height + * @return 0 - in case error communicating with the daemon. + * status() will return Status_Error and errorString() will return verbose error description + */ + virtual uint64_t daemonBlockChainTargetHeight() const = 0; + + /** + * @brief synchronized - checks if wallet was ever synchronized + * @return + */ + virtual bool synchronized() const = 0; + + static std::string displayAmount(uint64_t amount); + static uint64_t amountFromString(const std::string &amount); + static uint64_t amountFromDouble(double amount); + static std::string genPaymentId(); + static bool paymentIdValid(const std::string &paiment_id); + static bool addressValid(const std::string &str, NetworkType nettype); + static bool addressValid(const std::string &str, bool testnet) // deprecated + { + return addressValid(str, testnet ? TESTNET : MAINNET); + } + static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, NetworkType nettype, std::string &error); + static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error) // deprecated + { + return keyValid(secret_key_string, address_string, isViewKey, testnet ? TESTNET : MAINNET, error); + } + static std::string paymentIdFromAddress(const std::string &str, NetworkType nettype); + static std::string paymentIdFromAddress(const std::string &str, bool testnet) // deprecated + { + return paymentIdFromAddress(str, testnet ? TESTNET : MAINNET); + } + static uint64_t maximumAllowedAmount(); + // Easylogger wrapper + static void init(const char *argv0, const char *default_log_base_name) { init(argv0, default_log_base_name, "", true); } + static void init(const char *argv0, const char *default_log_base_name, const std::string &log_path, bool console); + static void debug(const std::string &category, const std::string &str); + static void info(const std::string &category, const std::string &str); + static void warning(const std::string &category, const std::string &str); + static void error(const std::string &category, const std::string &str); + + /** + * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) + */ + virtual void startRefresh() = 0; + /** + * @brief pauseRefresh - pause refresh thread + */ + virtual void pauseRefresh() = 0; + + /** + * @brief refresh - refreshes the wallet, updating transactions from daemon + * @return - true if refreshed successfully; + */ + virtual bool refresh() = 0; + + /** + * @brief refreshAsync - refreshes wallet asynchronously. + */ + virtual void refreshAsync() = 0; + + /** + * @brief rescanBlockchain - rescans the wallet, updating transactions from daemon + * @return - true if refreshed successfully; + */ + virtual bool rescanBlockchain() = 0; + + /** + * @brief rescanBlockchainAsync - rescans wallet asynchronously, starting from genesys + */ + virtual void rescanBlockchainAsync() = 0; + + /** + * @brief setAutoRefreshInterval - setup interval for automatic refresh. + * @param seconds - interval in millis. if zero or less than zero - automatic refresh disabled; + */ + virtual void setAutoRefreshInterval(int millis) = 0; + + /** + * @brief autoRefreshInterval - returns automatic refresh interval in millis + * @return + */ + virtual int autoRefreshInterval() const = 0; + + /** + * @brief addSubaddressAccount - appends a new subaddress account at the end of the last major index of existing subaddress accounts + * @param label - the label for the new account (which is the as the label of the primary address (accountIndex,0)) + */ + virtual void addSubaddressAccount(const std::string& label) = 0; + /** + * @brief numSubaddressAccounts - returns the number of existing subaddress accounts + */ + virtual size_t numSubaddressAccounts() const = 0; + /** + * @brief numSubaddresses - returns the number of existing subaddresses associated with the specified subaddress account + * @param accountIndex - the major index specifying the subaddress account + */ + virtual size_t numSubaddresses(uint32_t accountIndex) const = 0; + /** + * @brief addSubaddress - appends a new subaddress at the end of the last minor index of the specified subaddress account + * @param accountIndex - the major index specifying the subaddress account + * @param label - the label for the new subaddress + */ + virtual void addSubaddress(uint32_t accountIndex, const std::string& label) = 0; + /** + * @brief getSubaddressLabel - gets the label of the specified subaddress + * @param accountIndex - the major index specifying the subaddress account + * @param addressIndex - the minor index specifying the subaddress + */ + virtual std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const = 0; + /** + * @brief setSubaddressLabel - sets the label of the specified subaddress + * @param accountIndex - the major index specifying the subaddress account + * @param addressIndex - the minor index specifying the subaddress + * @param label - the new label for the specified subaddress + */ + virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0; + + /** + * @brief multisig - returns current state of multisig wallet creation process + * @return MultisigState struct + */ + virtual MultisigState multisig() const = 0; + /** + * @brief getMultisigInfo + * @return serialized and signed multisig info string + */ + virtual std::string getMultisigInfo() const = 0; + /** + * @brief makeMultisig - switches wallet in multisig state. The one and only creation phase for N / N wallets + * @param info - vector of multisig infos from other participants obtained with getMulitisInfo call + * @param threshold - number of required signers to make valid transaction. Must be <= number of participants + * @return in case of N / N wallets returns empty string since no more key exchanges needed. For N - 1 / N wallets returns base58 encoded extra multisig info + */ + virtual std::string makeMultisig(const std::vector& info, uint32_t threshold) = 0; + /** + * @brief exchange_multisig_keys - provides additional key exchange round for arbitrary multisig schemes (like N-1/N, M/N) + * @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call + * @param force_update_use_with_caution - force multisig account to update even if not all signers contribute round messages + * @return new info string if more rounds required or an empty string if wallet creation is done + */ + virtual std::string exchangeMultisigKeys(const std::vector &info, const bool force_update_use_with_caution) = 0; + /** + * @brief exportMultisigImages - exports transfers' key images + * @param images - output paramter for hex encoded array of images + * @return true if success + */ + virtual bool exportMultisigImages(std::string& images) = 0; + /** + * @brief importMultisigImages - imports other participants' multisig images + * @param images - array of hex encoded arrays of images obtained with exportMultisigImages + * @return number of imported images + */ + virtual size_t importMultisigImages(const std::vector& images) = 0; + /** + * @brief hasMultisigPartialKeyImages - checks if wallet needs to import multisig key images from other participants + * @return true if there are partial key images + */ + virtual bool hasMultisigPartialKeyImages() const = 0; + + /** + * @brief restoreMultisigTransaction creates PendingTransaction from signData + * @param signData encrypted unsigned transaction. Obtained with PendingTransaction::multisigSignData + * @return PendingTransaction + */ + virtual PendingTransaction* restoreMultisigTransaction(const std::string& signData) = 0; + + /*! + * \brief createTransactionMultDest creates transaction with multiple destinations. if dst_addr is an integrated address, payment_id is ignored + * \param dst_addr vector of destination address as string + * \param payment_id optional payment_id, can be empty string + * \param amount vector of amounts + * \param mixin_count mixin count. if 0 passed, wallet will use default value + * \param subaddr_account subaddress account from which the input funds are taken + * \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices + * \param priority + * \return PendingTransaction object. caller is responsible to check PendingTransaction::status() + * after object returned + */ + + virtual PendingTransaction * createTransactionMultDest(const std::vector &dst_addr, const std::string &payment_id, + optional> amount, uint32_t mixin_count, + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, + std::set subaddr_indices = {}) = 0; + + /*! + * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored + * \param dst_addr destination address as string + * \param payment_id optional payment_id, can be empty string + * \param amount amount + * \param mixin_count mixin count. if 0 passed, wallet will use default value + * \param subaddr_account subaddress account from which the input funds are taken + * \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices + * \param priority + * \return PendingTransaction object. caller is responsible to check PendingTransaction::status() + * after object returned + */ + + virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, + optional amount, uint32_t mixin_count, + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, + std::set subaddr_indices = {}) = 0; + + /*! + * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. + * \return PendingTransaction object. caller is responsible to check PendingTransaction::status() + * after object returned + */ + + virtual PendingTransaction * createSweepUnmixableTransaction() = 0; + + /*! + * \brief loadUnsignedTx - creates transaction from unsigned tx file + * \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status() + * after object returned + */ + virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; + + /*! + * \brief submitTransaction - submits transaction in signed tx file + * \return - true on success + */ + virtual bool submitTransaction(const std::string &fileName) = 0; + + + /*! + * \brief disposeTransaction - destroys transaction object + * \param t - pointer to the "PendingTransaction" object. Pointer is not valid after function returned; + */ + virtual void disposeTransaction(PendingTransaction * t) = 0; + + /*! + * \brief Estimates transaction fee. + * \param destinations Vector consisting of pairs. + * \return Estimated fee. + */ + virtual uint64_t estimateTransactionFee(const std::vector> &destinations, + PendingTransaction::Priority priority) const = 0; + + /*! + * \brief exportKeyImages - exports key images to file + * \param filename + * \param all - export all key images or only those that have not yet been exported + * \return - true on success + */ + virtual bool exportKeyImages(const std::string &filename, 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; + + /*! + * \brief importOutputs - exports outputs to file + * \param filename + * \return - true on success + */ + virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; + + /*! + * \brief importOutputs - imports outputs from file + * \param filename + * \return - true on success + */ + virtual bool importOutputs(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 + * \param txids - list of transaction ids + * \return - true on success + */ + virtual bool scanTransactions(const std::vector &txids) = 0; + + 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; + /*! + * \brief defaultMixin - returns number of mixins used in transactions + * \return + */ + virtual uint32_t defaultMixin() const = 0; + /*! + * \brief setDefaultMixin - setum number of mixins to be used for new transactions + * \param arg + */ + virtual void setDefaultMixin(uint32_t arg) = 0; + + /*! + * \brief setCacheAttribute - attach an arbitrary string to a wallet cache attribute + * \param key - the key + * \param val - the value + * \return true if successful, false otherwise + */ + virtual bool setCacheAttribute(const std::string &key, const std::string &val) = 0; + /*! + * \brief getCacheAttribute - return an arbitrary string attached to a wallet cache attribute + * \param key - the key + * \return the attached string, or empty string if there is none + */ + virtual std::string getCacheAttribute(const std::string &key) const = 0; + /*! + * \brief setUserNote - attach an arbitrary string note to a txid + * \param txid - the transaction id to attach the note to + * \param note - the note + * \return true if successful, false otherwise + */ + virtual bool setUserNote(const std::string &txid, const std::string ¬e) = 0; + /*! + * \brief getUserNote - return an arbitrary string note attached to a txid + * \param txid - the transaction id to attach the note to + * \return the attached note, or empty string if there is none + */ + virtual std::string getUserNote(const std::string &txid) const = 0; + virtual std::string getTxKey(const std::string &txid) const = 0; + virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0; + virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const = 0; + virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0; + virtual std::string getSpendProof(const std::string &txid, const std::string &message) const = 0; + virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const = 0; + /*! + * \brief getReserveProof - Generates a proof that proves the reserve of unspent funds + * Parameters `account_index` and `amount` are ignored when `all` is true + */ + virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const = 0; + virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const = 0; + + /* + * \brief signMessage - sign a message with the spend private key + * \param message - the message to sign (arbitrary byte data) + * \return the signature + */ + virtual std::string signMessage(const std::string &message, const std::string &address = "") = 0; + /*! + * \brief verifySignedMessage - verify a signature matches a given message + * \param message - the message (arbitrary byte data) + * \param address - the address the signature claims to be made with + * \param signature - the signature + * \return true if the signature verified, false otherwise + */ + virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0; + + /*! + * \brief signMultisigParticipant signs given message with the multisig public signer key + * \param message message to sign + * \return signature in case of success. Sets status to Error and return empty string in case of error + */ + virtual std::string signMultisigParticipant(const std::string &message) const = 0; + /*! + * \brief verifyMessageWithPublicKey verifies that message was signed with the given public key + * \param message message + * \param publicKey hex encoded public key + * \param signature signature of the message + * \return true if the signature is correct. false and sets error state in case of error + */ + virtual bool verifyMessageWithPublicKey(const std::string &message, const std::string &publicKey, const std::string &signature) const = 0; + + virtual 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) = 0; + virtual std::string make_uri(const std::string &address, const std::string &payment_id, uint64_t amount, const std::string &tx_description, const std::string &recipient_name, std::string &error) const = 0; + + virtual std::string getDefaultDataDir() const = 0; + + /* + * \brief rescanSpent - Rescan spent outputs - Can only be used with trusted daemon + * \return true on success + */ + virtual bool rescanSpent() = 0; + + /* + * \brief setOffline - toggle set offline on/off + * \param offline - true/false + */ + virtual void setOffline(bool offline) = 0; + virtual bool isOffline() const = 0; + + //! blackballs a set of outputs + virtual bool blackballOutputs(const std::vector &outputs, bool add) = 0; + + //! blackballs an output + virtual bool blackballOutput(const std::string &amount, const std::string &offset) = 0; + + //! unblackballs an output + virtual bool unblackballOutput(const std::string &amount, const std::string &offset) = 0; + + //! gets the ring used for a key image, if any + virtual bool getRing(const std::string &key_image, std::vector &ring) const = 0; + + //! gets the rings used for a txid, if any + virtual bool getRings(const std::string &txid, std::vector>> &rings) const = 0; + + //! sets the ring used for a key image + virtual bool setRing(const std::string &key_image, const std::vector &ring, bool relative) = 0; + + //! sets whether pre-fork outs are to be segregated + virtual void segregatePreForkOutputs(bool segregate) = 0; + + //! sets the height where segregation should occur + virtual void segregationHeight(uint64_t height) = 0; + + //! secondary key reuse mitigation + virtual void keyReuseMitigation2(bool mitigation) = 0; + + //! Light wallet authenticate and login + virtual bool lightWalletLogin(bool &isNewWallet) const = 0; + + //! Initiates a light wallet import wallet request + virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0; + + //! locks/unlocks the keys file; returns true on success + virtual bool lockKeysFile() = 0; + virtual bool unlockKeysFile() = 0; + //! returns true if the keys file is locked + virtual bool isKeysFileLocked() = 0; + + /*! + * \brief Queries backing device for wallet keys + * \return Device they are on + */ + virtual Device getDeviceType() const = 0; + + //! cold-device protocol key image sync + virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) = 0; + + //! shows address on device display + virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) = 0; + + //! attempt to reconnect to hardware device + virtual bool reconnectDevice() = 0; + + //! get bytes received + virtual uint64_t getBytesReceived() = 0; + + //! get bytes sent + virtual uint64_t getBytesSent() = 0; +}; + +/** + * @brief WalletManager - provides functions to manage wallets + */ +struct WalletManager +{ + + /*! + * \brief Creates new wallet + * \param path Name of wallet file + * \param password Password of wallet file + * \param language Language to be used to generate electrum seed mnemonic + * \param nettype Network type + * \param kdf_rounds Number of rounds for key derivation function + * \return Wallet instance (Wallet::status() needs to be called to check if created successfully) + */ + virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, NetworkType nettype, uint64_t kdf_rounds = 1) = 0; + Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, bool testnet = false) // deprecated + { + return createWallet(path, password, language, testnet ? TESTNET : MAINNET); + } + + /*! + * \brief Opens existing wallet + * \param path Name of wallet file + * \param password Password of wallet file + * \param nettype Network type + * \param kdf_rounds Number of rounds for key derivation function + * \param listener Wallet listener to set to the wallet after creation + * \return Wallet instance (Wallet::status() needs to be called to check if opened successfully) + */ + virtual Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype, uint64_t kdf_rounds = 1, WalletListener * listener = nullptr) = 0; + Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) // deprecated + { + return openWallet(path, password, testnet ? TESTNET : MAINNET); + } + + /*! + * \brief recovers existing wallet using mnemonic (electrum seed) + * \param path Name of wallet file to be created + * \param password Password of wallet file + * \param mnemonic mnemonic (25 words electrum seed) + * \param nettype Network type + * \param restoreHeight restore from start height + * \param kdf_rounds Number of rounds for key derivation function + * \param seed_offset Seed offset passphrase (optional) + * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) + */ + virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, + NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1, + const std::string &seed_offset = {}) = 0; + Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic, + bool testnet = false, uint64_t restoreHeight = 0) // deprecated + { + return recoveryWallet(path, password, mnemonic, testnet ? TESTNET : MAINNET, restoreHeight); + } + + /*! + * \deprecated this method creates a wallet WITHOUT a passphrase, use the alternate recoverWallet() method + * \brief recovers existing wallet using mnemonic (electrum seed) + * \param path Name of wallet file to be created + * \param mnemonic mnemonic (25 words electrum seed) + * \param nettype Network type + * \param restoreHeight restore from start height + * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) + */ + virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight = 0) = 0; + Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, bool testnet = false, uint64_t restoreHeight = 0) // deprecated + { + return recoveryWallet(path, mnemonic, testnet ? TESTNET : MAINNET, restoreHeight); + } + + /*! + * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted + * \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 addressString public address + * \param viewKeyString view key + * \param spendKeyString spend key (optional) + * \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 * createWalletFromKeys(const std::string &path, + const std::string &password, + const std::string &language, + NetworkType nettype, + uint64_t restoreHeight, + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = "", + uint64_t kdf_rounds = 1) = 0; + Wallet * createWalletFromKeys(const std::string &path, + const std::string &password, + const std::string &language, + bool testnet, + uint64_t restoreHeight, + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = "") // deprecated + { + return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); + } + + /*! + * \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 + * \param path Name of wallet file to be created + * \param language language + * \param nettype Network type + * \param restoreHeight restore from start height + * \param addressString public address + * \param viewKeyString view key + * \param spendKeyString spend key (optional) + * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) + */ + virtual Wallet * createWalletFromKeys(const std::string &path, + const std::string &language, + NetworkType nettype, + uint64_t restoreHeight, + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = "") = 0; + Wallet * createWalletFromKeys(const std::string &path, + const std::string &language, + bool testnet, + uint64_t restoreHeight, + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = "") // deprecated + { + return createWalletFromKeys(path, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString); + } + + /*! + * \brief creates wallet using hardware device. + * \param path Name of wallet file to be created + * \param password Password of wallet file + * \param nettype Network type + * \param deviceName Device name + * \param restoreHeight restore from start height (0 sets to current height) + * \param subaddressLookahead Size of subaddress lookahead (empty sets to some default low value) + * \param kdf_rounds Number of rounds for key derivation function + * \param listener Wallet listener to set to the wallet after creation + * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) + */ + virtual Wallet * createWalletFromDevice(const std::string &path, + const std::string &password, + NetworkType nettype, + const std::string &deviceName, + uint64_t restoreHeight = 0, + const std::string &subaddressLookahead = "", + uint64_t kdf_rounds = 1, + WalletListener * listener = nullptr) = 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 + * \return None + */ + virtual bool closeWallet(Wallet *wallet, bool store = true) = 0; + + /* + * ! checks if wallet with the given name already exists + */ + + /*! + * @brief TODO: delme walletExists - check if the given filename is the wallet + * @param path - filename + * @return - true if wallet exists + */ + virtual bool walletExists(const std::string &path) = 0; + + /*! + * @brief verifyWalletPassword - check if the given filename is the wallet + * @param keys_file_name - location of keys file + * @param password - password to verify + * @param no_spend_key - verify only view keys? + * @param kdf_rounds - number of rounds for key derivation function + * @return - true if password is correct + * + * @note + * This function will fail when the wallet keys file is opened because the wallet program locks the keys file. + * In this case, Wallet::unlockKeysFile() and Wallet::lockKeysFile() need to be called before and after the call to this function, respectively. + */ + virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key, uint64_t kdf_rounds = 1) const = 0; + + /*! + * \brief determine the key storage for the specified wallet file + * \param device_type (OUT) wallet backend as enumerated in Wallet::Device + * \param keys_file_name Keys file to verify password for + * \param password Password to verify + * \return true if password correct, else false + * + * for verification only - determines key storage hardware + * + */ + virtual bool queryWalletDevice(Wallet::Device& device_type, const std::string &keys_file_name, const std::string &password, uint64_t kdf_rounds = 1) const = 0; + + /*! + * \brief findWallets - searches for the wallet files by given path name recursively + * \param path - starting point to search + * \return - list of strings with found wallets (absolute paths); + */ + virtual std::vector findWallets(const std::string &path) = 0; + + //! returns verbose error string regarding last error; + virtual std::string errorString() const = 0; + + //! set the daemon address (hostname and port) + virtual void setDaemonAddress(const std::string &address) = 0; + + //! returns whether the daemon can be reached, and its version number + virtual bool connected(uint32_t *version = NULL) = 0; + + //! returns current blockchain height + virtual uint64_t blockchainHeight() = 0; + + //! returns current blockchain target height + virtual uint64_t blockchainTargetHeight() = 0; + + //! returns current network difficulty + virtual uint64_t networkDifficulty() = 0; + + //! returns current mining hash rate (0 if not mining) + virtual double miningHashRate() = 0; + + //! returns current block target + virtual uint64_t blockTarget() = 0; + + //! returns true iff mining + virtual bool isMining() = 0; + + //! starts mining with the set number of threads + virtual bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) = 0; + + //! stops mining + virtual bool stopMining() = 0; + + //! resolves an OpenAlias address to a monero address + virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0; + + //! checks for an update and returns version, hash and url + static std::tuple checkUpdates( + const std::string &software, + std::string subdir, + const char *buildtag = nullptr, + const char *current_version = nullptr); + + //! sets proxy address, empty string to disable + virtual bool setProxy(const std::string &address) = 0; +}; + + +struct WalletManagerFactory +{ + // logging levels for underlying library + enum LogLevel { + LogLevel_Silent = -1, + LogLevel_0 = 0, + LogLevel_1 = 1, + LogLevel_2 = 2, + LogLevel_3 = 3, + LogLevel_4 = 4, + LogLevel_Min = LogLevel_Silent, + LogLevel_Max = LogLevel_4 + }; + + static WalletManager * getWalletManager(); + static void setLogLevel(int level); + static void setLogCategories(const std::string &categories); +}; + + +} diff --git a/libbridge/src/main/cpp/wallet2_api_c.cpp b/libbridge/src/main/cpp/wallet2_api_c.cpp new file mode 100644 index 0000000..8a74b48 --- /dev/null +++ b/libbridge/src/main/cpp/wallet2_api_c.cpp @@ -0,0 +1,1722 @@ +/** + * Copyright (c) 2017 m2049r + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "wallet2_api_c.h" +#include "wallet2_api.h" + +//TODO explicit casting jlong, jint, jboolean to avoid warnings + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#define LOG_TAG "WalletNDK" +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG,__VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , LOG_TAG,__VA_ARGS__) +#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__) + +static JavaVM *cachedJVM; +static jclass class_String; +static jclass class_ArrayList; +static jclass class_WalletListener; +static jclass class_CoinsInfo; +static jclass class_TransactionInfo; +static jclass class_Transfer; +static jclass class_Ledger; +static jclass class_WalletStatus; + +std::mutex _listenerMutex; + +//void jstringToString(JNIEnv *env, std::string &str, jstring jstr) { +// if (!jstr) return; +// const int len = env->GetStringUTFLength(jstr); +// const char *chars = env->GetStringUTFChars(jstr, nullptr); +// str.assign(chars, len); +// env->ReleaseStringUTFChars(jstr, chars); +//} + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { + cachedJVM = jvm; + LOGI("JNI_OnLoad"); + JNIEnv *jenv; + if (jvm->GetEnv(reinterpret_cast(&jenv), JNI_VERSION_1_6) != JNI_OK) { + return -1; + } + //LOGI("JNI_OnLoad ok"); + + class_String = static_cast(jenv->NewGlobalRef( + jenv->FindClass("java/lang/String"))); + class_ArrayList = static_cast(jenv->NewGlobalRef( + jenv->FindClass("java/util/ArrayList"))); + class_CoinsInfo = static_cast(jenv->NewGlobalRef( + jenv->FindClass("com/m2049r/xmrwallet/model/CoinsInfo"))); + class_TransactionInfo = static_cast(jenv->NewGlobalRef( + jenv->FindClass("com/m2049r/xmrwallet/model/TransactionInfo"))); + class_Transfer = static_cast(jenv->NewGlobalRef( + jenv->FindClass("com/m2049r/xmrwallet/model/Transfer"))); + class_WalletListener = static_cast(jenv->NewGlobalRef( + jenv->FindClass("com/m2049r/xmrwallet/model/WalletListener"))); + class_Ledger = static_cast(jenv->NewGlobalRef( + jenv->FindClass("com/m2049r/xmrwallet/ledger/Ledger"))); + class_WalletStatus = static_cast(jenv->NewGlobalRef( + jenv->FindClass("com/m2049r/xmrwallet/model/Wallet$Status"))); + return JNI_VERSION_1_6; +} +#ifdef __cplusplus +} +#endif + +int attachJVM(JNIEnv **jenv) { + int envStat = cachedJVM->GetEnv((void **) jenv, JNI_VERSION_1_6); + if (envStat == JNI_EDETACHED) { + if (cachedJVM->AttachCurrentThread(jenv, nullptr) != 0) { + LOGE("Failed to attach"); + return JNI_ERR; + } + } else if (envStat == JNI_EVERSION) { + LOGE("GetEnv: version not supported"); + return JNI_ERR; + } + //LOGI("envStat=%i", envStat); + return envStat; +} + +void detachJVM(JNIEnv *jenv, int envStat) { + //LOGI("envStat=%i", envStat); + if (jenv->ExceptionCheck()) { + jenv->ExceptionDescribe(); + } + + if (envStat == JNI_EDETACHED) { + cachedJVM->DetachCurrentThread(); + } +} + +struct MyWalletListener : Monero::WalletListener { + jobject jlistener; + + MyWalletListener(JNIEnv *env, jobject aListener) { + LOGD("Created MyListener"); + jlistener = env->NewGlobalRef(aListener);; + } + + ~MyWalletListener() { + LOGD("Destroyed MyListener"); + }; + + void deleteGlobalJavaRef(JNIEnv *env) { + std::lock_guard lock(_listenerMutex); + env->DeleteGlobalRef(jlistener); + jlistener = nullptr; + } + + /** + * @brief updated - generic callback, called when any event (sent/received/block reveived/etc) happened with the wallet; + */ + void updated() { + std::lock_guard lock(_listenerMutex); + if (jlistener == nullptr) return; + LOGD("updated"); + JNIEnv *jenv; + int envStat = attachJVM(&jenv); + if (envStat == JNI_ERR) return; + + jmethodID listenerClass_updated = jenv->GetMethodID(class_WalletListener, "updated", "()V"); + jenv->CallVoidMethod(jlistener, listenerClass_updated); + + detachJVM(jenv, envStat); + } + + + /** + * @brief moneySpent - called when money spent + * @param txId - transaction id + * @param amount - amount + */ + void moneySpent(const std::string &txId, uint64_t amount) { + std::lock_guard lock(_listenerMutex); + if (jlistener == nullptr) return; + LOGD("moneySpent %" + PRIu64, amount); + } + + /** + * @brief moneyReceived - called when money received + * @param txId - transaction id + * @param amount - amount + */ + void moneyReceived(const std::string &txId, uint64_t amount) { + std::lock_guard lock(_listenerMutex); + if (jlistener == nullptr) return; + LOGD("moneyReceived %" + PRIu64, amount); + } + + /** + * @brief unconfirmedMoneyReceived - called when payment arrived in tx pool + * @param txId - transaction id + * @param amount - amount + */ + void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) { + std::lock_guard lock(_listenerMutex); + if (jlistener == nullptr) return; + LOGD("unconfirmedMoneyReceived %" + PRIu64, amount); + } + + /** + * @brief newBlock - called when new block received + * @param height - block height + */ + void newBlock(uint64_t height) { + std::lock_guard lock(_listenerMutex); + if (jlistener == nullptr) return; + //LOGD("newBlock"); + JNIEnv *jenv; + int envStat = attachJVM(&jenv); + if (envStat == JNI_ERR) return; + + jlong h = static_cast(height); + jmethodID listenerClass_newBlock = jenv->GetMethodID(class_WalletListener, "newBlock", + "(J)V"); + jenv->CallVoidMethod(jlistener, listenerClass_newBlock, h); + + detachJVM(jenv, envStat); + } + +/** + * @brief refreshed - called when wallet refreshed by background thread or explicitly refreshed by calling "refresh" synchronously + */ + void refreshed() { + std::lock_guard lock(_listenerMutex); + if (jlistener == nullptr) return; + LOGD("refreshed"); + JNIEnv *jenv; + + int envStat = attachJVM(&jenv); + if (envStat == JNI_ERR) return; + + jmethodID listenerClass_refreshed = jenv->GetMethodID(class_WalletListener, "refreshed", + "()V"); + jenv->CallVoidMethod(jlistener, listenerClass_refreshed); + detachJVM(jenv, envStat); + } +}; + + +//// helper methods +std::vector java2cpp(JNIEnv *env, jobject arrayList) { + + jmethodID java_util_ArrayList_size = env->GetMethodID(class_ArrayList, "size", "()I"); + jmethodID java_util_ArrayList_get = env->GetMethodID(class_ArrayList, "get", + "(I)Ljava/lang/Object;"); + + jint len = env->CallIntMethod(arrayList, java_util_ArrayList_size); + std::vector result; + result.reserve(len); + for (jint i = 0; i < len; i++) { + jstring element = static_cast(env->CallObjectMethod(arrayList, + java_util_ArrayList_get, i)); + const char *pchars = env->GetStringUTFChars(element, nullptr); + result.emplace_back(pchars); + env->ReleaseStringUTFChars(element, pchars); + env->DeleteLocalRef(element); + } + return result; +} + +jobject cpp2java(JNIEnv *env, const std::vector &vector) { + + jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "", "(I)V"); + jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", + "(Ljava/lang/Object;)Z"); + + jobject result = env->NewObject(class_ArrayList, java_util_ArrayList_, + static_cast (vector.size())); + for (const std::string &s: vector) { + jstring element = env->NewStringUTF(s.c_str()); + env->CallBooleanMethod(result, java_util_ArrayList_add, element); + env->DeleteLocalRef(element); + } + return result; +} + +/// end helpers + +#ifdef __cplusplus +extern "C" +{ +#endif + +// void* MONERO_createWalletJ(const char* path, const char* password, const char* language, int networkType); +void* MONERO_createWalletJ(const char* path, const char* password, const char* language, int networkType) { + Monero::NetworkType _networkType = static_cast(networkType); + + std::cout << "WE GOT OUT\n"; + + std::string _path(path); + std::string _password(password); + std::string _language(language); + std::cout << "_path = " << _path << "\n"; + std::cout << "_password = " << _password << "\n"; + std::cout << "_language = " << _language << "\n"; + + Monero::Wallet *wallet = + Monero::WalletManagerFactory::getWalletManager()->createWallet( + _path, + _password, + _language, + _networkType); + + int status; + std::string errorString; + wallet->statusWithErrorString(status, errorString); + + std::cout << status << " - " << errorString << "\n"; + + return reinterpret_cast(wallet); +} + + + + +/**********************************/ +/********** WalletManager *********/ +/**********************************/ +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_createWalletJ(JNIEnv *env, jobject instance, + jstring path, jstring password, + jstring language, + jint networkType) { + const char *_path = env->GetStringUTFChars(path, nullptr); + const char *_password = env->GetStringUTFChars(password, nullptr); + const char *_language = env->GetStringUTFChars(language, nullptr); + Monero::NetworkType _networkType = static_cast(networkType); + + Monero::Wallet *wallet = + Monero::WalletManagerFactory::getWalletManager()->createWallet( + std::string(_path), + std::string(_password), + std::string(_language), + _networkType); + + env->ReleaseStringUTFChars(path, _path); + env->ReleaseStringUTFChars(password, _password); + env->ReleaseStringUTFChars(language, _language); + return reinterpret_cast(wallet); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_openWalletJ(JNIEnv *env, jobject instance, + jstring path, jstring password, + jint networkType) { + const char *_path = env->GetStringUTFChars(path, nullptr); + const char *_password = env->GetStringUTFChars(password, nullptr); + Monero::NetworkType _networkType = static_cast(networkType); + + Monero::Wallet *wallet = + Monero::WalletManagerFactory::getWalletManager()->openWallet( + std::string(_path), + std::string(_password), + _networkType); + + env->ReleaseStringUTFChars(path, _path); + env->ReleaseStringUTFChars(password, _password); + return reinterpret_cast(wallet); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobject instance, + jstring path, jstring password, + jstring mnemonic, jstring offset, + jint networkType, + jlong restoreHeight) { + const char *_path = env->GetStringUTFChars(path, nullptr); + const char *_password = env->GetStringUTFChars(password, nullptr); + const char *_mnemonic = env->GetStringUTFChars(mnemonic, nullptr); + const char *_offset = env->GetStringUTFChars(offset, nullptr); + Monero::NetworkType _networkType = static_cast(networkType); + + Monero::Wallet *wallet = + Monero::WalletManagerFactory::getWalletManager()->recoveryWallet( + std::string(_path), + std::string(_password), + std::string(_mnemonic), + _networkType, + (uint64_t) restoreHeight, + 1, // kdf_rounds + std::string(_offset)); + + env->ReleaseStringUTFChars(path, _path); + env->ReleaseStringUTFChars(password, _password); + env->ReleaseStringUTFChars(mnemonic, _mnemonic); + env->ReleaseStringUTFChars(offset, _offset); + return reinterpret_cast(wallet); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromKeysJ(JNIEnv *env, jobject instance, + jstring path, jstring password, + jstring language, + jint networkType, + jlong restoreHeight, + jstring addressString, + jstring viewKeyString, + jstring spendKeyString) { + const char *_path = env->GetStringUTFChars(path, nullptr); + const char *_password = env->GetStringUTFChars(password, nullptr); + const char *_language = env->GetStringUTFChars(language, nullptr); + Monero::NetworkType _networkType = static_cast(networkType); + const char *_addressString = env->GetStringUTFChars(addressString, nullptr); + const char *_viewKeyString = env->GetStringUTFChars(viewKeyString, nullptr); + const char *_spendKeyString = env->GetStringUTFChars(spendKeyString, nullptr); + + Monero::Wallet *wallet = + Monero::WalletManagerFactory::getWalletManager()->createWalletFromKeys( + std::string(_path), + std::string(_password), + std::string(_language), + _networkType, + (uint64_t) restoreHeight, + std::string(_addressString), + std::string(_viewKeyString), + std::string(_spendKeyString)); + + env->ReleaseStringUTFChars(path, _path); + env->ReleaseStringUTFChars(password, _password); + env->ReleaseStringUTFChars(language, _language); + env->ReleaseStringUTFChars(addressString, _addressString); + env->ReleaseStringUTFChars(viewKeyString, _viewKeyString); + env->ReleaseStringUTFChars(spendKeyString, _spendKeyString); + return reinterpret_cast(wallet); +} + + +// virtual void setSubaddressLookahead(uint32_t major, uint32_t minor) = 0; + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromDeviceJ(JNIEnv *env, jobject instance, + jstring path, + jstring password, + jint networkType, + jstring deviceName, + jlong restoreHeight, + jstring subaddressLookahead) { + const char *_path = env->GetStringUTFChars(path, nullptr); + const char *_password = env->GetStringUTFChars(password, nullptr); + Monero::NetworkType _networkType = static_cast(networkType); + const char *_deviceName = env->GetStringUTFChars(deviceName, nullptr); + const char *_subaddressLookahead = env->GetStringUTFChars(subaddressLookahead, nullptr); + + Monero::Wallet *wallet = + Monero::WalletManagerFactory::getWalletManager()->createWalletFromDevice( + std::string(_path), + std::string(_password), + _networkType, + std::string(_deviceName), + (uint64_t) restoreHeight, + std::string(_subaddressLookahead)); + + env->ReleaseStringUTFChars(path, _path); + env->ReleaseStringUTFChars(password, _password); + env->ReleaseStringUTFChars(deviceName, _deviceName); + env->ReleaseStringUTFChars(subaddressLookahead, _subaddressLookahead); + return reinterpret_cast(wallet); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_walletExists(JNIEnv *env, jobject instance, + jstring path) { + const char *_path = env->GetStringUTFChars(path, nullptr); + bool exists = + Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(_path)); + env->ReleaseStringUTFChars(path, _path); + return static_cast(exists); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_verifyWalletPassword(JNIEnv *env, jobject instance, + jstring keys_file_name, + jstring password, + jboolean watch_only) { + const char *_keys_file_name = env->GetStringUTFChars(keys_file_name, nullptr); + const char *_password = env->GetStringUTFChars(password, nullptr); + bool passwordOk = + Monero::WalletManagerFactory::getWalletManager()->verifyWalletPassword( + std::string(_keys_file_name), std::string(_password), watch_only); + env->ReleaseStringUTFChars(keys_file_name, _keys_file_name); + env->ReleaseStringUTFChars(password, _password); + return static_cast(passwordOk); +} + +//virtual int queryWalletHardware(const std::string &keys_file_name, const std::string &password) const = 0; +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_queryWalletDeviceJ(JNIEnv *env, jobject instance, + jstring keys_file_name, + jstring password) { + const char *_keys_file_name = env->GetStringUTFChars(keys_file_name, nullptr); + const char *_password = env->GetStringUTFChars(password, nullptr); + Monero::Wallet::Device device_type; + bool ok = Monero::WalletManagerFactory::getWalletManager()-> + queryWalletDevice(device_type, std::string(_keys_file_name), std::string(_password)); + env->ReleaseStringUTFChars(keys_file_name, _keys_file_name); + env->ReleaseStringUTFChars(password, _password); + if (ok) + return static_cast(device_type); + else + return -1; +} + +JNIEXPORT jobject JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_findWallets(JNIEnv *env, jobject instance, + jstring path) { + const char *_path = env->GetStringUTFChars(path, nullptr); + std::vector walletPaths = + Monero::WalletManagerFactory::getWalletManager()->findWallets(std::string(_path)); + env->ReleaseStringUTFChars(path, _path); + return cpp2java(env, walletPaths); +} + +//TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0; + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_setDaemonAddressJ(JNIEnv *env, jobject instance, + jstring address) { + const char *_address = env->GetStringUTFChars(address, nullptr); + Monero::WalletManagerFactory::getWalletManager()->setDaemonAddress(std::string(_address)); + env->ReleaseStringUTFChars(address, _address); +} + +// returns whether the daemon can be reached, and its version number +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getDaemonVersion(JNIEnv *env, + jobject instance) { + uint32_t version; + bool isConnected = + Monero::WalletManagerFactory::getWalletManager()->connected(&version); + if (!isConnected) version = 0; + return version; +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getBlockchainHeight(JNIEnv *env, jobject instance) { + return Monero::WalletManagerFactory::getWalletManager()->blockchainHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getBlockchainTargetHeight(JNIEnv *env, + jobject instance) { + return Monero::WalletManagerFactory::getWalletManager()->blockchainTargetHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getNetworkDifficulty(JNIEnv *env, jobject instance) { + return Monero::WalletManagerFactory::getWalletManager()->networkDifficulty(); +} + +JNIEXPORT jdouble JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getMiningHashRate(JNIEnv *env, jobject instance) { + return Monero::WalletManagerFactory::getWalletManager()->miningHashRate(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_getBlockTarget(JNIEnv *env, jobject instance) { + return Monero::WalletManagerFactory::getWalletManager()->blockTarget(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_isMining(JNIEnv *env, jobject instance) { + return static_cast(Monero::WalletManagerFactory::getWalletManager()->isMining()); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_startMining(JNIEnv *env, jobject instance, + jstring address, + jboolean background_mining, + jboolean ignore_battery) { + const char *_address = env->GetStringUTFChars(address, nullptr); + bool success = + Monero::WalletManagerFactory::getWalletManager()->startMining(std::string(_address), + background_mining, + ignore_battery); + env->ReleaseStringUTFChars(address, _address); + return static_cast(success); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_stopMining(JNIEnv *env, jobject instance) { + return static_cast(Monero::WalletManagerFactory::getWalletManager()->stopMining()); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_resolveOpenAlias(JNIEnv *env, jobject instance, + jstring address, + jboolean dnssec_valid) { + const char *_address = env->GetStringUTFChars(address, nullptr); + bool _dnssec_valid = (bool) dnssec_valid; + std::string resolvedAlias = + Monero::WalletManagerFactory::getWalletManager()->resolveOpenAlias( + std::string(_address), + _dnssec_valid); + env->ReleaseStringUTFChars(address, _address); + return env->NewStringUTF(resolvedAlias.c_str()); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_setProxy(JNIEnv *env, jobject instance, + jstring address) { + const char *_address = env->GetStringUTFChars(address, nullptr); + bool rc = + Monero::WalletManagerFactory::getWalletManager()->setProxy(std::string(_address)); + env->ReleaseStringUTFChars(address, _address); + return rc; +} + + +//TODO static std::tuple checkUpdates(const std::string &software, const std::string &subdir); + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_closeJ(JNIEnv *env, jobject instance, + jobject walletInstance) { + Monero::Wallet *wallet = getHandle(env, walletInstance); + bool closeSuccess = Monero::WalletManagerFactory::getWalletManager()->closeWallet(wallet, + false); + if (closeSuccess) { + MyWalletListener *walletListener = getHandle(env, walletInstance, + "listenerHandle"); + if (walletListener != nullptr) { + walletListener->deleteGlobalJavaRef(env); + delete walletListener; + } + } + LOGD("wallet closed"); + return static_cast(closeSuccess); +} + + + + +/**********************************/ +/************ Wallet **************/ +/**********************************/ + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getSeed(JNIEnv *env, jobject instance, jstring seedOffset) { + const char *_seedOffset = env->GetStringUTFChars(seedOffset, nullptr); + Monero::Wallet *wallet = getHandle(env, instance); + jstring seed = env->NewStringUTF(wallet->seed(std::string(_seedOffset)).c_str()); + env->ReleaseStringUTFChars(seedOffset, _seedOffset); + return seed; +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getSeedLanguage(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return env->NewStringUTF(wallet->getSeedLanguage().c_str()); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setSeedLanguage(JNIEnv *env, jobject instance, + jstring language) { + const char *_language = env->GetStringUTFChars(language, nullptr); + Monero::Wallet *wallet = getHandle(env, instance); + wallet->setSeedLanguage(std::string(_language)); + env->ReleaseStringUTFChars(language, _language); +} + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getStatusJ(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->status(); +} + +jobject newWalletStatusInstance(JNIEnv *env, int status, const std::string &errorString) { + jmethodID init = env->GetMethodID(class_WalletStatus, "", + "(ILjava/lang/String;)V"); + jstring _errorString = env->NewStringUTF(errorString.c_str()); + jobject instance = env->NewObject(class_WalletStatus, init, status, _errorString); + env->DeleteLocalRef(_errorString); + return instance; +} + + +JNIEXPORT jobject JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_statusWithErrorString(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + + int status; + std::string errorString; + wallet->statusWithErrorString(status, errorString); + + return newWalletStatusInstance(env, status, errorString); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setPassword(JNIEnv *env, jobject instance, + jstring password) { + const char *_password = env->GetStringUTFChars(password, nullptr); + Monero::Wallet *wallet = getHandle(env, instance); + bool success = wallet->setPassword(std::string(_password)); + env->ReleaseStringUTFChars(password, _password); + return static_cast(success); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getAddressJ(JNIEnv *env, jobject instance, + jint accountIndex, + jint addressIndex) { + Monero::Wallet *wallet = getHandle(env, instance); + return env->NewStringUTF( + wallet->address((uint32_t) accountIndex, (uint32_t) addressIndex).c_str()); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getPath(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return env->NewStringUTF(wallet->path().c_str()); +} + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_nettype(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->nettype(); +} + +//TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; +//TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getIntegratedAddress(JNIEnv *env, jobject instance, + jstring payment_id) { + const char *_payment_id = env->GetStringUTFChars(payment_id, nullptr); + Monero::Wallet *wallet = getHandle(env, instance); + std::string address = wallet->integratedAddress(_payment_id); + env->ReleaseStringUTFChars(payment_id, _payment_id); + return env->NewStringUTF(address.c_str()); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getSecretViewKey(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return env->NewStringUTF(wallet->secretViewKey().c_str()); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getSecretSpendKey(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return env->NewStringUTF(wallet->secretSpendKey().c_str()); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_store(JNIEnv *env, jobject instance, + jstring path) { + const char *_path = env->GetStringUTFChars(path, nullptr); + Monero::Wallet *wallet = getHandle(env, instance); + bool success = wallet->store(std::string(_path)); + if (!success) { + LOGE("store() %s", wallet->errorString().c_str()); + } + env->ReleaseStringUTFChars(path, _path); + return static_cast(success); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getFilename(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return env->NewStringUTF(wallet->filename().c_str()); +} + +// virtual std::string keysFilename() const = 0; + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_initJ(JNIEnv *env, jobject instance, + jstring daemon_address, + jlong upper_transaction_size_limit, + jstring daemon_username, jstring daemon_password) { + const char *_daemon_address = env->GetStringUTFChars(daemon_address, nullptr); + const char *_daemon_username = env->GetStringUTFChars(daemon_username, nullptr); + const char *_daemon_password = env->GetStringUTFChars(daemon_password, nullptr); + Monero::Wallet *wallet = getHandle(env, instance); + bool status = wallet->init(_daemon_address, (uint64_t) upper_transaction_size_limit, + _daemon_username, + _daemon_password); + env->ReleaseStringUTFChars(daemon_address, _daemon_address); + env->ReleaseStringUTFChars(daemon_username, _daemon_username); + env->ReleaseStringUTFChars(daemon_password, _daemon_password); + return static_cast(status); +} + +// virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0; + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setRestoreHeight(JNIEnv *env, jobject instance, + jlong height) { + Monero::Wallet *wallet = getHandle(env, instance); + wallet->setRefreshFromBlockHeight((uint64_t) height); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getRestoreHeight(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->getRefreshFromBlockHeight(); +} + +// virtual void setRecoveringFromSeed(bool recoveringFromSeed) = 0; +// virtual bool connectToDaemon() = 0; + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getConnectionStatusJ(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->connected(); +} +//TODO virtual void setTrustedDaemon(bool arg) = 0; +//TODO virtual bool trustedDaemon() const = 0; + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setProxy(JNIEnv *env, jobject instance, + jstring address) { + const char *_address = env->GetStringUTFChars(address, nullptr); + Monero::Wallet *wallet = getHandle(env, instance); + bool rc = wallet->setProxy(std::string(_address)); + env->ReleaseStringUTFChars(address, _address); + return rc; +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getBalance(JNIEnv *env, jobject instance, + jint accountIndex) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->balance((uint32_t) accountIndex); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getBalanceAll(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->balanceAll(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getUnlockedBalance(JNIEnv *env, jobject instance, + jint accountIndex) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->unlockedBalance((uint32_t) accountIndex); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getUnlockedBalanceAll(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->unlockedBalanceAll(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_isWatchOnly(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return static_cast(wallet->watchOnly()); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getBlockChainHeight(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->blockChainHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getApproximateBlockChainHeight(JNIEnv *env, + jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->approximateBlockChainHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getDaemonBlockChainHeight(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->daemonBlockChainHeight(); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getDaemonBlockChainTargetHeight(JNIEnv *env, + jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->daemonBlockChainTargetHeight(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_isSynchronizedJ(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return static_cast(wallet->synchronized()); +} + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getDeviceTypeJ(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + Monero::Wallet::Device device_type = wallet->getDeviceType(); + return static_cast(device_type); +} + +//void cn_slow_hash(const void *data, size_t length, char *hash); // from crypto/hash-ops.h +JNIEXPORT jbyteArray JNICALL +Java_com_m2049r_xmrwallet_util_KeyStoreHelper_slowHash(JNIEnv *env, jclass clazz, + jbyteArray data, jint brokenVariant) { + char hash[HASH_SIZE]; + jsize size = env->GetArrayLength(data); + if ((brokenVariant > 0) && (size < 200 /*sizeof(union hash_state)*/)) { + return nullptr; + } + + jbyte *buffer = env->GetByteArrayElements(data, nullptr); + switch (brokenVariant) { + case 1: + slow_hash_broken(buffer, hash, 1); + break; + case 2: + slow_hash_broken(buffer, hash, 0); + break; + default: // not broken + slow_hash(buffer, (size_t) size, hash); + } + env->ReleaseByteArrayElements(data, buffer, JNI_ABORT); // do not update java byte[] + jbyteArray result = env->NewByteArray(HASH_SIZE); + env->SetByteArrayRegion(result, 0, HASH_SIZE, (jbyte *) hash); + return result; +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getDisplayAmount(JNIEnv *env, jclass clazz, + jlong amount) { + return env->NewStringUTF(Monero::Wallet::displayAmount(amount).c_str()); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getAmountFromString(JNIEnv *env, jclass clazz, + jstring amount) { + const char *_amount = env->GetStringUTFChars(amount, nullptr); + uint64_t x = Monero::Wallet::amountFromString(_amount); + env->ReleaseStringUTFChars(amount, _amount); + return x; +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getAmountFromDouble(JNIEnv *env, jclass clazz, + jdouble amount) { + return Monero::Wallet::amountFromDouble(amount); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_generatePaymentId(JNIEnv *env, jclass clazz) { + return env->NewStringUTF(Monero::Wallet::genPaymentId().c_str()); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_isPaymentIdValid(JNIEnv *env, jclass clazz, + jstring payment_id) { + const char *_payment_id = env->GetStringUTFChars(payment_id, nullptr); + bool isValid = Monero::Wallet::paymentIdValid(_payment_id); + env->ReleaseStringUTFChars(payment_id, _payment_id); + return static_cast(isValid); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_isAddressValid(JNIEnv *env, jclass clazz, + jstring address, jint networkType) { + const char *_address = env->GetStringUTFChars(address, nullptr); + Monero::NetworkType _networkType = static_cast(networkType); + bool isValid = Monero::Wallet::addressValid(_address, _networkType); + env->ReleaseStringUTFChars(address, _address); + return static_cast(isValid); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getPaymentIdFromAddress(JNIEnv *env, jclass clazz, + jstring address, + jint networkType) { + Monero::NetworkType _networkType = static_cast(networkType); + const char *_address = env->GetStringUTFChars(address, nullptr); + std::string payment_id = Monero::Wallet::paymentIdFromAddress(_address, _networkType); + env->ReleaseStringUTFChars(address, _address); + return env->NewStringUTF(payment_id.c_str()); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getMaximumAllowedAmount(JNIEnv *env, jclass clazz) { + return Monero::Wallet::maximumAllowedAmount(); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_startRefresh(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + wallet->startRefresh(); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_pauseRefresh(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + wallet->pauseRefresh(); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_refresh(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return static_cast(wallet->refresh()); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_refreshAsync(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + wallet->refreshAsync(); +} + +//TODO virtual bool rescanBlockchain() = 0; + +//virtual void rescanBlockchainAsync() = 0; +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_rescanBlockchainAsyncJ(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + wallet->rescanBlockchainAsync(); +} + + +//TODO virtual void setAutoRefreshInterval(int millis) = 0; +//TODO virtual int autoRefreshInterval() const = 0; + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_createTransactionMultDest(JNIEnv *env, jobject instance, + jobjectArray destinations, + jstring payment_id, + jlongArray amounts, + jint mixin_count, + jint priority, + jint accountIndex, + jintArray subaddresses) { + std::vector dst_addr; + std::vector amount; + + int destSize = env->GetArrayLength(destinations); + assert(destSize == env->GetArrayLength(amounts)); + jlong *_amounts = env->GetLongArrayElements(amounts, nullptr); + for (int i = 0; i < destSize; i++) { + jstring dest = (jstring) env->GetObjectArrayElement(destinations, i); + const char *_dest = env->GetStringUTFChars(dest, nullptr); + dst_addr.emplace_back(_dest); + env->ReleaseStringUTFChars(dest, _dest); + amount.emplace_back((uint64_t) _amounts[i]); + } + env->ReleaseLongArrayElements(amounts, _amounts, 0); + + std::set subaddr_indices; + if (subaddresses != nullptr) { + int subaddrSize = env->GetArrayLength(subaddresses); + jint *_subaddresses = env->GetIntArrayElements(subaddresses, nullptr); + for (int i = 0; i < subaddrSize; i++) { + subaddr_indices.insert((uint32_t) _subaddresses[i]); + } + env->ReleaseIntArrayElements(subaddresses, _subaddresses, 0); + } + + const char *_payment_id = env->GetStringUTFChars(payment_id, nullptr); + + Monero::PendingTransaction::Priority _priority = + static_cast(priority); + + Monero::Wallet *wallet = getHandle(env, instance); + + Monero::PendingTransaction *tx = + wallet->createTransactionMultDest(dst_addr, _payment_id, + amount, (uint32_t) mixin_count, + _priority, + (uint32_t) accountIndex, + subaddr_indices); + + env->ReleaseStringUTFChars(payment_id, _payment_id); + return reinterpret_cast(tx); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_createTransactionJ(JNIEnv *env, jobject instance, + jstring dst_addr, jstring payment_id, + jlong amount, jint mixin_count, + jint priority, + jint accountIndex) { + + const char *_dst_addr = env->GetStringUTFChars(dst_addr, nullptr); + const char *_payment_id = env->GetStringUTFChars(payment_id, nullptr); + Monero::PendingTransaction::Priority _priority = + static_cast(priority); + Monero::Wallet *wallet = getHandle(env, instance); + + Monero::PendingTransaction *tx = wallet->createTransaction(_dst_addr, _payment_id, + amount, (uint32_t) mixin_count, + _priority, + (uint32_t) accountIndex); + + env->ReleaseStringUTFChars(dst_addr, _dst_addr); + env->ReleaseStringUTFChars(payment_id, _payment_id); + return reinterpret_cast(tx); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_createSweepTransaction(JNIEnv *env, jobject instance, + jstring dst_addr, jstring payment_id, + jint mixin_count, + jint priority, + jint accountIndex) { + + const char *_dst_addr = env->GetStringUTFChars(dst_addr, nullptr); + const char *_payment_id = env->GetStringUTFChars(payment_id, nullptr); + Monero::PendingTransaction::Priority _priority = + static_cast(priority); + Monero::Wallet *wallet = getHandle(env, instance); + + Monero::optional empty; + + Monero::PendingTransaction *tx = wallet->createTransaction(_dst_addr, _payment_id, + empty, (uint32_t) mixin_count, + _priority, + (uint32_t) accountIndex); + + env->ReleaseStringUTFChars(dst_addr, _dst_addr); + env->ReleaseStringUTFChars(payment_id, _payment_id); + return reinterpret_cast(tx); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_createSweepUnmixableTransactionJ(JNIEnv *env, + jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + Monero::PendingTransaction *tx = wallet->createSweepUnmixableTransaction(); + return reinterpret_cast(tx); +} + +//virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; +//virtual bool submitTransaction(const std::string &fileName) = 0; + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_disposeTransaction(JNIEnv *env, jobject instance, + jobject pendingTransaction) { + Monero::Wallet *wallet = getHandle(env, instance); + Monero::PendingTransaction *_pendingTransaction = + getHandle(env, pendingTransaction); + wallet->disposeTransaction(_pendingTransaction); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_estimateTransactionFee(JNIEnv *env, jobject instance, + jobjectArray addresses, + jlongArray amounts, + jint priority) { + + std::vector> destinations; + + int destSize = env->GetArrayLength(addresses); + assert(destSize == env->GetArrayLength(amounts)); + jlong *_amounts = env->GetLongArrayElements(amounts, nullptr); + for (int i = 0; i < destSize; i++) { + std::pair pair; + jstring dest = (jstring) env->GetObjectArrayElement(addresses, i); + const char *_dest = env->GetStringUTFChars(dest, nullptr); + pair.first = _dest; + env->ReleaseStringUTFChars(dest, _dest); + pair.second = ((uint64_t) _amounts[i]); + destinations.emplace_back(pair); + } + env->ReleaseLongArrayElements(amounts, _amounts, 0); + + Monero::PendingTransaction::Priority _priority = + static_cast(priority); + + Monero::Wallet *wallet = getHandle(env, instance); + + return static_cast(wallet->estimateTransactionFee(destinations, _priority)); +} + +//virtual bool exportKeyImages(const std::string &filename) = 0; +//virtual bool importKeyImages(const std::string &filename) = 0; + + +//virtual TransactionHistory * history() const = 0; +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getHistoryJ(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return reinterpret_cast(wallet->history()); +} + +//virtual AddressBook * addressBook() const = 0; + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getCoinsJ(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return reinterpret_cast(wallet->coins()); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setListenerJ(JNIEnv *env, jobject instance, + jobject javaListener) { + Monero::Wallet *wallet = getHandle(env, instance); + wallet->setListener(nullptr); // clear old listener + // delete old listener + MyWalletListener *oldListener = getHandle(env, instance, + "listenerHandle"); + if (oldListener != nullptr) { + oldListener->deleteGlobalJavaRef(env); + delete oldListener; + } + if (javaListener == nullptr) { + LOGD("null listener"); + return 0; + } else { + MyWalletListener *listener = new MyWalletListener(env, javaListener); + wallet->setListener(listener); + return reinterpret_cast(listener); + } +} + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getDefaultMixin(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->defaultMixin(); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setDefaultMixin(JNIEnv *env, jobject instance, jint mixin) { + Monero::Wallet *wallet = getHandle(env, instance); + return wallet->setDefaultMixin(mixin); +} + +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setUserNote(JNIEnv *env, jobject instance, + jstring txid, jstring note) { + + const char *_txid = env->GetStringUTFChars(txid, nullptr); + const char *_note = env->GetStringUTFChars(note, nullptr); + + Monero::Wallet *wallet = getHandle(env, instance); + + bool success = wallet->setUserNote(_txid, _note); + + env->ReleaseStringUTFChars(txid, _txid); + env->ReleaseStringUTFChars(note, _note); + + return static_cast(success); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getUserNote(JNIEnv *env, jobject instance, + jstring txid) { + + const char *_txid = env->GetStringUTFChars(txid, nullptr); + + Monero::Wallet *wallet = getHandle(env, instance); + + std::string note = wallet->getUserNote(_txid); + + env->ReleaseStringUTFChars(txid, _txid); + return env->NewStringUTF(note.c_str()); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getTxKey(JNIEnv *env, jobject instance, + jstring txid) { + + const char *_txid = env->GetStringUTFChars(txid, nullptr); + + Monero::Wallet *wallet = getHandle(env, instance); + + std::string txKey = wallet->getTxKey(_txid); + + env->ReleaseStringUTFChars(txid, _txid); + return env->NewStringUTF(txKey.c_str()); +} + +//virtual void addSubaddressAccount(const std::string& label) = 0; +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_addAccount(JNIEnv *env, jobject instance, + jstring label) { + + const char *_label = env->GetStringUTFChars(label, nullptr); + + Monero::Wallet *wallet = getHandle(env, instance); + wallet->addSubaddressAccount(_label); + + env->ReleaseStringUTFChars(label, _label); +} + +//virtual std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const = 0; +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getSubaddressLabel(JNIEnv *env, jobject instance, + jint accountIndex, jint addressIndex) { + + Monero::Wallet *wallet = getHandle(env, instance); + + std::string label = wallet->getSubaddressLabel((uint32_t) accountIndex, + (uint32_t) addressIndex); + + return env->NewStringUTF(label.c_str()); +} + +//virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0; +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_setSubaddressLabel(JNIEnv *env, jobject instance, + jint accountIndex, jint addressIndex, + jstring label) { + + const char *_label = env->GetStringUTFChars(label, nullptr); + + Monero::Wallet *wallet = getHandle(env, instance); + wallet->setSubaddressLabel(accountIndex, addressIndex, _label); + + env->ReleaseStringUTFChars(label, _label); +} + +// virtual size_t numSubaddressAccounts() const = 0; +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getNumAccounts(JNIEnv *env, jobject instance) { + Monero::Wallet *wallet = getHandle(env, instance); + return static_cast(wallet->numSubaddressAccounts()); +} + +//virtual size_t numSubaddresses(uint32_t accountIndex) const = 0; +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getNumSubaddresses(JNIEnv *env, jobject instance, + jint accountIndex) { + Monero::Wallet *wallet = getHandle(env, instance); + return static_cast(wallet->numSubaddresses(accountIndex)); +} + +//virtual void addSubaddress(uint32_t accountIndex, const std::string &label) = 0; +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_addSubaddress(JNIEnv *env, jobject instance, + jint accountIndex, + jstring label) { + + const char *_label = env->GetStringUTFChars(label, nullptr); + Monero::Wallet *wallet = getHandle(env, instance); + wallet->addSubaddress(accountIndex, _label); + env->ReleaseStringUTFChars(label, _label); +} + +/*JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_Wallet_getLastSubaddress(JNIEnv *env, jobject instance, + jint accountIndex) { + + Monero::Wallet *wallet = getHandle(env, instance); + size_t num = wallet->numSubaddresses(accountIndex); + //wallet->subaddress()->getAll()[num]->getAddress().c_str() + Monero::Subaddress *s = wallet->subaddress(); + s->refresh(accountIndex); + std::vector v = s->getAll(); + return env->NewStringUTF(v[num - 1]->getAddress().c_str()); +} +*/ +//virtual std::string signMessage(const std::string &message) = 0; +//virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0; + +//virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &tvAmount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) = 0; +//virtual bool rescanSpent() = 0; + + +// TransactionHistory +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_TransactionHistory_getCount(JNIEnv *env, jobject instance) { + Monero::TransactionHistory *history = getHandle(env, + instance); + return history->count(); +} + +jobject newTransferInstance(JNIEnv *env, uint64_t amount, const std::string &address) { + jmethodID c = env->GetMethodID(class_Transfer, "", + "(JLjava/lang/String;)V"); + jstring _address = env->NewStringUTF(address.c_str()); + jobject transfer = env->NewObject(class_Transfer, c, static_cast (amount), _address); + env->DeleteLocalRef(_address); + return transfer; +} + +jobject newTransferList(JNIEnv *env, Monero::TransactionInfo *info) { + const std::vector &transfers = info->transfers(); + if (transfers.empty()) { // don't create empty Lists + return nullptr; + } + // make new ArrayList + jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "", "(I)V"); + jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", + "(Ljava/lang/Object;)Z"); + jobject result = env->NewObject(class_ArrayList, java_util_ArrayList_, + static_cast (transfers.size())); + // create Transfer objects and stick them in the List + for (const Monero::TransactionInfo::Transfer &s: transfers) { + jobject element = newTransferInstance(env, s.amount, s.address); + env->CallBooleanMethod(result, java_util_ArrayList_add, element); + env->DeleteLocalRef(element); + } + return result; +} + +jobject newTransactionInfo(JNIEnv *env, Monero::TransactionInfo *info) { + jmethodID c = env->GetMethodID(class_TransactionInfo, "", + "(IZZJJJLjava/lang/String;JLjava/lang/String;IIJJLjava/lang/String;Ljava/util/List;)V"); + jobject transfers = newTransferList(env, info); + jstring _hash = env->NewStringUTF(info->hash().c_str()); + jstring _paymentId = env->NewStringUTF(info->paymentId().c_str()); + jstring _label = env->NewStringUTF(info->label().c_str()); + uint32_t subaddrIndex = 0; + if (info->direction() == Monero::TransactionInfo::Direction_In) + subaddrIndex = *(info->subaddrIndex().begin()); + jobject result = env->NewObject(class_TransactionInfo, c, + info->direction(), + info->isPending(), + info->isFailed(), + static_cast (info->amount()), + static_cast (info->fee()), + static_cast (info->blockHeight()), + _hash, + static_cast (info->timestamp()), + _paymentId, + static_cast (info->subaddrAccount()), + static_cast (subaddrIndex), + static_cast (info->confirmations()), + static_cast (info->unlockTime()), + _label, + transfers); + env->DeleteLocalRef(transfers); + env->DeleteLocalRef(_hash); + env->DeleteLocalRef(_paymentId); + return result; +} + +#include +#include + +// Coins + +jobject newCoinsInfo(JNIEnv *env, Monero::CoinsInfo *info) { + jstring _hash = env->NewStringUTF(info->hash().c_str()); + + jmethodID c = env->GetMethodID(class_CoinsInfo, "", "(IIJJLjava/lang/String;ZZJZ)V"); + jobject result = env->NewObject(class_CoinsInfo, c, + static_cast (info->subaddrAccount()), + static_cast (info->subaddrIndex()), + static_cast (info->amount()), + static_cast (info->blockHeight()), + _hash, + info->spent(), + info->frozen(), + static_cast (info->unlockTime()), + info->unlocked()); + env->DeleteLocalRef(_hash); + return result; +} + +jobject coinsInfoArrayList(JNIEnv *env, const std::vector &vector, + uint32_t accountIndex, bool unspentOnly) { + + jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "", "(I)V"); + jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", + "(Ljava/lang/Object;)Z"); + + jobject arrayList = env->NewObject(class_ArrayList, java_util_ArrayList_, + static_cast (vector.size())); + for (Monero::CoinsInfo *s: vector) { + if (s->subaddrAccount() != accountIndex) continue; + if (s->spent() && unspentOnly) continue; + jobject info = newCoinsInfo(env, s); + env->CallBooleanMethod(arrayList, java_util_ArrayList_add, info); + env->DeleteLocalRef(info); + } + return arrayList; +} + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_Coins_getCount(JNIEnv *env, jobject instance) { + Monero::Coins *coins = getHandle(env, instance); + return coins->count(); +} + +JNIEXPORT jobject JNICALL +Java_com_m2049r_xmrwallet_model_Coins_refresh(JNIEnv *env, jobject instance, jint accountIndex, + jboolean unspentOnly) { + Monero::Coins *coins = getHandle(env, instance); + coins->refresh(); + return coinsInfoArrayList(env, coins->getAll(), (uint32_t) accountIndex, unspentOnly); +} + +jobject +transactionInfoArrayList(JNIEnv *env, const std::vector &vector, + uint32_t accountIndex) { + + jmethodID java_util_ArrayList_ = env->GetMethodID(class_ArrayList, "", "(I)V"); + jmethodID java_util_ArrayList_add = env->GetMethodID(class_ArrayList, "add", + "(Ljava/lang/Object;)Z"); + + jobject arrayList = env->NewObject(class_ArrayList, java_util_ArrayList_, + static_cast (vector.size())); + for (Monero::TransactionInfo *s: vector) { + if (s->subaddrAccount() != accountIndex) continue; + jobject info = newTransactionInfo(env, s); + env->CallBooleanMethod(arrayList, java_util_ArrayList_add, info); + env->DeleteLocalRef(info); + } + return arrayList; +} + +JNIEXPORT jobject JNICALL +Java_com_m2049r_xmrwallet_model_TransactionHistory_refreshJ(JNIEnv *env, jobject instance, + jint accountIndex) { + Monero::TransactionHistory *history = getHandle(env, + instance); + history->refresh(); + return transactionInfoArrayList(env, history->getAll(), (uint32_t) accountIndex); +} + +// TransactionInfo is implemented in Java - no need here + +JNIEXPORT jint JNICALL +Java_com_m2049r_xmrwallet_model_PendingTransaction_getStatusJ(JNIEnv *env, jobject instance) { + Monero::PendingTransaction *tx = getHandle(env, instance); + return tx->status(); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_PendingTransaction_getErrorString(JNIEnv *env, jobject instance) { + Monero::PendingTransaction *tx = getHandle(env, instance); + return env->NewStringUTF(tx->errorString().c_str()); +} + +// commit transaction or save to file if filename is provided. +JNIEXPORT jboolean JNICALL +Java_com_m2049r_xmrwallet_model_PendingTransaction_commit(JNIEnv *env, jobject instance, + jstring filename, jboolean overwrite) { + + const char *_filename = env->GetStringUTFChars(filename, nullptr); + + Monero::PendingTransaction *tx = getHandle(env, instance); + bool success = tx->commit(_filename, overwrite); + + env->ReleaseStringUTFChars(filename, _filename); + return static_cast(success); +} + + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_PendingTransaction_getAmount(JNIEnv *env, jobject instance) { + Monero::PendingTransaction *tx = getHandle(env, instance); + return static_cast(tx->amount()); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_PendingTransaction_getDust(JNIEnv *env, jobject instance) { + Monero::PendingTransaction *tx = getHandle(env, instance); + return static_cast(tx->dust()); +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_PendingTransaction_getFee(JNIEnv *env, jobject instance) { + Monero::PendingTransaction *tx = getHandle(env, instance); + return static_cast(tx->fee()); +} + +// TODO this returns a vector of strings - deal with this later - for now return first one +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_PendingTransaction_getFirstTxIdJ(JNIEnv *env, jobject instance) { + Monero::PendingTransaction *tx = getHandle(env, instance); + std::vector txids = tx->txid(); + if (!txids.empty()) + return env->NewStringUTF(txids.front().c_str()); + else + return nullptr; +} + +JNIEXPORT jlong JNICALL +Java_com_m2049r_xmrwallet_model_PendingTransaction_getTxCount(JNIEnv *env, jobject instance) { + Monero::PendingTransaction *tx = getHandle(env, instance); + return static_cast(tx->txCount()); +} + + +// these are all in Monero::Wallet - which I find wrong, so they are here! +//static void init(const char *argv0, const char *default_log_base_name); +//static void debug(const std::string &category, const std::string &str); +//static void info(const std::string &category, const std::string &str); +//static void warning(const std::string &category, const std::string &str); +//static void error(const std::string &category, const std::string &str); +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_initLogger(JNIEnv *env, jclass clazz, + jstring argv0, + jstring default_log_base_name) { + + const char *_argv0 = env->GetStringUTFChars(argv0, nullptr); + const char *_default_log_base_name = env->GetStringUTFChars(default_log_base_name, nullptr); + + Monero::Wallet::init(_argv0, _default_log_base_name); + + env->ReleaseStringUTFChars(argv0, _argv0); + env->ReleaseStringUTFChars(default_log_base_name, _default_log_base_name); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_logDebug(JNIEnv *env, jclass clazz, + jstring category, jstring message) { + + const char *_category = env->GetStringUTFChars(category, nullptr); + const char *_message = env->GetStringUTFChars(message, nullptr); + + Monero::Wallet::debug(_category, _message); + + env->ReleaseStringUTFChars(category, _category); + env->ReleaseStringUTFChars(message, _message); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_logInfo(JNIEnv *env, jclass clazz, + jstring category, jstring message) { + + const char *_category = env->GetStringUTFChars(category, nullptr); + const char *_message = env->GetStringUTFChars(message, nullptr); + + Monero::Wallet::info(_category, _message); + + env->ReleaseStringUTFChars(category, _category); + env->ReleaseStringUTFChars(message, _message); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_logWarning(JNIEnv *env, jclass clazz, + jstring category, jstring message) { + + const char *_category = env->GetStringUTFChars(category, nullptr); + const char *_message = env->GetStringUTFChars(message, nullptr); + + Monero::Wallet::warning(_category, _message); + + env->ReleaseStringUTFChars(category, _category); + env->ReleaseStringUTFChars(message, _message); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_logError(JNIEnv *env, jclass clazz, + jstring category, jstring message) { + + const char *_category = env->GetStringUTFChars(category, nullptr); + const char *_message = env->GetStringUTFChars(message, nullptr); + + Monero::Wallet::error(_category, _message); + + env->ReleaseStringUTFChars(category, _category); + env->ReleaseStringUTFChars(message, _message); +} + +JNIEXPORT void JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_setLogLevel(JNIEnv *env, jclass clazz, + jint level) { + Monero::WalletManagerFactory::setLogLevel(level); +} + +JNIEXPORT jstring JNICALL +Java_com_m2049r_xmrwallet_model_WalletManager_moneroVersion(JNIEnv *env, jclass clazz) { + return env->NewStringUTF(MONERO_VERSION); +} + +// +// Ledger Stuff +// + +/** + * @brief LedgerExchange - exchange data with Ledger Device + * @param command - buffer for data to send + * @param cmd_len - length of send to send + * @param response - buffer for received data + * @param max_resp_len - size of receive buffer + * + * @return length of received data in response or -1 if error + */ +int LedgerExchange( + unsigned char *command, + unsigned int cmd_len, + unsigned char *response, + unsigned int max_resp_len) { + LOGD("LedgerExchange"); + JNIEnv *jenv; + int envStat = attachJVM(&jenv); + if (envStat == JNI_ERR) return -1; + + jmethodID exchangeMethod = jenv->GetStaticMethodID(class_Ledger, "Exchange", "([B)[B"); + + jsize sendLen = static_cast(cmd_len); + jbyteArray dataSend = jenv->NewByteArray(sendLen); + jenv->SetByteArrayRegion(dataSend, 0, sendLen, (jbyte *) command); + jbyteArray dataRecv = (jbyteArray) jenv->CallStaticObjectMethod(class_Ledger, exchangeMethod, + dataSend); + jenv->DeleteLocalRef(dataSend); + if (dataRecv == nullptr) { + detachJVM(jenv, envStat); + LOGD("LedgerExchange SCARD_E_NO_READERS_AVAILABLE"); + return -1; + } + jsize len = jenv->GetArrayLength(dataRecv); + LOGD("LedgerExchange SCARD_S_SUCCESS %u/%d", cmd_len, len); + if (len <= max_resp_len) { + jenv->GetByteArrayRegion(dataRecv, 0, len, (jbyte *) response); + jenv->DeleteLocalRef(dataRecv); + detachJVM(jenv, envStat); + return static_cast(len);; + } else { + jenv->DeleteLocalRef(dataRecv); + detachJVM(jenv, envStat); + LOGE("LedgerExchange SCARD_E_INSUFFICIENT_BUFFER"); + return -1; + } +} + +/** + * @brief LedgerFind - find Ledger Device and return it's name + * @param buffer - buffer for name of found device + * @param len - length of buffer + * @return 0 - success + * -1 - no device connected / found + * -2 - JVM not found + */ +int LedgerFind(char *buffer, size_t len) { + LOGD("LedgerName"); + JNIEnv *jenv; + int envStat = attachJVM(&jenv); + if (envStat == JNI_ERR) return -2; + + jmethodID nameMethod = jenv->GetStaticMethodID(class_Ledger, "Name", "()Ljava/lang/String;"); + jstring name = (jstring) jenv->CallStaticObjectMethod(class_Ledger, nameMethod); + + int ret; + if (name != nullptr) { + const char *_name = jenv->GetStringUTFChars(name, nullptr); + strncpy(buffer, _name, len); + jenv->ReleaseStringUTFChars(name, _name); + buffer[len - 1] = 0; // terminate in case _name is bigger + ret = 0; + LOGD("LedgerName is %s", buffer); + } else { + buffer[0] = 0; + ret = -1; + } + + detachJVM(jenv, envStat); + return ret; +} + +#ifdef __cplusplus +} +#endif diff --git a/libbridge/src/main/cpp/wallet2_api_c.h b/libbridge/src/main/cpp/wallet2_api_c.h new file mode 100644 index 0000000..b0b3502 --- /dev/null +++ b/libbridge/src/main/cpp/wallet2_api_c.h @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2017 m2049r + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef XMRWALLET_WALLET_LIB_H +#define XMRWALLET_WALLET_LIB_H + +#include + +/* +#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__) +*/ + +jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handle") { + jclass c = env->GetObjectClass(obj); + return env->GetFieldID(c, fieldName, "J"); // of type long +} + +template +T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") { + jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName)); + return reinterpret_cast(handle); +} + +void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) { + env->SetLongField(obj, getHandleField(env, obj), handle); +} + +template +void setHandle(JNIEnv *env, jobject obj, T *t) { + jlong handle = reinterpret_cast(t); + setHandleFromLong(env, obj, handle); +} + +#ifdef __cplusplus +extern "C" +{ +#endif + +void* MONERO_createWalletJ(const char* path, const char* password, const char* language, int networkType); + +extern const char* const MONERO_VERSION; // the actual monero core version + +// from monero-core crypto/hash-ops.h - avoid #including monero code here +enum { + HASH_SIZE = 32, + HASH_DATA_AREA = 136 +}; + +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height); + +inline void slow_hash(const void *data, const size_t length, char *hash) { + cn_slow_hash(data, length, hash, 0 /*variant*/, 0 /*prehashed*/, 0 /*height*/); +} + +inline void slow_hash_broken(const void *data, char *hash, int variant) { + cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/, 0 /*height*/); +} + +#ifdef __cplusplus +} +#endif + +#endif //XMRWALLET_WALLET_LIB_H