diff --git a/Dockerfile b/.docker/Dockerfile similarity index 98% rename from Dockerfile rename to .docker/Dockerfile index 6a1ba87559..eb53aa84ec 100644 --- a/Dockerfile +++ b/.docker/Dockerfile @@ -6,7 +6,7 @@ # docker build --tag mm2 . # NB: The version here was picked to match the one tested in our CI. The latest Travis has (as of 2018-11) is Xenial. -FROM ubuntu:xenial +FROM docker.io/ubuntu:xenial RUN \ apt-get update &&\ diff --git a/Dockerfile.aarch64-linux-android b/.docker/Dockerfile.aarch64-linux-android similarity index 95% rename from Dockerfile.aarch64-linux-android rename to .docker/Dockerfile.aarch64-linux-android index 6d3c32598f..9f2c17fde6 100644 --- a/Dockerfile.aarch64-linux-android +++ b/.docker/Dockerfile.aarch64-linux-android @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM docker.io/ubuntu:16.04 RUN apt-get update && \ apt-get install -y --no-install-recommends \ @@ -23,7 +23,7 @@ RUN apt-get update && \ libc6-dev-i386 && \ apt-get clean -COPY android-ndk.sh / +COPY ./scripts/ci/android-ndk.sh / RUN bash /android-ndk.sh arm64 21 ENV PATH=$PATH:/android-ndk/bin ENV RUSTFMT=rustfmt diff --git a/Dockerfile.armv7-linux-androideabi b/.docker/Dockerfile.armv7-linux-androideabi similarity index 95% rename from Dockerfile.armv7-linux-androideabi rename to .docker/Dockerfile.armv7-linux-androideabi index f132042cd6..b6115d4fa8 100644 --- a/Dockerfile.armv7-linux-androideabi +++ b/.docker/Dockerfile.armv7-linux-androideabi @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM docker.io/ubuntu:16.04 RUN apt-get update && \ apt-get install -y --no-install-recommends \ @@ -23,7 +23,7 @@ RUN apt-get update && \ libc6-dev-i386 && \ apt-get clean -COPY android-ndk.sh / +COPY ./scripts/ci/android-ndk.sh / RUN bash /android-ndk.sh arm 21 ENV PATH=$PATH:/android-ndk/bin ENV RUSTFMT=rustfmt diff --git a/Dockerfile.armv7-unknown-linux-gnueabihf b/.docker/Dockerfile.armv7-unknown-linux-gnueabihf similarity index 81% rename from Dockerfile.armv7-unknown-linux-gnueabihf rename to .docker/Dockerfile.armv7-unknown-linux-gnueabihf index 613a1098b3..30c95315f6 100644 --- a/Dockerfile.armv7-unknown-linux-gnueabihf +++ b/.docker/Dockerfile.armv7-unknown-linux-gnueabihf @@ -1,4 +1,4 @@ -FROM rustembedded/cross:armv7-unknown-linux-gnueabihf-0.2.1 +FROM docker.io/rustembedded/cross:armv7-unknown-linux-gnueabihf-0.2.1 RUN dpkg --add-architecture armhf RUN apt-get update && apt-get install -y llvm-3.9-dev libclang-3.9-dev clang-3.9 libc6-dev-i386 libssl-dev:armhf && apt-get install -y g++-arm-linux-gnueabihf && apt-get clean ENV CMAKE_FORCE_C_COMPILER=arm-linux-gnueabihf-gcc \ diff --git a/.docker/Dockerfile.ci-container b/.docker/Dockerfile.ci-container new file mode 100644 index 0000000000..2553521750 --- /dev/null +++ b/.docker/Dockerfile.ci-container @@ -0,0 +1,55 @@ +FROM docker.io/debian:buster-slim + +MAINTAINER Onur Özkan + +RUN apt-get update -y + +RUN apt-get install -y \ + build-essential \ + cmake \ + gcc-multilib \ + ca-certificates \ + curl \ + wget \ + gnupg \ + git \ + zip \ + sudo + +RUN ln -s /usr/bin/python3 /bin/python + +RUN apt install -y \ + software-properties-common \ + lsb-release \ + gnupg + +RUN wget https://apt.llvm.org/llvm.sh + +RUN chmod +x llvm.sh + +RUN ./llvm.sh 16 + +RUN rm ./llvm.sh + +RUN ln -s /usr/bin/clang-16 /usr/bin/clang + +ENV AR=/usr/bin/llvm-ar-16 +ENV CC=/usr/bin/clang-16 + +RUN mkdir -m 0755 -p /etc/apt/keyrings + +RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + +RUN echo \ + "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ + "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + +RUN apt-get update -y + +RUN apt-get install -y \ + docker-ce \ + docker-ce-cli \ + containerd.io \ + docker-buildx-plugin + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ No newline at end of file diff --git a/Dockerfile.dev-release b/.docker/Dockerfile.dev-release similarity index 68% rename from Dockerfile.dev-release rename to .docker/Dockerfile.dev-release index 48e3f47e72..33aa9c5d27 100644 --- a/Dockerfile.dev-release +++ b/.docker/Dockerfile.dev-release @@ -1,4 +1,4 @@ -FROM debian:stable-slim +FROM docker.io/debian:stable-slim WORKDIR /mm2 COPY target/ci/mm2 /usr/local/bin/mm2 EXPOSE 7783 diff --git a/Dockerfile.i686-linux-android b/.docker/Dockerfile.i686-linux-android similarity index 95% rename from Dockerfile.i686-linux-android rename to .docker/Dockerfile.i686-linux-android index 01ec36f3ec..76717f40b9 100644 --- a/Dockerfile.i686-linux-android +++ b/.docker/Dockerfile.i686-linux-android @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM docker.io/ubuntu:16.04 RUN apt-get update && \ apt-get install -y --no-install-recommends \ @@ -23,7 +23,7 @@ RUN apt-get update && \ libc6-dev-i386 && \ apt-get clean -COPY android-ndk.sh / +COPY ./scripts/ci/android-ndk.sh / RUN bash /android-ndk.sh x86 21 ENV PATH=$PATH:/android-ndk/bin ENV RUSTFMT=rustfmt diff --git a/Dockerfile.parity.dev b/.docker/Dockerfile.parity.dev similarity index 72% rename from Dockerfile.parity.dev rename to .docker/Dockerfile.parity.dev index 8dddf5c208..92abb4da3c 100644 --- a/Dockerfile.parity.dev +++ b/.docker/Dockerfile.parity.dev @@ -1,10 +1,10 @@ # Setup Ethereum dev blockchain with pre-deployed swap contract and ERC20 token. # For more info check Parity docs: https://wiki.parity.io/Private-development-chain and chain config file: parity.dev.chain.json # Usage example: -# docker build . -f Dockerfile.parity.dev -t artempikulin/parity_dev_node +# docker build . -f .docker/Dockerfile.parity.dev -t artempikulin/parity_dev_node # docker run -p 8545:8545 artempikulin/parity_dev_node -FROM parity/parity:beta -COPY parity.dev.chain.json /home/parity/.local/share/io.parity.ethereum/chain.json +FROM docker.io/parity/parity:beta +COPY .docker/parity.dev.chain.json /home/parity/.local/share/io.parity.ethereum/chain.json USER root RUN chmod -R 777 /home/parity/.local/share/io.parity.ethereum USER parity diff --git a/Dockerfile.release b/.docker/Dockerfile.release similarity index 69% rename from Dockerfile.release rename to .docker/Dockerfile.release index 0b07d7f3a0..8de75301d6 100644 --- a/Dockerfile.release +++ b/.docker/Dockerfile.release @@ -1,4 +1,4 @@ -FROM debian:stable-slim +FROM docker.io/debian:stable-slim WORKDIR /mm2 COPY target/release/mm2 /usr/local/bin/mm2 EXPOSE 7783 diff --git a/Dockerfile.ubuntu.ci b/.docker/Dockerfile.ubuntu.ci similarity index 95% rename from Dockerfile.ubuntu.ci rename to .docker/Dockerfile.ubuntu.ci index 028117edd7..fc759dbcca 100644 --- a/Dockerfile.ubuntu.ci +++ b/.docker/Dockerfile.ubuntu.ci @@ -1,4 +1,4 @@ -FROM ubuntu:xenial-20151218.1 +FROM docker.io/ubuntu:xenial-20151218.1 RUN \ apt-get update &&\ diff --git a/Dockerfile.x86_64-linux-android b/.docker/Dockerfile.x86_64-linux-android similarity index 95% rename from Dockerfile.x86_64-linux-android rename to .docker/Dockerfile.x86_64-linux-android index 54b61089f4..d404629908 100644 --- a/Dockerfile.x86_64-linux-android +++ b/.docker/Dockerfile.x86_64-linux-android @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM docker.io/ubuntu:16.04 RUN apt-get update && \ apt-get install -y --no-install-recommends \ @@ -24,7 +24,7 @@ RUN apt-get update && \ libc6-dev-i386 && \ apt-get clean -COPY android-ndk.sh / +COPY ./scripts/ci/android-ndk.sh / RUN bash /android-ndk.sh x86_64 21 ENV PATH=$PATH:/android-ndk/bin ENV RUSTFMT=rustfmt diff --git a/parity.dev.chain.json b/.docker/parity.dev.chain.json similarity index 100% rename from parity.dev.chain.json rename to .docker/parity.dev.chain.json diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml index 900048e6ae..e43b91bfe6 100644 --- a/.github/workflows/dev-build.yml +++ b/.github/workflows/dev-build.yml @@ -1,11 +1,8 @@ name: Development builds on: push: - branches: - - dev - pull_request: - branches: - - dev + branches-ignore: + - main concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -19,9 +16,18 @@ env: jobs: linux-x86-64: timeout-minutes: 30 - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + container: komodoofficial/ci-container:latest steps: - uses: actions/checkout@v3 + + - name: pre scripts for ci container + run: | + git config --global --add safe.directory /__w/atomicDEX-API/atomicDEX-API + echo "/bin" >> $GITHUB_PATH + echo "/usr/bin" >> $GITHUB_PATH + echo "/root/.cargo/bin" >> $GITHUB_PATH + - name: Install toolchain run: | rustup toolchain install nightly-2022-10-29 --no-self-update --profile=minimal @@ -42,6 +48,9 @@ jobs: cargo build --bin mm2 --profile ci - name: Compress build output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} run: | NAME="mm2_$COMMIT_HASH-linux-x86-64.zip" zip $NAME target/ci/mm2 -j @@ -49,6 +58,9 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} uses: garygrossgarten/github-action-scp@release with: host: ${{ secrets.FILE_SERVER_HOST }} @@ -59,14 +71,14 @@ jobs: remote: "/uploads/${{ env.BRANCH_NAME }}" - name: Login to dockerhub - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/dev' run: docker login --username ${{ secrets.DOCKER_HUB_USERNAME }} --password ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} docker.io - name: Build and push container image - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/dev' run: | CONTAINER_TAG="dev-$COMMIT_HASH" - docker build -t komodoofficial/atomicdexapi:"$CONTAINER_TAG" -t komodoofficial/atomicdexapi:dev-latest -f Dockerfile.dev-release . + docker build -t komodoofficial/atomicdexapi:"$CONTAINER_TAG" -t komodoofficial/atomicdexapi:dev-latest -f .docker/Dockerfile.dev-release . docker push komodoofficial/atomicdexapi:"$CONTAINER_TAG" docker push komodoofficial/atomicdexapi:dev-latest @@ -95,6 +107,9 @@ jobs: cargo build --bin mm2 --profile ci --target x86_64-apple-darwin - name: Compress build output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} run: | NAME="mm2_$COMMIT_HASH-mac-x86-64.zip" zip $NAME target/x86_64-apple-darwin/ci/mm2 -j @@ -102,6 +117,9 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} uses: garygrossgarten/github-action-scp@release with: host: ${{ secrets.FILE_SERVER_HOST }} @@ -138,6 +156,9 @@ jobs: cargo build --bin mm2 --profile ci - name: Compress build output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} run: | $NAME="mm2_$Env:COMMIT_HASH-win-x86-64.zip" 7z a $NAME .\target\ci\mm2.exe .\target\ci\*.dll @@ -145,6 +166,9 @@ jobs: mv $NAME ./$Env:BRANCH_NAME/ - name: Upload output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} uses: garygrossgarten/github-action-scp@release with: host: ${{ secrets.FILE_SERVER_HOST }} @@ -179,6 +203,9 @@ jobs: cargo rustc --target x86_64-apple-darwin --lib --profile ci --package mm2_bin_lib --crate-type=staticlib - name: Compress build output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} run: | NAME="mm2_$COMMIT_HASH-mac-dylib-x86-64.zip" mv target/x86_64-apple-darwin/ci/libmm2lib.a target/x86_64-apple-darwin/ci/libmm2.a @@ -187,6 +214,9 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} uses: garygrossgarten/github-action-scp@release with: host: ${{ secrets.FILE_SERVER_HOST }} @@ -198,9 +228,18 @@ jobs: wasm: timeout-minutes: 30 - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + container: komodoofficial/ci-container:latest steps: - uses: actions/checkout@v3 + + - name: pre scripts for ci container + run: | + git config --global --add safe.directory /__w/atomicDEX-API/atomicDEX-API + echo "/bin" >> $GITHUB_PATH + echo "/usr/bin" >> $GITHUB_PATH + echo "/root/.cargo/bin" >> $GITHUB_PATH + - name: Install toolchain run: | rustup toolchain install nightly-2022-10-29 --no-self-update --profile=minimal @@ -208,7 +247,7 @@ jobs: rustup target add wasm32-unknown-unknown - name: Install wasm-pack - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | bash -s -- -y - name: Calculate commit hash for PR commit if: github.event_name == 'pull_request' @@ -225,6 +264,9 @@ jobs: wasm-pack build mm2src/mm2_bin_lib --target web --out-dir ../../target/target-wasm-release - name: Compress build output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} run: | NAME="mm2_$COMMIT_HASH-wasm.zip" zip $NAME ./target/target-wasm-release/mm2lib_bg.wasm ./target/target-wasm-release/mm2lib.js ./target/target-wasm-release/snippets -j @@ -232,6 +274,9 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} uses: garygrossgarten/github-action-scp@release with: host: ${{ secrets.FILE_SERVER_HOST }} @@ -267,6 +312,9 @@ jobs: cargo rustc --target aarch64-apple-ios --lib --profile ci --package mm2_bin_lib --crate-type=staticlib - name: Compress build output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} run: | NAME="mm2_$COMMIT_HASH-ios-aarch64.zip" mv target/aarch64-apple-ios/ci/libmm2lib.a target/aarch64-apple-ios/ci/libmm2.a @@ -275,6 +323,9 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} uses: garygrossgarten/github-action-scp@release with: host: ${{ secrets.FILE_SERVER_HOST }} @@ -286,9 +337,18 @@ jobs: android-aarch64: timeout-minutes: 30 - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + container: komodoofficial/ci-container:latest steps: - uses: actions/checkout@v3 + + - name: pre scripts for ci container + run: | + git config --global --add safe.directory /__w/atomicDEX-API/atomicDEX-API + echo "/bin" >> $GITHUB_PATH + echo "/usr/bin" >> $GITHUB_PATH + echo "/root/.cargo/bin" >> $GITHUB_PATH + - name: Install toolchain run: | rustup toolchain install nightly-2022-10-29 --no-self-update --profile=minimal @@ -296,7 +356,7 @@ jobs: rustup target add aarch64-linux-android - name: Setup NDK - run: ./android-ndk.sh x86 21 + run: ./scripts/ci/android-ndk.sh x86 21 - name: Calculate commit hash for PR commit if: github.event_name == 'pull_request' @@ -315,6 +375,9 @@ jobs: CC_aarch64_linux_android=aarch64-linux-android21-clang CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=aarch64-linux-android21-clang cargo rustc --target=aarch64-linux-android --lib --profile ci --crate-type=staticlib --package mm2_bin_lib - name: Compress build output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} run: | NAME="mm2_$COMMIT_HASH-android-aarch64.zip" mv target/aarch64-linux-android/ci/libmm2lib.a target/aarch64-linux-android/ci/libmm2.a @@ -323,6 +386,9 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} uses: garygrossgarten/github-action-scp@release with: host: ${{ secrets.FILE_SERVER_HOST }} @@ -334,9 +400,18 @@ jobs: android-armv7: timeout-minutes: 30 - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + container: komodoofficial/ci-container:latest steps: - uses: actions/checkout@v3 + + - name: pre scripts for ci container + run: | + git config --global --add safe.directory /__w/atomicDEX-API/atomicDEX-API + echo "/bin" >> $GITHUB_PATH + echo "/usr/bin" >> $GITHUB_PATH + echo "/root/.cargo/bin" >> $GITHUB_PATH + - name: Install toolchain run: | rustup toolchain install nightly-2022-10-29 --no-self-update --profile=minimal @@ -344,7 +419,7 @@ jobs: rustup target add armv7-linux-androideabi - name: Setup NDK - run: ./android-ndk.sh x86 21 + run: ./scripts/ci/android-ndk.sh x86 21 - name: Calculate commit hash for PR commit if: github.event_name == 'pull_request' @@ -363,6 +438,9 @@ jobs: CC_armv7_linux_androideabi=armv7a-linux-androideabi21-clang CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=armv7a-linux-androideabi21-clang cargo rustc --target=armv7-linux-androideabi --lib --profile ci --crate-type=staticlib --package mm2_bin_lib - name: Compress build output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} run: | NAME="mm2_$COMMIT_HASH-android-armv7.zip" mv target/armv7-linux-androideabi/ci/libmm2lib.a target/armv7-linux-androideabi/ci/libmm2.a @@ -371,6 +449,9 @@ jobs: mv $NAME ./$BRANCH_NAME/ - name: Upload output + env: + AVAILABLE: ${{ secrets.FILE_SERVER_KEY }} + if: ${{ env.AVAILABLE != '' }} uses: garygrossgarten/github-action-scp@release with: host: ${{ secrets.FILE_SERVER_HOST }} @@ -382,13 +463,21 @@ jobs: deployment-commitment: - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/dev' needs: linux-x86-64 timeout-minutes: 15 - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + container: komodoofficial/ci-container:latest steps: - uses: actions/checkout@v2 + - name: pre scripts for ci container + run: | + git config --global --add safe.directory /__w/atomicDEX-API/atomicDEX-API + echo "/bin" >> $GITHUB_PATH + echo "/usr/bin" >> $GITHUB_PATH + echo "/root/.cargo/bin" >> $GITHUB_PATH + - name: Calculate commit hash for PR commit if: github.event_name == 'pull_request' run: echo "COMMIT_HASH=$(git rev-parse --short=7 ${{ github.event.pull_request.head.sha }})" >> $GITHUB_ENV diff --git a/.github/workflows/fmt-and-lint.yml b/.github/workflows/fmt-and-lint.yml index b9c5c92701..09ab32ba8b 100644 --- a/.github/workflows/fmt-and-lint.yml +++ b/.github/workflows/fmt-and-lint.yml @@ -1,13 +1,5 @@ name: Format and Lint -on: - push: - branches: - - main - - dev - pull_request: - branches: - - main - - dev +on: [push] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 9cf009f810..25b0700948 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -16,9 +16,18 @@ env: jobs: linux-x86-64: timeout-minutes: 60 - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + container: komodoofficial/ci-container:latest steps: - uses: actions/checkout@v3 + + - name: pre scripts for ci container + run: | + git config --global --add safe.directory /__w/atomicDEX-API/atomicDEX-API + echo "/bin" >> $GITHUB_PATH + echo "/usr/bin" >> $GITHUB_PATH + echo "/root/.cargo/bin" >> $GITHUB_PATH + - name: Install toolchain run: | rustup toolchain install nightly-2022-10-29 --no-self-update --profile=minimal @@ -61,7 +70,7 @@ jobs: - name: Build and push container image run: | export CONTAINER_TAG=$(./target/release/mm2 --version | awk '{print $3}') - docker build -t komodoofficial/atomicdexapi:"$CONTAINER_TAG" -t komodoofficial/atomicdexapi:main-latest -f Dockerfile.release . + docker build -t komodoofficial/atomicdexapi:"$CONTAINER_TAG" -t komodoofficial/atomicdexapi:main-latest -f .docker/Dockerfile.release . docker push komodoofficial/atomicdexapi:"$CONTAINER_TAG" docker push komodoofficial/atomicdexapi:main-latest @@ -193,9 +202,18 @@ jobs: wasm: timeout-minutes: 60 - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + container: komodoofficial/ci-container:latest steps: - uses: actions/checkout@v3 + + - name: pre scripts for ci container + run: | + git config --global --add safe.directory /__w/atomicDEX-API/atomicDEX-API + echo "/bin" >> $GITHUB_PATH + echo "/usr/bin" >> $GITHUB_PATH + echo "/root/.cargo/bin" >> $GITHUB_PATH + - name: Install toolchain run: | rustup toolchain install nightly-2022-10-29 --no-self-update --profile=minimal @@ -203,7 +221,7 @@ jobs: rustup target add wasm32-unknown-unknown - name: Install wasm-pack - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | bash -s -- -y - name: Calculate commit hash for PR commit if: github.event_name == 'pull_request' @@ -281,9 +299,18 @@ jobs: android-aarch64: timeout-minutes: 60 - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + container: komodoofficial/ci-container:latest steps: - uses: actions/checkout@v3 + + - name: pre scripts for ci container + run: | + git config --global --add safe.directory /__w/atomicDEX-API/atomicDEX-API + echo "/bin" >> $GITHUB_PATH + echo "/usr/bin" >> $GITHUB_PATH + echo "/root/.cargo/bin" >> $GITHUB_PATH + - name: Install toolchain run: | rustup toolchain install nightly-2022-10-29 --no-self-update --profile=minimal @@ -291,7 +318,7 @@ jobs: rustup target add aarch64-linux-android - name: Setup NDK - run: ./android-ndk.sh x86 21 + run: ./scripts/ci/android-ndk.sh x86 21 - name: Calculate commit hash for PR commit if: github.event_name == 'pull_request' @@ -329,9 +356,18 @@ jobs: android-armv7: timeout-minutes: 60 - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + container: komodoofficial/ci-container:latest steps: - uses: actions/checkout@v3 + + - name: pre scripts for ci container + run: | + git config --global --add safe.directory /__w/atomicDEX-API/atomicDEX-API + echo "/bin" >> $GITHUB_PATH + echo "/usr/bin" >> $GITHUB_PATH + echo "/root/.cargo/bin" >> $GITHUB_PATH + - name: Install toolchain run: | rustup toolchain install nightly-2022-10-29 --no-self-update --profile=minimal @@ -339,7 +375,7 @@ jobs: rustup target add armv7-linux-androideabi - name: Setup NDK - run: ./android-ndk.sh x86 21 + run: ./scripts/ci/android-ndk.sh x86 21 - name: Calculate commit hash for PR commit if: github.event_name == 'pull_request' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5cd82b5311..5ff6f0e491 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,13 +1,5 @@ name: Test -on: - push: - branches: - - main - - dev - pull_request: - branches: - - main - - dev +on: [push] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} diff --git a/.gitignore b/.gitignore index a29a345ca0..db90f0d053 100755 --- a/.gitignore +++ b/.gitignore @@ -15,13 +15,12 @@ DB/* .env.client .env.seed -etomic_build/client/DB -etomic_build/client/stats.log -etomic_build/client/unparsed.txt -etomic_build/seed/DB -etomic_build/seed/stats.log -etomic_build/seed/unparsed.txt -iguana/exchanges/manychains +scripts/mm2/client/DB +scripts/mm2/client/stats.log +scripts/mm2/client/unparsed.txt +scripts/mm2/seed/DB +scripts/mm2/seed/stats.log +scripts/mm2/seed/unparsed.txt /marketmaker_depends /x64 @@ -40,7 +39,6 @@ iguana/exchanges/manychains /MM_VERSION /MM_VERSION.tmp /target -/iguana/exchanges/etomicswap/target # Subcrate artefacts /mm2src/*/target @@ -66,4 +64,7 @@ MM2.json [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] -[._]sw[a-p] \ No newline at end of file +[._]sw[a-p] + +# mergetool +*.orig \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 2be033cc91..0000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,110 +0,0 @@ -stages: - - initial-build - - post-build - -variables: - GIT_CLEAN_FLAGS: none - -android-armv7: - tags: - - android - - armv7 - stage: initial-build - script: - - . $HOME/.cargo/env - - export PATH=$PATH:/home/azureagent/android-ndk/arch-ndk/armv7/android-ndk/bin - - CC_armv7_linux_androideabi=armv7a-linux-androideabi21-clang CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=armv7a-linux-androideabi21-clang cargo build --features native --target=armv7-linux-androideabi --release --lib - artifacts: - paths: - - target/armv7-linux-androideabi/release/libmm2.a - -android-aarch64: - tags: - - android - - aarch64 - stage: initial-build - script: - - . $HOME/.cargo/env - - export PATH=$PATH:/home/azureagent/android-ndk/arch-ndk/aarch64/android-ndk/bin/ - - CC_aarch64_linux_android=aarch64-linux-android21-clang CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=aarch64-linux-android21-clang cargo build --features native --target=aarch64-linux-android --release --lib - artifacts: - paths: - - target/aarch64-linux-android/release/libmm2.a - -android-x86_64: - tags: - - android - - x86_64 - stage: initial-build - script: - - . $HOME/.cargo/env - - export PATH=$PATH:/home/azureagent/android-ndk/arch-ndk/x86_64/android-ndk/bin - - CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=x86_64-linux-android21-clang CC_x86_64_linux_android=x86_64-linux-android21-clang cargo build --features native --target=x86_64-linux-android --release --lib - artifacts: - paths: - - target/x86_64-linux-android/release/libmm2.a - -android-i686: - tags: - - android - - i686 - stage: initial-build - script: - - . $HOME/.cargo/env - - export PATH=$PATH:/home/azureagent/android-ndk/arch-ndk/i686/android-ndk/bin - - CARGO_TARGET_I686_LINUX_ANDROID_LINKER=i686-linux-android21-clang CC_i686_linux_android=i686-linux-android21-clang cargo build --features native --target=i686-linux-android --release --lib - artifacts: - paths: - - target/i686-linux-android/release/libmm2.a - -macos-aarch64: - tags: - - mac - - aarch64 - stage: post-build - needs: - - job: macos-build - script: - - cp /Users/hetzner/GitLab/archive/aarch64/libmm2.a . - artifacts: - paths: - - libmm2.a - -#macos-x86_64: -# tags: -# - mac -# - x86_64 -# stage: post-build -# needs: -# - job: macos-build -# script: -# - cp /Users/administrator/GitLab/archive/x86_64/libmm2.a . -# artifacts: -# paths: -# - libmm2.a - -#macos-universal: -# tags: -# - mac -# - universal -# stage: post-build -# needs: -# - job: macos-build -# script: -# - cp /Users/administrator/GitLab/archive/universal/libmm2.a . -# artifacts: -# paths: -# - libmm2.a - -macos-build: - tags: - - mac - - universal - stage: initial-build - script: -# - cargo lipo --features native --targets x86_64-apple-ios,aarch64-apple-ios --release -# TMP disable universal builds until x86_64 build is fixed on server - - IPHONEOS_DEPLOYMENT_TARGET=9.0 cargo build --target aarch64-apple-ios --release --lib - - cp target/aarch64-apple-ios/release/libmm2.a /Users/hetzner/GitLab/archive/aarch64/libmm2.a -# - cp target/universal/release/libmm2.a /Users/hetzner/GitLab/archive/universal/libmm2.a -# - cp target/x86_64-apple-ios/release/libmm2.a /Users/hetzner/GitLab/archive/x86_64/libmm2.a diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ae1785eb0..6e23dc19aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## v1.0.2-beta - 2023-04-11 + +**Features:** +- `adex-cli` command line utility was introduced that supplies commands: `init`, `start`, `stop`, `status` [#1729](https://github.com/KomodoPlatform/atomicDEX-API/pull/1729) + +**Enhancements/Fixes:** +- CI/CD workflow logics are improved [#1736](https://github.com/KomodoPlatform/atomicDEX-API/pull/1736) +- Project root is simplified/refactored [#1738](https://github.com/KomodoPlatform/atomicDEX-API/pull/1738) +- Created base image to provide more glibc compatible pre-built binaries for linux [#1741](https://github.com/KomodoPlatform/atomicDEX-API/pull/1741) +- Set default log level as "info" [#1747](https://github.com/KomodoPlatform/atomicDEX-API/pull/1747) +- Refactor `native_log` module [#1751](https://github.com/KomodoPlatform/atomicDEX-API/pull/1751) + - implement stdout/err streaming to persistent file without dependencies + - Add new parameter `silent_console` to mm conf + + ## v1.0.1-beta - 2023-03-17 **Features:** diff --git a/Cargo.lock b/Cargo.lock index de68861053..c062b19c4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,12 +155,6 @@ version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" -[[package]] -name = "arc-swap" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8" - [[package]] name = "arrayref" version = "0.3.6" @@ -284,7 +278,7 @@ dependencies = [ "byteorder 1.4.3", "bytes 0.5.6", "common", - "env_logger 0.7.1", + "env_logger", "fnv", "futures 0.3.15", "futures_codec", @@ -1181,6 +1175,8 @@ dependencies = [ "cfg-if 1.0.0", "chrono", "crossbeam 0.8.2", + "derive_more", + "env_logger", "findshlibs", "fnv", "futures 0.1.29", @@ -1199,11 +1195,11 @@ dependencies = [ "libc", "lightning", "log 0.4.14", - "log4rs", "parking_lot 0.12.0", "parking_lot_core 0.6.2", "primitive-types", "rand 0.7.3", + "regex", "ser_error", "ser_error_derive", "serde", @@ -2099,19 +2095,6 @@ dependencies = [ "syn 1.0.95", ] -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime 1.3.0", - "log 0.4.14", - "regex", - "termcolor", -] - [[package]] name = "env_logger" version = "0.9.0" @@ -2119,7 +2102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" dependencies = [ "atty", - "humantime 2.1.0", + "humantime", "log 0.4.14", "regex", "termcolor", @@ -2965,15 +2948,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - [[package]] name = "humantime" version = "2.1.0" @@ -3924,31 +3898,6 @@ dependencies = [ "value-bag", ] -[[package]] -name = "log-mdc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" - -[[package]] -name = "log4rs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1572a880d1115ff867396eee7ae2bc924554225e67a0d3c85c745b3e60ca211" -dependencies = [ - "anyhow", - "arc-swap", - "chrono", - "derivative", - "fnv", - "libc", - "log 0.4.14", - "log-mdc", - "thiserror", - "thread-id", - "winapi", -] - [[package]] name = "lru" version = "0.7.5" @@ -4154,7 +4103,7 @@ dependencies = [ "atomicdex-gossipsub", "common", "derive_more", - "env_logger 0.7.1", + "env_logger", "futures 0.3.15", "futures-rustls 0.21.1", "getrandom 0.2.6", @@ -4179,7 +4128,7 @@ dependencies = [ [[package]] name = "mm2_bin_lib" -version = "1.0.1-beta" +version = "1.0.2-beta" dependencies = [ "chrono", "common", @@ -4355,7 +4304,6 @@ dependencies = [ "either", "enum-primitive-derive", "enum_from", - "env_logger 0.7.1", "ethereum-types", "futures 0.1.29", "futures 0.3.15", @@ -5421,8 +5369,6 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" dependencies = [ - "env_logger 0.7.1", - "log 0.4.14", "rand 0.7.3", "rand_core 0.5.1", ] @@ -6879,7 +6825,7 @@ version = "1.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942dc59fc9da66d178362051b658646b65dc11cea0bc804e4ecd2528d3c1279f" dependencies = [ - "env_logger 0.9.0", + "env_logger", "lazy_static", "log 0.4.14", ] @@ -6900,7 +6846,7 @@ version = "1.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9774cd8309f599797b1612731fbc56df6c612879ab4923a3dc7234400eea419" dependencies = [ - "env_logger 0.9.0", + "env_logger", "gethostname", "lazy_static", "log 0.4.14", @@ -7924,17 +7870,6 @@ dependencies = [ "syn 1.0.95", ] -[[package]] -name = "thread-id" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" -dependencies = [ - "libc", - "redox_syscall 0.1.56", - "winapi", -] - [[package]] name = "time" version = "0.1.43" diff --git a/Cargo.toml b/Cargo.toml index 421bdca2c6..388116abd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,10 @@ members = [ "mm2src/trezor", ] +exclude = [ + "mm2src/adex_cli" +] + # https://doc.rust-lang.org/beta/cargo/reference/features.html#feature-resolver-version-2 resolver = "2" diff --git a/README.md b/README.md index b5e9acaa39..40f82f6e81 100755 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ The AtomicDEX API core is open-source [atomic-swap](https://komodoplatform.com/en/academy/atomic-swaps/) software for seamless, decentralised, peer to peer trading between almost every blockchain asset in existence. This software works with propagation of orderbooks and swap states through the [libp2p](https://libp2p.io/) protocol and uses [Hash Time Lock Contracts (HTLCs)](https://en.bitcoinwiki.org/wiki/Hashed_Timelock_Contracts) for ensuring that the two parties in a swap either mutually complete a trade, or funds return to thier original owner. -There is no 3rd party intermediatary, no proxy tokens, and at all times users remain in sole possession of their private keys. +There is no 3rd party intermediary, no proxy tokens, and at all times users remain in sole possession of their private keys. A [well documented API](https://developers.komodoplatform.com/basic-docs/atomicdex/introduction-to-atomicdex.html) offers simple access to the underlying services using simple language agnostic JSON structured methods and parameters such that users can communicate with the core in a variety of methods such as [curl](https://developers.komodoplatform.com/basic-docs/atomicdex-api-legacy/buy.html) in CLI, or fully functioning [desktop and mobile applications](https://atomicdex.io/) like [AtomicDEX Desktop](https://github.com/KomodoPlatform/atomicDEX-Desktop). @@ -115,6 +115,7 @@ For example: The coins file contains information about the coins and tokens you want to trade. A regularly updated version is maintained in the [Komodo Platform coins repository](https://github.com/KomodoPlatform/coins/blob/master/coins). Pull Requests to add any coins not yet included are welcome. +To facilitate interoperability with the `mm2` service, there is the `adex-cli` command line utility. It provides a questionnaire initialization mode to set up the configuration and obtain the proper coin set through the internet. It can also be used to start or stop the service. ## Usage @@ -169,7 +170,7 @@ Refer to the [Komodo Developer Docs](https://developers.komodoplatform.com/basic ## Additional docs for developers -- [Contribution guide](./CONTRIBUTING.md) +- [Contribution guide](./docs/CONTRIBUTING.md) - [Setting up the environment to run the full tests suite](./docs/DEV_ENVIRONMENT.md) - [Git flow and general workflow](./docs/GIT_FLOW_AND_WORKING_PROCESS.md) - [Komodo Developer Docs](https://developers.komodoplatform.com/basic-docs/atomicdex/introduction-to-atomicdex.html) diff --git a/docs/ANDROID.md b/docs/ANDROID.md index 3a665493cf..75e4016650 100644 --- a/docs/ANDROID.md +++ b/docs/ANDROID.md @@ -27,19 +27,19 @@ The [Android NDK installer](https://github.com/rust-embedded/cross/tree/master/d #### armeabi-v7a ABI Docker image - (cd supernet && docker build --tag armv7-linux-androideabi-aga -f Dockerfile.armv7-linux-androideabi .) + (cd supernet && docker build --tag armv7-linux-androideabi-aga -f .docker/Dockerfile.armv7-linux-androideabi .) #### arm64-v8a ABI Docker image - (cd supernet && docker build --tag aarch64-linux-android-aga -f Dockerfile.aarch64-linux-android .) + (cd supernet && docker build --tag aarch64-linux-android-aga -f .docker/Dockerfile.aarch64-linux-android .) ### x86 ABI Docker image - (cd supernet && docker build --tag i686-linux-android-aga -f Dockerfile.i686-linux-android .) + (cd supernet && docker build --tag i686-linux-android-aga -f .docker/Dockerfile.i686-linux-android .) ### x86_64 ABI Docker image - (cd supernet && docker build --tag x86_64-linux-android-aga -f Dockerfile.x86_64-linux-android .) + (cd supernet && docker build --tag x86_64-linux-android-aga -f .docker/Dockerfile.x86_64-linux-android .) ### Setup the NDK_HOME variable diff --git a/docs/ANDROID_CROSS_ON_M1_MAC.md b/docs/ANDROID_CROSS_ON_M1_MAC.md index 3d9321a49e..666e7be92c 100644 --- a/docs/ANDROID_CROSS_ON_M1_MAC.md +++ b/docs/ANDROID_CROSS_ON_M1_MAC.md @@ -2,7 +2,7 @@ 1. Ensure that your terminal is added to `Developer tools` in MacOS Security & Privacy settings. 2. The cross-compilation requires Android NDK version 21. Custom brew cask file is located at the root of this repo. -3. Install android-ndk by `brew install --cask android-ndk.rb` or other preferred way. +3. Install android-ndk by `brew install --cask ./scripts/ci/android-ndk.rb` or other preferred way. 4. Add Android targets via rustup ```shell rustup target add armv7-linux-androideabi diff --git a/CONTRIBUTING.md b/docs/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to docs/CONTRIBUTING.md diff --git a/docs/RASPBERRY_PI4_CROSS.md b/docs/RASPBERRY_PI4_CROSS.md index b4cf3ca072..91bb6a27dd 100644 --- a/docs/RASPBERRY_PI4_CROSS.md +++ b/docs/RASPBERRY_PI4_CROSS.md @@ -4,6 +4,6 @@ rustup install nightly rustup default nightly ``` 2. Install cross: `cargo install cross`. -3. Build the Docker image for cross compilation: `docker build -f Dockerfile.armv7-unknown-linux-gnueabihf -t mm2-armv7-unknown-linux-gnueabihf .` +3. Build the Docker image for cross compilation: `docker build -f .docker/Dockerfile.armv7-unknown-linux-gnueabihf -t mm2-armv7-unknown-linux-gnueabihf .` 4. Build mm2: `cross build --target armv7-unknown-linux-gnueabihf` or `cross build --target armv7-unknown-linux-gnueabihf --release` for release build. 5. The binary path will be `target/armv7-unknown-linux-gnueabihf/debug/mm2` or `target/armv7-unknown-linux-gnueabihf/release/mm2` for release build. \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000..e33942a767 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,3 @@ +# AtomicDEX-API Examples + +This directory contains example implementation/use cases of AtomicDEX-API \ No newline at end of file diff --git a/wasm_build/README.md b/examples/wasm/README.md similarity index 100% rename from wasm_build/README.md rename to examples/wasm/README.md diff --git a/wasm_build/index.html b/examples/wasm/index.html similarity index 100% rename from wasm_build/index.html rename to examples/wasm/index.html diff --git a/wasm_build/script.js b/examples/wasm/script.js similarity index 100% rename from wasm_build/script.js rename to examples/wasm/script.js diff --git a/iguana/tools/getos.pyc b/iguana/tools/getos.pyc deleted file mode 100755 index 72f8ff3ebe..0000000000 Binary files a/iguana/tools/getos.pyc and /dev/null differ diff --git a/js/Dockerfile b/js/Dockerfile deleted file mode 100644 index b6cba127a4..0000000000 --- a/js/Dockerfile +++ /dev/null @@ -1,75 +0,0 @@ -# Demo of driving the peers communication from the portable layer of the code. -# -# docker build --file js/Dockerfile --tag mm2-peers-demo . -# docker run -ti -e BOB_PASSPHRASE -e ALICE_PASSPHRASE mm2-peers-demo - -# -- Begins with parts copied from the MM Dockerfile in order to share those filesystem layers -- - -FROM ubuntu:xenial - -RUN \ - apt-get update &&\ - apt-get install -y git build-essential libssl-dev wget &&\ - apt-get install -y cmake &&\ - # https://github.com/rust-lang/rust-bindgen/blob/master/book/src/requirements.md#debian-based-linuxes - apt-get install -y llvm-3.9-dev libclang-3.9-dev clang-3.9 &&\ - # openssl-sys requirements, cf. https://crates.io/crates/openssl-sys - apt-get install -y pkg-config libssl-dev &&\ - apt-get clean - -RUN \ - wget -O- https://sh.rustup.rs > /tmp/rustup-init.sh &&\ - sh /tmp/rustup-init.sh -y --default-toolchain none &&\ - . /root/.cargo/env &&\ - rustup set profile minimal &&\ - rustup install nightly-2020-02-01 &&\ - rustup default nightly-2020-02-01 &&\ - # It seems that bindgen won't prettify without it: - rustup component add rustfmt-preview &&\ - rm -f /tmp/rustup-init.sh - -ENV PATH="/root/.cargo/bin:${PATH}" - -# First 7 characters of the commit ID. -ENV MM_VERSION="737ae6e" - -RUN cd /tmp &&\ - wget https://api.github.com/repos/KomodoPlatform/atomicDEX-API/tarball/$MM_VERSION &&\ - tar -xzf $MM_VERSION &&\ - ls &&\ - mv KomodoPlatform-atomicDEX-API-$MM_VERSION /mm2 &&\ - rm $MM_VERSION &&\ - echo $MM_VERSION > /mm2/MM_VERSION - -RUN cd /mm2 && cargo fetch - -# -- End of the shared parts -- - -RUN cd /tmp &&\ - wget https://nodejs.org/dist/v12.13.0/node-v12.13.0-linux-x64.tar.xz &&\ - tar -xJf node-v12.13.0-linux-x64.tar.xz &&\ - cd /tmp/node-v12.13.0-linux-x64 &&\ - cp -r * /usr/local/ &&\ - cd /tmp &&\ - rm -rf node* &&\ - node --version - -RUN rustup target add wasm32-unknown-unknown - -RUN cargo install rustfilt - -# This will overwrite the Git version with the local one. -# Only needed when we're developing or changing something locally. -#COPY . /mm2 - -RUN cd /mm2 &&\ - cargo build --target=wasm32-unknown-unknown --release &&\ - mv target/wasm32-unknown-unknown/release/mm2.wasm js/ &&\ - cargo build --bin mm2 &&\ - cp target/debug/mm2 js/ &&\ - rm -rf target - -RUN cd /mm2/js &&\ - npm install > npm-install.log 2>&1 - -CMD cd /mm2/js && node wasm-run.js 2>&1 | rustfilt diff --git a/js/package-lock.json b/js/package-lock.json deleted file mode 100644 index eb2ca9c56a..0000000000 --- a/js/package-lock.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "name": "wasm-run", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "bencode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/bencode/-/bencode-2.0.1.tgz", - "integrity": "sha512-2uhEl8FdjSBUyb69qDTgOEeeqDTa+n3yMQzLW0cOzNf1Ow5bwcg3idf+qsWisIKRH8Bk8oC7UXL8irRcPA8ZEQ==", - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "crc-32": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", - "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", - "requires": { - "exit-on-epipe": "~1.0.1", - "printj": "~1.1.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "electrum-client": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/electrum-client/-/electrum-client-0.0.6.tgz", - "integrity": "sha512-u6B63JbWuufkPr7/DVPfCtMbefeqU+R6oYlwUyiZ7cGMUgbjbJYBk8+wQnJm3K+uXOupoDeyOgVHoFjz5vMwrQ==" - }, - "exit-on-epipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", - "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "printj": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", - "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - } - } -} diff --git a/js/package.json b/js/package.json deleted file mode 100644 index 31781bb7cb..0000000000 --- a/js/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "wasm-run", - "version": "1.0.0", - "description": "", - "main": "wasm-run.js", - "scripts": {}, - "author": "", - "license": "GPL-2.0-only", - "dependencies": { - "bencode": "^2.0.1", - "crc-32": "^1.2.0", - "cross-spawn": "^6.0.5", - "electrum-client": "0.0.6" - } -} diff --git a/js/wasm-build.sh b/js/wasm-build.sh deleted file mode 100644 index 255be3bdf7..0000000000 --- a/js/wasm-build.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/dash -# Run with `dash js/wasm-build.sh`. -set -ex - -cargo build --bin mm2 -ln -f target/debug/mm2 js/mm2 - -cargo build --target=wasm32-unknown-unknown --release -ln -f target/wasm32-unknown-unknown/release/mm2.wasm js/mm2.wasm diff --git a/js/wasm-run.js b/js/wasm-run.js deleted file mode 100644 index 4c0262912c..0000000000 --- a/js/wasm-run.js +++ /dev/null @@ -1,316 +0,0 @@ -// Run with: -// -// dash js/wasm-build.sh && (cd js && node wasm-run.js 2>&1 | rustfilt) - -const bencode = require('bencode') -const { Buffer } = require('buffer'); -const crc32 = require('crc-32'); // TODO: Calculate the checksum in Rust instead. -const ElectrumCli = require('electrum-client'); // https://www.npmjs.com/package/electrum-client -const fs = require('fs'); -const http = require('http'); // https://nodejs.org/dist/latest-v12.x/docs/api/http.html -const os = require('os'); -// https://nodejs.org/api/child_process.html -// https://www.npmjs.com/package/cross-spawn -const spawn = require('cross-spawn'); -// https://nodejs.org/api/worker_threads.html -// https://medium.com/@Trott/using-worker-threads-in-node-js-80494136dbb6 -// https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md -const worker_threads = require('worker_threads'); - -const snooze = ms => new Promise (resolve => setTimeout (resolve, ms)); - -const keepAliveAgent = new http.Agent ({keepAlive: true}); - -function from_utf8 (memory, ptr, len) { - const view = new Uint8Array (memory.buffer, ptr, len); - const utf8dec = new TextDecoder ('utf-8'); - return utf8dec.decode (view)} - -function to_utf8 (memory, rbuf, rcap, str) { - const encoder = new TextEncoder(); - const view = encoder.encode (str); - if (view.length > rcap) return -1; - const rbuf_slice = new Uint8Array (wasmShared.memory.buffer, rbuf, rcap); - for (let i = 0; i < view.length; ++i) rbuf_slice[i] = view[i]; - return view.length} - -function http_helper (helper, timeout_ms, payload, cb) { - const cs = crc32.buf (payload); - return http.request ({ - method: 'POST', - headers: { - 'Content-Type': 'application/octet-stream', - 'Content-Length': payload.length, - 'X-Helper-Checksum': cs - }, - hostname: '127.0.0.1', - port: 7783, - path: '/helper/' + helper, - agent: keepAliveAgent, - timeout: timeout_ms - }, cb) -} - -const wasmShared = {}; - -function registerCallback (f) { - for (;;) { - const ri = Math.ceil (Math.random() * 2147483647); - const ris = '' + ri; // TODO: Use a sparse array. - if (wasmShared.callbacks[ris] != null) continue; - wasmShared.callbacks[ris] = f; - return ri}} - -async function runWasm() { - // Wait for the helpers RPC server to start. - await snooze (500); - - const wasmBytes = fs.readFileSync ('mm2.wasm'); - const httpRequests = {}; - wasmShared.callbacks = {}; - wasmShared.electrums = {}; - const memory = new WebAssembly.Memory ({initial: 1, shared: true}); - const wasmEnv = { - //memory: memory, - call_back: function (cb_id, ptr, len) { - //console.log ('call_back', cb_id, 'invoked, ptr', ptr, 'len', len); - const cb_id_s = '' + cb_id; - const f = wasmShared.callbacks[cb_id_s]; - if (f != null) f (Buffer.from (wasmShared.memory.buffer.slice (ptr, ptr + len))); - delete wasmShared.callbacks[cb_id_s]}, - console_log: function (ptr, len) { - const decoded = from_utf8 (wasmShared.memory, ptr, len); - console.log (decoded)}, - date_now: function() {return Date.now()}, - host_env: function (ptr, len, rbuf, rcap) { - const name = from_utf8 (wasmShared.memory, ptr, len); - const v = process.env[name]; - if (v == null) return -1; - return to_utf8 (wasmShared.memory, rbuf, rcap, v)}, - host_electrum_connect: function (ptr, len) { - const args_s = from_utf8 (wasmShared.memory, ptr, len); - const args = JSON.parse (args_s); - const url = args.url; - const protocol = args.protocol.toLowerCase(); - const disable_cert_verification = args.disable_cert_verification; - const caps = /^(.*?):(\d+)$/.exec (url); - if (caps == null) return -1; - const host = caps[1]; - const port = Number (caps[2]); - if (protocol != 'tls' && protocol != 'tcp') return -2; - const ecl = new ElectrumCli (port, host, protocol); - var ri = 0, ris = ''; - for (;;) { - ri = Math.ceil (Math.random() * 2147483647); - ris = '' + ri; // TODO: Use a sparse array. - if (wasmShared.electrums[ris] == null) { - wasmShared.electrums[ris] = { - host: host, - port: port, - ecl: ecl, - connected: false, - replies: []}; - break}} - ecl.connect().then (_ => {wasmShared.electrums[ris].connected = true}); - // cf. https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-headers-subscribe - ecl.subscribe.on ('blockchain.headers.subscribe', (header) => { - console.log ('host_electrum_connect] TBD, Electrum header', header); - // TODO: Pass the `header` to Rust? - // The `header` looks like `[{hex: '040000', height: 175560}]` except the `hex` is large (seen ~3k). - }); - return ri}, - host_electrum_is_connected: function (ri) { - const ris = '' + ri; // TODO: Use a sparse array. - const en = wasmShared.electrums[ris]; - if (en == null) return -1; - return en.connected ? 1 : 0}, - host_electrum_request: function (ri, ptr, len) { - const ris = '' + ri; // TODO: Use a sparse array. - const en = wasmShared.electrums[ris]; - if (en == null) return -1; - const req_s = from_utf8 (wasmShared.memory, ptr, len); - const req = JSON.parse (req_s); // JsonRpcRequest - const id = Number (req.id); - if (id > 2147483647) throw new Error ('Electrum JsonRpcRequest id is too large'); - //console.log ('host_electrum_request] ri', ri, 'req:', req); - - en.ecl.request (req.method, req.params) - .then (res => { - //console.log ('host_electrum_request] ri', ri, 'req', req, '⇒', res); - const res_s = JSON.stringify (res); - //console.log ('res_s', res_s); - en.replies[id] = res_s; - wasmShared.exports.electrum_replied (ri, id)}) - .catch (err => { - wasmShared.exports.electrum_replied (ri, id)}); - return 0}, - host_electrum_reply: function (ri, id, rbuf, rcap) { - const ris = '' + ri; // TODO: Use a sparse array. - const en = wasmShared.electrums[ris]; - if (en == null) return -2; - const res_s = en.replies[id]; - if (res_s == null) return -3; - return to_utf8 (wasmShared.memory, rbuf, rcap, res_s)}, - host_ensure_dir_is_writable: function (ptr, len) { - const path = from_utf8 (wasmShared.memory, ptr, len); - // https://nodejs.org/docs/latest/api/fs.html#fs_fs_existssync_path - const exists = fs.existsSync (path); - if (exists) { - // https://nodejs.org/docs/latest/api/fs.html#fs_fs_statsync_path_options - const stats = fs.statSync (path); - if (!stats.isDirectory()) throw new Error ('path ' + path + ' is not a directory') - } else { - // https://nodejs.org/docs/latest/api/fs.html#fs_fs_mkdirsync_path_options - fs.mkdirSync (path, {recursive: true})} - return 0}, - host_read_dir: function (path_p, path_l, rbuf, rcap) { - const path = from_utf8 (wasmShared.memory, path_p, path_l); - const dir = fs.opendirSync (path); // NB: Needs NodeJS >= 12.12.0 - let entries = []; - for (;;) { - const en = dir.readSync(); - if (en == null) break; - if (!en.isFile()) continue; - const name = en.name; - const stats = fs.statSync (path + '/' + name); - const lm = Math.floor (stats.mtimeMs); - entries.push ([lm, name])} - const jens = JSON.stringify (entries); - console.log ('host_read_dir:', jens); - return to_utf8 (wasmShared.memory, rbuf, rcap, jens)}, - host_rm: function (ptr, len) { - const path = from_utf8 (wasmShared.memory, ptr, len); - fs.unlinkSync (path); - return 0}, - host_slurp: function (path_p, path_l, rbuf, rcap) { - const path = from_utf8 (wasmShared.memory, path_p, path_l); - if (!fs.existsSync (path)) return 0; - const content = fs.readFileSync (path, {encoding: 'utf8'}); - return to_utf8 (wasmShared.memory, rbuf, rcap, content)}, - host_write: function (path_p, path_l, ptr, len) { - const path = from_utf8 (wasmShared.memory, path_p, path_l); - const content = new Uint8Array (wasmShared.memory.buffer, ptr, len); - // https://nodejs.org/docs/latest/api/fs.html#fs_fs_writefilesync_file_data_options - fs.writeFileSync (path, content); - return 0}, - http_helper_check: function (http_request_id, rbuf, rcap) { - let ris = '' + http_request_id; // TODO: Use a sparse array. - if (httpRequests[ris] == null) return -1; - if (httpRequests[ris].buf == null) return -1; - const ben = { - status: httpRequests[ris].status, - ct: httpRequests[ris].ct, - cs: httpRequests[ris].cs, - body: httpRequests[ris].buf}; - const buf = bencode.encode (ben); - if (buf.length > rcap) return -buf.length; - const rbuf_slice = new Uint8Array (wasmShared.memory.buffer, rbuf, rcap); - for (let i = 0; i < buf.length; ++i) rbuf_slice[i] = buf[i]; - return buf.length}, - http_helper_if: function (helper_ptr, helper_len, payload_ptr, payload_len, timeout_ms) { - const helper = from_utf8 (wasmShared.memory, helper_ptr, helper_len); - //const payload = new Uint8Array (wasmShared.memory, payload_ptr, payload_len); - const payload = Buffer.from (wasmShared.memory.buffer.slice (payload_ptr, payload_ptr + payload_len)); - - // Find a random ID. - let ri, ris; - for (;;) { - ri = Math.ceil (Math.random() * 2147483647); - ris = '' + ri; // TODO: Use a sparse array. - if (httpRequests[ris] == null) { - httpRequests[ris] = {}; - break}} - - let chunks = []; - const req = http_helper (helper, timeout_ms, payload, (res) => { - res.on ('data', (chunk) => chunks.push (chunk)); - res.on ('end', () => { - let len = 0; - for (const chunk of chunks) {len += chunk.length} - if (res.headers['content-length'] != null && len != res.headers['content-length']) { - throw new Error ('Content-Length mismatch')} - const buf = new Uint8Array (len); - let pos = 0; - for (const chunk of chunks) { - for (let i = 0; i < chunk.length; ++i) { - buf[pos] = chunk[i]; - ++pos}} - if (pos != len) throw new Error ('length mismatch'); - httpRequests[ris].status = res.statusCode; - httpRequests[ris].ct = res.headers['content-type']; - httpRequests[ris].cs = res.headers['x-helper-checksum']; - httpRequests[ris].buf = buf; - wasmShared.exports.http_ready (ri)});}); - req.on ('error', function (err) { - httpRequests[ris].status = 0; - httpRequests[ris].ct = 'nodejs error'; - httpRequests[ris].buf = '' + err; - wasmShared.exports.http_ready (ri)}); - req.write (payload); - req.end(); - return ri}, //< request id - peers_drop_send_handler: function (shp1, shp2) { - const payload = bencode.encode ([shp1, shp2]); - const req = http_helper ('peers_drop_send_handler', 100, payload, (res) => {res.on ('end', () => {})}); - req.on ('error', function (_) {}); - req.write (payload); - req.end()}, - temp_dir: function (rbuf, rcap) {return to_utf8 (wasmShared.memory, rbuf, rcap, os.tmpdir())}}; - - const worker = new worker_threads.Worker ('./worker.js', { - // Fails with `DOMException [DataCloneError]: # could not be cloned.` - //workerData: memory - }); - worker.on ('error', (err) => {throw err}); - // Fails with `DOMException [DataCloneError]: # could not be cloned.` - //worker.on ('online', (_) => {worker.postMessage (memory)}); - - const wasmInstantiated = await WebAssembly.instantiate (wasmBytes, {env: wasmEnv}); - const exports = wasmInstantiated.instance.exports; - /** @type {WebAssembly.Memory} */ - wasmShared.memory = exports.memory; - wasmShared.exports = exports; - - const executor_i = setInterval (function() {exports.run_executor()}, 200); - - exports.set_panic_hook(); - - //await test_peers_dht(); - await trade_test_electrum_and_eth_coins(); - - clearInterval (executor_i)} - -async function test_peers_dht() { - const peers_check = exports.peers_check(); - console.log ('wasm-run] test_peers_dht…'); - const test_finished = {}; - const cb_id = registerCallback (r => test_finished.yep = true); - wasmShared.exports.test_peers_dht (cb_id); - while (!test_finished.yep) {await snooze (100)} - console.log ('wasm-run] test_peers_dht ✅')} - -async function trade_test_electrum_and_eth_coins() { - console.log ('wasm-run] trade_test_electrum_and_eth_coins…'); - const test_finished = {}; - const cb_id = registerCallback (r => test_finished.yep = true); - wasmShared.exports.trade_test_electrum_and_eth_coins (cb_id); - while (!test_finished.yep) {await snooze (100)} - console.log ('wasm-run] trade_test_electrum_and_eth_coins ✅')} - -function stop() { - const req = http.request ({ - method: 'POST', - hostname: '127.0.0.1', - port: 7783, - agent: keepAliveAgent, - }, (res) => {}); - req.on ('error', function (_) {}); - req.write ('{"method": "stop", "userpass": "pass"}'); - req.end()} - -// Start the native helpers. -const mm2 = spawn ('js/mm2', ['{"passphrase": "-", "rpc_password": "pass", "coins": []}'], {cwd: '..'}); -mm2.stdout.on ('data', (data) => console.log ('native] ' + String (data) .trim())); -mm2.stderr.on ('data', (data) => console.log ('native] ' + String (data) .trim())); - -runWasm().then (_ => stop()) .catch (ex => {console.log (ex); stop()}); diff --git a/js/worker.js b/js/worker.js deleted file mode 100644 index 7a6ec78f84..0000000000 --- a/js/worker.js +++ /dev/null @@ -1,3 +0,0 @@ -const worker_threads = require('worker_threads'); - -//console.log ('worker.js]', worker_threads.workerData); diff --git a/mm2src/adex_cli/Cargo.toml b/mm2src/adex_cli/Cargo.toml new file mode 100644 index 0000000000..2f1abdc0a0 --- /dev/null +++ b/mm2src/adex_cli/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "adex-cli" +version = "0.1.0" +edition = "2021" +authors = ["Rozhkov Dmitrii "] +description = "Provides a CLI interface and facilitates interoperating to komodo atomic dex through the mm2 service" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +clap = "2.33.3" +common = { path = "../common" } +derive_more = "0.99" +env_logger = "0.7.1" +gstuff = { version = "=0.7.4" , features = [ "nightly" ]} +inquire = "0.6" +log = "0.4" +mm2_net = { path = "../mm2_net" } +passwords = "3.1" +serde = "1.0" +serde_json = { version = "1", features = ["preserve_order", "raw_value"] } +sysinfo = "0.28" +tiny-bip39 = "0.8.0" +tokio = { version = "1.20", features = [ "macros" ] } + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3.3", features = ["processthreadsapi", "winnt"] } diff --git a/mm2src/adex_cli/src/cli.rs b/mm2src/adex_cli/src/cli.rs new file mode 100644 index 0000000000..fdbf65161d --- /dev/null +++ b/mm2src/adex_cli/src/cli.rs @@ -0,0 +1,113 @@ +use clap::{App, Arg, SubCommand}; +use log::error; +use std::env; + +use crate::scenarios::{get_status, init, start_process, stop_process}; + +enum Command { + Init { + mm_coins_path: String, + mm_conf_path: String, + }, + Start { + mm_conf_path: Option, + mm_coins_path: Option, + mm_log: Option, + }, + Stop, + Status, +} + +pub fn process_cli() { + let mut app = App::new(env!("CARGO_PKG_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .author(env!("CARGO_PKG_AUTHORS")) + .about(env!("CARGO_PKG_DESCRIPTION")) + .subcommand( + SubCommand::with_name("init") + .about("Initialize predefined mm2 coin set and configuration") + .arg( + Arg::with_name("mm-coins-path") + .long("mm-coins-path") + .value_name("FILE") + .help("coin set file path") + .default_value("coins"), + ) + .arg( + Arg::with_name("mm-conf-path") + .long("mm-conf-path") + .value_name("FILE") + .help("mm2 configuration file path") + .default_value("MM2.json"), + ), + ) + .subcommand( + SubCommand::with_name("start") + .about("Start mm2 service") + .arg( + Arg::with_name("mm-conf-path") + .long("mm-conf-path") + .value_name("FILE") + .help("mm2 configuration file path"), + ) + .arg( + Arg::with_name("mm-coins-path") + .long("mm-coins-path") + .value_name("FILE") + .help("coin set file path"), + ) + .arg( + Arg::with_name("mm-log") + .long("mm-log") + .value_name("FILE") + .help("log file path"), + ), + ) + .subcommand(SubCommand::with_name("stop").about("Stop mm2 instance")) + .subcommand(SubCommand::with_name("status").about("Get mm2 running status")); + + let matches = app.clone().get_matches(); + + let command = match matches.subcommand() { + ("init", Some(init_matches)) => { + let mm_coins_path = init_matches.value_of("mm-coins-path").unwrap_or("coins").to_owned(); + let mm_conf_path = init_matches.value_of("mm-conf-path").unwrap_or("MM2.json").to_owned(); + Command::Init { + mm_coins_path, + mm_conf_path, + } + }, + ("start", Some(start_matches)) => { + let mm_conf_path = start_matches.value_of("mm-conf-path").map(|s| s.to_owned()); + let mm_coins_path = start_matches.value_of("mm-coins-path").map(|s| s.to_owned()); + let mm_log = start_matches.value_of("mm-log").map(|s| s.to_owned()); + Command::Start { + mm_conf_path, + mm_coins_path, + mm_log, + } + }, + ("stop", _) => Command::Stop, + ("status", _) => Command::Status, + _ => { + let _ = app + .print_long_help() + .map_err(|error| error!("Failed to print_long_help: {error}")); + return; + }, + }; + + match command { + Command::Init { + mm_coins_path: coins_file, + mm_conf_path: mm2_cfg_file, + } => init(&mm2_cfg_file, &coins_file), + Command::Start { + mm_conf_path: mm2_cfg_file, + mm_coins_path: coins_file, + mm_log: log_file, + } => start_process(&mm2_cfg_file, &coins_file, &log_file), + Command::Stop => stop_process(), + Command::Status => get_status(), + } +} diff --git a/mm2src/adex_cli/src/log.rs b/mm2src/adex_cli/src/log.rs new file mode 100644 index 0000000000..60ffb13180 --- /dev/null +++ b/mm2src/adex_cli/src/log.rs @@ -0,0 +1,13 @@ +use log::LevelFilter; +use std::io::Write; + +pub fn init_logging() { + let mut builder = env_logger::builder(); + let level = std::env::var("RUST_LOG") + .map(|s| s.parse().expect("Failed to parse RUST_LOG")) + .unwrap_or(LevelFilter::Info); + builder + .filter_level(level) + .format(|buf, record| writeln!(buf, "{}", record.args())); + builder.init(); +} diff --git a/mm2src/adex_cli/src/main.rs b/mm2src/adex_cli/src/main.rs new file mode 100644 index 0000000000..eb80a3b868 --- /dev/null +++ b/mm2src/adex_cli/src/main.rs @@ -0,0 +1,12 @@ +#[cfg(not(target_arch = "wasm32"))] mod cli; +#[cfg(not(target_arch = "wasm32"))] mod log; +#[cfg(not(target_arch = "wasm32"))] mod scenarios; + +#[cfg(target_arch = "wasm32")] +fn main() {} + +#[cfg(not(target_arch = "wasm32"))] +fn main() { + log::init_logging(); + cli::process_cli(); +} diff --git a/mm2src/adex_cli/src/scenarios/helpers.rs b/mm2src/adex_cli/src/scenarios/helpers.rs new file mode 100644 index 0000000000..2282bf4b36 --- /dev/null +++ b/mm2src/adex_cli/src/scenarios/helpers.rs @@ -0,0 +1,34 @@ +use common::log::error; +use serde::Serialize; +use std::fs::OpenOptions; +use std::io::Write; +use std::ops::Deref; + +pub fn rewrite_data_file(data: T, file: &str) -> Result<(), ()> +where + T: Deref, +{ + let mut writer = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(file) + .map_err(|error| { + error!("Failed to open {file}: {error}"); + })?; + + writer.write(&data).map_err(|error| { + error!("Failed to write data into {file}: {error}"); + })?; + Ok(()) +} + +pub fn rewrite_json_file(value: &T, file: &str) -> Result<(), ()> +where + T: Serialize, +{ + let data = serde_json::to_vec_pretty(value).map_err(|error| { + error!("Failed to serialize data {error}"); + })?; + rewrite_data_file(data, file) +} diff --git a/mm2src/adex_cli/src/scenarios/init_coins.rs b/mm2src/adex_cli/src/scenarios/init_coins.rs new file mode 100644 index 0000000000..fefcdd7c66 --- /dev/null +++ b/mm2src/adex_cli/src/scenarios/init_coins.rs @@ -0,0 +1,45 @@ +use common::log::{error, info}; +use derive_more::Display; +use mm2_net::transport::slurp_url; + +use super::helpers::rewrite_data_file; + +#[derive(Clone, Copy, Debug, Display)] +pub enum CoinSet { + Empty, + Full, +} + +#[tokio::main(flavor = "current_thread")] +pub async fn init_coins(coins_file: &str) -> Result<(), ()> { + const FULL_COIN_SET_ADDRESS: &str = "https://raw.githubusercontent.com/KomodoPlatform/coins/master/coins"; + const EMPTY_COIN_SET_DATA: &[u8] = b"[]\n"; + let coin_set = inquire_coin_set(coins_file)?; + info!("Start getting mm2 coins"); + let coins_data = match coin_set { + CoinSet::Empty => Vec::::from(EMPTY_COIN_SET_DATA), + CoinSet::Full => { + info!("Getting coin set from: {FULL_COIN_SET_ADDRESS}"); + let (_status_code, _headers, data) = slurp_url(FULL_COIN_SET_ADDRESS).await.map_err(|error| { + error!("Failed to get coin set from: {FULL_COIN_SET_ADDRESS}, error: {error}"); + })?; + data + }, + }; + + rewrite_data_file(coins_data, coins_file)?; + info!("Got coins data, written into: {coins_file}"); + Ok(()) +} + +fn inquire_coin_set(coins_file: &str) -> Result { + inquire::Select::new( + format!("Select one of predefined coin sets to save into: {coins_file}").as_str(), + vec![CoinSet::Empty, CoinSet::Full], + ) + .with_help_message("Information about the currencies: their ticker symbols, names, ports, addresses, etc.") + .prompt() + .map_err(|error| { + error!("Failed to select coin_set: {error}"); + }) +} diff --git a/mm2src/adex_cli/src/scenarios/init_mm2_cfg.rs b/mm2src/adex_cli/src/scenarios/init_mm2_cfg.rs new file mode 100644 index 0000000000..551cf21598 --- /dev/null +++ b/mm2src/adex_cli/src/scenarios/init_mm2_cfg.rs @@ -0,0 +1,331 @@ +use bip39::{Language, Mnemonic, MnemonicType}; +use inquire::{validator::Validation, Confirm, CustomType, CustomUserError, Text}; +use passwords::PasswordGenerator; +use serde::Serialize; +use std::net::Ipv4Addr; +use std::ops::Not; +use std::path::Path; + +use super::helpers; +use super::inquire_extentions::{InquireOption, DEFAULT_DEFAULT_OPTION_BOOL_FORMATTER, DEFAULT_OPTION_BOOL_FORMATTER, + OPTION_BOOL_PARSER}; +use common::log::{error, info}; +use common::password_policy; + +const DEFAULT_NET_ID: u16 = 7777; +const DEFAULT_GID: &str = "adex-cli"; +const DEFAULT_OPTION_PLACEHOLDER: &str = "Tap enter to skip"; +const RPC_PORT_MIN: u16 = 1024; +const RPC_PORT_MAX: u16 = 49151; + +pub fn init_mm2_cfg(cfg_file: &str) -> Result<(), ()> { + let mut mm2_cfg = Mm2Cfg::new(); + info!("Start collecting mm2_cfg into: {cfg_file}"); + mm2_cfg.inquire()?; + helpers::rewrite_json_file(&mm2_cfg, cfg_file)?; + info!("mm2_cfg has been writen into: {cfg_file}"); + + Ok(()) +} + +#[derive(Serialize)] +pub struct Mm2Cfg { + pub gui: Option, + pub netid: Option, + pub rpc_password: Option, + #[serde(rename = "passphrase", skip_serializing_if = "Option::is_none")] + pub seed_phrase: Option, + pub allow_weak_password: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub dbdir: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rpcip: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rpcport: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub rpc_local_only: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub i_am_seed: Option, + #[serde(skip_serializing_if = "Vec::::is_empty")] + pub seednodes: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub hd_account_id: Option, +} + +impl Mm2Cfg { + pub fn new() -> Mm2Cfg { + Mm2Cfg { + gui: None, + netid: None, + rpc_password: None, + seed_phrase: None, + allow_weak_password: None, + dbdir: None, + rpcip: None, + rpcport: None, + rpc_local_only: None, + i_am_seed: None, + seednodes: Vec::::new(), + hd_account_id: None, + } + } + + fn inquire(&mut self) -> Result<(), ()> { + self.inquire_gui()?; + self.inquire_net_id()?; + self.inquire_seed_phrase()?; + self.inquire_allow_weak_password()?; + self.inquire_rpc_password()?; + self.inquire_dbdir()?; + self.inquire_rpcip()?; + self.inquire_rpcport()?; + self.inquire_rpc_local_only()?; + self.inquire_i_am_a_seed()?; + self.inquire_seednodes()?; + self.inquire_hd_account_id()?; + Ok(()) + } + + #[inline] + fn inquire_dbdir(&mut self) -> Result<(), ()> { + let is_reachable_dir = |dbdir: &InquireOption| -> Result { + match dbdir { + InquireOption::None => Ok(Validation::Valid), + InquireOption::Some(dbdir) => { + let path = Path::new(dbdir); + if path.is_dir().not() { + return Ok(Validation::Invalid( + format!("\"{dbdir}\" - is not a directory or does not exist").into(), + )); + } + Ok(Validation::Valid) + }, + } + }; + + self.dbdir = CustomType::>::new("What is dbdir") + .with_placeholder(DEFAULT_OPTION_PLACEHOLDER) + .with_help_message("AtomicDEX API database path. Optional, defaults to a subfolder named DB in the path of your mm2 binary") + .with_validator(is_reachable_dir) + .prompt() + .map_err(|error| { + error!("Failed to get dbdir: {error}"); + })?.into(); + + Ok(()) + } + + #[inline] + fn inquire_gui(&mut self) -> Result<(), ()> { + self.gui = Some(DEFAULT_GID.into()); + info!("> gui is set by default: {DEFAULT_GID}"); + Ok(()) + } + + #[inline] + fn inquire_net_id(&mut self) -> Result<(), ()> { + self.netid = CustomType::::new("What is the network `mm2` is going to be a part, netid:") + .with_default(DEFAULT_NET_ID) + .with_help_message(r#"Network ID number, telling the AtomicDEX API which network to join. 7777 is the current main network, though alternative netids can be used for testing or "private" trades"#) + .with_placeholder(format!("{DEFAULT_NET_ID}").as_str()) + .prompt() + .map_err(|error| { + error!("Failed to get netid: {error}"); + })?.into(); + Ok(()) + } + + #[inline] + fn inquire_seed_phrase(&mut self) -> Result<(), ()> { + let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); + let default_password: &str = mnemonic.phrase(); + self.seed_phrase = Text::new("What is the seed phrase:") + .with_default(default_password) + .with_validator(|phrase: &str| { + if phrase == "none" { + return Ok(Validation::Valid); + } + match Mnemonic::validate(phrase, Language::English) { + Ok(_) => Ok(Validation::Valid), + Err(error) => Ok(Validation::Invalid(error.into())), + } + }) + .with_placeholder(default_password) + .with_help_message( + "Type \"none\" to leave it blank and use limited service\n\ + Your passphrase; this is the source of each of your coins' private keys. KEEP IT SAFE!", + ) + .prompt() + .map_err(|error| { + error!("Failed to get passphrase: {error}"); + }) + .map(|value| if "none" == value { None } else { Some(value) })?; + + Ok(()) + } + + #[inline] + fn inquire_rpc_password(&mut self) -> Result<(), ()> { + let allow_weak_password = self.allow_weak_password; + let validator = move |password: &str| { + if let Some(false) = allow_weak_password { + match password_policy::password_policy(password) { + Err(error) => Ok(Validation::Invalid(error.into())), + Ok(_) => Ok(Validation::Valid), + } + } else { + Ok(Validation::Valid) + } + }; + let default_password = Self::generate_password()?; + + self.rpc_password = Text::new("What is the rpc_password:") + .with_help_message("Your password for protected RPC methods (userpass)") + .with_validator(validator) + .with_default(default_password.as_str()) + .with_placeholder(default_password.as_str()) + .prompt() + .map_err(|error| { + error!("Failed to get rpc_password: {error}"); + })? + .into(); + Ok(()) + } + + fn generate_password() -> Result { + let pg = PasswordGenerator { + length: 8, + numbers: true, + lowercase_letters: true, + uppercase_letters: true, + symbols: true, + spaces: false, + exclude_similar_characters: true, + strict: true, + }; + let mut password = String::new(); + while password_policy::password_policy(&password).is_err() { + password = pg + .generate_one() + .map_err(|error| error!("Failed to generate password: {error}"))?; + } + Ok(password) + } + + #[inline] + fn inquire_allow_weak_password(&mut self) -> Result<(), ()> { + self.allow_weak_password = Confirm::new("Allow weak password:") + .with_default(false) + .with_placeholder("No") + .with_help_message(r#"If true, will allow low entropy rpc_password. If false rpc_password must not have 3 of the same characters in a row, must be at least 8 characters long, must contain at least one of each of the following: numeric, uppercase, lowercase, special character (e.g. !#$*). It also can not contain the word "password", or the chars <, >, and &. Defaults to false."#) + .prompt() + .map_err(|error| { + error!("Failed to get allow_weak_password: {error}"); + })? + .into(); + Ok(()) + } + + #[inline] + fn inquire_rpcip(&mut self) -> Result<(), ()> { + self.rpcip = CustomType::>::new("What is rpcip:") + .with_placeholder(DEFAULT_OPTION_PLACEHOLDER) + .with_help_message("IP address to bind to for RPC server. Optional, defaults to 127.0.0.1") + .prompt() + .map_err(|error| { + error!("Failed to get rpcip: {error}"); + })? + .into(); + Ok(()) + } + + #[inline] + fn inquire_rpcport(&mut self) -> Result<(), ()> { + let validator = |value: &InquireOption| -> Result { + match value { + InquireOption::None => Ok(Validation::Valid), + InquireOption::Some(value) => { + if (RPC_PORT_MIN..RPC_PORT_MAX + 1).contains(value) { + Ok(Validation::Valid) + } else { + Ok(Validation::Invalid( + format!("rpc_port is out of range: [{RPC_PORT_MIN}, {RPC_PORT_MAX}]").into(), + )) + } + }, + } + }; + self.rpcport = CustomType::>::new("What is the rpcport:") + .with_help_message(r#"Port to use for RPC communication. Optional, defaults to 7783"#) + .with_validator(validator) + .with_placeholder(DEFAULT_OPTION_PLACEHOLDER) + .prompt() + .map_err(|error| { + error!("Failed to get rpcport: {error}"); + })? + .into(); + Ok(()) + } + + #[inline] + fn inquire_rpc_local_only(&mut self) -> Result<(), ()> { + self.rpc_local_only = CustomType::>::new("What is rpc_local_only:") + .with_parser(OPTION_BOOL_PARSER) + .with_formatter(DEFAULT_OPTION_BOOL_FORMATTER) + .with_default_value_formatter(DEFAULT_DEFAULT_OPTION_BOOL_FORMATTER) + .with_default(InquireOption::None) + .with_help_message("If false the AtomicDEX API will allow rpc methods sent from external IP addresses. Optional, defaults to true. Warning: Only use this if you know what you are doing, and have put the appropriate security measures in place.") + .prompt() + .map_err(|error| { + error!("Failed to get rpc_local_only: {error}"); + })?.into(); + Ok(()) + } + + #[inline] + fn inquire_i_am_a_seed(&mut self) -> Result<(), ()> { + self.i_am_seed = CustomType::>::new("What is i_am_a_seed:") + .with_parser(OPTION_BOOL_PARSER) + .with_formatter(DEFAULT_OPTION_BOOL_FORMATTER) + .with_default_value_formatter(DEFAULT_DEFAULT_OPTION_BOOL_FORMATTER) + .with_default(InquireOption::None) + .with_help_message("Runs AtomicDEX API as a seed node mode (acting as a relay for AtomicDEX API clients). Optional, defaults to false. Use of this mode is not reccomended on the main network (7777) as it could result in a pubkey ban if non-compliant. on alternative testing or private networks, at least one seed node is required to relay information to other AtomicDEX API clients using the same netID.") + .prompt() + .map_err(|error| { + error!("Failed to get i_am_a_seed: {error}"); + })?.into(); + Ok(()) + } + + #[inline] + fn inquire_seednodes(&mut self) -> Result<(), ()> { + info!("Reading seed nodes until tap enter is met"); + loop { + let seednode: Option = CustomType::>::new("What is the next seednode:") + .with_help_message("Optional. If operating on a test or private netID, the IP address of at least one seed node is required (on the main network, these are already hardcoded)") + .with_placeholder(DEFAULT_OPTION_PLACEHOLDER) + .prompt() + .map_err(|error| { + error!("Failed to get seed node: {error}"); + })?.into(); + let Some(seednode) = seednode else { + break; + }; + self.seednodes.push(seednode); + } + Ok(()) + } + + #[inline] + fn inquire_hd_account_id(&mut self) -> Result<(), ()> { + self.hd_account_id = CustomType::>::new("What is hd_account_id:") + .with_help_message(r#"Optional. If this value is set, the AtomicDEX-API will work in only the HD derivation mode, coins will need to have a coin derivation path entry in the coins file for activation. The hd_account_id value effectively takes its place in the full derivation as follows: m/44'/COIN_ID'/'/CHAIN/ADDRESS_ID"#) + .with_placeholder(DEFAULT_OPTION_PLACEHOLDER) + .prompt() + .map_err(|error| { + error!("Failed to get hd_account_id: {error}"); + })? + .into(); + Ok(()) + } +} diff --git a/mm2src/adex_cli/src/scenarios/inquire_extentions.rs b/mm2src/adex_cli/src/scenarios/inquire_extentions.rs new file mode 100644 index 0000000000..9416fe01a5 --- /dev/null +++ b/mm2src/adex_cli/src/scenarios/inquire_extentions.rs @@ -0,0 +1,64 @@ +use inquire::parser::DEFAULT_BOOL_PARSER; +use std::str::FromStr; + +#[derive(Clone)] +pub enum InquireOption { + Some(T), + None, +} + +type OptionalConfirm = InquireOption; + +impl From> for Option { + fn from(value: InquireOption) -> Self { + match value { + InquireOption::None => None, + InquireOption::Some(value) => Some(value), + } + } +} + +impl FromStr for InquireOption +where + ::Err: ToString, +{ + type Err = T::Err; + fn from_str(s: &str) -> Result { + if s.is_empty() || s.to_lowercase() == "none" { + return Ok(InquireOption::None); + } + T::from_str(s).map(InquireOption::Some) + } +} + +impl ToString for InquireOption { + fn to_string(&self) -> String { + match self { + InquireOption::Some(value) => value.to_string(), + InquireOption::None => "None".to_string(), + } + } +} + +pub type OptionBoolFormatter<'a> = &'a dyn Fn(OptionalConfirm) -> String; +pub const DEFAULT_OPTION_BOOL_FORMATTER: OptionBoolFormatter = &|ans| -> String { + match ans { + InquireOption::None => String::new(), + InquireOption::Some(true) => String::from("yes"), + InquireOption::Some(false) => String::from("no"), + } +}; + +pub type OptionBoolParser<'a> = &'a dyn Fn(&str) -> Result, ()>; +pub const OPTION_BOOL_PARSER: OptionBoolParser = &|ans: &str| -> Result, ()> { + if ans.is_empty() { + return Ok(InquireOption::None); + } + DEFAULT_BOOL_PARSER(ans).map(InquireOption::Some) +}; + +pub const DEFAULT_DEFAULT_OPTION_BOOL_FORMATTER: OptionBoolFormatter = &|ans: InquireOption| match ans { + InquireOption::None => String::from("Tap enter to skip/yes/no"), + InquireOption::Some(true) => String::from("none/Yes/no"), + InquireOption::Some(false) => String::from("none/yes/No"), +}; diff --git a/mm2src/adex_cli/src/scenarios/mm2_proc_mng.rs b/mm2src/adex_cli/src/scenarios/mm2_proc_mng.rs new file mode 100644 index 0000000000..73a3460bbc --- /dev/null +++ b/mm2src/adex_cli/src/scenarios/mm2_proc_mng.rs @@ -0,0 +1,331 @@ +use common::log::{error, info}; +use std::env; +use std::path::PathBuf; + +#[cfg(not(target_os = "macos"))] +pub use sysinfo::{PidExt, ProcessExt, System, SystemExt}; + +#[cfg(windows)] +mod reexport { + pub use std::ffi::CString; + pub use std::mem; + pub use std::mem::size_of; + pub use std::ptr::null; + pub use std::u32; + pub use winapi::um::processthreadsapi::{CreateProcessA, OpenProcess, TerminateProcess, PROCESS_INFORMATION, + STARTUPINFOA}; + pub use winapi::um::winnt::{PROCESS_TERMINATE, SYNCHRONIZE}; + + pub const MM2_BINARY: &str = "mm2.exe"; +} + +#[cfg(windows)] use reexport::*; + +#[cfg(all(unix, not(target_os = "macos")))] +mod unix_not_macos_reexport { + pub use std::process::{Command, Stdio}; + + pub const KILL_CMD: &str = "kill"; +} + +#[cfg(all(unix, not(target_os = "macos")))] +use unix_not_macos_reexport::*; + +#[cfg(unix)] +mod unix_reexport { + pub const MM2_BINARY: &str = "mm2"; +} + +#[cfg(unix)] use unix_reexport::*; + +#[cfg(target_os = "macos")] +mod macos_reexport { + pub use std::fs; + pub const LAUNCH_CTL_COOL_DOWN_TIMEOUT_MS: u64 = 500; + pub use common::log::debug; + pub use std::process::{Command, Stdio}; + pub use std::thread::sleep; + pub use std::time::Duration; + pub const LAUNCHCTL_MM2_ID: &str = "com.mm2.daemon"; +} + +#[cfg(target_os = "macos")] use macos_reexport::*; + +#[cfg(not(target_os = "macos"))] +pub fn get_status() { + let pids = find_proc_by_name(MM2_BINARY); + if pids.is_empty() { + info!("Process not found: {MM2_BINARY}"); + } + pids.iter().map(u32::to_string).for_each(|pid| { + info!("Found {MM2_BINARY} is running, pid: {pid}"); + }); +} + +#[cfg(not(target_os = "macos"))] +fn find_proc_by_name(pname: &'_ str) -> Vec { + let s = System::new_all(); + + s.processes() + .iter() + .filter(|(_, process)| process.name() == pname) + .map(|(pid, _)| pid.as_u32()) + .collect() +} + +fn get_mm2_binary_path() -> Result { + let mut dir = env::current_exe().map_err(|error| { + error!("Failed to get current binary dir: {error}"); + })?; + dir.pop(); + dir.push(MM2_BINARY); + Ok(dir) +} + +#[cfg(not(target_os = "macos"))] +pub fn start_process(mm2_cfg_file: &Option, coins_file: &Option, log_file: &Option) { + if let Some(mm2_cfg_file) = mm2_cfg_file { + info!("Set env MM_CONF_PATH as: {mm2_cfg_file}"); + env::set_var("MM_CONF_PATH", mm2_cfg_file); + } + if let Some(coins_file) = coins_file { + info!("Set env MM_COINS_PATH as: {coins_file}"); + env::set_var("MM_COINS_PATH", coins_file); + } + if let Some(log_file) = log_file { + info!("Set env MM_LOG as: {log_file}"); + env::set_var("MM_LOG", log_file); + } + + let Ok(mm2_binary) = get_mm2_binary_path() else { return; }; + if !mm2_binary.exists() { + error!("Failed to start mm2, no file: {mm2_binary:?}"); + return; + } + start_process_impl(mm2_binary); +} + +#[cfg(all(unix, not(target_os = "macos")))] +pub fn start_process_impl(mm2_binary: PathBuf) { + let mut command = Command::new(&mm2_binary); + let file_name = mm2_binary.file_name().expect("No file_name in mm2_binary"); + let process = match command.stdout(Stdio::null()).stdout(Stdio::null()).spawn() { + Ok(process) => process, + Err(error) => { + error!("Failed to start process: {mm2_binary:?}, error: {error}"); + return; + }, + }; + let pid = process.id(); + std::mem::forget(process); + info!("Started child process: {file_name:?}, pid: {pid}"); +} + +#[cfg(windows)] +pub fn start_process_impl(mm2_binary: PathBuf) { + let Some(program) = mm2_binary.to_str() else { + error!("Failed to cast mm2_binary to &str"); + return; + }; + let program = match CString::new(program) { + Ok(program) => program, + Err(error) => { + error!("Failed to construct CString program path: {error}"); + return; + }, + }; + + let mut startup_info: STARTUPINFOA = unsafe { mem::zeroed() }; + startup_info.cb = size_of::() as u32; + let mut process_info: PROCESS_INFORMATION = unsafe { mem::zeroed() }; + + let result = unsafe { + CreateProcessA( + null(), + program.into_raw() as *mut i8, + std::ptr::null_mut(), + std::ptr::null_mut(), + 0, + 0, + std::ptr::null_mut(), + std::ptr::null(), + &mut startup_info, + &mut process_info, + ) + }; + + match result { + 0 => error!("Failed to start: {MM2_BINARY}"), + _ => info!("Successfully started: {MM2_BINARY}"), + } +} + +#[cfg(all(unix, not(target_os = "macos")))] +pub fn stop_process() { + let pids = find_proc_by_name(MM2_BINARY); + if pids.is_empty() { + info!("Process not found: {MM2_BINARY}"); + } + pids.iter().map(u32::to_string).for_each(|pid| { + match Command::new(KILL_CMD) + .arg(&pid) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + { + Ok(status) => { + if status.success() { + info!("Process killed: {MM2_BINARY}:{pid}") + } else { + error!("Failed to kill process: {MM2_BINARY}:{pid}") + } + }, + Err(e) => error!("Failed to kill process: {MM2_BINARY}:{pid}. Error: {e}"), + }; + }); +} + +#[cfg(windows)] +pub fn stop_process() { + let processes = find_proc_by_name(MM2_BINARY); + for pid in processes { + info!("Terminate process: {}", pid); + unsafe { + let handy = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, true as i32, pid); + TerminateProcess(handy, 1); + } + } +} + +#[cfg(target_os = "macos")] +pub fn start_process(mm2_cfg_file: &Option, coins_file: &Option, log_file: &Option) { + let Ok(mm2_binary) = get_mm2_binary_path() else { return; }; + + let Ok(current_dir) = env::current_dir() else { + error!("Failed to get current_dir"); + return + }; + + let Ok(plist_path) = get_plist_path() else {return;}; + + let plist = format!( + r#" + + + + Label + {} + ProgramArguments + + {} + + WorkingDirectory + {} + EnvironmentVariables + {}{}{} + RunAtLoad + + KeepAlive + + + "#, + LAUNCHCTL_MM2_ID, + mm2_binary.display(), + current_dir.display(), + log_file + .as_deref() + .map(|log_file| format!("MM_LOG{log_file}")) + .unwrap_or_default(), + mm2_cfg_file + .as_deref() + .map(|cfg_file| format!("MM_CONF_PATH{cfg_file}")) + .unwrap_or_default(), + coins_file + .as_deref() + .map(|coins_file| format!("MM_COINS_PATH{coins_file}")) + .unwrap_or_default(), + ); + + if let Err(error) = fs::write(&plist_path, plist) { + error!("Failed to write plist file: {error}"); + return; + } + + match Command::new("launchctl") + .arg("enable") + .arg(format!("system/{LAUNCHCTL_MM2_ID}").as_str()) + .spawn() + { + Ok(_) => debug!("Successfully enabled using launchctl, label: {LAUNCHCTL_MM2_ID}"), + Err(error) => error!("Failed to enable process: {error}"), + } + + match Command::new("launchctl").arg("load").arg(&plist_path).spawn() { + Ok(_) => debug!("Successfully loaded using launchctl, label: {LAUNCHCTL_MM2_ID}"), + Err(error) => error!("Failed to load process: {error}"), + } + + match Command::new("launchctl").args(["start", LAUNCHCTL_MM2_ID]).spawn() { + Ok(_) => info!("Successfully started using launchctl, label: {LAUNCHCTL_MM2_ID}"), + Err(error) => error!("Failed to start process: {error}"), + } +} + +#[cfg(target_os = "macos")] +fn get_plist_path() -> Result { + match env::current_dir() { + Err(error) => { + error!("Failed to get current_dir to construct plist_path: {error}"); + Err(()) + }, + Ok(mut current_dir) => { + current_dir.push(&format!("{LAUNCHCTL_MM2_ID}.plist")); + Ok(current_dir) + }, + } +} + +#[cfg(target_os = "macos")] +pub fn stop_process() { + let Ok(plist_path) = get_plist_path() else { return; }; + + if let Err(error) = Command::new("launchctl").arg("unload").arg(&plist_path).spawn() { + error!("Failed to unload process using launchctl: {}", error); + } else { + info!("mm2 successfully stopped by launchctl"); + } + sleep(Duration::from_millis(LAUNCH_CTL_COOL_DOWN_TIMEOUT_MS)); + if let Err(err) = fs::remove_file(&plist_path) { + error!("Failed to remove plist file: {}", err); + } +} + +#[cfg(target_os = "macos")] +pub fn get_status() { + let output = Command::new("launchctl") + .args(["list", LAUNCHCTL_MM2_ID]) + .output() + .unwrap(); + + if !output.status.success() { + info!("Service '{LAUNCHCTL_MM2_ID}' is not running"); + return; + } + + if let Some(found) = String::from_utf8_lossy(&output.stdout) + .lines() + .filter(|line| line.contains("PID")) + .last() + { + let pid = found + .trim() + .matches(char::is_numeric) + .fold(String::default(), |mut pid, ch| { + pid.push_str(ch); + pid + }); + info!("Service '{LAUNCHCTL_MM2_ID}' is running under launchctl, pid: {}", pid); + } else { + info!("Service '{LAUNCHCTL_MM2_ID}' is not running"); + }; +} diff --git a/mm2src/adex_cli/src/scenarios/mod.rs b/mm2src/adex_cli/src/scenarios/mod.rs new file mode 100644 index 0000000000..2a9d637f01 --- /dev/null +++ b/mm2src/adex_cli/src/scenarios/mod.rs @@ -0,0 +1,16 @@ +mod helpers; +mod init_coins; +mod init_mm2_cfg; +mod inquire_extentions; +mod mm2_proc_mng; + +use init_coins::init_coins; +use init_mm2_cfg::init_mm2_cfg; +pub use mm2_proc_mng::{get_status, start_process, stop_process}; + +pub fn init(cfg_file: &str, coins_file: &str) { + if init_mm2_cfg(cfg_file).is_err() { + return; + } + let _ = init_coins(coins_file); +} diff --git a/mm2src/common/Cargo.toml b/mm2src/common/Cargo.toml index dfe77611e4..8516e8c04d 100644 --- a/mm2src/common/Cargo.toml +++ b/mm2src/common/Cargo.toml @@ -19,6 +19,8 @@ backtrace = "0.3" bytes = "1.1" cfg-if = "1.0" crossbeam = "0.8" +env_logger = "0.9.0" +derive_more = "0.99" fnv = "1.0.6" futures01 = { version = "0.1", package = "futures" } futures = { version = "0.3", package = "futures", features = ["compat", "async-await", "thread-pool"] } @@ -33,6 +35,7 @@ parking_lot = { version = "0.12.0", features = ["nightly"] } parking_lot_core = { version = "0.6", features = ["nightly"] } primitive-types = "0.11.1" rand = { version = "0.7", features = ["std", "small_rng"] } +regex = "1" serde = "1" serde_derive = "1" serde_json = { version = "1", features = ["preserve_order", "raw_value"] } @@ -65,7 +68,6 @@ hyper = { version = "0.14.11", features = ["client", "http2", "server", "tcp"] } hyper-rustls = { version = "0.23", default-features = false, features = ["http1", "http2", "webpki-tokio"] } libc = { version = "0.2" } lightning = "0.0.113" -log4rs = { version = "1.0", default-features = false, features = ["console_appender", "pattern_encoder"] } tokio = { version = "1.20", features = ["io-util", "rt-multi-thread", "net"] } [target.'cfg(windows)'.dependencies] diff --git a/mm2src/common/build.rs b/mm2src/common/build.rs index 2850c0141a..0230591b5b 100644 --- a/mm2src/common/build.rs +++ b/mm2src/common/build.rs @@ -83,7 +83,8 @@ fn build_c_code() { return; } - if cfg!(windows) { + let target_os = var("CARGO_CFG_TARGET_FAMILY").expect("!CARGO_CFG_TARGET_FAMILY"); + if target_os == "windows" { // Link in the Windows-specific crash handling code. let lm_seh = last_modified_sec(&"seh.c").expect("Can't stat seh.c"); let out_dir = var("OUT_DIR").expect("!OUT_DIR"); diff --git a/mm2src/common/common.rs b/mm2src/common/common.rs index e9000bd0da..67e7ff7ce8 100644 --- a/mm2src/common/common.rs +++ b/mm2src/common/common.rs @@ -118,6 +118,7 @@ pub mod custom_futures; pub mod custom_iter; #[path = "executor/mod.rs"] pub mod executor; pub mod number_type_casting; +pub mod password_policy; pub mod seri; #[path = "patterns/state_machine.rs"] pub mod state_machine; pub mod time_cache; @@ -182,11 +183,17 @@ pub const APPLICATION_GRPC_WEB_PROTO: &str = "application/grpc-web+proto"; pub const SATOSHIS: u64 = 100_000_000; pub const DEX_FEE_ADDR_PUBKEY: &str = "03bc2c7ba671bae4a6fc835244c9762b41647b9827d4780a89a949b984a8ddcc06"; + lazy_static! { pub static ref DEX_FEE_ADDR_RAW_PUBKEY: Vec = hex::decode(DEX_FEE_ADDR_PUBKEY).expect("DEX_FEE_ADDR_PUBKEY is expected to be a hexadecimal string"); } +#[cfg(not(target_arch = "wasm32"))] +lazy_static! { + pub(crate) static ref LOG_FILE: Mutex> = Mutex::new(open_log_file()); +} + pub auto trait NotSame {} impl !NotSame for (X, X) {} // Makes the error conversion work for structs/enums containing Box @@ -660,7 +667,7 @@ pub fn temp_dir() -> PathBuf { env::temp_dir() } /// Prints a warning to `stdout` if there's a problem opening the file. /// Returns `None` if `MM_LOG` variable is not present or if the specified path can't be opened. #[cfg(not(target_arch = "wasm32"))] -fn open_log_file() -> Option { +pub(crate) fn open_log_file() -> Option { let mm_log = match var("MM_LOG") { Ok(v) => v, Err(_) => return None, @@ -685,10 +692,6 @@ fn open_log_file() -> Option { pub fn writeln(line: &str) { use std::panic::catch_unwind; - lazy_static! { - static ref LOG_FILE: Mutex> = Mutex::new(open_log_file()); - } - // `catch_unwind` protects the tests from error // // thread 'CORE' panicked at 'cannot access stdout during shutdown' diff --git a/mm2src/common/log.rs b/mm2src/common/log.rs index 700dc0b9dd..a30b0b83de 100644 --- a/mm2src/common/log.rs +++ b/mm2src/common/log.rs @@ -995,7 +995,6 @@ impl LightningLogger for LogState { let record = Record::builder() .args(record.args) .level(level) - .target("mm_log") .module_path(Some(record.module_path)) .file(Some(record.file)) .line(Some(record.line)) @@ -1094,30 +1093,16 @@ impl fmt::Display for LogLevel { pub fn format_record(record: &Record) -> String { const DATE_FORMAT: &str = "%d %H:%M:%S"; - fn extract_crate_name(module_path: &str) -> &str { - match module_path.find("::") { - Some(ofs) => &module_path[0..ofs], - None => module_path, - } - } - let metadata = record.metadata(); let level = metadata.level(); let date = Utc::now().format(DATE_FORMAT); let line = record.line().unwrap_or(0); - let file = record.file().map(filename).unwrap_or("???"); - let module = record.module_path().unwrap_or(""); let message = record.args(); - let file = if module.contains("mm2") { - file.to_owned() - } else { - format!("{}:{}", extract_crate_name(module), file) - }; format!( - "{d}, {f}:{L}] {l} {m}", + "{d}, {p}:{L}] {l} {m}", d = date, - f = file, + p = record.module_path().unwrap_or(""), L = line, l = level, m = message diff --git a/mm2src/common/log/native_log.rs b/mm2src/common/log/native_log.rs index baa4148377..269b9fb28f 100644 --- a/mm2src/common/log/native_log.rs +++ b/mm2src/common/log/native_log.rs @@ -1,13 +1,9 @@ -use super::{chunk2log, format_record, LevelFilter, LogCallback}; -use log::Record; -use log4rs::encode::pattern; -use log4rs::{append, config}; +use super::{format_record, LogCallback}; +use std::env; +use std::io::Write; use std::os::raw::c_char; use std::str::FromStr; -const DEFAULT_CONSOLE_FORMAT: &str = "[{d(%Y-%m-%d %H:%M:%S %Z)(utc)} {h({l})} {M}:{f}:{L}] {m}\n"; -const DEFAULT_LEVEL_FILTER: LogLevel = LogLevel::Info; - #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] pub enum LogLevel { /// A level lower than all log levels. @@ -32,7 +28,7 @@ impl LogLevel { } impl Default for LogLevel { - fn default() -> Self { DEFAULT_LEVEL_FILTER } + fn default() -> Self { LogLevel::Info } } pub struct FfiCallback { @@ -52,84 +48,47 @@ impl LogCallback for FfiCallback { } } +#[derive(Default)] pub struct UnifiedLoggerBuilder { - console_format: String, - filter: LogLevel, - console: bool, - mm_log: bool, -} - -impl Default for UnifiedLoggerBuilder { - fn default() -> UnifiedLoggerBuilder { - UnifiedLoggerBuilder { - console_format: DEFAULT_CONSOLE_FORMAT.to_owned(), - filter: LogLevel::default(), - console: true, - mm_log: false, - } - } + /// Prevents writing to stdout/err + silent_console: bool, } impl UnifiedLoggerBuilder { pub fn new() -> UnifiedLoggerBuilder { UnifiedLoggerBuilder::default() } - pub fn console_format(mut self, console_format: &str) -> UnifiedLoggerBuilder { - self.console_format = console_format.to_owned(); + pub fn silent_console(mut self, silent_console: bool) -> UnifiedLoggerBuilder { + self.silent_console = silent_console; self } - pub fn level_filter(mut self, filter: LogLevel) -> UnifiedLoggerBuilder { - self.filter = filter; - self - } + pub fn init(self) { + const MM2_LOG_ENV_KEY: &str = "RUST_LOG"; - pub fn console(mut self, console: bool) -> UnifiedLoggerBuilder { - self.console = console; - self - } + if env::var_os(MM2_LOG_ENV_KEY).is_none() { + env::set_var(MM2_LOG_ENV_KEY, "info"); + }; - pub fn mm_log(mut self, mm_log: bool) -> UnifiedLoggerBuilder { - self.mm_log = mm_log; - self - } + let mut logger = env_logger::builder(); - pub fn try_init(self) -> Result<(), String> { - let mut appenders = Vec::new(); - - if self.mm_log { - appenders.push(config::Appender::builder().build("mm_log", Box::new(MmLogAppender))); - } - - if self.console { - let encoder = Box::new(pattern::PatternEncoder::new(&self.console_format)); - let appender = append::console::ConsoleAppender::builder() - .encoder(encoder) - .target(append::console::Target::Stdout) - .build(); - appenders.push(config::Appender::builder().build("console", Box::new(appender))); - } - - let app_names: Vec<_> = appenders.iter().map(|app| app.name()).collect(); - let root = config::Root::builder() - .appenders(app_names) - .build(LevelFilter::from(self.filter)); - let config = try_s!(config::Config::builder().appenders(appenders).build(root)); - - try_s!(log4rs::init_config(config)); - Ok(()) - } -} + logger.format(move |buf, record| { + let log = format_record(record); -#[derive(Debug)] -struct MmLogAppender; + if let Ok(mut log_file) = crate::LOG_FILE.lock() { + if let Some(ref mut log_file) = *log_file { + writeln!(log_file, "{}", log)?; + } + } -impl append::Append for MmLogAppender { - fn append(&self, record: &Record) -> anyhow::Result<()> { - let as_string = format_record(record); - let level = LogLevel::from(record.metadata().level()); - chunk2log(as_string, level); - Ok(()) - } + if !self.silent_console { + writeln!(buf, "{}", log)?; + } + + Ok(()) + }); - fn flush(&self) {} + if let Err(e) = logger.try_init() { + log::error!("env_logger is already initialized. {}", e); + }; + } } diff --git a/mm2src/common/password_policy.rs b/mm2src/common/password_policy.rs new file mode 100644 index 0000000000..5ffe3720eb --- /dev/null +++ b/mm2src/common/password_policy.rs @@ -0,0 +1,116 @@ +use derive_more::Display; +use regex::Regex; + +pub const PASSWORD_MAXIMUM_CONSECUTIVE_CHARACTERS: usize = 3; + +#[derive(Debug, Display, PartialEq)] +pub enum PasswordPolicyError { + #[display(fmt = "Password can't contain the word password")] + ContainsTheWordPassword, + #[display(fmt = "Password length should be at least 8 characters long")] + PasswordLength, + #[display(fmt = "Password should contain at least 1 digit")] + PasswordMissDigit, + #[display(fmt = "Password should contain at least 1 lowercase character")] + PasswordMissLowercase, + #[display(fmt = "Password should contain at least 1 uppercase character")] + PasswordMissUppercase, + #[display(fmt = "Password should contain at least 1 special character")] + PasswordMissSpecialCharacter, + #[display(fmt = "Password can't contain the same character 3 times in a row")] + PasswordConsecutiveCharactersExceeded, +} + +pub fn password_policy(password: &str) -> Result<(), PasswordPolicyError> { + lazy_static! { + static ref REGEX_NUMBER: Regex = Regex::new(".*[0-9].*").unwrap(); + static ref REGEX_LOWERCASE: Regex = Regex::new(".*[a-z].*").unwrap(); + static ref REGEX_UPPERCASE: Regex = Regex::new(".*[A-Z].*").unwrap(); + static ref REGEX_SPECIFIC_CHARS: Regex = Regex::new(".*[^A-Za-z0-9].*").unwrap(); + } + if password.to_lowercase().contains("password") { + return Err(PasswordPolicyError::ContainsTheWordPassword); + } + let password_len = password.chars().count(); + if (0..8).contains(&password_len) { + return Err(PasswordPolicyError::PasswordLength); + } + if !REGEX_NUMBER.is_match(password) { + return Err(PasswordPolicyError::PasswordMissDigit); + } + if !REGEX_LOWERCASE.is_match(password) { + return Err(PasswordPolicyError::PasswordMissLowercase); + } + if !REGEX_UPPERCASE.is_match(password) { + return Err(PasswordPolicyError::PasswordMissUppercase); + } + if !REGEX_SPECIFIC_CHARS.is_match(password) { + return Err(PasswordPolicyError::PasswordMissSpecialCharacter); + } + if !super::is_acceptable_input_on_repeated_characters(password, PASSWORD_MAXIMUM_CONSECUTIVE_CHARACTERS) { + return Err(PasswordPolicyError::PasswordConsecutiveCharactersExceeded); + } + Ok(()) +} + +#[test] +fn check_password_policy() { + use crate::password_policy::PasswordPolicyError; + // Length + assert_eq!( + password_policy("1234567").unwrap_err(), + PasswordPolicyError::PasswordLength + ); + + // Miss special character + assert_eq!( + password_policy("pass123worD").unwrap_err(), + PasswordPolicyError::PasswordMissSpecialCharacter + ); + + // Miss digit + assert_eq!( + password_policy("SecretPassSoStrong$*").unwrap_err(), + PasswordPolicyError::PasswordMissDigit + ); + + // Miss lowercase + assert_eq!( + password_policy("SECRETPASS-SOSTRONG123*").unwrap_err(), + PasswordPolicyError::PasswordMissLowercase + ); + + // Miss uppercase + assert_eq!( + password_policy("secretpass-sostrong123*").unwrap_err(), + PasswordPolicyError::PasswordMissUppercase + ); + + // Contains the same character 3 times in a row + assert_eq!( + password_policy("SecretPassSoStrong123*aaa").unwrap_err(), + PasswordPolicyError::PasswordConsecutiveCharactersExceeded + ); + + // Contains Password uppercase + assert_eq!( + password_policy("Password123*$").unwrap_err(), + PasswordPolicyError::ContainsTheWordPassword + ); + + // Contains Password lowercase + assert_eq!( + password_policy("Foopassword123*$").unwrap_err(), + PasswordPolicyError::ContainsTheWordPassword + ); + + // Check valid long password + let long_pass = "SecretPassSoStrong*!1234567891012"; + assert!(long_pass.len() > 32); + assert!(password_policy(long_pass).is_ok()); + + // Valid passwords + password_policy("StrongPass123*").unwrap(); + password_policy(r#"StrongPass123[]\± "#).unwrap(); + password_policy("StrongPass123£StrongPass123£Pass").unwrap(); +} diff --git a/mm2src/gossipsub/Cargo.toml b/mm2src/gossipsub/Cargo.toml index ad7ddbc03e..1d09159115 100644 --- a/mm2src/gossipsub/Cargo.toml +++ b/mm2src/gossipsub/Cargo.toml @@ -32,10 +32,10 @@ wasm-timer = "0.2.4" [dev-dependencies] async-std = "1.6.2" -env_logger = "0.7.1" +env_logger = "0.9.0" libp2p-plaintext = { git = "https://github.com/libp2p/rust-libp2p.git", tag ="v0.45.1" } libp2p-yamux = { git = "https://github.com/libp2p/rust-libp2p.git", tag ="v0.45.1" } -quickcheck = "0.9.2" +quickcheck= { version = "0.9.2", default-features = false } [build-dependencies] prost-build = { version = "0.10.4", default-features = false } diff --git a/mm2src/mm2_bin_lib/Cargo.toml b/mm2src/mm2_bin_lib/Cargo.toml index 6ab41e38a4..c216278742 100644 --- a/mm2src/mm2_bin_lib/Cargo.toml +++ b/mm2src/mm2_bin_lib/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "mm2_bin_lib" -version = "1.0.1-beta" +version = "1.0.2-beta" authors = ["James Lee", "Artem Pikulin", "Artem Grinblat", "Omar S.", "Onur Ozkan", "Alina Sharon", "Caglar Kaya", "Cipi", "Sergey Boiko", "Samuel Onoja", "Roman Sztergbaum", "Kadan Stadelmann "] edition = "2018" default-run = "mm2" diff --git a/mm2src/mm2_libp2p/Cargo.toml b/mm2src/mm2_libp2p/Cargo.toml index 8098ac9358..c16025cef5 100644 --- a/mm2src/mm2_libp2p/Cargo.toml +++ b/mm2src/mm2_libp2p/Cargo.toml @@ -39,5 +39,5 @@ wasm-bindgen-futures = "0.4.21" [dev-dependencies] async-std = { version = "1.6.2", features = ["unstable"] } -env_logger = "0.7.1" +env_logger = "0.9.0" serde_json = { version = "1", features = ["preserve_order", "raw_value"] } diff --git a/mm2src/mm2_main/Cargo.toml b/mm2src/mm2_main/Cargo.toml index 443055e4d1..4d806ab933 100644 --- a/mm2src/mm2_main/Cargo.toml +++ b/mm2src/mm2_main/Cargo.toml @@ -42,7 +42,6 @@ either = "1.6" ethereum-types = { version = "0.13", default-features = false, features = ["std", "serialize"] } enum_from = { path = "../derives/enum_from" } enum-primitive-derive = "0.2" -env_logger = "0.7.1" futures01 = { version = "0.1", package = "futures" } futures = { version = "0.3.1", package = "futures", features = ["compat", "async-await"] } gstuff = { version = "0.7", features = ["nightly"] } diff --git a/mm2src/mm2_main/src/lp_ordermatch/simple_market_maker_tests.rs b/mm2src/mm2_main/src/lp_ordermatch/simple_market_maker_tests.rs index 230bb0d872..8928656415 100644 --- a/mm2src/mm2_main/src/lp_ordermatch/simple_market_maker_tests.rs +++ b/mm2src/mm2_main/src/lp_ordermatch/simple_market_maker_tests.rs @@ -49,7 +49,7 @@ mod tests { #[test] #[cfg(not(target_arch = "wasm32"))] fn test_vwap_single_base_side() { - UnifiedLoggerBuilder::default().try_init().unwrap_or(()); + UnifiedLoggerBuilder::default().init(); let base_swaps = generate_swaps_from_values(vec![(MmNumber::from("29.99997438"), MmNumber::from("222.76277576"))]); let rel_swaps = generate_swaps_from_values(vec![]); @@ -62,7 +62,7 @@ mod tests { #[test] #[cfg(not(target_arch = "wasm32"))] fn test_vwap_single_base_side_forced_price() { - UnifiedLoggerBuilder::default().try_init().unwrap_or(()); + UnifiedLoggerBuilder::default().init(); let base_swaps = generate_swaps_from_values(vec![(MmNumber::from("29.99997438"), MmNumber::from("222.76277576"))]); let rel_swaps = generate_swaps_from_values(vec![]); @@ -78,7 +78,7 @@ mod tests { #[test] #[cfg(not(target_arch = "wasm32"))] fn test_vwap_multiple_base_side() { - UnifiedLoggerBuilder::default().try_init().unwrap_or(()); + UnifiedLoggerBuilder::default().init(); let base_swaps = generate_swaps_from_values(vec![ (MmNumber::from("29.99997438"), MmNumber::from("222.76277576")), (MmNumber::from("14.99998719"), MmNumber::from("105.38138788")), @@ -96,7 +96,7 @@ mod tests { #[test] #[cfg(not(target_arch = "wasm32"))] fn test_vwap_multiple_base_side_forced_price() { - UnifiedLoggerBuilder::default().try_init().unwrap_or(()); + UnifiedLoggerBuilder::default().init(); let base_swaps = generate_swaps_from_values(vec![ (MmNumber::from("29.99997438"), MmNumber::from("222.76277576")), (MmNumber::from("29.99997438"), MmNumber::from("190.76277576")), @@ -112,7 +112,7 @@ mod tests { #[test] #[cfg(not(target_arch = "wasm32"))] fn test_vwap_single_reversed_side() { - UnifiedLoggerBuilder::default().try_init().unwrap_or(()); + UnifiedLoggerBuilder::default().init(); let base_swaps = generate_swaps_from_values(vec![]); let rel_swaps = generate_swaps_from_values(vec![(MmNumber::from("219.4709"), MmNumber::from("29.99999"))]); let mut calculated_price = MmNumber::from("7.14455729"); diff --git a/mm2src/mm2_main/src/mm2.rs b/mm2src/mm2_main/src/mm2.rs index c31aff6439..e07218eaf4 100644 --- a/mm2src/mm2_main/src/mm2.rs +++ b/mm2src/mm2_main/src/mm2.rs @@ -28,6 +28,7 @@ use common::crash_reports::init_crash_reports; use common::double_panic_crash; use common::log::LogLevel; +use common::password_policy::password_policy; use mm2_core::mm_ctx::MmCtxBuilder; #[cfg(feature = "custom-swap-locktime")] use common::log::warn; @@ -36,10 +37,7 @@ use lp_swap::PAYMENT_LOCKTIME; #[cfg(feature = "custom-swap-locktime")] use std::sync::atomic::Ordering; -use derive_more::Display; use gstuff::slurp; -use lazy_static::lazy_static; -use regex::Regex; use serde::ser::Serialize; use serde_json::{self as json, Value as Json}; @@ -99,117 +97,6 @@ impl LpMainParams { } } -#[derive(Debug, Display, PartialEq)] -pub enum PasswordPolicyError { - #[display(fmt = "Password can't contain the word password")] - ContainsTheWordPassword, - #[display(fmt = "Password length should be at least 8 characters long")] - PasswordLength, - #[display(fmt = "Password should contain at least 1 digit")] - PasswordMissDigit, - #[display(fmt = "Password should contain at least 1 lowercase character")] - PasswordMissLowercase, - #[display(fmt = "Password should contain at least 1 uppercase character")] - PasswordMissUppercase, - #[display(fmt = "Password should contain at least 1 special character")] - PasswordMissSpecialCharacter, - #[display(fmt = "Password can't contain the same character 3 times in a row")] - PasswordConsecutiveCharactersExceeded, -} - -pub fn password_policy(password: &str) -> Result<(), MmError> { - lazy_static! { - static ref REGEX_NUMBER: Regex = Regex::new(".*[0-9].*").unwrap(); - static ref REGEX_LOWERCASE: Regex = Regex::new(".*[a-z].*").unwrap(); - static ref REGEX_UPPERCASE: Regex = Regex::new(".*[A-Z].*").unwrap(); - static ref REGEX_SPECIFIC_CHARS: Regex = Regex::new(".*[^A-Za-z0-9].*").unwrap(); - } - if password.to_lowercase().contains("password") { - return MmError::err(PasswordPolicyError::ContainsTheWordPassword); - } - let password_len = password.chars().count(); - if (0..8).contains(&password_len) { - return MmError::err(PasswordPolicyError::PasswordLength); - } - if !REGEX_NUMBER.is_match(password) { - return MmError::err(PasswordPolicyError::PasswordMissDigit); - } - if !REGEX_LOWERCASE.is_match(password) { - return MmError::err(PasswordPolicyError::PasswordMissLowercase); - } - if !REGEX_UPPERCASE.is_match(password) { - return MmError::err(PasswordPolicyError::PasswordMissUppercase); - } - if !REGEX_SPECIFIC_CHARS.is_match(password) { - return MmError::err(PasswordPolicyError::PasswordMissSpecialCharacter); - } - if !common::is_acceptable_input_on_repeated_characters(password, PASSWORD_MAXIMUM_CONSECUTIVE_CHARACTERS) { - return MmError::err(PasswordPolicyError::PasswordConsecutiveCharactersExceeded); - } - Ok(()) -} - -#[test] -fn check_password_policy() { - // Length - assert_eq!( - password_policy("1234567").unwrap_err().into_inner(), - PasswordPolicyError::PasswordLength - ); - - // Miss special character - assert_eq!( - password_policy("pass123worD").unwrap_err().into_inner(), - PasswordPolicyError::PasswordMissSpecialCharacter - ); - - // Miss digit - assert_eq!( - password_policy("SecretPassSoStrong$*").unwrap_err().into_inner(), - PasswordPolicyError::PasswordMissDigit - ); - - // Miss lowercase - assert_eq!( - password_policy("SECRETPASS-SOSTRONG123*").unwrap_err().into_inner(), - PasswordPolicyError::PasswordMissLowercase - ); - - // Miss uppercase - assert_eq!( - password_policy("secretpass-sostrong123*").unwrap_err().into_inner(), - PasswordPolicyError::PasswordMissUppercase - ); - - // Contains the same character 3 times in a row - assert_eq!( - password_policy("SecretPassSoStrong123*aaa").unwrap_err().into_inner(), - PasswordPolicyError::PasswordConsecutiveCharactersExceeded - ); - - // Contains Password uppercase - assert_eq!( - password_policy("Password123*$").unwrap_err().into_inner(), - PasswordPolicyError::ContainsTheWordPassword - ); - - // Contains Password lowercase - assert_eq!( - password_policy("Foopassword123*$").unwrap_err().into_inner(), - PasswordPolicyError::ContainsTheWordPassword - ); - - // Check valid long password - let long_pass = "SecretPassSoStrong*!1234567891012"; - assert!(long_pass.len() > 32); - assert!(password_policy(long_pass).is_ok()); - - // Valid passwords - password_policy("StrongPass123*").unwrap(); - password_policy(r#"StrongPass123[]\± "#).unwrap(); - password_policy("StrongPass123£StrongPass123£Pass").unwrap(); -} - #[cfg(feature = "custom-swap-locktime")] /// Reads `payment_locktime` from conf arg and assigns it into `PAYMENT_LOCKTIME` in lp_swap. /// Assigns 900 if `payment_locktime` is invalid or not provided. @@ -236,7 +123,7 @@ pub async fn lp_main( let log_filter = params.filter.unwrap_or_default(); // Logger can be initialized once. // If `mm2` is linked as a library, and `mm2` is restarted, `init_logger` returns an error. - init_logger(log_filter).ok(); + init_logger(log_filter, params.conf["silent_console"].as_bool().unwrap_or_default()).ok(); let conf = params.conf; if !conf["rpc_password"].is_null() { @@ -312,7 +199,6 @@ Some (but not all) of the JSON configuration parameters (* - required): seednodes .. Seednode IPs that node will use. At least one seed IP must be present if the node is not a seed itself. stderr .. Print a message to stderr and exit. - userhome .. System home directory of a user ('/root' by default). wif .. `1` to add WIFs to the information we provide about a coin. Environment variables: @@ -456,8 +342,6 @@ pub fn run_lp_main( version: String, datetime: String, ) -> Result<(), String> { - env_logger::init(); - let conf = get_mm2config(first_arg)?; let log_filter = LogLevel::from_env(); @@ -497,17 +381,15 @@ fn on_update_config(args: &[OsString]) -> Result<(), String> { } #[cfg(not(target_arch = "wasm32"))] -fn init_logger(level: LogLevel) -> Result<(), String> { - use common::log::UnifiedLoggerBuilder; - - UnifiedLoggerBuilder::default() - .level_filter(level) - .console(false) - .mm_log(true) - .try_init() +fn init_logger(_level: LogLevel, silent_console: bool) -> Result<(), String> { + common::log::UnifiedLoggerBuilder::default() + .silent_console(silent_console) + .init(); + + Ok(()) } #[cfg(target_arch = "wasm32")] -fn init_logger(level: LogLevel) -> Result<(), String> { +fn init_logger(level: LogLevel, _silent_console: bool) -> Result<(), String> { common::log::WasmLoggerBuilder::default().level_filter(level).try_init() } diff --git a/android-ndk.rb b/scripts/ci/android-ndk.rb similarity index 100% rename from android-ndk.rb rename to scripts/ci/android-ndk.rb diff --git a/android-ndk.sh b/scripts/ci/android-ndk.sh similarity index 95% rename from android-ndk.sh rename to scripts/ci/android-ndk.sh index 07ef7eb782..5a6f038ebe 100755 --- a/android-ndk.sh +++ b/scripts/ci/android-ndk.sh @@ -1,3 +1,5 @@ +#!/bin/bash + set -ex NDK_URL=https://dl.google.com/android/repository/android-ndk-r21b-linux-x86_64.zip @@ -9,6 +11,7 @@ main() { local dependencies=( unzip python3 + python3-distutils curl ) diff --git a/etomic_build/autoprice b/scripts/mm2/autoprice similarity index 100% rename from etomic_build/autoprice rename to scripts/mm2/autoprice diff --git a/etomic_build/buy b/scripts/mm2/buy similarity index 100% rename from etomic_build/buy rename to scripts/mm2/buy diff --git a/etomic_build/client/all_swaps_uuids_by_filter b/scripts/mm2/client/all_swaps_uuids_by_filter similarity index 100% rename from etomic_build/client/all_swaps_uuids_by_filter rename to scripts/mm2/client/all_swaps_uuids_by_filter diff --git a/etomic_build/client/ban_pubkey b/scripts/mm2/client/ban_pubkey similarity index 100% rename from etomic_build/client/ban_pubkey rename to scripts/mm2/client/ban_pubkey diff --git a/etomic_build/client/best_orders b/scripts/mm2/client/best_orders similarity index 100% rename from etomic_build/client/best_orders rename to scripts/mm2/client/best_orders diff --git a/etomic_build/client/buy_ONE_ANOTHER b/scripts/mm2/client/buy_ONE_ANOTHER similarity index 100% rename from etomic_build/client/buy_ONE_ANOTHER rename to scripts/mm2/client/buy_ONE_ANOTHER diff --git a/etomic_build/client/client b/scripts/mm2/client/client similarity index 100% rename from etomic_build/client/client rename to scripts/mm2/client/client diff --git a/etomic_build/client/client_debug b/scripts/mm2/client/client_debug similarity index 100% rename from etomic_build/client/client_debug rename to scripts/mm2/client/client_debug diff --git a/etomic_build/client/disable_coin b/scripts/mm2/client/disable_coin similarity index 100% rename from etomic_build/client/disable_coin rename to scripts/mm2/client/disable_coin diff --git a/etomic_build/client/enable b/scripts/mm2/client/enable similarity index 100% rename from etomic_build/client/enable rename to scripts/mm2/client/enable diff --git a/etomic_build/client/enable_ADEX b/scripts/mm2/client/enable_ADEX similarity index 100% rename from etomic_build/client/enable_ADEX rename to scripts/mm2/client/enable_ADEX diff --git a/etomic_build/client/enable_BCH b/scripts/mm2/client/enable_BCH similarity index 100% rename from etomic_build/client/enable_BCH rename to scripts/mm2/client/enable_BCH diff --git a/etomic_build/client/enable_BNB b/scripts/mm2/client/enable_BNB similarity index 100% rename from etomic_build/client/enable_BNB rename to scripts/mm2/client/enable_BNB diff --git a/etomic_build/client/enable_BNBT b/scripts/mm2/client/enable_BNBT similarity index 100% rename from etomic_build/client/enable_BNBT rename to scripts/mm2/client/enable_BNBT diff --git a/etomic_build/client/enable_GRS b/scripts/mm2/client/enable_GRS similarity index 100% rename from etomic_build/client/enable_GRS rename to scripts/mm2/client/enable_GRS diff --git a/etomic_build/client/enable_MORTY b/scripts/mm2/client/enable_MORTY similarity index 100% rename from etomic_build/client/enable_MORTY rename to scripts/mm2/client/enable_MORTY diff --git a/etomic_build/client/enable_RICK b/scripts/mm2/client/enable_RICK similarity index 100% rename from etomic_build/client/enable_RICK rename to scripts/mm2/client/enable_RICK diff --git a/etomic_build/client/enable_USDF b/scripts/mm2/client/enable_USDF similarity index 100% rename from etomic_build/client/enable_USDF rename to scripts/mm2/client/enable_USDF diff --git a/etomic_build/client/enable_tBCH b/scripts/mm2/client/enable_tBCH similarity index 100% rename from etomic_build/client/enable_tBCH rename to scripts/mm2/client/enable_tBCH diff --git a/etomic_build/client/enable_tBCH_USDF b/scripts/mm2/client/enable_tBCH_USDF similarity index 100% rename from etomic_build/client/enable_tBCH_USDF rename to scripts/mm2/client/enable_tBCH_USDF diff --git a/etomic_build/client/get_trade_fee b/scripts/mm2/client/get_trade_fee similarity index 100% rename from etomic_build/client/get_trade_fee rename to scripts/mm2/client/get_trade_fee diff --git a/etomic_build/client/list_banned_pubkeys b/scripts/mm2/client/list_banned_pubkeys similarity index 100% rename from etomic_build/client/list_banned_pubkeys rename to scripts/mm2/client/list_banned_pubkeys diff --git a/etomic_build/client/min_trading_vol b/scripts/mm2/client/min_trading_vol similarity index 100% rename from etomic_build/client/min_trading_vol rename to scripts/mm2/client/min_trading_vol diff --git a/etomic_build/client/my_balance b/scripts/mm2/client/my_balance similarity index 100% rename from etomic_build/client/my_balance rename to scripts/mm2/client/my_balance diff --git a/etomic_build/client/my_recent_swaps b/scripts/mm2/client/my_recent_swaps similarity index 100% rename from etomic_build/client/my_recent_swaps rename to scripts/mm2/client/my_recent_swaps diff --git a/etomic_build/client/my_tx_history_from_id b/scripts/mm2/client/my_tx_history_from_id similarity index 100% rename from etomic_build/client/my_tx_history_from_id rename to scripts/mm2/client/my_tx_history_from_id diff --git a/etomic_build/client/my_tx_history_page_number b/scripts/mm2/client/my_tx_history_page_number similarity index 100% rename from etomic_build/client/my_tx_history_page_number rename to scripts/mm2/client/my_tx_history_page_number diff --git a/etomic_build/client/passphrase b/scripts/mm2/client/passphrase similarity index 100% rename from etomic_build/client/passphrase rename to scripts/mm2/client/passphrase diff --git a/etomic_build/client/setpassphrase b/scripts/mm2/client/setpassphrase similarity index 100% rename from etomic_build/client/setpassphrase rename to scripts/mm2/client/setpassphrase diff --git a/etomic_build/client/setprice_ONE_ANOTHER b/scripts/mm2/client/setprice_ONE_ANOTHER similarity index 100% rename from etomic_build/client/setprice_ONE_ANOTHER rename to scripts/mm2/client/setprice_ONE_ANOTHER diff --git a/etomic_build/client/userpass b/scripts/mm2/client/userpass similarity index 100% rename from etomic_build/client/userpass rename to scripts/mm2/client/userpass diff --git a/etomic_build/client/validate_address b/scripts/mm2/client/validate_address similarity index 100% rename from etomic_build/client/validate_address rename to scripts/mm2/client/validate_address diff --git a/etomic_build/client/withdraw b/scripts/mm2/client/withdraw similarity index 100% rename from etomic_build/client/withdraw rename to scripts/mm2/client/withdraw diff --git a/etomic_build/enable b/scripts/mm2/enable similarity index 100% rename from etomic_build/enable rename to scripts/mm2/enable diff --git a/etomic_build/orderbook b/scripts/mm2/orderbook similarity index 100% rename from etomic_build/orderbook rename to scripts/mm2/orderbook diff --git a/etomic_build/seed/enable b/scripts/mm2/seed/enable similarity index 100% rename from etomic_build/seed/enable rename to scripts/mm2/seed/enable diff --git a/etomic_build/seed/myipaddr b/scripts/mm2/seed/myipaddr similarity index 100% rename from etomic_build/seed/myipaddr rename to scripts/mm2/seed/myipaddr diff --git a/etomic_build/seed/passphrase b/scripts/mm2/seed/passphrase similarity index 100% rename from etomic_build/seed/passphrase rename to scripts/mm2/seed/passphrase diff --git a/etomic_build/seed/run b/scripts/mm2/seed/run similarity index 100% rename from etomic_build/seed/run rename to scripts/mm2/seed/run diff --git a/etomic_build/seed/run_debug b/scripts/mm2/seed/run_debug similarity index 100% rename from etomic_build/seed/run_debug rename to scripts/mm2/seed/run_debug diff --git a/etomic_build/seed/sell_ONE_ANOTHER b/scripts/mm2/seed/sell_ONE_ANOTHER similarity index 100% rename from etomic_build/seed/sell_ONE_ANOTHER rename to scripts/mm2/seed/sell_ONE_ANOTHER diff --git a/etomic_build/seed/setpassphrase b/scripts/mm2/seed/setpassphrase similarity index 100% rename from etomic_build/seed/setpassphrase rename to scripts/mm2/seed/setpassphrase diff --git a/etomic_build/seed/userpass b/scripts/mm2/seed/userpass similarity index 100% rename from etomic_build/seed/userpass rename to scripts/mm2/seed/userpass diff --git a/etomic_build/setpassphrase b/scripts/mm2/setpassphrase similarity index 100% rename from etomic_build/setpassphrase rename to scripts/mm2/setpassphrase diff --git a/etomic_build/stop b/scripts/mm2/stop similarity index 100% rename from etomic_build/stop rename to scripts/mm2/stop diff --git a/etomic_build/userpass b/scripts/mm2/userpass similarity index 100% rename from etomic_build/userpass rename to scripts/mm2/userpass diff --git a/start_ONE_ANOTHER_trade.sh b/start_ONE_ANOTHER_trade.sh deleted file mode 100755 index 57f7ecfb42..0000000000 --- a/start_ONE_ANOTHER_trade.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -sleep 5 -docker-compose exec -T clientnode ./enable -sleep 3 -docker-compose exec -T seednode ./enable -sleep 3 -docker-compose exec -T seednode ./sell_ONE_ANOTHER $1 $2 -sleep 3 -docker-compose exec -T clientnode ./buy_ONE_ANOTHER $1 $2 diff --git a/travis_cmake_linux.sh b/travis_cmake_linux.sh deleted file mode 100755 index f11b08e4db..0000000000 --- a/travis_cmake_linux.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -sudo rm -rf /usr/local/cmake-3.9.2 && sudo rm -rf /usr/local/cmake -wget https://cmake.org/files/v3.12/cmake-3.12.0-rc2-Linux-x86_64.sh -chmod +x cmake-3.12.0-rc2-Linux-x86_64.sh -sudo ./cmake-3.12.0-rc2-Linux-x86_64.sh --skip-license --exclude-subdir --prefix=/usr/local \ No newline at end of file diff --git a/travis_cmake_mac.sh b/travis_cmake_mac.sh deleted file mode 100644 index 0e7c8eda11..0000000000 --- a/travis_cmake_mac.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -brew info libidn2 -brew uninstall cmake --force -wget https://cmake.org/files/v3.12/cmake-3.12.0-rc2-Darwin-x86_64.tar.gz -tar -xzf cmake-3.12.0-rc2-Darwin-x86_64.tar.gz -cp -r cmake-3.12.0-rc2-Darwin-x86_64/CMake.app/Contents/bin/* /usr/local/bin/ -cp -r cmake-3.12.0-rc2-Darwin-x86_64/CMake.app/Contents/share/* /usr/local/share/ \ No newline at end of file