From 7e41d521b6d97ba987ce5b4dfa503c0c25bc0290 Mon Sep 17 00:00:00 2001 From: Xavier Lau Date: Sat, 24 Jun 2023 02:53:55 +0800 Subject: [PATCH] Release `v0.1.0` (#1) --- .github/workflows/checks.yml | 38 +- .github/workflows/pages.yml | 34 - .github/workflows/release.yml | 11 +- .github/workflows/staging.yml | 11 +- .gitignore | 3 +- Cargo.lock | 4352 +++++++++++++++++++++++++++ Cargo.toml | 39 +- README.md | 43 +- build.rs | 18 +- configuration-template.toml | 128 + src/hunter.rs | 517 ++++ src/hunter/configuration.rs | 254 ++ src/hunter/graphql.rs | 122 + src/hunter/node.rs | 176 ++ src/hunter/notification.rs | 67 + src/hunter/tx.rs | 169 ++ src/hunter/util.rs | 240 ++ src/lib.rs | 0 src/main.rs | 60 + src/primitive.rs | 411 +++ src/primitive/runtime.rs | 43 + src/primitive/runtime/constant.rs | 43 + src/primitive/runtime/event.rs | 60 + src/primitive/runtime/storage.rs | 19 + test/guide.md | 65 + test/integration/Dockerfile | 10 + test/integration/docker-compose.yml | 61 + test/integration/rococo.toml | 126 + 28 files changed, 7029 insertions(+), 91 deletions(-) delete mode 100644 .github/workflows/pages.yml create mode 100644 Cargo.lock create mode 100644 configuration-template.toml create mode 100644 src/hunter.rs create mode 100644 src/hunter/configuration.rs create mode 100644 src/hunter/graphql.rs create mode 100644 src/hunter/node.rs create mode 100644 src/hunter/notification.rs create mode 100644 src/hunter/tx.rs create mode 100644 src/hunter/util.rs delete mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/primitive.rs create mode 100644 src/primitive/runtime.rs create mode 100644 src/primitive/runtime/constant.rs create mode 100644 src/primitive/runtime/event.rs create mode 100644 src/primitive/runtime/storage.rs create mode 100644 test/guide.md create mode 100644 test/integration/Dockerfile create mode 100644 test/integration/docker-compose.yml create mode 100644 test/integration/rococo.toml diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 0df7e8f..9d9f6a3 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -8,34 +8,24 @@ on: - main env: + CARGO_INCREMENTAL: 1 CARGO_TERM_COLOR: always GITHUB_CACHE_VERSION: 1 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RUST_BACKTRACE: full - RUSTC_WRAPPER: sccache - - SCCACHE_LINK: https://github.com/mozilla/sccache/releases/download - SCCACHE_VERSION: v0.3.0 - SCCACHE_DIR: /home/runner/.cache/sccache jobs: - cargo_checks: + cargo-checks: name: Task cargo ${{ matrix.action }} runs-on: ubuntu-latest strategy: matrix: - action: [clippy, nextest] + action: [clippy, fmt, nextest] steps: - name: Fetch latest code uses: actions/checkout@v3 - - name: Install Sccache - run: | - export SCCACHE_FILE=sccache-${{ env.SCCACHE_VERSION }}-x86_64-unknown-linux-musl - curl -L ${{ env.SCCACHE_LINK }}/${{ env.SCCACHE_VERSION }}/$SCCACHE_FILE.tar.gz | tar xz - sudo mv $SCCACHE_FILE/sccache /usr/bin - sudo chmod u+x /usr/bin/sccache - name: Cache cargo uses: actions/cache@v3 with: @@ -45,24 +35,26 @@ jobs: target key: cargo-${{ env.GITHUB_CACHE_VERSION }}-${{ matrix.action }}-${{ hashFiles('**/Cargo.lock') }} restore-keys: cargo-${{ env.GITHUB_CACHE_VERSION }}-${{ matrix.action }}- - - name: Cache sccache - uses: actions/cache@v3 - with: - path: ${{ env.SCCACHE_DIR}} - key: sccache-${{ env.GITHUB_CACHE_VERSION }}-${{ matrix.action }}--${{ hashFiles('**/Cargo.lock') }} - restore-keys: sccache-${{ env.GITHUB_CACHE_VERSION }}-${{ matrix.action }}- - name: Cargo ${{ matrix.action }} if: matrix.action == 'clippy' uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} args: --workspace --all-features --all-targets --locked + - name: Cargo ${{ matrix.action }} + if: matrix.action == 'fmt' + run: cargo ${{ matrix.action }} --all -- --check - name: Install cargo-nextest if: matrix.action == 'nextest' uses: taiki-e/install-action@nextest - - name: Cargo nextest + - name: Cargo ${{ matrix.action }} if: matrix.action == 'nextest' - uses: actions-rs/cargo@v1 + # Skip `node-test` feature. + run: cargo ${{ matrix.action }} run --release --workspace --all-targets --locked + - name: Fast fail + uses: vishnudxb/cancel-workflow@v1.2 + if: failure() with: - command: nextest - args: run --workspace --all-features --all-targets --locked + repo: hack-ink/slothunter + workflow_id: ${{ github.run_id }} + access_token: ${{ github.token }} diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml deleted file mode 100644 index 936848c..0000000 --- a/.github/workflows/pages.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Pages - -on: - push: - branches: - - main - pull_request: - branches: - - main - -env: - CARGO_TERM_COLOR: always - -jobs: - deploy: - name: Deploy pages - runs-on: ubuntu-latest - concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - steps: - - name: Fetch latest code - uses: actions/checkout@v3 - - name: Setup mdBook - uses: peaceiris/actions-mdbook@v1 - with: - mdbook-version: "latest" - - name: Build pages - run: mdbook build doc - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - if: ${{ github.ref == 'refs/heads/main' }} - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: doc/book diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b6eebc4..256e2ad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,18 +30,15 @@ jobs: - name: Setup Rust toolchain run: rustup target add ${{ matrix.target.name }} - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --release --locked --target ${{ matrix.target.name }} + run: cargo build --release --locked --target ${{ matrix.target.name }} - name: Compress run: | - mv target/${{ matrix.target.name }}/release/${{ matrix.target.extension }} . - zstd --ultra -22 -o -${{ matrix.target.name }}.zst ${{ matrix.target.extension }} + mv target/${{ matrix.target.name }}/release/slothunter${{ matrix.target.extension }} . + zstd --ultra -22 -o slothunter-${{ matrix.target.name }}.zst slothunter${{ matrix.target.extension }} - name: Collect artifact run: | mkdir -p artifacts - mv -${{ matrix.target.name }}.zst artifacts + mv slothunter-${{ matrix.target.name }}.zst artifacts - name: Upload artifact uses: actions/upload-artifact@v3.1.2 with: diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 8278e8f..71e04a8 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -28,18 +28,15 @@ jobs: - name: Setup Rust toolchain run: rustup target add ${{ matrix.target.name }} - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --release --locked --target ${{ matrix.target.name }} + run: cargo build --release --locked --target ${{ matrix.target.name }} - name: Compress run: | - mv target/${{ matrix.target.name }}/release/${{ matrix.target.extension }} . - zstd --ultra -22 -o -${{ matrix.target.name }}.zst ${{ matrix.target.extension }} + mv target/${{ matrix.target.name }}/release/slothunter${{ matrix.target.extension }} . + zstd --ultra -22 -o slothunter-${{ matrix.target.name }}.zst slothunter${{ matrix.target.extension }} - name: Collect artifact run: | mkdir -p artifacts - mv -${{ matrix.target.name }}.zst artifacts + mv slothunter-${{ matrix.target.name }}.zst artifacts - name: Upload artifact uses: actions/upload-artifact@v3.1.2 with: diff --git a/.gitignore b/.gitignore index 261e78d..05108a7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ book index.html # Test data -test-data +test/integration/data +test/integration/secret.toml diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f06b972 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4352 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.10", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom 0.2.10", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "app_dirs2" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e7b35733e3a8c1ccb90385088dd5b6eaa61325cb4d1ad56e683b5224ff352e" +dependencies = [ + "jni", + "ndk-context", + "winapi", + "xdg", +] + +[[package]] +name = "array-bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + +[[package]] +name = "array-bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "bounded-collections" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b05133427c07c4776906f673ccf36c21b102c9829c641a5b56bd151d44fd6" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "winapi", +] + +[[package]] +name = "clap" +version = "4.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2686c4115cb0810d9a984776e197823d08ec94f176549a89a9efded477c456dc" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e53afce1efce6ed1f633cf0e57612fe51db54a1ee4fd8f8503d078fe02d69ae" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "constant_time_eq" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-entity" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" +dependencies = [ + "serde", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +dependencies = [ + "darling_core 0.20.1", + "darling_macro 0.20.1", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.18", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +dependencies = [ + "darling_core 0.20.1", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "email-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75" +dependencies = [ + "base64 0.21.2", + "memchr", +] + +[[package]] +name = "email_address" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "environmental" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "frame-metadata" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hash-db" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.7", + "hmac 0.8.1", +] + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "webpki-roots", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix 0.37.20", + "windows-sys 0.48.0", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-types", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb" +dependencies = [ + "futures-util", + "http", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "rustls-native-certs", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "webpki-roots", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" +dependencies = [ + "anyhow", + "async-lock", + "async-trait", + "beef", + "futures-channel", + "futures-timer", + "futures-util", + "hyper", + "jsonrpsee-types", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad" +dependencies = [ + "async-trait", + "hyper", + "hyper-rustls", + "jsonrpsee-core", + "jsonrpsee-types", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lettre" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bd09637ae3ec7bd605b8e135e757980b3968430ff2b1a4a94fb7769e50166d" +dependencies = [ + "base64 0.21.2", + "email-encoding", + "email_address", + "fastrand", + "futures-util", + "hostname", + "httpdate", + "idna 0.3.0", + "mime", + "native-tls", + "nom", + "once_cell", + "quoted_printable", + "socket2", + "tokio", +] + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memfd" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +dependencies = [ + "rustix 0.37.20", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory-db" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +dependencies = [ + "hash-db", +] + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.4", + "itoa", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "parity-scale-codec" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2287753623c76f953acd29d15d8100bcab84d29db78fb6f352adb3c53e83b967" +dependencies = [ + "arrayvec 0.7.4", + "bitvec", + "byte-slice-cast", + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6937b5e67bfba3351b87b040d48352a2fcb6ad72f81855412ce97b45c8f110" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.0", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quoted_printable" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64 0.21.2", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustix" +version = "0.36.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e4d67015953998ad0eb82887a0eb0129e18a7e2f3b7b0f6c422fddcd503d62" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.37.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.2", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scale-bits" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd7aca73785181cc41f0bbe017263e682b585ca660540ba569133901d013ecf" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", +] + +[[package]] +name = "scale-decode" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0459d00b0dbd2e765009924a78ef36b2ff7ba116292d732f00eb0ed8e465d15" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-decode-derive", + "scale-info", + "smallvec", + "thiserror", +] + +[[package]] +name = "scale-decode-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4391f0dfbb6690f035f6d2a15d6a12f88cc5395c36bcc056db07ffa2a90870ec" +dependencies = [ + "darling 0.14.4", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-encode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0401b7cdae8b8aa33725f3611a051358d5b32887ecaa0fda5953a775b2d4d76" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-encode-derive", + "scale-info", + "smallvec", + "thiserror", +] + +[[package]] +name = "scale-encode-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "316e0fb10ec0fee266822bd641bab5e332a4ab80ef8c5b5ff35e5401a394f5a6" +dependencies = [ + "darling 0.14.4", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-info" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad560913365790f17cbf12479491169f01b9d46d29cfc7422bf8c64bdc61b731" +dependencies = [ + "bitvec", + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde", +] + +[[package]] +name = "scale-info-derive" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19df9bd9ace6cc2fe19387c96ce677e823e07d017ceed253e7bb3d1d1bd9c73b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-value" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2096d36e94ce9bf87d8addb752423b6b19730dc88edd7cc452bb2b90573f7a7" +dependencies = [ + "base58", + "blake2", + "either", + "frame-metadata", + "parity-scale-codec", + "scale-bits", + "scale-decode", + "scale-encode", + "scale-info", + "serde", + "thiserror", + "yap", +] + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.3", + "cfg-if", + "hashbrown 0.13.2", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3", + "rand_core 0.5.1", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "serde_json" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slothunter" +version = "0.1.0" +dependencies = [ + "anyhow", + "app_dirs2", + "array-bytes 6.1.0", + "clap", + "color-eyre", + "futures", + "lettre", + "parity-scale-codec", + "regex", + "reqwest", + "scale-decode", + "scale-value", + "serde", + "serde_json", + "sp-core", + "sp-runtime", + "subxt", + "tokio", + "toml", + "tracing", + "tracing-subscriber 0.3.17", + "vergen", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes", + "futures", + "httparse", + "log", + "rand 0.8.5", + "sha-1", +] + +[[package]] +name = "sp-application-crypto" +version = "23.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899492ea547816d5dfe9a5a2ecc32f65a7110805af6da3380aa4902371b31dc2" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-arithmetic" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6020576e544c6824a51d651bc8df8e6ab67cd59f1c9ac09868bb81a5199ded" +dependencies = [ + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "sp-std", + "static_assertions", +] + +[[package]] +name = "sp-core" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f18d9e2f67d8661f9729f35347069ac29d92758b59135176799db966947a7336" +dependencies = [ + "array-bytes 4.2.0", + "bitflags", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "parking_lot", + "paste", + "primitive-types", + "rand 0.8.5", + "regex", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-core-hashing", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror", + "tiny-bip39", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee599a8399448e65197f9a6cee338ad192e9023e35e31f22382964c3c174c68" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.7", + "sha2 0.10.7", + "sha3", + "sp-std", + "twox-hash", +] + +[[package]] +name = "sp-debug-derive" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f531814d2f16995144c74428830ccf7d94ff4a7749632b83ad8199b181140c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "sp-externalities" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f71c671e01a8ca60da925d43a1b351b69626e268b8837f8371e320cf1dd100" +dependencies = [ + "environmental", + "parity-scale-codec", + "sp-std", + "sp-storage", +] + +[[package]] +name = "sp-io" +version = "23.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d597e35a9628fe7454b08965b2442e3ec0f264b0a90d41328e87422cec02e99" +dependencies = [ + "bytes", + "ed25519", + "ed25519-dalek", + "futures", + "libsecp256k1", + "log", + "parity-scale-codec", + "rustversion", + "secp256k1", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std", + "sp-tracing", + "sp-trie", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keystore" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be3cdd67cc1d9c1db17c5cbc4ec4924054a8437009d167f21f6590797e4aa45" +dependencies = [ + "futures", + "parity-scale-codec", + "parking_lot", + "sp-core", + "sp-externalities", + "thiserror", +] + +[[package]] +name = "sp-panic-handler" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd2de46003fa8212426838ca71cd42ee36a26480ba9ffea983506ce03131033" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + +[[package]] +name = "sp-runtime" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21c5bfc764a1a8259d7e8f7cfd22c84006275a512c958d3ff966c92151e134d5" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std", + "sp-weights", +] + +[[package]] +name = "sp-runtime-interface" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e676128182f90015e916f806cba635c8141e341e7abbc45d25525472e1bbce8" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d5bd5566fe5633ec48dfa35ab152fd29f8a577c21971e1c6db9f28afb9bbb9" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "sp-state-machine" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef45d31f9e7ac648f8899a0cd038a3608f8499028bff55b6c799702592325b6" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot", + "rand 0.8.5", + "smallvec", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std", + "sp-trie", + "thiserror", + "tracing", +] + +[[package]] +name = "sp-std" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53458e3c57df53698b3401ec0934bea8e8cfce034816873c0b0abbd83d7bac0d" + +[[package]] +name = "sp-storage" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94294be83f11d4958cfea89ed5798f0b6605f5defc3a996948848458abbcc18e" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "sp-tracing" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357f7591980dd58305956d32f8f6646d0a8ea9ea0e7e868e46f53b68ddf00cec" +dependencies = [ + "parity-scale-codec", + "sp-std", + "tracing", + "tracing-core", + "tracing-subscriber 0.2.25", +] + +[[package]] +name = "sp-trie" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4eeb7ef23f79eba8609db79ef9cef242f994f1f87a3c0387b4b5f177fda74" +dependencies = [ + "ahash 0.8.3", + "hash-db", + "hashbrown 0.13.2", + "lazy_static", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot", + "scale-info", + "schnellru", + "sp-core", + "sp-std", + "thiserror", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-wasm-interface" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19c122609ca5d8246be6386888596320d03c7bc880959eaa2c36bcd5acd6846" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std", + "wasmtime", +] + +[[package]] +name = "sp-weights" +version = "20.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d084c735544f70625b821c3acdbc7a2fc1893ca98b85f1942631284692c75b" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "ss58-registry" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47a8ad42e5fc72d5b1eb104a5546937eaf39843499948bb666d6e93c62423b" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "subxt" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31a734d66fa935fbda56ba6a71d7e969f424c8c5608d416ba8499d71d8cbfc1f" +dependencies = [ + "base58", + "blake2", + "derivative", + "either", + "frame-metadata", + "futures", + "getrandom 0.2.10", + "hex", + "impl-serde", + "jsonrpsee", + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-decode", + "scale-encode", + "scale-info", + "scale-value", + "serde", + "serde_json", + "sp-core", + "sp-core-hashing", + "sp-runtime", + "subxt-macro", + "subxt-metadata", + "thiserror", + "tracing", +] + +[[package]] +name = "subxt-codegen" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2f231d97c145c564bd544212c0cc0c29c09ff516af199f4ce00c8e055f8138" +dependencies = [ + "frame-metadata", + "heck", + "hex", + "jsonrpsee", + "parity-scale-codec", + "proc-macro2", + "quote", + "scale-info", + "subxt-metadata", + "syn 2.0.18", + "thiserror", + "tokio", +] + +[[package]] +name = "subxt-macro" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e544e41e1c84b616632cd2f86862342868f62e11e4cd9062a9e3dbf5fc871f64" +dependencies = [ + "darling 0.20.1", + "proc-macro-error", + "subxt-codegen", + "syn 2.0.18", +] + +[[package]] +name = "subxt-metadata" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01ce5044c81db3404d38c56f1e69d72eff72c54e5913c9bba4c0b58d376031f" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-core-hashing", + "thiserror", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" + +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", + "cfg-if", + "fastrand", + "redox_syscall", + "rustix 0.37.20", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac 0.12.1", + "once_cell", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash", + "sha2 0.10.7", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber 0.3.17", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "trie-db" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" +dependencies = [ + "hash-db", + "hashbrown 0.13.2", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-root" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" +dependencies = [ + "hash-db", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "digest 0.10.7", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna 0.4.0", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vergen" +version = "8.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b3c89c2c7e50f33e4d35527e5bf9c11d6d132226dbbd1753f0fbe9f19ef88c6" +dependencies = [ + "anyhow", + "rustversion", + "time", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasmparser" +version = "0.102.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" +dependencies = [ + "indexmap", + "url", +] + +[[package]] +name = "wasmtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap", + "libc", + "log", + "object", + "once_cell", + "paste", + "psm", + "serde", + "target-lexicon", + "wasmparser", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-environ" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli", + "indexmap", + "log", + "object", + "serde", + "target-lexicon", + "thiserror", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" +dependencies = [ + "addr2line", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli", + "log", + "object", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" +dependencies = [ + "once_cell", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "paste", + "rand 0.8.5", + "rustix 0.36.14", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-types" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", + "wasmparser", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "xdg" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688597db5a750e9cad4511cb94729a078e274308099a0382b5b8203bbc767fee" +dependencies = [ + "home", +] + +[[package]] +name = "yap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2a7eb6d82a11e4d0b8e6bda8347169aff4ccd8235d039bba7c47482d977dcf7" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] diff --git a/Cargo.toml b/Cargo.toml index 5cf288e..944b2b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,42 @@ [package] authors = ["Xavier Lau "] build = "build.rs" -description = "" +description = "A bot for Polkadot parachain auction." edition = "2021" -homepage = "https://.hack.ink" +homepage = "https://slothunter.hack.ink" license = "GPL-3.0" -name = "" +name = "slothunter" readme = "README.md" -repository = "https://github.com/hack-ink/" +repository = "https://github.com/hack-ink/slothunter" version = "0.1.0" + +[features] +node-test = [] + +[build-dependencies] +# crates.io +vergen = { version = "8.2", features = ["build", "cargo", "git", "gitcl"] } + +[dependencies] +# crates.io +anyhow = { version = "1.0" } +app_dirs2 = { version = "2.5" } +array-bytes = { version = "6.1" } +clap = { version = "4.3", features = ["derive"] } +color-eyre = { version = "0.6" } +futures = { version = "0.3" } +lettre = { version = "0.10" } +parity-scale-codec = { version = "3.6" } +regex = { version = "1.8" } +reqwest = { version = "0.11", features = ["json"] } +scale-decode = { version = "0.7" } +scale-value = { version = "0.10" } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0" } +sp-core = { version = "21.0" } +sp-runtime = { version = "24.0" } +subxt = { version = "0.29" } +tokio = { version = "1.28", features = ["macros", "rt-multi-thread"] } +toml = { version = "0.7" } +tracing = { version = "0.1" } +tracing-subscriber = { version = "0.3" } diff --git a/README.md b/README.md index 00d358c..9b35a21 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,42 @@
-# \ -### \ +# Slothunter +### A bot for Polkadot parachain auction. [![License](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) -[![Checks](https://github.com/hack-ink//actions/workflows/checks.yml/badge.svg?branch=main)](https://github.com/hack-ink//actions/workflows/checks.yml) -[![Release](https://github.com/hack-ink//actions/workflows/release.yml/badge.svg)](https://github.com/hack-ink//actions/workflows/release.yml) -[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/hack-ink/)](https://github.com/hack-ink//tags) -[![GitHub code lines](https://tokei.rs/b1/github/hack-ink/)](https://github.com/hack-ink/) -[![GitHub last commit](https://img.shields.io/github/last-commit/hack-ink/?color=red&style=plastic)](https://github.com/hack-ink/) +[![Checks](https://github.com/hack-ink/slothunter/actions/workflows/checks.yml/badge.svg?branch=main)](https://github.com/hack-ink/slothunter/actions/workflows/checks.yml) +[![Release](https://github.com/hack-ink/slothunter/actions/workflows/release.yml/badge.svg)](https://github.com/hack-ink/slothunter/actions/workflows/release.yml) +[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/hack-ink/slothunter)](https://github.com/hack-ink/slothunter/tags) +[![GitHub code lines](https://tokei.rs/b1/github/hack-ink/slothunter)](https://github.com/hack-ink/slothunter) +[![GitHub last commit](https://img.shields.io/github/last-commit/hack-ink/slothunter?color=red&style=plastic)](https://github.com/hack-ink/slothunter)
+ +## Usage +```sh +# Use `--help` flag to get more information. +slothunter --help +``` + +### Configuration +To create a template configuration file for the program, run it directly for the first time and use the command below. +After that, press `CTRL-C`. +```sh +slothunter +# Or +slothunter -c config.toml +``` + +If a path is provided, it will create the configuration file at that location. +Otherwise, Slothunter will create the configuration file at the default path. +The default paths are: +``` +Linux: /home/alice/.config/slothunter +Windows: C:\Users\Alice\AppData\Roaming\slothunter +MacOS: /Users/Alice/Library/Application Support/slothunter +``` + +Open the configuration file and edit the items. The file contains highly detailed documentation for each item. + +### Addition +For more details, please refer to [guide.md](test/guide.md). diff --git a/build.rs b/build.rs index f6072f5..5f8f3b2 100644 --- a/build.rs +++ b/build.rs @@ -1,17 +1,19 @@ // crates.io -use vergen::{Config, ShaKind}; +use vergen::EmitBuilder; fn main() { - let mut config = Config::default(); + let mut emitter = EmitBuilder::builder(); - *config.git_mut().sha_kind_mut() = ShaKind::Short; + emitter.cargo_target_triple(); // Disable the git version if installed from . - if vergen::vergen(config.clone()).is_err() { - *config.git_mut().enabled_mut() = false; + if emitter.clone().git_sha(true).fail_on_error().emit().is_err() { + println!("cargo:rustc-env=VERGEN_GIT_SHA=crates.io"); - println!("cargo:rustc-env=VERGEN_GIT_SHA_SHORT=crates.io"); - - vergen::vergen(config).unwrap(); + emitter + } else { + *emitter.git_sha(true) } + .emit() + .unwrap(); } diff --git a/configuration-template.toml b/configuration-template.toml new file mode 100644 index 0000000..e8f7a88 --- /dev/null +++ b/configuration-template.toml @@ -0,0 +1,128 @@ +# Relaychain network. +# +# Possible values: "kusama", "polkadot". +network = "polkadot" +# GraphQL HTTP(S) URI. +# +# This line can be commented out. Otherwise, it will replace the default URI of `network`. +# graphql-endpoint = "http://127.0.0.1:3000/graphql" +# Network WS(S) URI. +# +# This line can be commented out. Otherwise, it will replace the default URI of `network`. +# node-endpoint = "ws://127.0.0.1:9944" +# Block subscription mode. +# +# Possible values: "best", "finalized". +# +# best: Subscribe to the best block, this strategy is faster and can increase your chances of +# winning the auction, but it becomes unsafe if there is a fork. +# +# finalized: Subscribe to the latest finalized block. +block-subscription-mode = "finalized" + +[bid] +# Parachain's identity you are bidding for. +para-id = 2000 +# Lease(s) you are bidding for. +# +# E.G. +# Bid for 8 leases. +# Example values: [0, 7], [10, 17], [100, 107] +leases = [0, 0] +# Bidding type. +# +# Possible values: "self-funded", "crowdloan". +type = "self-funded" +# The real account's public key. +# +# E.G. +# Secret Key URI `//Alice` is account: +# Network ID: substrate +# Secret seed: 0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a +# Public key (hex): 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d +# Account ID: 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d +# Public key (SS58): 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +# SS58 Address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +real = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" +# Delegate/proxy account's private key/seed. +# +# For better safety, Slothunter only allows the use of delegate/proxy accounts for bidding/contributing. +# +# E.G. +# Secret Key URI `//Bob` is account: +# Network ID: substrate +# Secret seed: 0x398f0c28f98885e046333d4a41c19cee4c37368a9832c6502f6cfd182e2aef89 +# Public key (hex): 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48 +# Account ID: 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48 +# Public key (SS58): 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty +# SS58 Address: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty +delegate = "0x398f0c28f98885e046333d4a41c19cee4c37368a9832c6502f6cfd182e2aef89" +# Upper limit of the bids. +# +# Slothunter will stop bidding if the minimum winning amount exceeds this value. +# +# Generally, this value should be equal to or greater than the increment. +upper-limit = "1000000000000000" +# Bid increment. +# +# This should be at least 1 DOT/KSM. +# +# Generally, the minimum value(1 DOT/KSM) should help you win the auction. +# However, if there are a lot of Slothunters or someone bids at the same time, you may lose the auction. +# To increase your chances of winning, consider increasing this value. +# +# Note that this value will serve as the default bid for the first bidder in case there are no other bidders. +# +# E.G. +# A : (0, 3) -> 10 +# B : (0, 1) -> 10 +# C : (2, 2) -> 5 +# D : (1, 3) -> 11 +# E : (3, 3) -> 16 +# You: (1, 2) -> x +# +# Current winners are `B`, `C`, and `E`, the threshold is calculated as `2 * 10 + 5 + 16 = 41`. +# Slothunter will calculate the value of `x` which is equal to `12` and then bid with the amount of `(x + increment)`. +# The calculation for winning the bid would be: `(x(12) + increment(1)) * 2 + 16 = 42 > 41`, hence, win. +# Since DOT's decimals are ten, there should be ten zeros here. +increment = "10000000000" + +[notification] +# Notification webhooks. +# +# Periodic notification will be sent to the following webhook addresses. +# +# If you don't wish to use any webhooks, leave this field empty. +# +# E.G. +# This can be integrated into: +# - A Discord channel using a Discord webhook. +# - A Telegram channel using a Telegram bot. +# - A Slack channel using a Slack webhook. +webhooks = [ + # "https://example.com/webhook", + # "https://example.com/webhook1", + # "https://example.com/webhook2", +] +# Notification mail configurations. +# +# If you don't wish to use any mail notifications, keep this(`[notification.mail]`) whole section commented out. +[notification.mail] +# Notification will be sent to the following recipients. +# receivers = [ +# "example1@gmail.com", +# "example2@gmail.com", +# "example@gmail.com", +# ] +# [notification.mail.sender] +# Sender's mail password: +# Note: +# This is usually an app-specific password instead of your account password. +# For example, you can generate an app-spceific password by following the instructions provided in this link: https://support.google.com/accounts/answer/185833?hl=en. +# password = "google mail app password" +# Sender's mail address. +# username = "example@gmail.com" +# SMTP server address. +# +# The default value is the SMTP server address of Gmail. +# stmp = "smtp.gmail.com" diff --git a/src/hunter.rs b/src/hunter.rs new file mode 100644 index 0000000..b07c830 --- /dev/null +++ b/src/hunter.rs @@ -0,0 +1,517 @@ +pub mod util; + +mod configuration; +pub use configuration::*; + +mod graphql; +pub use graphql::*; + +mod node; +pub use node::*; + +mod notification; +pub use notification::*; + +mod tx; +pub use tx::*; + +pub use crate::prelude::*; + +// std +use std::mem; +// crates.io +use futures::StreamExt; +use reqwest::Client; +use scale_value::ValueDef; +#[cfg(feature = "node-test")] use sp_core::{sr25519::Pair, Pair as _}; +#[cfg(feature = "node-test")] use subxt::tx::PairSigner; +use subxt::{config::polkadot::H256, OnlineClient, PolkadotConfig}; + +type BlockStream = std::pin::Pin< + Box>>, +>; +type Block = + subxt::blocks::Block>; + +#[derive(Debug)] +pub struct Hunter { + pub configuration: Configuration, + pub http: Client, + pub node: OnlineClient, + pub auction: Option, + pub auction_ending_period: BlockNumber, + pub auction_sample_length: BlockNumber, + pub auction_is_open: bool, + pub bidder: AccountId, +} +impl Hunter { + #[allow(unused)] + #[cfg(feature = "node-test")] + async fn tester() -> Self { + let configuration = Configuration { + graphql_endpoint: "http://127.0.0.1:3000/graphql".into(), + node_endpoint: "ws://127.0.0.1:9944".into(), + block_subscription_mode: BlockSubscriptionMode::Best, + token: Token { symbol: "UNIT", decimals: 12. }, + bid: Bid { + para_id: 2000, + leases: (0, 0), + watch_only: false, + r#type: BidType::SelfFunded, + real: array_bytes::hex2array_unchecked( + "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + ), + delegate: PairSigner::new(Pair::from_seed(&array_bytes::hex2array_unchecked( + "0x3c881bc4d45926680c64a7f9315eeda3dd287f8d598f3653d7c107799c5422b3", + ))), + upper_limit: 100_000_000_000_000, + increment: 1_000_000_000_000, + }, + notification: Notification { mail: None, webhooks: Vec::new() }, + }; + let node = OnlineClient::from_url(&configuration.node_endpoint).await.unwrap(); + + Self { + configuration, + http: util::http_json_client(), + node, + auction: None, + auction_ending_period: 0, + auction_sample_length: 0, + auction_is_open: false, + bidder: AccountId::default(), + } + } + + fn watch_only(&self) -> bool { + self.configuration.bid.watch_only + } + + fn is_self_funded(&self) -> bool { + self.configuration.bid.r#type == BidType::SelfFunded + } + + fn can_spend(&self, value: Balance) -> bool { + self.configuration.bid.upper_limit >= value + } + + fn is_bidder(&self, who: &AccountId, para_id: ParaId) -> bool { + who == &self.bidder && para_id == self.configuration.bid.para_id + } + + fn is_winner(&self, winners: &[Winner]) -> bool { + winners.iter().any(|w| self.is_bidder(&w.who, w.para_id)) + } + + pub async fn start(&mut self) -> Result<()> { + let mut block_stream = self.initialize().await?; + let mut has_bid = false; + + loop { + let block = if has_bid { + tracing::info!("skip 1 block after tendering"); + + has_bid = false; + + Self::next_block(&mut block_stream).await?; + + Self::next_block(&mut block_stream).await? + } else { + Self::next_block(&mut block_stream).await? + }; + let block_height = block.number(); + let block_hash = block.hash(); + + tracing::info!("block(#{block_height}, {block_hash:?})"); + + self.update(&block_hash).await?; + self.hunt(block_height, block_hash, &mut has_bid).await?; + } + } + + async fn initialize(&mut self) -> Result { + self.auction_ending_period = self.auction_ending_period().await?; + self.auction_sample_length = self.auction_sample_length().await?; + + let mut block_stream = match self.configuration.block_subscription_mode { + BlockSubscriptionMode::Best => self.node.blocks().subscribe_best().await?, + BlockSubscriptionMode::Finalized => self.node.blocks().subscribe_finalized().await?, + }; + let block_hash = Self::next_block(&mut block_stream).await?.hash(); + + self.check(&block_hash).await?; + + self.auction = self.auction_at(&block_hash).await?; + self.auction_is_open = self.auction.is_some(); + self.bidder = if self.is_self_funded() { + self.configuration.bid.real + } else { + util::crowdloan_id_of(self.fund_index_at(&block_hash).await?.expect(&format!( + "no existing crowdloan found for parachain({})", + self.configuration.bid.para_id, + ))) + }; + + Ok(block_stream) + } + + async fn next_block(block_stream: &mut BlockStream) -> Result { + Ok(block_stream.next().await.ok_or(anyhow::anyhow!("failed to get the next block"))??) + } + + async fn check(&self, block_hash: &H256) -> Result<()> { + tracing::info!("############################################################"); + + { + let uri = &self.configuration.node_endpoint; + + if util::check_ws_uri(uri) { + tracing::info!("node endpoint({uri})"); + } else { + panic!("invalid node endpoint({uri})"); + } + } + + { + let uri = &self.configuration.graphql_endpoint; + + if util::check_http_uri(uri) { + tracing::info!("graphql endpoint({uri})"); + } else { + panic!("invalid graphql endpoint({uri})"); + } + } + + tracing::info!("bid"); + tracing::info!(" hunting a slot for parachain({})", self.configuration.bid.para_id); + tracing::info!(" watch-only({})", self.watch_only()); + + if !self.watch_only() { + const E_DELEGATE: &str = "no delegate(`ProxyType::All` or `ProxyType::Auction`) found, please check your configurations"; + + tracing::info!(" funding type({})", self.configuration.bid.r#type); + + let real = &self.configuration.bid.real; + let delegate = self.configuration.bid.delegate.account_id(); + + tracing::info!(" real account({})", array_bytes::bytes2hex("0x", real)); + tracing::info!(" proxy delegate({})", array_bytes::bytes2hex("0x", delegate.0)); + + let ValueDef::Variant(v) = self + .proxies_at(block_hash, real) + .await? + .expect(E_DELEGATE) + .into_iter() + .find(|p| p.delegate.r#type == delegate.0) + .expect(E_DELEGATE) + .proxy_type + .value else { panic!("{E_DELEGATE}") }; + + if !matches!(v.name.as_str(), "All" | "Auction") { + panic!("{E_DELEGATE}") + } + + tracing::info!(" proxy type({})", v.name); + tracing::info!( + " upper limit {}", + self.configuration.token.fmt(self.configuration.bid.upper_limit) + ); + + let increment = self.configuration.bid.increment; + + tracing::info!(" increment {}", self.configuration.token.fmt(increment)); + + assert!( + increment as f64 / 10_f64.powf(self.configuration.token.decimals) >= 1., + "increment should be at least {}(1)", + self.configuration.token.symbol + ); + } + + tracing::info!("notification"); + tracing::info!(" webhooks"); + + self.configuration.notification.webhooks.iter().for_each(|u| { + tracing::info!(" uri({})", u); + }); + + if let Some(m) = &self.configuration.notification.mail { + tracing::info!(" mail"); + tracing::info!(" sender({})", m.sender.username.email); + + if util::check_smtp_uri(&m.sender.smtp) { + tracing::info!(" smtp({})", m.sender.smtp); + } else { + panic!("invalid smtp({})", m.sender.smtp); + } + + for to in &m.receivers { + tracing::info!(" receiver({})", to.email); + } + } + + tracing::info!("############################################################"); + + Ok(()) + } + + async fn update(&mut self, block_hash: &H256) -> Result<()> { + let previous_auction = { + let auction = self.auction_at(block_hash).await?; + + mem::replace(&mut self.auction, auction) + }; + + self.auction_is_open = match (self.auction_is_open, self.auction.is_some()) { + // An auction has just been opened. + (false, true) => { + let a = self.auction.as_ref().expect("`self.auction` must be some"); + + self.notify_mail(a, "auction has just been started"); + self.notify_webhook(a, "auction has just been started").await; + + true + }, + // An auction has just been closed. + (true, false) => { + let a = previous_auction.expect("`previous_auction` must be some"); + + self.notify_mail(&a, "auction has just been closed"); + self.notify_webhook(&a, "auction has just been closed").await; + + false + }, + // Still in/not in an auction. + (_, is_some) => is_some, + }; + + Ok(()) + } + + async fn hunt( + &self, + block_height: BlockNumber, + block_hash: H256, + has_bid: &mut bool, + ) -> Result<()> { + let Some(auction) = &self.auction else { return Ok(()) }; + let end_at = auction.ending_period_start_at + self.auction_ending_period; + + if end_at <= block_height { + return Ok(()); + } + + tracing::info!(" {}", auction.fmt(block_height, end_at)); + + let Some(self_bid) = self.analyze_bidders(&block_hash, auction, has_bid).await? else { return Ok(()); }; + let Some(winning) = self.analyze_winning( + &block_hash, + block_height, + auction, + ).await? else { return Ok(()); }; + + self.analyze_winners(block_height, &block_hash, auction, &winning, self_bid, has_bid).await + } + + async fn analyze_bidders( + &self, + block_hash: &H256, + auction: &AuctionDetail, + has_bid: &mut bool, + ) -> Result> { + tracing::info!(" bidders"); + + let bidders = self.bidders_at(block_hash).await?; + + if bidders.is_empty() { + tracing::info!(" no bidders were found"); + + self.try_tender(auction.index, self.configuration.bid.increment, 0).await?; + + *has_bid = true; + + return Ok(None); + } + + let mut self_bid = 0; + + bidders.into_iter().for_each(|b| { + tracing::info!(" {}", b.fmt(&self.configuration.token)); + + if let Some(l) = &b.last_accepted_bid { + tracing::info!(" last accepted bid is {}", l.fmt(&self.configuration.token)); + + if self.is_bidder(&b.who, b.para_id) { + self_bid = l.amount; + } + } + }); + + Ok(Some(self_bid)) + } + + async fn analyze_winning( + &self, + block_hash: &H256, + block_height: BlockNumber, + auction: &AuctionDetail, + ) -> Result> { + tracing::info!(" winning"); + + let winning = self + .winning_at(block_hash, block_height, auction.ending_period_start_at) + .await? + .expect("winning must be some if bidders is not empty"); + + if winning.0.iter().all(Option::is_none) { + tracing::info!(" no winning has been calculated yet"); + + return Ok(None); + } + + winning + .fmt(&self.configuration.token, auction.first_lease_period) + .into_iter() + .for_each(|w| tracing::info!(" {w}")); + + Ok(Some(winning)) + } + + async fn analyze_winners( + &self, + block_height: BlockNumber, + block_hash: &H256, + auction: &AuctionDetail, + winning: &Winning, + self_bid: Balance, + has_bid: &mut bool, + ) -> Result<()> { + tracing::info!(" winner(s)"); + + let (winners, threshold) = winning.result(); + let notification = winners + .iter() + .map(|w| { + let n = w.fmt(&self.configuration.token, auction.first_lease_period); + + tracing::info!(" {n}"); + + n + }) + .collect::>() + .join(","); + + self.notify_webhook( + &serde_json::json!({ + "block": { + "height": block_height, + "hash": block_hash, + }, + "winning": winning.0.as_slice(), + "winners": winners, + + }), + &format!("at block(#{block_height}, {block_hash:?})\n{notification}"), + ) + .await; + + if !self.is_winner(&winners) { + let leases = ( + self.configuration.bid.leases.0 - auction.first_lease_period, + self.configuration.bid.leases.1 - auction.first_lease_period, + ); + let bid = + winning.minimum_bid_to_win(&leases, threshold) + self.configuration.bid.increment; + + self.try_tender(auction.index, bid, self_bid).await?; + + *has_bid = true; + } + + Ok(()) + } + + async fn try_tender(&self, auction_index: u32, bid: Balance, self_bid: Balance) -> Result<()> { + if self.watch_only() { + fn log(mode: &str, bid: String) { + tracing::warn!(" slothunter is running under the watch-only mode, {mode} {bid} manually to win"); + } + + if self.is_self_funded() { + log("bid", self.configuration.token.fmt(bid)) + } else { + log("contribute", self.configuration.token.fmt(bid - self_bid)) + } + + Ok(()) + } else { + fn log(mode: &str, bid: String, upper_limit: String) -> String { + format!(" skip {mode} {bid} because it exceeds the upper limit {upper_limit}") + } + + let notification; + + if self.is_self_funded() { + if self.can_spend(bid) { + if let Err(e) = self.bid(auction_index, bid).await? { + let n = format!(" bid failed due to error({e:?})"); + + tracing::error!("{n}"); + + notification = n.trim_start_matches(' ').to_string(); + } else { + let n = format!(" bid with {}", self.configuration.token.fmt(bid)); + + tracing::info!("{n}"); + + notification = n.trim_start_matches(' ').to_string(); + } + } else { + let n = log( + "bidding", + self.configuration.token.fmt(bid), + self.configuration.token.fmt(self.configuration.bid.upper_limit), + ); + + tracing::warn!("{n}"); + + notification = n.trim_start_matches(' ').to_string(); + } + } else { + let bid = bid - self_bid; + + if self.can_spend(bid) { + if let Err(e) = self.contribute(bid).await? { + let n = format!(" contribute failed due to error({e:?})"); + + tracing::error!("{n}"); + + notification = n.trim_start_matches(' ').to_string(); + } else { + let n = + format!(" contribute with {}", self.configuration.token.fmt(bid)); + + tracing::info!("{n}"); + + notification = n.trim_start_matches(' ').to_string(); + } + } else { + let n = log( + "contributing", + self.configuration.token.fmt(bid), + self.configuration.token.fmt(self.configuration.bid.upper_limit), + ); + + tracing::warn!("{n}"); + + notification = n.trim_start_matches(' ').to_string(); + } + } + + self.notify_mail(&None::<()>, ¬ification); + self.notify_webhook(&None::<()>, ¬ification).await; + + Ok(()) + } + } +} diff --git a/src/hunter/configuration.rs b/src/hunter/configuration.rs new file mode 100644 index 0000000..6191181 --- /dev/null +++ b/src/hunter/configuration.rs @@ -0,0 +1,254 @@ +// std +use std::{ + fmt::{Debug, Display, Formatter, Result as FmtResult}, + fs, + path::PathBuf, +}; +// crates.io +use app_dirs2::{AppDataType, AppInfo}; +use serde::Deserialize; +use sp_core::{sr25519::Pair, Pair as _}; +use subxt::tx::PairSigner; +// slothunter +use crate::hunter::*; + +pub const SLOTHUNTER: AppInfo = AppInfo { name: "slothunter", author: "Xavier Lau" }; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct ConfigurationToml { + pub network: Network, + pub graphql_endpoint: Option, + pub node_endpoint: Option, + pub block_subscription_mode: BlockSubscriptionMode, + pub bid: BidToml, + pub notification: NotificationToml, +} +impl ConfigurationToml { + pub fn load(path: Option) -> Result { + fn initialize(path: &PathBuf) -> Result { + if !path.is_file() { + let s = include_str!("../../configuration-template.toml"); + + fs::write(path, s)?; + + Ok(toml::from_str(s)?) + } else { + Ok(toml::from_str(&fs::read_to_string(path)?)?) + } + } + + let path = path.unwrap_or(app_dirs2::app_root(AppDataType::UserConfig, &SLOTHUNTER)?); + + if matches!(path.extension().map(|s| s.to_str().unwrap_or_default()), Some("toml")) { + return initialize(&path); + } + if !path.is_dir() { + fs::create_dir_all(&path)?; + } + + let path = path.join("config.toml"); + + initialize(&path) + } + + pub fn try_into_configuration(self) -> Result { + let Self { + network, + graphql_endpoint, + node_endpoint, + block_subscription_mode, + bid: + BidToml { para_id, leases, watch_only, r#type, real, delegate, upper_limit, increment }, + notification: NotificationToml { mail, webhooks }, + } = self; + let graphql_endpoint = + graphql_endpoint.unwrap_or_else(|| network.graphql_endpoint().to_owned()); + let node_endpoint = node_endpoint.unwrap_or_else(|| network.node_endpoint().to_owned()); + + Ok(Configuration { + graphql_endpoint, + node_endpoint, + block_subscription_mode, + token: network.token(), + bid: Bid { + para_id, + leases, + watch_only, + r#type, + real: array_bytes::hex2array(real) + .map_err(|e| anyhow::anyhow!("invalid public key, {e:?}"))?, + delegate: PairSigner::new(Pair::from_seed( + &array_bytes::hex2array(delegate) + .map_err(|e| anyhow::anyhow!("invalid seed, {e:?}"))?, + )), + upper_limit: upper_limit.parse()?, + increment: increment.parse()?, + }, + notification: Notification { + mail: mail + .map(|m| -> Result<_> { + Ok(Mail { + sender: Sender { + username: m.sender.username.parse()?, + password: m.sender.password, + smtp: m.sender.smtp, + }, + receivers: m + .receivers + .into_iter() + .map(|r| r.parse()) + .collect::>()?, + }) + }) + .transpose()?, + webhooks, + }, + }) + } +} +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct BidToml { + pub para_id: ParaId, + pub leases: SlotRange, + pub watch_only: bool, + pub r#type: BidType, + pub real: String, + pub delegate: String, + pub upper_limit: String, + pub increment: String, +} +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct NotificationToml { + pub mail: Option, + pub webhooks: Vec, +} +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct MailToml { + pub sender: SenderToml, + pub receivers: Vec, +} +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct SenderToml { + pub username: String, + pub password: String, + pub smtp: String, +} + +#[derive(Debug)] +pub struct Configuration { + pub graphql_endpoint: String, + pub node_endpoint: String, + pub block_subscription_mode: BlockSubscriptionMode, + pub token: Token, + pub bid: Bid, + pub notification: Notification, +} +pub struct Bid { + pub para_id: ParaId, + pub leases: SlotRange, + pub watch_only: bool, + pub r#type: BidType, + pub real: AccountId, + pub delegate: PairSigner, + pub upper_limit: Balance, + pub increment: Balance, +} +impl Debug for Bid { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + f.debug_struct("Configuration") + .field("para_id", &self.para_id) + .field("real", &array_bytes::bytes2hex("0x", self.real)) + .field("type", &self.r#type) + .field("delegate", &self.delegate.account_id()) + .finish() + } +} +#[derive(Debug)] +pub struct Notification { + pub mail: Option, + pub webhooks: Vec, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum BlockSubscriptionMode { + Best, + Finalized, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum Network { + Polkadot, + Kusama, +} +impl Network { + pub fn graphql_endpoint(&self) -> &'static str { + match self { + Self::Polkadot => "https://polkadot.explorer.subsquid.io/graphql", + Self::Kusama => "https://kusama.explorer.subsquid.io/graphql", + } + } + + pub fn node_endpoint(&self) -> &'static str { + match self { + Self::Polkadot => "wss://rpc.polkadot.io:443", + Self::Kusama => "wss://kusama-rpc.polkadot.io:443", + } + } + + pub fn token(&self) -> Token { + match self { + Self::Polkadot => Token { symbol: "DOT", decimals: 10. }, + Self::Kusama => Token { symbol: "KSM", decimals: 12. }, + } + } +} + +#[derive(Debug, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum BidType { + SelfFunded, + Crowdloan, +} +impl Display for BidType { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + match self { + Self::SelfFunded => write!(f, "self-funded"), + Self::Crowdloan => write!(f, "crowdloan"), + } + } +} + +#[derive(Debug)] +pub struct Token { + pub symbol: &'static str, + pub decimals: f64, +} +impl Token { + pub fn fmt(&self, balance: Balance) -> String { + format!("{}({})", self.symbol, balance as f64 / 10_f64.powf(self.decimals)) + } +} + +impl Hunter { + pub async fn from_configuration(configuration: Configuration) -> Result { + let node = OnlineClient::from_url(&configuration.node_endpoint).await?; + + Ok(Self { + configuration, + http: util::http_json_client(), + node, + auction: None, + auction_ending_period: 0, + auction_sample_length: 0, + auction_is_open: false, + bidder: AccountId::default(), + }) + } +} diff --git a/src/hunter/graphql.rs b/src/hunter/graphql.rs new file mode 100644 index 0000000..ceabd0d --- /dev/null +++ b/src/hunter/graphql.rs @@ -0,0 +1,122 @@ +// std +use std::fmt::{Display, Formatter, Result as FmtResult}; +// crates.io +use serde::de::DeserializeOwned; +// slothunter +use crate::hunter::*; + +#[derive(Debug)] +pub struct Query<'a> { + pub limit: u32, + pub name: &'a str, + pub args_json_contains: Option<&'a str>, +} +#[allow(unused)] +impl<'a> Query<'a> { + pub fn new(name: &'a str) -> Self { + Self { limit: 1, name, args_json_contains: None } + } + + pub fn limit(mut self, limit: u32) -> Self { + self.limit = limit; + + self + } + + pub fn name(mut self, name: &'a str) -> Self { + self.name = name; + + self + } + + pub fn args_json_contains(mut self, args_json_contains: &'a str) -> Self { + self.args_json_contains = Some(args_json_contains); + + self + } +} +impl<'a> Display for Query<'a> { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + write!( + f, + "{{\ + \"query\":\"{{\ + events(\ + limit:{},\ + orderBy:block_id_DESC,\ + where:{{\ + name_eq:\\\"{}\\\"\ + {}\ + }}\ + ){{\ + args,\ + block{{height}}\ + }}\ + }}\"\ + }}", + self.limit, + self.name, + if let Some(args_json_contains) = self.args_json_contains { + format!(",args_jsonContains:\\\"{}\\\"", args_json_contains) + } else { + "".into() + } + ) + } +} +#[test] +fn query_string_should_work() { + assert_eq!( + Query::new("Auctions.BidAccepted") + .args_json_contains( + "{{\\\\\\\"bidder\\\\\\\":\\\\\\\"0x1234\\\\\\\",\\\\\\\"paraId\\\\\\\":2000}}", + ) + .to_string(), + "{\"query\":\"{events(limit:1,orderBy:block_id_DESC,where:{name_eq:\\\"Auctions.BidAccepted\\\",args_jsonContains:\\\"{{\\\\\\\"bidder\\\\\\\":\\\\\\\"0x1234\\\\\\\",\\\\\\\"paraId\\\\\\\":2000}}\\\"}){args,block{height}}}\"}" + ); +} + +impl Hunter { + pub async fn query<'a, D>(&self, query: Query<'a>) -> Result + where + D: DeserializeOwned, + { + Ok(self + .http + .post(&self.configuration.graphql_endpoint) + .body(query.to_string()) + .send() + .await? + .json() + .await?) + } + + pub async fn last_accepted_bid_of( + &self, + who: &str, + para_id: ParaId, + ) -> Result> { + let mut events = self + .query::>>( + Query::new("Auctions.BidAccepted").args_json_contains(&format!( + "{{\\\\\\\"bidder\\\\\\\":\\\\\\\"{who}\\\\\\\",\\\\\\\"paraId\\\\\\\":{para_id}}}", + )), + ) + .await? + .data + .events; + + if events.is_empty() { + Ok(None) + } else { + let e = events.remove(0); + + Ok(Some(AcceptedBid { + at: e.block.height, + amount: e.args.amount.parse().expect("`amount` must be invalid digit"), + first_slot: e.args.first_slot, + last_slot: e.args.last_slot, + })) + } + } +} diff --git a/src/hunter/node.rs b/src/hunter/node.rs new file mode 100644 index 0000000..d082bfa --- /dev/null +++ b/src/hunter/node.rs @@ -0,0 +1,176 @@ +// crates.io +use parity_scale_codec::Decode; +use serde::Deserialize; +use subxt::{ + config::polkadot::H256, + dynamic::{self, At, Value}, +}; +// slothunter +use crate::hunter::*; + +const E_DE: &str = "failed to decode/deserialize, type need to be updated"; +const E_TYPE_CONVERSION: &str = "type conversion never fails"; + +impl Hunter { + pub async fn auction_ending_period(&self) -> Result { + Ok(self + .node + .constants() + .at(&dynamic::constant("Auctions", "EndingPeriod"))? + .to_value()? + .as_u128() + .expect(E_TYPE_CONVERSION) as _) + } + + pub async fn auction_sample_length(&self) -> Result { + Ok(self + .node + .constants() + .at(&dynamic::constant("Auctions", "SampleLength"))? + .to_value()? + .as_u128() + .expect(E_TYPE_CONVERSION) as _) + } + + pub async fn proxies_at( + &self, + block: &H256, + real: &AccountId, + ) -> Result>> { + self.node + .storage() + .at(block.to_owned()) + .fetch(&dynamic::storage("Proxy", "Proxies", vec![Value::from_bytes(real)])) + .await? + .map(|p| { + // https://github.com/paritytech/substrate/blob/51b2f0ed6af8dd4facb18f1a489e192fd0673f7b/frame/proxy/src/lib.rs#L573 + let (p, _) = + <(Vec>, Balance)>::deserialize(p.to_value()?) + .expect(E_DE); + let r: Result> = Ok(p.into_iter().map(|p| p.r#type).collect()); + + r + }) + .transpose() + } + + pub async fn auction_at(&self, block: &H256) -> Result> { + const E_STORAGE_TYPE: &str = "`AuctionInfo` has an invalid storage type"; + + if let Some(auction_info) = self + .node + .storage() + .at(block.to_owned()) + .fetch(&dynamic::storage_root("Auctions", "AuctionInfo")) + .await? + { + let auction_info = auction_info.to_value()?; + let auction_counter = self + .node + .storage() + .at(block.to_owned()) + .fetch(&dynamic::storage_root("Auctions", "AuctionCounter")) + .await? + .expect("`Auction::AuctionCounter` must exist"); + let auction_counter = auction_counter.to_value()?; + + Ok(Some(AuctionDetail { + index: auction_counter.as_u128().expect(E_TYPE_CONVERSION) as _, + first_lease_period: auction_info + .at(0) + .expect(E_STORAGE_TYPE) + .as_u128() + .expect(E_TYPE_CONVERSION) as _, + ending_period_start_at: auction_info + .at(1) + .expect(E_STORAGE_TYPE) + .as_u128() + .expect(E_TYPE_CONVERSION) as _, + })) + } else { + Ok(None) + } + } + + pub async fn fund_index_at(&self, block: &H256) -> Result> { + if let Some(f) = self + .node + .storage() + .at(block.to_owned()) + .fetch(&dynamic::storage( + "Crowdloan", + "Funds", + vec![Value::u128(self.configuration.bid.para_id as _)], + )) + .await? + { + Ok(Some( + f.to_value()?.at("fund_index").expect(E_DE).as_u128().expect(E_TYPE_CONVERSION) + as _, + )) + } else { + Ok(None) + } + } + + pub async fn bidders_at(&self, block: &H256) -> Result> { + let mut bidders = Vec::new(); + let mut bidders_storage = self + .node + .storage() + .at(block.to_owned()) + .iter(dynamic::storage_root("Auctions", "ReservedAmounts"), 32) + .await?; + + while let Some((k, v)) = bidders_storage.next().await? { + // twox64_concat + // (twox128(b"Auctions") + twox128(b"ReservedAmounts") + twox64(key)).len() = 40 + // key = k.0[40..] + let (who, para_id) = <(AccountId, ParaId)>::decode(&mut &k.0[40..]).expect(E_DE); + let existing_deposit = self + .node + .storage() + .at(block.to_owned()) + .fetch(&dynamic::storage("Slots", "Leases", vec![Value::u128(para_id as _)])) + .await? + .and_then(|l: dynamic::DecodedValueThunk| { + SLeases::decode(&mut &*l.into_encoded()) + .expect(E_DE) + .into_iter() + .filter_map(|l| l.and_then(|(w, a)| if who == w { Some(a) } else { None })) + .max() + }) + .unwrap_or_default(); + let last_accepted_bid = + self.last_accepted_bid_of(&array_bytes::bytes2hex("0x", who), para_id).await?; + + bidders.push(Bidder { + who, + para_id, + reserved: v.to_value()?.as_u128().expect(E_TYPE_CONVERSION), + existing_deposit, + last_accepted_bid, + }); + } + + Ok(bidders) + } + + pub async fn winning_at( + &self, + block: &H256, + now: BlockNumber, + ending_period_start_at: BlockNumber, + ) -> Result> { + let winning_offset = + util::winning_offset_of(now, ending_period_start_at, self.auction_sample_length); + + Ok(self + .node + .storage() + .at(block.to_owned()) + .fetch(&dynamic::storage("Auctions", "Winning", vec![Value::u128(winning_offset as _)])) + .await? + .map(|w| Winning::of(SWinning::decode(&mut &*w.into_encoded()).expect(E_DE)))) + } +} diff --git a/src/hunter/notification.rs b/src/hunter/notification.rs new file mode 100644 index 0000000..1aaf429 --- /dev/null +++ b/src/hunter/notification.rs @@ -0,0 +1,67 @@ +// crates.io +use lettre::{ + message::Mailbox, transport::smtp::authentication::Credentials, Message, SmtpTransport, + Transport, +}; +use serde::Serialize; +// slothunter +use crate::hunter::*; + +#[derive(Debug)] +pub struct Mail { + pub sender: Sender, + pub receivers: Vec, +} +#[derive(Debug)] +pub struct Sender { + pub username: Mailbox, + pub password: String, + pub smtp: String, +} + +impl Hunter { + pub fn notify_mail(&self, object: &S, addition: &str) + where + S: Serialize, + { + if let Some(m) = &self.configuration.notification.mail { + let notification = serde_json::to_string(&serde_json::json!({ + "object": object, + "addition": addition, + })) + .expect("json must be valid"); + + for to in &m.receivers { + let mail = Message::builder() + .from(m.sender.username.clone()) + .to(to.to_owned()) + .subject("Slothunter") + .body(notification.clone()) + .expect("message must be valid"); + let smtp_transport = SmtpTransport::relay(&m.sender.smtp) + .expect("smtp must be valid") + .credentials(Credentials::new( + m.sender.username.email.to_string(), + m.sender.password.clone(), + )) + .build(); + + let _ = smtp_transport.send(&mail); + } + } + } + + pub async fn notify_webhook(&self, object: &S, addition: &str) + where + S: Serialize, + { + let json = serde_json::json!({ + "object": object, + "addition": addition, + }); + + for u in &self.configuration.notification.webhooks { + let _ = self.http.post(u).json(&json).send().await; + } + } +} diff --git a/src/hunter/tx.rs b/src/hunter/tx.rs new file mode 100644 index 0000000..7a1b155 --- /dev/null +++ b/src/hunter/tx.rs @@ -0,0 +1,169 @@ +// crates.io +use subxt::{ + dynamic::{self, Value}, + tx::TxPayload, + Error, +}; +// slothunter +use crate::hunter::*; + +impl Hunter { + async fn tx(&self, call: &C) -> Result + where + C: TxPayload, + { + match self + .node + .tx() + .sign_and_submit_then_watch_default(call, &self.configuration.bid.delegate) + .await? + .wait_for_in_block() + .await? + .wait_for_success() + .await + { + Ok(r) => Ok(r + // Always using proxy in production, this must be some. + .find_first::()? + .map(EProxyExecuted::into_dispatch_result) + .unwrap_or_else(|| { + tracing::warn!("this log should only appear in a test"); + + Ok(()) + })), + Err(Error::Runtime(e)) => Ok(Err(e.to_string())), + Err(e) => Err(e)?, + } + } + + pub async fn bid(&self, auction_index: u32, value: Balance) -> Result { + let bid = dynamic::tx( + "Auctions", + "bid", + vec![ + Value::u128(self.configuration.bid.para_id as _), + Value::u128(auction_index as _), + Value::u128(self.configuration.bid.leases.0 as _), + Value::u128(self.configuration.bid.leases.1 as _), + Value::u128(value), + ], + ); + let proxied_bid = util::proxy_of(&self.configuration.bid.real, bid); + + self.tx(&proxied_bid).await + } + + pub async fn contribute(&self, value: Balance) -> Result { + let contribute = dynamic::tx( + "Crowdloan", + "contribute", + vec![ + Value::u128(self.configuration.bid.para_id as _), + Value::u128(value), + Value::unnamed_variant("None", []), + ], + ); + let proxied_contribute = util::proxy_of(&self.configuration.bid.real, contribute); + + self.tx(&proxied_contribute).await + } +} + +#[cfg(feature = "node-test")] +#[tokio::test] +async fn tx_should_work() { + let hunter = Hunter::tester().await; + + { + // Use `Alice//stash` to transfer funds from `Alice` to `Alice//stash`. + let transfer = dynamic::tx( + "Balances", + "transfer", + vec![ + Value::unnamed_variant( + "Id", + [Value::from_bytes(hunter.configuration.bid.delegate.account_id())], + ), + Value::u128(1_000_000_000_000), + ], + ); + let proxied_transfer = util::proxy_of(&hunter.configuration.bid.real, transfer); + + assert!(hunter.tx(&proxied_transfer).await.unwrap().is_ok()); + } + + { + // Use `Alice//stash` to transfer too much funds from `Alice` to `Alice//stash`. + let transfer_too_much = dynamic::tx( + "Balances", + "transfer", + vec![ + Value::unnamed_variant( + "Id", + [Value::from_bytes(hunter.configuration.bid.delegate.account_id())], + ), + Value::u128(10_000_000_000_000_000_000), + ], + ); + let proxied_transfer_too_much = + util::proxy_of(&hunter.configuration.bid.real, transfer_too_much); + + assert_eq!( + hunter.tx(&proxied_transfer_too_much).await.unwrap(), + Err("Value { value: Variant(Variant { name: \"Token\", values: Unnamed([Value { value: Variant(Variant { name: \"FundsUnavailable\", values: Unnamed([]) }), context: 27 }]) }), context: 25 }".into()) + ); + } + + { + // Transfer too much funds from `Alice//stash` to `Alice`. + let transfer_too_much = dynamic::tx( + "Balances", + "transfer", + vec![ + Value::unnamed_variant("Id", [Value::from_bytes(hunter.configuration.bid.real)]), + Value::u128(10_000_000_000_000_000_000), + ], + ); + + assert_eq!( + hunter.tx(&transfer_too_much).await.unwrap(), + Err("Token error: Funds are unavailable.".into()) + ); + } + + { + // Transfer all funds from `Alice//stash` to `Alice`. + // Note: after this, `Alice//stash` will no longer be available for other tests. + let transfer_all = dynamic::tx( + "Balances", + "transfer_all", + vec![ + Value::unnamed_variant("Id", [Value::from_bytes(hunter.configuration.bid.real)]), + Value::bool(true), + ], + ); + + assert!(hunter.tx(&transfer_all).await.unwrap().is_ok()); + } + + { + // Use `Alice//stash` to transfer funds from `Alice` to `Alice//stash`. + let transfer = dynamic::tx( + "Balances", + "transfer", + vec![ + Value::unnamed_variant( + "Id", + [Value::from_bytes(hunter.configuration.bid.delegate.account_id())], + ), + Value::u128(1_000_000_000_000), + ], + ); + let proxied_transfer = util::proxy_of(&hunter.configuration.bid.real, transfer); + + assert_eq!( + hunter.tx(&proxied_transfer).await.unwrap_err().to_string(), + "Rpc error: RPC error: RPC call failed: ErrorObject { code: ServerError(1010), message: \"Invalid Transaction\", data: Some(RawValue(\"Inability to pay some fees (e.g. account balance too low)\")) }".to_string() + ); + } +} diff --git a/src/hunter/util.rs b/src/hunter/util.rs new file mode 100644 index 0000000..b91eb88 --- /dev/null +++ b/src/hunter/util.rs @@ -0,0 +1,240 @@ +// crates.io +use parity_scale_codec::{Decode, Encode}; +use regex::Regex; +use reqwest::{ + header::{HeaderMap, CONTENT_TYPE}, + Client, ClientBuilder, +}; +use scale_value::Composite; +use serde::ser::Serializer; +use sp_runtime::{traits::AccountIdConversion, TypeId}; +use subxt::{ + dynamic::{self, Value}, + tx::Payload, +}; +// slothunter +use crate::hunter::*; + +const E_REGEX_MUST_BE_VALID: &str = "regex must be valid"; + +pub fn check_http_uri(uri: &str) -> bool { + Regex::new(r"^https?://[^\s/$.?#].[^\s]*$").expect(E_REGEX_MUST_BE_VALID).is_match(uri) +} +pub fn check_ws_uri(uri: &str) -> bool { + Regex::new(r"^wss?://[^\s/$.?#].[^\s]*$").expect(E_REGEX_MUST_BE_VALID).is_match(uri) +} +pub fn check_smtp_uri(uri: &str) -> bool { + Regex::new(r"^[a-zA-Z0-9.-]+(:\d+)?$").expect(E_REGEX_MUST_BE_VALID).is_match(uri) +} +#[test] +fn check_uri_should_work() { + assert!(check_http_uri("http://localhost:8080/path/to/file.html")); + assert!(check_http_uri("https://www.example.com")); + assert!(!check_http_uri("invalid url")); + + assert!(check_ws_uri("ws://localhost:8080/socket")); + assert!(check_ws_uri("wss://localhost:8080/socket")); + assert!(!check_ws_uri("invalid url")); + + assert!(check_smtp_uri("smtp.example.com")); + assert!(check_smtp_uri("smtp.example.com:587")); + assert!(!check_smtp_uri("invalid url")); +} + +pub fn http_json_client() -> Client { + ClientBuilder::new() + .default_headers(HeaderMap::from_iter([( + CONTENT_TYPE, + "application/json".parse().unwrap(), + )])) + .build() + .unwrap() +} + +pub fn crowdloan_id_of(index: u32) -> AccountId { + #[derive(Encode, Decode)] + pub struct CrowdloanId(pub [u8; 8]); + impl TypeId for CrowdloanId { + const TYPE_ID: [u8; 4] = *b"modl"; + } + + CrowdloanId(*b"py/cfund").into_sub_account_truncating(index) +} +#[test] +fn crowdloan_id_of_should_work() { + assert_eq!( + crowdloan_id_of(0), + array_bytes::hex2array_unchecked( + "0x6d6f646c70792f6366756e640000000000000000000000000000000000000000" + ) + ); +} + +pub fn winning_offset_of( + block_number: BlockNumber, + ending_period_start_at: BlockNumber, + sample_length: BlockNumber, +) -> u32 { + if let Some(ending_duration) = block_number.checked_sub(ending_period_start_at) { + ending_duration / sample_length + } else { + 0 + } +} +#[test] +fn winning_offset_of_should_work() { + const TWO_MINUTES: BlockNumber = 2 * 60 / 6; + + assert_eq!(winning_offset_of(4, 5, TWO_MINUTES), 0); + assert_eq!(winning_offset_of(24, 5, TWO_MINUTES), 0); + assert_eq!(winning_offset_of(25, 5, TWO_MINUTES), 1); + assert_eq!(winning_offset_of(45, 5, TWO_MINUTES), 2); + assert_eq!(winning_offset_of(65, 5, TWO_MINUTES), 3); + assert_eq!(winning_offset_of(123456, 789, TWO_MINUTES), 6133); +} + +pub fn position_in_ranges(slot_range: &SlotRange) -> Option { + C_SLOT_RANGES.iter().position(|s| s == slot_range) +} +#[test] +fn position_in_ranges_should_work() { + assert_eq!(position_in_ranges(&(0, 0)), Some(0)); + assert_eq!(position_in_ranges(&(0, 1)), Some(1)); + assert_eq!(position_in_ranges(&(0, 2)), Some(2)); + assert_eq!(position_in_ranges(&(0, 3)), Some(3)); + assert_eq!(position_in_ranges(&(0, 4)), Some(4)); + assert_eq!(position_in_ranges(&(0, 5)), Some(5)); + assert_eq!(position_in_ranges(&(0, 6)), Some(6)); + assert_eq!(position_in_ranges(&(0, 7)), Some(7)); + assert_eq!(position_in_ranges(&(0, 8)), None); + assert_eq!(position_in_ranges(&(1, 1)), Some(8)); + assert_eq!(position_in_ranges(&(1, 2)), Some(9)); + assert_eq!(position_in_ranges(&(1, 3)), Some(10)); + assert_eq!(position_in_ranges(&(1, 4)), Some(11)); + assert_eq!(position_in_ranges(&(1, 5)), Some(12)); + assert_eq!(position_in_ranges(&(1, 6)), Some(13)); + assert_eq!(position_in_ranges(&(1, 7)), Some(14)); + assert_eq!(position_in_ranges(&(2, 2)), Some(15)); + assert_eq!(position_in_ranges(&(2, 3)), Some(16)); + assert_eq!(position_in_ranges(&(2, 4)), Some(17)); + assert_eq!(position_in_ranges(&(2, 5)), Some(18)); + assert_eq!(position_in_ranges(&(2, 6)), Some(19)); + assert_eq!(position_in_ranges(&(2, 7)), Some(20)); + assert_eq!(position_in_ranges(&(3, 3)), Some(21)); + assert_eq!(position_in_ranges(&(3, 4)), Some(22)); + assert_eq!(position_in_ranges(&(3, 5)), Some(23)); + assert_eq!(position_in_ranges(&(3, 6)), Some(24)); + assert_eq!(position_in_ranges(&(3, 7)), Some(25)); + assert_eq!(position_in_ranges(&(4, 4)), Some(26)); + assert_eq!(position_in_ranges(&(4, 5)), Some(27)); + assert_eq!(position_in_ranges(&(4, 6)), Some(28)); + assert_eq!(position_in_ranges(&(4, 7)), Some(29)); + assert_eq!(position_in_ranges(&(5, 5)), Some(30)); + assert_eq!(position_in_ranges(&(5, 6)), Some(31)); + assert_eq!(position_in_ranges(&(5, 7)), Some(32)); + assert_eq!(position_in_ranges(&(6, 6)), Some(33)); + assert_eq!(position_in_ranges(&(6, 7)), Some(34)); + assert_eq!(position_in_ranges(&(7, 7)), Some(35)); + assert_eq!(position_in_ranges(&(8, 7)), None); +} + +pub fn leases_length(leases: &SlotRange) -> u32 { + leases.1 - leases.0 + 1 +} + +pub fn ranges_are_intersecting(a: &SlotRange, b: &SlotRange) -> bool { + a.0 <= b.1 && a.1 >= b.0 +} +#[test] +fn ranges_are_intersecting_should_work() { + // w = [1, 2], l = [2, 3] + assert!(ranges_are_intersecting(&(1, 2), &(2, 3))); + // w = [3, 4], l = [2, 3] + assert!(ranges_are_intersecting(&(3, 4), &(2, 3))); + // w = [1, 4], l = [2, 3] + assert!(ranges_are_intersecting(&(1, 4), &(2, 3))); +} + +pub fn combinations(elements: &[T]) -> Vec> { + let n = elements.len(); + let mut combinations = Vec::new(); + + (1..=(1 << n) - 1).for_each(|i| { + let mut combination = Vec::new(); + + (0..n).for_each(|j| { + if i & (1 << j) != 0 { + combination.push(&elements[j]); + } + }); + + combinations.push(combination); + }); + + combinations +} +#[test] +fn combinations_should_work() { + assert_eq!( + combinations(&['A', 'B', 'C', 'D']), + vec![ + vec![&'A'], + vec![&'B'], + vec![&'A', &'B'], + vec![&'C'], + vec![&'A', &'C'], + vec![&'B', &'C'], + vec![&'A', &'B', &'C'], + vec![&'D'], + vec![&'A', &'D'], + vec![&'B', &'D'], + vec![&'A', &'B', &'D'], + vec![&'C', &'D'], + vec![&'A', &'C', &'D'], + vec![&'B', &'C', &'D'], + vec![&'A', &'B', &'C', &'D'] + ] + ); +} + +pub fn blocks2time(blocks_count: BlockNumber) -> String { + const HOUR: u64 = 60 * 60; + const DAY: u64 = HOUR * 24; + const BLOCK_TIME: u64 = 6; + + let seconds = blocks_count as u64 * BLOCK_TIME; + let d = seconds / DAY; + let h = (seconds % DAY) / HOUR; + let m = (seconds % HOUR) / 60; + let s = seconds % 60; + + format!("{d}d:{h}h:{m}m:{s}s") +} +#[test] +fn blocks2time_should_work() { + assert_eq!(blocks2time(0), "0d:0h:0m:0s"); + assert_eq!(blocks2time(1), "0d:0h:0m:6s"); + assert_eq!(blocks2time(10), "0d:0h:1m:0s"); + assert_eq!(blocks2time(600), "0d:1h:0m:0s"); + assert_eq!(blocks2time(14400), "1d:0h:0m:0s"); + assert_eq!(blocks2time(123456), "8d:13h:45m:36s"); +} + +pub fn serialize_account_id(account_id: &AccountId, serializer: S) -> StdResult +where + S: Serializer, +{ + serializer.serialize_str(&array_bytes::bytes2hex("0x", account_id)) +} + +pub fn proxy_of(real: &AccountId, call: Payload>) -> Payload> { + dynamic::tx( + "Proxy", + "proxy", + vec![ + Value::unnamed_variant("Id", [Value::from_bytes(real)]), + Value::unnamed_variant("None", []), + call.into_value(), + ], + ) +} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b8b660f --- /dev/null +++ b/src/main.rs @@ -0,0 +1,60 @@ +//! A bot for Polkadot parachain auction. + +mod hunter; +use hunter::*; + +mod primitive; + +mod prelude { + pub use std::result::Result as StdResult; + + pub use anyhow::Result; + + pub use crate::primitive::*; +} + +// std +use std::path::PathBuf; +// crates.io +use clap::Parser; + +#[derive(Debug, Parser)] +#[command( + version = concat!( + env!("CARGO_PKG_VERSION"), + "-", + env!("VERGEN_GIT_SHA"), + "-", + env!("VERGEN_CARGO_TARGET_TRIPLE"), + ), + about, + rename_all = "kebab", +)] +struct Cli { + /// Path to the configuration toml's file or folder. + /// + /// If a file is provided, it will be loaded as the configuration TOML. + /// Otherwise, Slothunter will search for the config.toml file in the specified folder. + /// + /// Default paths are: + /// Linux: /home/alice/.config/slothunter + /// Windows: C:\Users\Alice\AppData\Roaming\slothunter + /// MacOS: /Users/Alice/Library/Application Support/slothunter + #[arg(long, short, value_name = "PATH", verbatim_doc_comment)] + configuration: Option, +} + +#[tokio::main] +async fn main() -> Result<()> { + color_eyre::install().map_err(|e| anyhow::anyhow!(e))?; + tracing_subscriber::fmt::init(); + + let Cli { configuration } = Cli::parse(); + + Hunter::from_configuration(ConfigurationToml::load(configuration)?.try_into_configuration()?) + .await? + .start() + .await?; + + Ok(()) +} diff --git a/src/primitive.rs b/src/primitive.rs new file mode 100644 index 0000000..92c37a1 --- /dev/null +++ b/src/primitive.rs @@ -0,0 +1,411 @@ +mod runtime; +pub use runtime::*; + +// std +#[cfg(test)] use std::fmt::{Debug, Formatter, Result as FmtResult}; +// crates.io +use serde::Serialize; +// slothunter +use crate::hunter::*; + +pub type BlockNumber = u32; +pub type AccountId = [u8; 32]; +pub type Balance = u128; +pub type ParaId = u32; +pub type SlotRange = (u32, u32); + +#[derive(Debug, Serialize)] +pub struct AuctionDetail { + pub index: u32, + pub first_lease_period: u32, + pub ending_period_start_at: BlockNumber, +} +impl AuctionDetail { + pub fn fmt(&self, now: BlockNumber, end_at: BlockNumber) -> String { + let remain_blocks = end_at.saturating_sub(now); + + format!( + "auction(#{}) for leases[#{}, #{}) has been activated for block range[#{}, #{end_at}], remain {remain_blocks} block(s) approximately {}", + self.index, + self.first_lease_period, + self.first_lease_period + C_RANGE_COUNT, + self.ending_period_start_at, + util::blocks2time(remain_blocks) + ) + } +} + +#[derive(Debug)] +pub struct Bidder { + pub who: AccountId, + pub para_id: ParaId, + pub reserved: Balance, + pub existing_deposit: Balance, + pub last_accepted_bid: Option, +} +impl Bidder { + pub fn fmt(&self, token: &Token) -> String { + format!( + "bidder({}, {}) has bid with extra reservation {} this turn{}", + array_bytes::bytes2hex("0x", self.who), + self.para_id, + token.fmt(self.reserved), + if self.existing_deposit == 0 { + "".into() + } else { + format!( + " and found it has an existing lease with deposit {}", + token.fmt(self.existing_deposit) + ) + }, + ) + } +} +#[derive(Debug)] +pub struct AcceptedBid { + pub at: BlockNumber, + pub amount: Balance, + pub first_slot: u32, + pub last_slot: u32, +} +impl AcceptedBid { + pub fn fmt(&self, token: &Token) -> String { + format!( + "{} for lease(s)[#{}, #{}] at block height(#{})", + token.fmt(self.amount), + self.first_slot, + self.last_slot, + self.at, + ) + } +} + +#[derive(Debug)] +pub struct Winning(pub [Option; 36]); +impl Winning { + pub fn of(s_winning: SWinning) -> Self { + Self(array_bytes::vec2array_unchecked( + s_winning + .iter() + .cloned() + .enumerate() + .map(|(i, w)| w.map(|w| Winner::construct(w, C_SLOT_RANGES[i]))) + .collect(), + )) + } + + pub fn fmt(&self, token: &Token, first_lease_period: u32) -> Vec { + self.0 + .iter() + .filter_map(|w| w.as_ref().map(|w| w.fmt(token, first_lease_period))) + .collect::>() + } + + pub fn result(&self) -> (Vec, Balance) { + fn winner_of(winning: &Winning, leases: &SlotRange) -> (Option, Balance) { + util::position_in_ranges(leases) + .and_then(|i| { + winning.0.get(i).and_then(|w| { + w.as_ref().map(|w| { + (Some(w.para_id), w.value * util::leases_length(&w.leases) as Balance) + }) + }) + }) + .unwrap_or_default() + } + + let mut winning = [ + (Vec::new(), 0), + (Vec::new(), 0), + (Vec::new(), 0), + (Vec::new(), 0), + (Vec::new(), 0), + (Vec::new(), 0), + (Vec::new(), 0), + (Vec::new(), 0), + ]; + + assert_eq!(winning.len(), C_RANGE_COUNT as usize); + + (0..C_RANGE_COUNT).for_each(|i| { + let (para_id, value) = winner_of(self, &(0, i)); + + winning[i as usize] = (para_id.map(|p| vec![p]).unwrap_or_default(), value); + + (0..i).for_each(|j| { + let (para_id, mut value) = winner_of(self, &(j + 1, i)); + + value += winning[j as usize].1; + + if value > winning[i as usize].1 { + winning[i as usize] = ( + if let Some(p) = para_id { + let mut winners = winning[j as usize].0.clone(); + + winners.push(p); + + winners + } else { + winning[j as usize].0.clone() + }, + value, + ); + } + }); + }); + + winning + .last() + .map(|(winners, threshold)| { + ( + winners + .iter() + .map(|p| { + self.0 + .iter() + .filter_map(Option::as_ref) + .find(|w| &w.para_id == p) + .expect("para id must exist") + }) + .cloned() + .collect(), + *threshold, + ) + }) + .expect("winners must be some") + } + + pub fn minimum_bid_to_win(&self, leases: &SlotRange, threshold: Balance) -> Balance { + let intersecting_leases = self + .0 + .iter() + .filter_map(Option::as_ref) + .filter(|w| !util::ranges_are_intersecting(&w.leases, leases)) + .collect::>(); + let mut combinations = util::combinations(&intersecting_leases); + + combinations.retain(|c| { + for i in 0..c.len() { + for j in i + 1..c.len() { + if util::ranges_are_intersecting(&c[i].leases, &c[j].leases) { + return false; + } + } + } + + true + }); + + let leases_length = util::leases_length(leases) as Balance; + + combinations + .into_iter() + .map(|c| { + (threshold + - c.into_iter() + .fold(0, |v, w| v + w.value * util::leases_length(&w.leases) as Balance)) + / leases_length + }) + .min() + .unwrap_or(threshold / leases_length) + } +} +impl Default for Winning { + fn default() -> Self { + Self([None; 36]) + } +} +#[derive(Clone, Copy, Serialize)] +#[cfg_attr(not(test), derive(Debug))] +#[cfg_attr(test, derive(PartialEq))] +pub struct Winner { + #[serde(serialize_with = "util::serialize_account_id")] + pub who: AccountId, + pub para_id: ParaId, + pub leases: SlotRange, + pub value: Balance, +} +impl Winner { + pub fn construct(s_winner: SWinner, leases: SlotRange) -> Self { + Self { who: s_winner.0, para_id: s_winner.1, leases, value: s_winner.2 } + } + + pub fn fmt(&self, token: &Token, first_lease_period: u32) -> String { + format!( + "bidder({}, {}) has won the lease(s)[#{}, #{}] with {}", + array_bytes::bytes2hex("0x", self.who), + self.para_id, + first_lease_period + self.leases.0, + first_lease_period + self.leases.1, + token.fmt(self.value), + ) + } +} +#[cfg(test)] +impl Debug for Winner { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + write!( + f, + "Winner {{ para_id: {}, leases: {:?}, value: {} }}", + self.para_id as u8 as char, self.leases, self.value, + ) + } +} +#[cfg(test)] +fn winner(para_id: char, leases: SlotRange, value: Balance) -> Winner { + Winner { who: [0; 32], para_id: para_id as ParaId, leases, value } +} +#[cfg(test)] +fn add_winner(winning: &mut Winning, para_id: char, leases: SlotRange, value: Balance) { + winning.0[util::position_in_ranges(&leases).unwrap()] = Some(winner(para_id, leases, value)); +} +#[test] +fn winning_result_should_work() { + { + let mut winning = Winning([None; 36]); + + assert_eq!(winning.result(), (Vec::new(), 0)); + + // A: (0, 1) -> 5 + // B: (0, 2) -> 6 + // C: (1, 2) -> 3 + // D: (0, 3) -> 7 + // E: (2, 3) -> 4 + add_winner(&mut winning, 'A', (0, 1), 5); + add_winner(&mut winning, 'B', (0, 2), 6); + add_winner(&mut winning, 'C', (1, 2), 3); + add_winner(&mut winning, 'D', (0, 3), 7); + add_winner(&mut winning, 'E', (2, 3), 4); + + assert_eq!(winning.result(), (vec![winner('D', (0, 3), 7)], 28)); + } + + { + let mut winning = Winning([None; 36]); + + // A: (0, 0) -> 5 + // B: (1, 1) -> 6 + // C: (2, 2) -> 7 + // D: (3, 3) -> 8 + // E: (0, 3) -> 3 + add_winner(&mut winning, 'A', (0, 0), 5); + add_winner(&mut winning, 'B', (1, 1), 6); + add_winner(&mut winning, 'C', (2, 2), 7); + add_winner(&mut winning, 'D', (3, 3), 8); + add_winner(&mut winning, 'E', (0, 3), 3); + + assert_eq!( + winning.result(), + ( + vec![ + winner('A', (0, 0), 5), + winner('B', (1, 1), 6), + winner('C', (2, 2), 7), + winner('D', (3, 3), 8), + ], + 26 + ), + ); + } + + { + let mut winning = Winning([None; 36]); + + // A: (0, 0) -> 5 + // B: (1, 1) -> 6 + // C: (2, 2) -> 7 + // D: (3, 3) -> 8 + // E: (0, 3) -> 3 + add_winner(&mut winning, 'A', (0, 7), 10); + add_winner(&mut winning, 'B', (0, 2), 4); + add_winner(&mut winning, 'C', (3, 3), 5); + add_winner(&mut winning, 'D', (1, 7), 11); + add_winner(&mut winning, 'E', (4, 7), 16); + + assert_eq!( + winning.result(), + (vec![winner('B', (0, 2), 4), winner('C', (3, 3), 5), winner('E', (4, 7), 16)], 81), + ); + } +} +#[test] +fn minimum_bid_to_win_should_work() { + { + let mut winning = Winning([None; 36]); + + // A: (0, 3) -> 10 + add_winner(&mut winning, 'A', (0, 3), 10); + + assert_eq!(winning.minimum_bid_to_win(&(1, 2), winning.result().1), 20); + } + + { + let mut winning = Winning([None; 36]); + + // A: (0, 3) -> 10 + // B: (0, 1) -> 10 + add_winner(&mut winning, 'A', (0, 3), 10); + add_winner(&mut winning, 'B', (0, 1), 5); + + assert_eq!(winning.minimum_bid_to_win(&(2, 3), winning.result().1), 15); + } + + { + let mut winning = Winning([None; 36]); + + // A: (0, 3) -> 10 + // B: (0, 1) -> 5 + // C: (0, 2) -> 8 + // D: (1, 2) -> 7 + add_winner(&mut winning, 'A', (0, 3), 10); + add_winner(&mut winning, 'B', (0, 1), 5); + add_winner(&mut winning, 'C', (0, 2), 8); + add_winner(&mut winning, 'D', (1, 2), 7); + + assert_eq!(winning.minimum_bid_to_win(&(0, 0), winning.result().1), 26); + } + + { + let mut winning = Winning([None; 36]); + + // A: (1, 3) -> 11 + // B: (2, 6) -> 21 + // C: (7, 7) -> 34 + add_winner(&mut winning, 'A', (1, 3), 11); + add_winner(&mut winning, 'B', (2, 6), 21); + add_winner(&mut winning, 'C', (7, 7), 34); + + assert_eq!(winning.minimum_bid_to_win(&(0, 7), winning.result().1), 17); + } + + { + let mut winning = Winning([None; 36]); + + // A: (1, 2) -> 11 + // B: (3, 6) -> 21 + // C: (7, 7) -> 34 + add_winner(&mut winning, 'A', (1, 2), 11); + add_winner(&mut winning, 'B', (3, 6), 21); + add_winner(&mut winning, 'C', (7, 7), 34); + + assert_eq!(winning.minimum_bid_to_win(&(0, 7), winning.result().1), 17); + } + + { + let mut winning = Winning([None; 36]); + + // A: (0, 3) -> 10 + // B: (0, 1) -> 10 + // C: (2, 2) -> 5 + // D: (1, 3) -> 11 + // E: (3, 3) -> 16 + add_winner(&mut winning, 'A', (0, 3), 10); + add_winner(&mut winning, 'B', (0, 1), 10); + add_winner(&mut winning, 'C', (2, 2), 5); + add_winner(&mut winning, 'D', (1, 3), 11); + add_winner(&mut winning, 'E', (3, 3), 16); + add_winner(&mut winning, 'F', (0, 0), 1); + + assert_eq!(winning.minimum_bid_to_win(&(1, 2), winning.result().1), 12); + } +} diff --git a/src/primitive/runtime.rs b/src/primitive/runtime.rs new file mode 100644 index 0000000..8abed23 --- /dev/null +++ b/src/primitive/runtime.rs @@ -0,0 +1,43 @@ +mod constant; +pub use constant::*; + +mod event; +pub use event::*; + +mod storage; +pub use storage::*; + +// crates.io +use serde::Deserialize; +use subxt::dynamic::Value; +// slothunter +use crate::prelude::*; + +pub type DispatchResult = StdResult<(), String>; + +#[derive(Debug, Deserialize)] +pub struct UnnamedWrapper { + pub r#type: T, +} + +#[derive(Debug, Deserialize)] +pub enum DispatchError { + CannotLookup, + BadOrigin, + Module(UnnamedWrapper), + ConsumerRemaining, + NoProviders, + TooManyConsumers, + Token(Value<()>), + Arithmetic(Value<()>), + Transactional(Value<()>), + Exhausted, + Corruption, + Unavailable, + RootNotAllowed, +} +#[derive(Debug, Deserialize)] +pub struct ModuleError { + pub index: u8, + pub error: [u8; 4], +} diff --git a/src/primitive/runtime/constant.rs b/src/primitive/runtime/constant.rs new file mode 100644 index 0000000..eabd562 --- /dev/null +++ b/src/primitive/runtime/constant.rs @@ -0,0 +1,43 @@ +// slothunter +use crate::hunter::*; + +// https://github.com/paritytech/polkadot/blob/b1cc6fa14330261a305d56be36c04e9c99518993/runtime/common/src/slot_range.rs#L20 +pub const C_RANGE_COUNT: u32 = 8; +pub const C_SLOT_RANGES: [SlotRange; 36] = [ + (0, 0), + (0, 1), + (0, 2), + (0, 3), + (0, 4), + (0, 5), + (0, 6), + (0, 7), + (1, 1), + (1, 2), + (1, 3), + (1, 4), + (1, 5), + (1, 6), + (1, 7), + (2, 2), + (2, 3), + (2, 4), + (2, 5), + (2, 6), + (2, 7), + (3, 3), + (3, 4), + (3, 5), + (3, 6), + (3, 7), + (4, 4), + (4, 5), + (4, 6), + (4, 7), + (5, 5), + (5, 6), + (5, 7), + (6, 6), + (6, 7), + (7, 7), +]; diff --git a/src/primitive/runtime/event.rs b/src/primitive/runtime/event.rs new file mode 100644 index 0000000..287fb06 --- /dev/null +++ b/src/primitive/runtime/event.rs @@ -0,0 +1,60 @@ +// crates.io +use scale_decode::DecodeAsType; +use serde::Deserialize; +use serde_json::Value; +use subxt::{dynamic::DecodedValue, events::StaticEvent}; +// slothunter +use crate::hunter::*; + +#[derive(Debug, Deserialize)] +pub struct EResponse { + pub data: Events, +} + +#[derive(Debug, Deserialize)] +pub struct Events { + pub events: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct RawEvent { + pub args: Value, + pub block: Value, +} + +#[derive(Debug, Deserialize)] +pub struct Event { + pub args: E, + pub block: BlockResponse, +} + +#[derive(Debug, Deserialize)] +pub struct BlockResponse { + pub height: BlockNumber, +} + +#[derive(Debug, Deserialize)] +pub struct EBidAccepted { + pub amount: String, + #[serde(rename(deserialize = "firstSlot"))] + pub first_slot: u32, + #[serde(rename(deserialize = "lastSlot"))] + pub last_slot: u32, +} + +#[derive(Debug, DecodeAsType)] +pub struct EProxyExecuted { + pub result: StdResult<(), DecodedValue>, +} +impl EProxyExecuted { + pub fn into_dispatch_result(self) -> DispatchResult { + match self.result { + Ok(()) => Ok(()), + Err(v) => Err(format!("{v:?}")), + } + } +} +impl StaticEvent for EProxyExecuted { + const EVENT: &'static str = "ProxyExecuted"; + const PALLET: &'static str = "Proxy"; +} diff --git a/src/primitive/runtime/storage.rs b/src/primitive/runtime/storage.rs new file mode 100644 index 0000000..7022468 --- /dev/null +++ b/src/primitive/runtime/storage.rs @@ -0,0 +1,19 @@ +// crates.io +use scale_value::Value; +use serde::Deserialize; +// slothunter +use crate::hunter::*; + +// https://github.com/paritytech/polkadot/blob/b1cc6fa14330261a305d56be36c04e9c99518993/runtime/common/src/slots/mod.rs#L117 +pub type SLeases = Vec>; +// https://github.com/paritytech/polkadot/blob/b1cc6fa14330261a305d56be36c04e9c99518993/runtime/common/src/auctions.rs#L73 +pub type SWinning = [Option; 36]; +pub type SWinner = (AccountId, ParaId, Balance); + +// https://github.com/paritytech/substrate/blob/51b2f0ed6af8dd4facb18f1a489e192fd0673f7b/frame/proxy/src/lib.rs#LL76C32-L76C32 +#[derive(Debug, Deserialize)] +pub struct ProxyDefinition { + pub delegate: UnnamedWrapper, + pub proxy_type: Value<()>, + pub delay: BlockNumber, +} diff --git a/test/guide.md b/test/guide.md new file mode 100644 index 0000000..99e8f58 --- /dev/null +++ b/test/guide.md @@ -0,0 +1,65 @@ +# Test guide +## Unit test +### Preparation +- Install + - Rust toolchain +### Test +1. Go to the root directory of this repository and run `cargo test` + +## Integration test +### Preparation +- Install + - Rust toolchain + - Docker + - Docker Compose +- Download + - [Polkadot v0.9.43](https://github.com/paritytech/polkadot/releases/download/v0.9.43/polkadot) + - [Rococo testing chainspec](#) +- Addition + - Ensure that no other programs are using ports `3000`, `8000`, and `9944` +### Basic test +1. Go to the root directory of this repository +2. Run `docker-compose -f test/integration/docker-compose.yml up -d` +3. Run `cargo test --features node-test` +### Advance test +#### Configuration +1. Open `test/integration/rococo.toml` +2. Change the `para-id` if you want to test with different id +3. Change the `leases` if you want to test with different lease(s) +4. Change to `watch-only` to `true` if you want to test without the bid components +5. Change the `type` to test different bidding type +6. Adjust the `upper-limit` and `increment` to test more situation if you want +7. Add some `webhooks` to test the notification webhook component + - You can use [webhook.site](https://webhook.site), the result should be listed on the left of the website + - You can use any other webhook listener +8. Edit the `mail` to test the notification mail component + - Add some `receivers` + - Add a `sender` + - Recommending using Gmail for testing + - Add `username`, e.g. example@gmail.com + - Add `password`, e.g. [app password](https://support.google.com/accounts/answer/185833?hl=en) +#### Test +1. Go to the root directory of this repository +2. Run + - `docker-compose -f test/integration/docker-compose.yml down && rm -rf test/integration/data/db && docker-compose -f test/integration/docker-compose.yml up -d` if you have run the basic test before + - `docker-compose -f test/integration/docker-compose.yml up -d` if you haven't run the basic test before +3. Run `cargo run -- -c test/integration/rococo.toml` +4. Open browser and navigate to [Polkadot/Substrate Portal](https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9944#/explorer) +5. Navigate to `Developer tab -> Sudo tab -> auctions module -> newAuction call` +6. *Optional* for crowdloan + 1. Navigate to `Network tab -> Parachains tab -> Crowdloan tab -> Add fund button` + 2. Set `crowdfund cap` recommend `1000 UNIT` + 3. Set `ending block` recommend `1000` + 3. Set `periods` recommend `(0, 0)` +7. Submit the new auction call, remember to uncheck the `use a proxy for this call` in the pop-up window +8. A notification should appear in the webhook and mail to indicate the start of an auction +9. Additionally, a notification regarding the initial bid should be included in both the webhook and mail +10. Use `Charlie` and `Dave` to bid for `2001` and `2002` and check the results +11. Wait until the auction ends, uou should receive a notification via webhook and mail indicating that the auction has ended + +## Addition +- Default real account is `//Alice` +- Default proxy accounts are `//Alice/stash` with `ProxyType::All` and `//Bob` with `ProxyType::Auction` +- Default parachains and their owners are `(Alice, 2000)`, `(Charlie, 2001)` and `(Dave, 2002)` + +All of this data is pre-set and was built from Polkadot's latest Rococo-local chain with the assistance of [Subalfred fork-off](https://subalfred.hack.ink/user/cli/state.html#command-state-fork-off). diff --git a/test/integration/Dockerfile b/test/integration/Dockerfile new file mode 100644 index 0000000..d2babde --- /dev/null +++ b/test/integration/Dockerfile @@ -0,0 +1,10 @@ +FROM archlinux:latest + +COPY\ + data/polkadot\ + data/rococo.json.fork-off\ + ./ + +RUN chmod u+x polkadot + +ENTRYPOINT ./polkadot --unsafe-rpc-external --rpc-cors all --rpc-methods unsafe --tmp --chain rococo.json.fork-off --alice diff --git a/test/integration/docker-compose.yml b/test/integration/docker-compose.yml new file mode 100644 index 0000000..1fd8846 --- /dev/null +++ b/test/integration/docker-compose.yml @@ -0,0 +1,61 @@ +services: + db: + image: postgres:12 + restart: always + volumes: + - ./data/db/postgres:/db/postgres + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: squid-archive + + node: + build: . + ports: + - "9944:9944" + volumes: + - ./data/db/node:/db/node + + ingest: + depends_on: + - db + - node + restart: on-failure + image: subsquid/substrate-ingest:firesquid + extra_hosts: + - "host.docker.internal:host-gateway" + command: + [ + "-e", + "ws://host.docker.internal:9944", + "--out", + "postgres://postgres:postgres@db:5432/squid-archive" + ] + + gateway: + depends_on: + - db + image: subsquid/substrate-gateway:firesquid + environment: + RUST_LOG: "substrate_gateway=info,actix_server=info" + command: + [ + "--database-url", + "postgres://postgres:postgres@db:5432/squid-archive", + "--database-max-connections", + "3" + ] + ports: + - "8000:8000" + + explorer: + image: subsquid/substrate-explorer:firesquid + environment: + DB_TYPE: postgres + DB_HOST: db + DB_PORT: "5432" + DB_NAME: "squid-archive" + DB_USER: "postgres" + DB_PASS: "postgres" + ports: + - "3000:3000" diff --git a/test/integration/rococo.toml b/test/integration/rococo.toml new file mode 100644 index 0000000..bdef4d0 --- /dev/null +++ b/test/integration/rococo.toml @@ -0,0 +1,126 @@ +# Relaychain network. +# +# Possible values: "kusama", "polkadot". +network = "kusama" +# GraphQL HTTP(S) URI. +# +# This line can be commented out. Otherwise, it will replace the default URI of `network`. +graphql-endpoint = "http://127.0.0.1:3000/graphql" +# Network WS(S) URI. +# +# This line can be commented out. Otherwise, it will replace the default URI of `network`. +node-endpoint = "ws://127.0.0.1:9944" +# Block subscription mode. +# +# Possible values: "best", "finalized". +# +# best: Subscribe to the best block, this strategy is faster and can increase your chances of +# winning the auction, but it becomes unsafe if there is a fork. +# +# finalized: Subscribe to the latest finalized block. +block-subscription-mode = "best" + +[bid] +# Parachain's identity you are bidding for. +para-id = 2000 +# Lease(s) you are bidding for. +# +# E.G. +# Bid for 8 leases. +# Example values: [0, 7], [10, 17], [100, 107] +leases = [0, 0] +# Watch-only mode. +# +# No extrinsic will be sent in this mode. +watch-only = false +# Bidding type. +# +# Possible values: "self-funded", "crowdloan". +type = "self-funded" +# The real account's public key. +# +# E.G. +# +# Secret Key URI `//Alice` is account: +# Network ID: substrate +# Secret seed: 0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a +# Public key (hex): 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d +# Account ID: 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d +# Public key (SS58): 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +# SS58 Address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +real = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d" +# Delegate/proxy account's private key/seed. +# +# For better safety, Slothunter only allows the use of delegate/proxy accounts for bidding/contributing. +# +# E.G. +# Secret Key URI `//Bob` is account: +# Network ID: substrate +# Secret seed: 0x398f0c28f98885e046333d4a41c19cee4c37368a9832c6502f6cfd182e2aef89 +# Public key (hex): 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48 +# Account ID: 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48 +# Public key (SS58): 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty +# SS58 Address: 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty +delegate = "0x398f0c28f98885e046333d4a41c19cee4c37368a9832c6502f6cfd182e2aef89" +# Upper limit of the bids. +# +# Slothunter will stop bidding if the minimum winning amount exceeds this value. +# +# Generally, this value should be equal to or greater than the increment. +upper-limit = "1000000000000000" +# Bid increment. +# +# This should be at least 1 DOT/KSM. +# +# Generally, the minimum value(1 DOT/KSM) should help you win the auction. +# However, if there are a lot of Slothunters or someone bids at the same time, you may lose the auction. +# To increase your chances of winning, consider increasing this value. +# +# Note that this value will serve as the default bid for the first bidder in case there are no other bidders. +# +# E.G. +# +# A : (0, 3) -> 10 +# B : (0, 1) -> 10 +# C : (2, 2) -> 5 +# D : (1, 3) -> 11 +# E : (3, 3) -> 16 +# You: (1, 2) -> x +# +# Current winners are `B`, `C`, and `E`, the threshold is calculated as `2 * 10 + 5 + 16 = 41`. +# Slothunter will calculate the value of `x` which is equal to `12` and then bid with the amount of `(x + increment)`. +# The calculation for winning the bid would be: `(x(12) + increment(1)) * 2 + 16 = 42 > 41`, hence, win. +# Since DOT's decimals are ten, there should be ten zeros here. +increment = "10000000000000" + +[notification] +# Notification webhooks. +# +# Periodic notification will be sent to the following webhook addresses. +# +# If you don't wish to use any webhooks, leave this field empty. +# +# E.G. +# This can be integrated into: +# - A Discord channel using a Discord webhook. +# - A Telegram channel using a Telegram bot. +# - A Slack channel using a Slack webhook. +webhooks = [] +# Notification mail configurations. +# +# If you don't wish to use any mail notifications, keep this(`[notification.mail]`) whole section commented out. +[notification.mail] +# Notification will be sent to the following recipients. +receivers = [] +[notification.mail.sender] +# Sender's mail password: +# Note: +# This is usually an app-specific password instead of your account password. +# For example, you can generate an app-spceific password by following the instructions provided in this link: https://support.google.com/accounts/answer/185833?hl=en. +password = "PASSWORD" +# Sender's mail address. +username = "USERNAME" +# SMTP server address. +# +# The default value is the SMTP server address of Gmail. +smtp = "smtp.gmail.com"