diff --git a/.buildkite/hooks/post-checkout b/.buildkite/hooks/post-checkout index 1a680832ce2f15..2acfe194e40225 100644 --- a/.buildkite/hooks/post-checkout +++ b/.buildkite/hooks/post-checkout @@ -8,14 +8,17 @@ source ci/env.sh # previous CI job # ( - containers=$(docker ps -q) - if [[ $(hostname) != metrics-solana-com && -n $containers ]]; then - echo "+++ Killing stale docker containers" - docker ps + echo "+++ Killing stale docker containers" + while read -r line; do + read -r id image _ <<<"$line" - # shellcheck disable=SC2086 # Don't want to double quote $containers - docker kill $containers - fi + if [[ $image =~ "solanalabs/rust" ]]; then + if docker kill "$id" >/dev/null; then + echo "kill $id $image" + fi + continue + fi + done < <(docker ps | tail -n +2) ) # Processes from previously aborted CI jobs seem to loiter, unclear why as one @@ -37,6 +40,6 @@ source ci/env.sh # HACK: These are in our docker images, need to be removed from CARGO_HOME # because we try to cache downloads across builds with CARGO_HOME # cargo lacks a facility for "system" tooling, always tries CARGO_HOME first -cargo uninstall cargo-audit || true -cargo uninstall svgbob_cli || true -cargo uninstall mdbook || true +cargo uninstall cargo-audit &>/dev/null || true +cargo uninstall svgbob_cli &>/dev/null || true +cargo uninstall mdbook &>/dev/null || true diff --git a/.github/workflows/client-targets.yml b/.github/workflows/client-targets.yml index 1c574ea0d39911..c5c85324619e1e 100644 --- a/.github/workflows/client-targets.yml +++ b/.github/workflows/client-targets.yml @@ -71,3 +71,12 @@ jobs: with: command: build args: -p solana-client --target ${{ matrix.target }} + - name: Send Slack notifiaction + if: failure() + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_NOTIFICATIONS_BOT_TOKEN }} + uses: voxmedia/github-action-slack-notify-build@v1 + with: + channel: ${{ secrets.SLACK_CHANNEL }} + status: FAILED + color: danger diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e57865565d352d..3ce7091598dfbf 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -13,7 +13,9 @@ on: - v[0-9]+.[0-9]+ jobs: - docs-build: + check: + outputs: + continue: ${{ steps.check.outputs.need_to_build }} runs-on: ubuntu-20.04 steps: - name: Checkout @@ -21,46 +23,85 @@ jobs: with: fetch-depth: 0 + - name: Get commit range (push) + if: ${{ github.event_name == 'push' }} + run: | + echo "COMMIT_RANGE=${{ github.event.before }}..$GITHUB_SHA" >> $GITHUB_ENV + + - name: Get commit range (pull_request) + if: ${{ github.event_name == 'pull_request' }} + run: | + echo "COMMIT_RANGE=${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV + + - name: Get file status + run: | + set +e + git diff --name-only $COMMIT_RANGE | grep \ + -e '.github/workflows/docs.yml' \ + -e 'docs/**' + echo "FILE_CHANGED=$?" >> $GITHUB_ENV + - name: Check id: check + shell: bash run: | source ci/env.sh - echo "::set-output name=tag::$CI_TAG" eval "$(ci/channel-info.sh)" - echo "::set-output name=channel::$CHANNEL" + TAG=$CI_TAG - - name: Get specific changed files - id: changed-files-specific - uses: tj-actions/changed-files@v19 - with: - files: | - docs/** + echo "TAG: $TAG" + echo "CHANNEL: $CHANNEL" + echo "FILE_CHANGED: $FILE_CHANGED" - - name: Pre Build - id: prebuild - run: | - echo "::set-output name=need_to_build::${{ - steps.check.outputs.tag != '' - || - ( - (steps.check.outputs.channel == 'edge' || steps.check.outputs.channel == 'beta') - && - steps.changed-files-specific.outputs.any_change != '' - ) - }}" - shell: bash + echo need_to_build="$( + if [ "$TAG" != '' ] + then + echo 1 + elif [ $FILE_CHANGED = 0 ] && ( [ "$CHANNEL" = "beta" ] || [ "$CHANNEL" = "edge" ] ) + then + echo 1 + else + echo 0 + fi + )" >> $GITHUB_OUTPUT + + build_and_deploy: + needs: + - check + if: ${{ needs.check.outputs.continue == 1 }} + # the name is used by .mergify.yml as well + name: build & deploy docs + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v3 - name: Setup Node - if: ${{ steps.prebuild.outputs.need_to_build == 'true' }} uses: actions/setup-node@v3 with: - node-version: 14 + node-version: 16 - name: Build - if: ${{ steps.prebuild.outputs.need_to_build == 'true' }} working-directory: docs run: | npm install ./build.sh env: VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_SCOPE: ${{ secrets.VERCEL_SCOPE }} + + notification: + if: failure() + runs-on: ubuntu-20.04 + needs: + - check + - build_and_deploy + steps: + - name: Send Slack notifiaction + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_NOTIFICATIONS_BOT_TOKEN }} + uses: voxmedia/github-action-slack-notify-build@v1 + with: + channel: ${{ secrets.SLACK_CHANNEL }} + status: FAILED + color: danger diff --git a/.github/workflows/export-github-repo.yml b/.github/workflows/export-github-repo.yml index 00be594f80483c..3a14a21e868b47 100644 --- a/.github/workflows/export-github-repo.yml +++ b/.github/workflows/export-github-repo.yml @@ -25,3 +25,12 @@ jobs: chmod +x ./ci/export-github-repo.sh ./ci/export-github-repo.sh web3.js/ solana-web3.js shell: bash + - name: Send Slack notifiaction + if: failure() + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_NOTIFICATIONS_BOT_TOKEN }} + uses: voxmedia/github-action-slack-notify-build@v1 + with: + channel: ${{ secrets.SLACK_CHANNEL }} + status: FAILED + color: danger diff --git a/.github/workflows/release-artifacts-auto.yml b/.github/workflows/release-artifacts-auto.yml index 900bec36d974bd..d6b490daa8cf98 100644 --- a/.github/workflows/release-artifacts-auto.yml +++ b/.github/workflows/release-artifacts-auto.yml @@ -8,6 +8,10 @@ on: tags: - v[0-9]+.[0-9]+.[0-9]+ +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: release-artifacts: uses: ./.github/workflows/release-artifacts.yml @@ -17,3 +21,17 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} + + send-slack-notification: + runs-on: ubuntu-20.04 + needs: + - release-artifacts + if: failure() + steps: + - env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_NOTIFICATIONS_BOT_TOKEN }} + uses: voxmedia/github-action-slack-notify-build@v1 + with: + channel: ${{ secrets.SLACK_CHANNEL }} + status: FAILED + color: danger diff --git a/.github/workflows/release-artifacts.yml b/.github/workflows/release-artifacts.yml index 5db8e25e660ac1..82dc135e0fec16 100644 --- a/.github/workflows/release-artifacts.yml +++ b/.github/workflows/release-artifacts.yml @@ -47,9 +47,9 @@ jobs: id: build shell: bash run: | - choco install openssl - export OPENSSL_DIR="C:\Program Files\OpenSSL-Win64" + export OPENSSL_DIR="C:\Program Files\OpenSSL" choco install protoc + export PROTOC="C:\ProgramData\chocolatey\lib\protoc\tools\bin\protoc.exe" source /tmp/env.sh echo "::set-output name=tag::$CI_TAG" eval "$(ci/channel-info.sh)" diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000000..3008deaa5b8d6c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "firedancer"] + path = firedancer + url = git@github.com:tinydancer-io/firedancer.git diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3f4918fd7f10b5..4a64afe79e7b3c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -250,20 +250,12 @@ confused with 3-letter acronyms. ## Design Proposals -Solana's architecture is described by docs generated from markdown files in -the `docs/src/` directory, maintained by an *editor* (currently @garious). To -add a design proposal, you'll need to include it in the -[Accepted Design Proposals](https://docs.solana.com/proposals/accepted-design-proposals) -section of the Solana docs. Here's the full process: - -1. Propose a design by creating a PR that adds a markdown document to the - `docs/src/proposals` directory and references it from the [table of - contents](docs/src/SUMMARY.md). Add any relevant *maintainers* to the PR - review. -2. The PR being merged indicates your proposed change was accepted and that the - maintainers support your plan of attack. -3. Submit PRs that implement the proposal. When the implementation reveals the - need for tweaks to the proposal, be sure to update the proposal and have that - change reviewed by the same people as in step 1. -4. Once the implementation is complete, submit a PR that moves the link from - the Accepted Proposals to the Implemented Proposals section. +Solana's architecture is described by docs generated from markdown files in the `docs/src/` +directory and viewable on the official [Solana Documentation](https://docs.solana.com) website. + +Current design proposals may be viewed on the docs site: + +1. [Accepted Proposals](https://docs.solana.com/proposals/accepted-design-proposals.md). +2. [Implemented Proposals](https://docs.solana.com/implemented-proposals/implemented-proposals.md) + +New design proposals should follow this guide on [how to submit a design proposal](./docs/src/proposals.md#submit-a-design-proposal). \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index f859ba86145cf3..ef7cdb0d1352be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,16 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "annotate-snippets" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36" +dependencies = [ + "unicode-width", + "yansi-term", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -143,7 +153,7 @@ dependencies = [ "asn1-rs-derive", "asn1-rs-impl", "displaydoc", - "nom 7.0.0", + "nom", "num-traits", "rusticata-macros", "thiserror", @@ -156,9 +166,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", "synstructure", ] @@ -168,9 +178,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -232,9 +242,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -243,9 +253,9 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -254,7 +264,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi 0.3.9", ] @@ -282,7 +292,7 @@ checksum = "47594e438a243791dba58124b6669561f5baa14cb12046641d8008bf035e5a25" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -305,9 +315,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.1" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a671c9ae99531afdd5d3ee8340b8da547779430689947144c140fc74a740244" +checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" dependencies = [ "async-trait", "bytes", @@ -315,6 +325,8 @@ dependencies = [ "http", "http-body", "mime", + "tower-layer", + "tower-service", ] [[package]] @@ -360,21 +372,45 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.59.1" +version = "0.60.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2 1.0.66", + "quote 1.0.33", + "regex", + "rustc-hash", + "shlex", +] + +[[package]] +name = "bindgen" +version = "0.66.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453c49e5950bb0eb63bb3df640e31618846c89d5b7faa54040d76e98e0134375" +checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" dependencies = [ - "bitflags", + "annotate-snippets", + "bitflags 2.4.0", "cexpr", "clang-sys", "lazy_static", "lazycell", + "log", "peeking_take_while", - "proc-macro2 1.0.41", - "quote 1.0.18", + "prettyplease 0.2.12", + "proc-macro2 1.0.66", + "quote 1.0.33", "regex", "rustc-hash", "shlex", + "syn 2.0.29", + "which", ] [[package]] @@ -398,6 +434,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bitmaps" version = "2.1.0" @@ -407,18 +449,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "bitvec" -version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "blake3" version = "1.3.1" @@ -498,8 +528,8 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.41", - "syn 1.0.98", + "proc-macro2 1.0.66", + "syn 1.0.109", ] [[package]] @@ -508,9 +538,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -519,9 +549,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -602,22 +632,22 @@ checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" [[package]] name = "bytemuck" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e" +checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -640,9 +670,9 @@ checksum = "6c58ec36aac5066d5ca17df51b3e70279f5670a72102f5752cb7e7c856adfc70" [[package]] name = "bzip2" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" dependencies = [ "bzip2-sys", "libc", @@ -674,7 +704,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61bf7211aad104ce2769ec05efcdfabf85ee84ac92461d142f22cf8badd0e54c" dependencies = [ - "errno", + "errno 0.2.8", "libc", "thiserror", ] @@ -721,11 +751,11 @@ dependencies = [ [[package]] name = "cexpr" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db507a7679252d2276ed0dd8113c6875ec56d3089f9225b2b42c30cc1f8e5c89" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 6.1.2", + "nom", ] [[package]] @@ -801,7 +831,7 @@ checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim 0.8.0", "textwrap 0.11.0", "unicode-width", @@ -810,32 +840,41 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.8" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", + "clap_lex", "indexmap", - "lazy_static", - "os_str_bytes", + "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.15.0", + "textwrap 0.16.0", ] [[package]] name = "clap_derive" -version = "3.1.7" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck 0.4.0", "proc-macro-error", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", ] [[package]] @@ -907,8 +946,8 @@ version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "unicode-xid 0.2.2", ] @@ -1142,7 +1181,7 @@ checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" dependencies = [ "asn1-rs", "displaydoc", - "nom 7.0.0", + "nom", "num-bigint 0.4.3", "num-traits", "rusticata-macros", @@ -1161,10 +1200,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" dependencies = [ "convert_case", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "rustc_version 0.3.3", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -1249,9 +1288,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -1331,9 +1370,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86b50932a01e7ec5c06160492ab660fb19b6bb2a7878030dd6cd68d21df9d4d" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -1372,9 +1411,9 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -1385,9 +1424,9 @@ checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef" dependencies = [ "num-bigint 0.4.3", "num-traits", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -1397,9 +1436,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb" dependencies = [ "once_cell", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -1426,6 +1465,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -1488,7 +1538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca" dependencies = [ "cfg-if 1.0.0", - "rustix", + "rustix 0.34.3", "windows-sys 0.30.0", ] @@ -1517,10 +1567,18 @@ checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "winapi 0.3.9", ] +[[package]] +name = "firedancer-sys" +version = "0.3.0" +dependencies = [ + "bindgen 0.66.1", + "paste", +] + [[package]] name = "fixedbitset" version = "0.4.0" @@ -1580,12 +1638,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - [[package]] name = "futures" version = "0.1.31" @@ -1647,9 +1699,9 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -1704,7 +1756,7 @@ dependencies = [ [[package]] name = "gen-headers" -version = "1.11.6" +version = "1.14.24" dependencies = [ "log", "regex", @@ -1712,7 +1764,7 @@ dependencies = [ [[package]] name = "gen-syscall-list" -version = "1.11.6" +version = "1.14.24" dependencies = [ "regex", ] @@ -1824,9 +1876,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.11" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -1837,16 +1889,10 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.7.1", "tracing", ] -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - [[package]] name = "hash32" version = "0.2.1" @@ -1881,7 +1927,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" dependencies = [ "base64 0.13.0", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -1923,6 +1969,12 @@ 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" @@ -1989,9 +2041,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -2199,6 +2251,17 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504" +[[package]] +name = "io-lifetimes" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "ipnet" version = "2.3.1" @@ -2306,9 +2369,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" dependencies = [ "proc-macro-crate 0.1.5", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -2405,9 +2468,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libloading" @@ -2427,11 +2490,11 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" [[package]] name = "librocksdb-sys" -version = "0.6.1+6.28.2" +version = "0.8.0+7.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc587013734dadb7cf23468e531aa120788b87243648be42e2d3a072186291" +checksum = "611804e4666a25136fcc5f8cf425ab4d26c7f74ea245ffe92ea23b85b6420b5d" dependencies = [ - "bindgen", + "bindgen 0.60.1", "bzip2-sys", "cc", "glob", @@ -2510,6 +2573,12 @@ version = "0.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7" +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + [[package]] name = "lock_api" version = "0.4.6" @@ -2530,18 +2599,18 @@ dependencies = [ [[package]] name = "lru" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84e6fe5655adc6ce00787cf7dcaf8dc4f998a0565d23eafc207a8b08ca3349a" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" dependencies = [ - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] name = "lz4" -version = "1.23.3" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edcb94251b1c375c459e5abe9fb0168c1c826c3370172684844f8f3f8d1a885" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" dependencies = [ "libc", "lz4-sys", @@ -2549,9 +2618,9 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7be8908e2ed6f31c02db8a9fa962f03e36c53fbfde437363eae3306b85d7e17" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" dependencies = [ "cc", "libc", @@ -2676,9 +2745,9 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -2722,24 +2791,12 @@ version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "libc", "memoffset", ] -[[package]] -name = "nom" -version = "6.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" -dependencies = [ - "bitvec", - "funty", - "memchr", - "version_check", -] - [[package]] name = "nom" version = "7.0.0" @@ -2812,9 +2869,9 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -2865,29 +2922,29 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" dependencies = [ "proc-macro-crate 1.1.0", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -2934,11 +2991,11 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.41" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "foreign-types", "libc", @@ -2953,9 +3010,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -2966,20 +3023,19 @@ checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" [[package]] name = "openssl-src" -version = "111.22.0+1.1.1q" +version = "111.25.0+1.1.1t" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853" +checksum = "3173cd3626c43e3854b1b727422a276e568d9ec5fe8cec197822cf52cfb743d6" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.75" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "openssl-src", @@ -3011,9 +3067,6 @@ name = "os_str_bytes" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] [[package]] name = "ouroboros" @@ -3034,9 +3087,9 @@ checksum = "084fd65d5dd8b3772edccb5ffd1e4b7eba43897ecd0f9401e330e8c542959408" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -3083,7 +3136,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "smallvec", "winapi 0.3.9", ] @@ -3096,27 +3149,24 @@ checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "smallvec", "windows-sys 0.32.0", ] [[package]] -name = "pbkdf2" -version = "0.4.0" +name = "paste" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" -dependencies = [ - "crypto-mac", -] +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" -version = "0.10.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" dependencies = [ - "digest 0.10.3", + "crypto-mac", ] [[package]] @@ -3191,9 +3241,9 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -3219,14 +3269,11 @@ dependencies = [ [[package]] name = "pickledb" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9161694d67f6c5163519d42be942ae36bbdb55f439460144f105bc4f9f7d1d61" +checksum = "c53a5ade47760e8cc4986bdc5e72daeffaaaee64cbc374f9cfe0a00c1cd87b1f" dependencies = [ - "bincode", "serde", - "serde_cbor", - "serde_json", "serde_yaml", ] @@ -3245,9 +3292,9 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -3342,8 +3389,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b83ec2d0af5c5c556257ff52c9f98934e243b9fd39604bfb2a9b75ec2e97f18" dependencies = [ - "proc-macro2 1.0.41", - "syn 1.0.98", + "proc-macro2 1.0.66", + "syn 1.0.109", +] + +[[package]] +name = "prettyplease" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +dependencies = [ + "proc-macro2 1.0.66", + "syn 2.0.29", ] [[package]] @@ -3372,9 +3429,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", "version_check", ] @@ -3384,8 +3441,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "version_check", ] @@ -3400,9 +3457,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.41" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc2916cde080c1876ff40292a396541241fe0072ef928cd76582e9ea5d60d2" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -3414,7 +3471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" dependencies = [ "bit-set", - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", @@ -3495,9 +3552,9 @@ checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -3508,9 +3565,9 @@ checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -3533,6 +3590,14 @@ dependencies = [ "prost 0.11.0", ] +[[package]] +name = "proto" +version = "1.14.24" +dependencies = [ + "protobuf-src", + "tonic-build 0.8.0", +] + [[package]] name = "protobuf-src" version = "1.0.5+3.19.3" @@ -3628,19 +3693,13 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.66", ] -[[package]] -name = "radium" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" - [[package]] name = "rand" version = "0.4.6" @@ -3790,15 +3849,15 @@ dependencies = [ [[package]] name = "rbpf-cli" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "clap 3.1.8", + "clap 3.2.23", "serde", "serde_json", "solana-bpf-loader-program", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana_rbpf", ] @@ -3829,7 +3888,16 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -3839,18 +3907,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom 0.2.3", - "redox_syscall", + "redox_syscall 0.2.10", ] [[package]] name = "reed-solomon-erasure" -version = "5.0.3" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2fe31452b684b8b33f65f8730c8b8812c3f5a0bb8a096934717edb1ac488641" +checksum = "7263373d500d4d4f505d43a2a662d475a894aa94503a1ee28e9188b5f3960d4f" dependencies = [ "cc", "libc", "libm", + "lru", "parking_lot 0.11.2", "smallvec", "spin 0.9.2", @@ -3879,15 +3948,6 @@ version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "reqwest" version = "0.11.11" @@ -3949,9 +4009,9 @@ dependencies = [ [[package]] name = "rocksdb" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "620f4129485ff1a7128d184bc687470c21c7951b64779ebc9cfdad3dcd920290" +checksum = "7e9562ea1d70c0cc63a34a22d977753b50cca91cc6b6527750463bd5dd8697bc" dependencies = [ "libc", "librocksdb-sys", @@ -4005,7 +4065,7 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ - "nom 7.0.0", + "nom", ] [[package]] @@ -4014,14 +4074,28 @@ version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb617eb09c4ef1536405e357e3b63f39e3ab4cc2159db05395278ad5c352bb16" dependencies = [ - "bitflags", - "errno", - "io-lifetimes", + "bitflags 1.3.2", + "errno 0.2.8", + "io-lifetimes 0.6.1", "libc", - "linux-raw-sys", + "linux-raw-sys 0.0.42", "winapi 0.3.9", ] +[[package]] +name = "rustix" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e78cc525325c06b4a7ff02db283472f3c042b7ff0c391f96c6d5ac6f4f91b75" +dependencies = [ + "bitflags 1.3.2", + "errno 0.3.0", + "io-lifetimes 1.0.9", + "libc", + "linux-raw-sys 0.3.1", + "windows-sys 0.45.0", +] + [[package]] name = "rustls" version = "0.19.1" @@ -4141,9 +4215,9 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -4172,7 +4246,7 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -4234,25 +4308,15 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_derive" version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -4310,10 +4374,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1b95bb2f4f624565e8fe8140c789af7e2082c0e0561b5a82a1b678baa9703dc" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "rustversion", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -4509,7 +4573,7 @@ dependencies = [ [[package]] name = "solana-account-decoder" -version = "1.11.6" +version = "1.14.24" dependencies = [ "Inflector", "base64 0.13.0", @@ -4520,8 +4584,9 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "solana-address-lookup-table-program", "solana-config-program", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-vote-program", "spl-token", "spl-token-2022", @@ -4531,21 +4596,21 @@ dependencies = [ [[package]] name = "solana-accounts-bench" -version = "1.11.6" +version = "1.14.24" dependencies = [ "clap 2.33.3", "log", "rayon", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-version", ] [[package]] name = "solana-accounts-cluster-bench" -version = "1.11.6" +version = "1.14.24" dependencies = [ "clap 2.33.3", "log", @@ -4558,11 +4623,11 @@ dependencies = [ "solana-faucet", "solana-gossip", "solana-local-cluster", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-net-utils", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-test-validator", "solana-transaction-status", @@ -4572,7 +4637,7 @@ dependencies = [ [[package]] name = "solana-address-lookup-table-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "bytemuck", @@ -4581,30 +4646,30 @@ dependencies = [ "num-traits", "rustc_version 0.4.0", "serde", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-program 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-program 1.14.24", "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", ] [[package]] name = "solana-address-lookup-table-program-tests" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_matches", "bincode", "solana-address-lookup-table-program", "solana-program-test", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-banking-bench" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "clap 3.1.8", + "clap 3.2.23", "crossbeam-channel", "log", "rand 0.7.3", @@ -4613,27 +4678,27 @@ dependencies = [ "solana-core", "solana-gossip", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-perf", "solana-poh", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-version", ] [[package]] name = "solana-banks-client" -version = "1.11.6" +version = "1.14.24" dependencies = [ "borsh", "futures 0.3.21", "solana-banks-interface", "solana-banks-server", - "solana-program 1.11.6", + "solana-program 1.14.24", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "tarpc", "thiserror", "tokio", @@ -4642,16 +4707,16 @@ dependencies = [ [[package]] name = "solana-banks-interface" -version = "1.11.6" +version = "1.14.24" dependencies = [ "serde", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "tarpc", ] [[package]] name = "solana-banks-server" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "crossbeam-channel", @@ -4659,7 +4724,7 @@ dependencies = [ "solana-banks-interface", "solana-client", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-send-transaction-service", "tarpc", "tokio", @@ -4669,9 +4734,9 @@ dependencies = [ [[package]] name = "solana-bench-streamer" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "clap 3.1.8", + "clap 3.2.23", "crossbeam-channel", "solana-net-utils", "solana-streamer", @@ -4680,7 +4745,7 @@ dependencies = [ [[package]] name = "solana-bench-tps" -version = "1.11.6" +version = "1.14.24" dependencies = [ "clap 2.33.3", "crossbeam-channel", @@ -4697,13 +4762,13 @@ dependencies = [ "solana-genesis", "solana-gossip", "solana-local-cluster", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-metrics", "solana-net-utils", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-test-validator", "solana-version", @@ -4712,7 +4777,7 @@ dependencies = [ [[package]] name = "solana-bloom" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bv", "fnv", @@ -4722,14 +4787,14 @@ dependencies = [ "rustc_version 0.4.0", "serde", "serde_derive", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-sdk 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-sdk 1.14.24", ] [[package]] name = "solana-bpf-loader-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "byteorder", @@ -4740,26 +4805,26 @@ dependencies = [ "solana-metrics", "solana-program-runtime", "solana-runtime", - "solana-sdk 1.11.6", - "solana-zk-token-sdk 1.11.6", + "solana-sdk 1.14.24", + "solana-zk-token-sdk 1.14.24", "solana_rbpf", "thiserror", ] [[package]] name = "solana-bpf-loader-program-tests" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_matches", "bincode", "solana-bpf-loader-program", "solana-program-test", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-bucket-map" -version = "1.11.6" +version = "1.14.24" dependencies = [ "fs_extra", "log", @@ -4767,63 +4832,63 @@ dependencies = [ "modular-bitfield", "rand 0.7.3", "rayon", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "tempfile", ] [[package]] name = "solana-cargo-build-bpf" -version = "1.11.6" +version = "1.14.24" dependencies = [ "cargo_metadata", - "clap 3.1.8", - "solana-sdk 1.11.6", + "clap 3.2.23", + "solana-sdk 1.14.24", ] [[package]] name = "solana-cargo-build-sbf" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bzip2", "cargo_metadata", - "clap 3.1.8", + "clap 3.2.23", "log", "regex", "serial_test", "solana-download-utils", - "solana-logger 1.11.6", - "solana-sdk 1.11.6", + "solana-logger 1.14.24", + "solana-sdk 1.14.24", "tar", ] [[package]] name = "solana-cargo-test-bpf" -version = "1.11.6" +version = "1.14.24" dependencies = [ "cargo_metadata", - "clap 3.1.8", + "clap 3.2.23", ] [[package]] name = "solana-cargo-test-sbf" -version = "1.11.6" +version = "1.14.24" dependencies = [ "cargo_metadata", - "clap 3.1.8", + "clap 3.2.23", ] [[package]] name = "solana-clap-utils" -version = "1.11.6" +version = "1.14.24" dependencies = [ "chrono", "clap 2.33.3", "rpassword", "solana-perf", "solana-remote-wallet", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "tempfile", "thiserror", "tiny-bip39", @@ -4833,14 +4898,14 @@ dependencies = [ [[package]] name = "solana-clap-v3-utils" -version = "1.11.6" +version = "1.14.24" dependencies = [ "chrono", - "clap 3.1.8", + "clap 3.2.23", "rpassword", "solana-perf", "solana-remote-wallet", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "tempfile", "thiserror", "tiny-bip39", @@ -4850,7 +4915,7 @@ dependencies = [ [[package]] name = "solana-cli" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "bs58", @@ -4870,6 +4935,7 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", + "solana-address-lookup-table-program", "solana-bpf-loader-program", "solana-clap-utils", "solana-cli-config", @@ -4877,10 +4943,10 @@ dependencies = [ "solana-client", "solana-config-program", "solana-faucet", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-program-runtime", "solana-remote-wallet", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-test-validator", "solana-transaction-status", @@ -4895,7 +4961,7 @@ dependencies = [ [[package]] name = "solana-cli-config" -version = "1.11.6" +version = "1.14.24" dependencies = [ "anyhow", "dirs-next", @@ -4904,13 +4970,13 @@ dependencies = [ "serde_derive", "serde_yaml", "solana-clap-utils", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "url 2.2.2", ] [[package]] name = "solana-cli-output" -version = "1.11.6" +version = "1.14.24" dependencies = [ "Inflector", "base64 0.13.0", @@ -4928,7 +4994,7 @@ dependencies = [ "solana-clap-utils", "solana-cli-config", "solana-client", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-transaction-status", "solana-vote-program", "spl-memo", @@ -4936,7 +5002,7 @@ dependencies = [ [[package]] name = "solana-client" -version = "1.11.6" +version = "1.14.24" dependencies = [ "anyhow", "assert_matches", @@ -4972,12 +5038,12 @@ dependencies = [ "solana-account-decoder", "solana-clap-utils", "solana-faucet", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-metrics", "solana-net-utils", "solana-perf", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-transaction-status", "solana-version", @@ -4993,14 +5059,14 @@ dependencies = [ [[package]] name = "solana-client-test" -version = "1.11.6" +version = "1.14.24" dependencies = [ "futures-util", "serde_json", "serial_test", "solana-client", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-merkle-tree", "solana-metrics", @@ -5008,7 +5074,7 @@ dependencies = [ "solana-rayon-threadlimit", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-test-validator", "solana-transaction-status", @@ -5019,28 +5085,28 @@ dependencies = [ [[package]] name = "solana-compute-budget-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-config-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "chrono", "serde", "serde_derive", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-core" -version = "1.11.6" +version = "1.14.24" dependencies = [ "ahash", "base64 0.13.0", @@ -5059,6 +5125,7 @@ dependencies = [ "lru", "matches", "min-max-heap", + "num_enum", "rand 0.7.3", "rand_chacha 0.2.2", "raptorq", @@ -5072,12 +5139,12 @@ dependencies = [ "solana-bloom", "solana-client", "solana-entry", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", "solana-geyser-plugin-manager", "solana-gossip", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-metrics", "solana-net-utils", @@ -5087,7 +5154,7 @@ dependencies = [ "solana-rayon-threadlimit", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-send-transaction-service", "solana-stake-program", "solana-streamer", @@ -5107,10 +5174,10 @@ dependencies = [ [[package]] name = "solana-dos" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", - "clap 3.1.8", + "clap 3.2.23", "crossbeam-channel", "itertools", "log", @@ -5123,42 +5190,42 @@ dependencies = [ "solana-faucet", "solana-gossip", "solana-local-cluster", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-net-utils", "solana-perf", "solana-rpc", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-version", ] [[package]] name = "solana-download-utils" -version = "1.11.6" +version = "1.14.24" dependencies = [ "console", "indicatif", "log", "reqwest", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-ed25519-program-tests" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_matches", "ed25519-dalek", "rand 0.7.3", "solana-program-test", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-entry" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "crossbeam-channel", @@ -5170,18 +5237,18 @@ dependencies = [ "rand 0.7.3", "rayon", "serde", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-merkle-tree", "solana-metrics", "solana-perf", "solana-rayon-threadlimit", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-faucet" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "byteorder", @@ -5192,9 +5259,9 @@ dependencies = [ "serde_derive", "solana-clap-utils", "solana-cli-config", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-metrics", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-version", "spl-memo", "thiserror", @@ -5203,29 +5270,41 @@ dependencies = [ [[package]] name = "solana-frozen-abi" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a5d3280421bb53fc12bdba1eaa505153fb4f99a06b5609dae22192652ead3b" +checksum = "23b4953578272ac0fadec245e85e83ae86454611f0c0a7fff7d906835124bdcf" dependencies = [ + "ahash", + "blake3", + "block-buffer 0.9.0", "bs58", "bv", + "byteorder", + "cc", + "either", "generic-array 0.14.5", + "getrandom 0.1.16", + "hashbrown 0.12.3", "im", "lazy_static", "log", "memmap2", + "once_cell", + "rand_core 0.6.3", "rustc_version 0.4.0", "serde", "serde_bytes", "serde_derive", + "serde_json", "sha2 0.10.2", - "solana-frozen-abi-macro 1.10.33", + "solana-frozen-abi-macro 1.14.16", + "subtle", "thiserror", ] [[package]] name = "solana-frozen-abi" -version = "1.11.6" +version = "1.14.24" dependencies = [ "ahash", "blake3", @@ -5250,37 +5329,37 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.10.2", - "solana-frozen-abi-macro 1.11.6", - "solana-logger 1.11.6", + "solana-frozen-abi-macro 1.14.24", + "solana-logger 1.14.24", "subtle", "thiserror", ] [[package]] name = "solana-frozen-abi-macro" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "635c60ac96b1347af272c625465068b908aff919d19f29b5795a44310310494d" +checksum = "57892538250428ad3dc3cbe05f6cd75ad14f4f16734fcb91bc7cd5fbb63d6315" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "rustc_version 0.4.0", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] name = "solana-frozen-abi-macro" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "rustc_version 0.4.0", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] name = "solana-genesis" -version = "1.11.6" +version = "1.14.24" dependencies = [ "base64 0.13.0", "clap 2.33.3", @@ -5291,9 +5370,9 @@ dependencies = [ "solana-cli-config", "solana-entry", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-stake-program", "solana-version", "solana-vote-program", @@ -5302,26 +5381,26 @@ dependencies = [ [[package]] name = "solana-genesis-utils" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-download-utils", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-geyser-plugin-interface" -version = "1.11.6" +version = "1.14.24" dependencies = [ "log", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-transaction-status", "thiserror", ] [[package]] name = "solana-geyser-plugin-manager" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bs58", "crossbeam-channel", @@ -5334,14 +5413,14 @@ dependencies = [ "solana-metrics", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-transaction-status", "thiserror", ] [[package]] name = "solana-gossip" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "bv", @@ -5368,17 +5447,17 @@ dependencies = [ "solana-clap-utils", "solana-client", "solana-entry", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-metrics", "solana-net-utils", "solana-perf", "solana-rayon-threadlimit", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-version", "solana-vote-program", @@ -5387,7 +5466,7 @@ dependencies = [ [[package]] name = "solana-install" -version = "1.11.6" +version = "1.14.24" dependencies = [ "atty", "bincode", @@ -5408,8 +5487,8 @@ dependencies = [ "solana-clap-utils", "solana-client", "solana-config-program", - "solana-logger 1.11.6", - "solana-sdk 1.11.6", + "solana-logger 1.14.24", + "solana-sdk 1.14.24", "solana-version", "tar", "tempfile", @@ -5420,27 +5499,27 @@ dependencies = [ [[package]] name = "solana-keygen" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bs58", - "clap 3.1.8", + "clap 3.2.23", "dirs-next", "num_cpus", "solana-clap-v3-utils", "solana-cli-config", "solana-remote-wallet", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-version", "tiny-bip39", ] [[package]] name = "solana-ledger" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_matches", "bincode", - "bitflags", + "bitflags 1.3.2", "bs58", "byteorder", "chrono", @@ -5470,23 +5549,26 @@ dependencies = [ "solana-account-decoder", "solana-bpf-loader-program", "solana-entry", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-logger 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-logger 1.14.24", "solana-measure", "solana-metrics", "solana-perf", "solana-program-runtime", "solana-rayon-threadlimit", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-stake-program", "solana-storage-bigtable", "solana-storage-proto", "solana-transaction-status", "solana-vote-program", + "spl-token", + "spl-token-2022", "static_assertions", "tempfile", + "test-case", "thiserror", "tokio", "tokio-stream", @@ -5495,10 +5577,10 @@ dependencies = [ [[package]] name = "solana-ledger-tool" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_cmd", - "bs58", + "base64 0.13.0", "bytecount", "chrono", "clap 2.33.3", @@ -5517,10 +5599,10 @@ dependencies = [ "solana-core", "solana-entry", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-stake-program", "solana-storage-bigtable", "solana-transaction-status", @@ -5532,7 +5614,7 @@ dependencies = [ [[package]] name = "solana-local-cluster" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_matches", "crossbeam-channel", @@ -5550,9 +5632,9 @@ dependencies = [ "solana-entry", "solana-gossip", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-stake-program", "solana-streamer", "solana-vote-program", @@ -5561,21 +5643,21 @@ dependencies = [ [[package]] name = "solana-log-analyzer" -version = "1.11.6" +version = "1.14.24" dependencies = [ "byte-unit", - "clap 3.1.8", + "clap 3.2.23", "serde", "serde_json", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-version", ] [[package]] name = "solana-logger" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12cb6e6f1f9c9876d356c928b8c2ac532f6715e7cd2a1b4343d747bee3eca73" +checksum = "06aa701c49493e93085dd1e800c05475baca15a9d4d527b59794f2ed0b66e055" dependencies = [ "env_logger", "lazy_static", @@ -5584,7 +5666,7 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.11.6" +version = "1.14.24" dependencies = [ "env_logger", "lazy_static", @@ -5593,38 +5675,38 @@ dependencies = [ [[package]] name = "solana-measure" -version = "1.11.6" +version = "1.14.24" dependencies = [ "log", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-merkle-root-bench" -version = "1.11.6" +version = "1.14.24" dependencies = [ "clap 2.33.3", "log", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-version", ] [[package]] name = "solana-merkle-tree" -version = "1.11.6" +version = "1.14.24" dependencies = [ "fast-math", "hex", "matches", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-metrics" -version = "1.11.6" +version = "1.14.24" dependencies = [ "crossbeam-channel", "env_logger", @@ -5634,26 +5716,26 @@ dependencies = [ "rand 0.7.3", "reqwest", "serial_test", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-net-shaper" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "clap 3.1.8", + "clap 3.2.23", "rand 0.7.3", "serde", "serde_json", - "solana-logger 1.11.6", + "solana-logger 1.14.24", ] [[package]] name = "solana-net-utils" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", - "clap 3.1.8", + "clap 3.2.23", "crossbeam-channel", "log", "nix", @@ -5661,8 +5743,8 @@ dependencies = [ "serde", "serde_derive", "socket2", - "solana-logger 1.11.6", - "solana-sdk 1.11.6", + "solana-logger 1.14.24", + "solana-sdk 1.14.24", "solana-version", "tokio", "url 2.2.2", @@ -5670,7 +5752,7 @@ dependencies = [ [[package]] name = "solana-notifier" -version = "1.11.6" +version = "1.14.24" dependencies = [ "log", "reqwest", @@ -5679,7 +5761,7 @@ dependencies = [ [[package]] name = "solana-perf" -version = "1.11.6" +version = "1.14.24" dependencies = [ "ahash", "bincode", @@ -5695,18 +5777,20 @@ dependencies = [ "matches", "nix", "rand 0.7.3", + "rand_chacha 0.2.2", "rayon", "serde", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-metrics", "solana-rayon-threadlimit", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-vote-program", + "test-case", ] [[package]] name = "solana-poh" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "core_affinity", @@ -5716,83 +5800,90 @@ dependencies = [ "rand 0.7.3", "solana-entry", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-metrics", "solana-perf", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-sys-tuner", "thiserror", ] [[package]] name = "solana-poh-bench" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "clap 3.1.8", + "clap 3.2.23", "log", "rand 0.7.3", "rayon", "solana-entry", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-perf", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-version", ] [[package]] name = "solana-program" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeecf504cee2821b006871f70e7a1f54db15f914cedf259eaf5976fe606470f0" +checksum = "3f99052873619df68913cb8e92e28ff251a5483828925e87fa97ba15a9cbad51" dependencies = [ "base64 0.13.0", "bincode", - "bitflags", + "bitflags 1.3.2", "blake3", "borsh", "borsh-derive", "bs58", "bv", "bytemuck", + "cc", "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.1.16", + "getrandom 0.2.3", "itertools", "js-sys", "lazy_static", + "libc", "libsecp256k1", "log", + "memoffset", "num-derive", "num-traits", "parking_lot 0.12.1", "rand 0.7.3", + "rand_chacha 0.2.2", "rustc_version 0.4.0", "rustversion", "serde", "serde_bytes", "serde_derive", + "serde_json", "sha2 0.10.2", "sha3 0.10.1", - "solana-frozen-abi 1.10.33", - "solana-frozen-abi-macro 1.10.33", - "solana-sdk-macro 1.10.33", + "solana-frozen-abi 1.14.16", + "solana-frozen-abi-macro 1.14.16", + "solana-sdk-macro 1.14.16", "thiserror", + "tiny-bip39", "wasm-bindgen", + "zeroize", ] [[package]] name = "solana-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "anyhow", "assert_matches", "base64 0.13.0", "bincode", - "bitflags", + "bitflags 1.3.2", "blake3", "borsh", "borsh-derive", @@ -5824,10 +5915,10 @@ dependencies = [ "serde_json", "sha2 0.10.2", "sha3 0.10.1", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-logger 1.11.6", - "solana-sdk-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-logger 1.14.24", + "solana-sdk-macro 1.14.24", "static_assertions", "thiserror", "tiny-bip39", @@ -5837,7 +5928,7 @@ dependencies = [ [[package]] name = "solana-program-runtime" -version = "1.11.6" +version = "1.14.24" dependencies = [ "base64 0.13.0", "bincode", @@ -5849,20 +5940,21 @@ dependencies = [ "log", "num-derive", "num-traits", + "rand 0.7.3", "rustc_version 0.4.0", "serde", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-logger 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-logger 1.14.24", "solana-measure", "solana-metrics", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", ] [[package]] name = "solana-program-test" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_matches", "async-trait", @@ -5874,10 +5966,11 @@ dependencies = [ "solana-banks-client", "solana-banks-server", "solana-bpf-loader-program", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-program-runtime", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", + "solana-stake-program", "solana-vote-program", "thiserror", "tokio", @@ -5885,15 +5978,25 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" -version = "1.11.6" +version = "1.14.24" dependencies = [ "lazy_static", "num_cpus", ] +[[package]] +name = "solana-receipt-tree" +version = "1.14.24" +dependencies = [ + "firedancer-sys", + "log", + "rand 0.8.5", + "solana-sdk 1.14.24", +] + [[package]] name = "solana-remote-wallet" -version = "1.11.6" +version = "1.14.24" dependencies = [ "console", "dialoguer", @@ -5904,14 +6007,14 @@ dependencies = [ "parking_lot 0.12.1", "qstring", "semver 1.0.10", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", "uriparse", ] [[package]] name = "solana-rpc" -version = "1.11.6" +version = "1.14.24" dependencies = [ "base64 0.13.0", "bincode", @@ -5947,7 +6050,7 @@ dependencies = [ "solana-poh", "solana-rayon-threadlimit", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-send-transaction-service", "solana-stake-program", "solana-storage-bigtable", @@ -5966,7 +6069,7 @@ dependencies = [ [[package]] name = "solana-rpc-test" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "bs58", @@ -5978,9 +6081,9 @@ dependencies = [ "serde_json", "solana-account-decoder", "solana-client", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-rpc", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-test-validator", "solana-transaction-status", @@ -5989,7 +6092,7 @@ dependencies = [ [[package]] name = "solana-runtime" -version = "1.11.6" +version = "1.14.24" dependencies = [ "arrayref", "assert_matches", @@ -6011,6 +6114,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", + "lru", "lz4", "memmap2", "num-derive", @@ -6029,18 +6133,20 @@ dependencies = [ "solana-bucket-map", "solana-compute-budget-program", "solana-config-program", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-logger 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-logger 1.14.24", "solana-measure", + "solana-merkle-tree", "solana-metrics", "solana-program-runtime", "solana-rayon-threadlimit", - "solana-sdk 1.11.6", + "solana-receipt-tree", + "solana-sdk 1.14.24", "solana-stake-program", "solana-vote-program", "solana-zk-token-proof-program", - "solana-zk-token-sdk 1.11.6", + "solana-zk-token-sdk 1.14.24", "strum", "strum_macros", "symlink", @@ -6052,14 +6158,14 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "636f6c615aca6f75e22b6baceaf0ffed9d74367f9320b07ed57cd9b5ce2e4ff9" +checksum = "edb47da3e18cb669f6ace0b40cee0610e278903783e0c9f7fce1e1beb881a1b7" dependencies = [ "assert_matches", "base64 0.13.0", "bincode", - "bitflags", + "bitflags 1.3.2", "borsh", "bs58", "bytemuck", @@ -6079,7 +6185,7 @@ dependencies = [ "memmap2", "num-derive", "num-traits", - "pbkdf2 0.10.1", + "pbkdf2 0.11.0", "qstring", "rand 0.7.3", "rand_chacha 0.2.2", @@ -6091,11 +6197,11 @@ dependencies = [ "serde_json", "sha2 0.10.2", "sha3 0.10.1", - "solana-frozen-abi 1.10.33", - "solana-frozen-abi-macro 1.10.33", - "solana-logger 1.10.33", - "solana-program 1.10.33", - "solana-sdk-macro 1.10.33", + "solana-frozen-abi 1.14.16", + "solana-frozen-abi-macro 1.14.16", + "solana-logger 1.14.16", + "solana-program 1.14.16", + "solana-sdk-macro 1.14.16", "thiserror", "uriparse", "wasm-bindgen", @@ -6103,13 +6209,13 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.11.6" +version = "1.14.24" dependencies = [ "anyhow", "assert_matches", "base64 0.13.0", "bincode", - "bitflags", + "bitflags 1.3.2", "borsh", "bs58", "bytemuck", @@ -6121,6 +6227,7 @@ dependencies = [ "ed25519-dalek", "ed25519-dalek-bip32", "generic-array 0.14.5", + "hex", "hmac 0.12.1", "itertools", "js-sys", @@ -6142,11 +6249,11 @@ dependencies = [ "serde_json", "sha2 0.10.2", "sha3 0.10.1", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-logger 1.11.6", - "solana-program 1.11.6", - "solana-sdk-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-logger 1.14.24", + "solana-program 1.14.24", + "solana-sdk-macro 1.14.24", "static_assertions", "thiserror", "tiny-bip39", @@ -6156,45 +6263,45 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8bcac4394644f21dc013e932a7df9f536fcecef3e5df43fe362b4ec532ce30" +checksum = "7d41a09b9cecd0a4df63c78a192adee99ebf2d3757c19713a68246e1d9789c7c" dependencies = [ "bs58", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "rustversion", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] name = "solana-sdk-macro" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bs58", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "rustversion", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] name = "solana-send-transaction-service" -version = "1.11.6" +version = "1.14.24" dependencies = [ "crossbeam-channel", "log", "solana-client", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-metrics", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-stake-accounts" -version = "1.11.6" +version = "1.14.24" dependencies = [ "clap 2.33.3", "solana-clap-utils", @@ -6202,13 +6309,13 @@ dependencies = [ "solana-client", "solana-remote-wallet", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-stake-program", ] [[package]] name = "solana-stake-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_matches", "bincode", @@ -6220,12 +6327,12 @@ dependencies = [ "serde", "serde_derive", "solana-config-program", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-logger 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-logger 1.14.24", "solana-metrics", "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-vote-program", "test-case", "thiserror", @@ -6233,7 +6340,7 @@ dependencies = [ [[package]] name = "solana-storage-bigtable" -version = "1.11.6" +version = "1.14.24" dependencies = [ "backoff", "bincode", @@ -6254,7 +6361,7 @@ dependencies = [ "serde_derive", "smpl_jwt", "solana-metrics", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-storage-proto", "solana-transaction-status", "thiserror", @@ -6265,7 +6372,7 @@ dependencies = [ [[package]] name = "solana-storage-proto" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "bs58", @@ -6274,25 +6381,25 @@ dependencies = [ "protobuf-src", "serde", "solana-account-decoder", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-transaction-status", "tonic-build 0.8.0", ] [[package]] name = "solana-store-tool" -version = "1.11.6" +version = "1.14.24" dependencies = [ "clap 2.33.3", "log", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-runtime", "solana-version", ] [[package]] name = "solana-streamer" -version = "1.11.6" +version = "1.14.24" dependencies = [ "crossbeam-channel", "futures-util", @@ -6309,10 +6416,10 @@ dependencies = [ "rand 0.7.3", "rcgen", "rustls 0.20.6", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-metrics", "solana-perf", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", "tokio", "x509-parser", @@ -6320,13 +6427,13 @@ dependencies = [ [[package]] name = "solana-sys-tuner" -version = "1.11.6" +version = "1.14.24" dependencies = [ "clap 2.33.3", "libc", "log", "nix", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-version", "sysctl", "unix_socket2", @@ -6335,7 +6442,7 @@ dependencies = [ [[package]] name = "solana-test-validator" -version = "1.11.6" +version = "1.14.24" dependencies = [ "base64 0.13.0", "log", @@ -6346,20 +6453,20 @@ dependencies = [ "solana-core", "solana-gossip", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-net-utils", "solana-program-runtime", "solana-program-test", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "tokio", ] [[package]] name = "solana-tokens" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "chrono", @@ -6375,9 +6482,9 @@ dependencies = [ "solana-clap-utils", "solana-cli-config", "solana-client", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-remote-wallet", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-test-validator", "solana-transaction-status", @@ -6390,7 +6497,7 @@ dependencies = [ [[package]] name = "solana-transaction-dos" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "clap 2.33.3", @@ -6404,11 +6511,11 @@ dependencies = [ "solana-faucet", "solana-gossip", "solana-local-cluster", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-net-utils", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-transaction-status", "solana-version", @@ -6416,7 +6523,7 @@ dependencies = [ [[package]] name = "solana-transaction-status" -version = "1.11.6" +version = "1.14.24" dependencies = [ "Inflector", "base64 0.13.0", @@ -6429,10 +6536,10 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", + "solana-address-lookup-table-program", "solana-measure", "solana-metrics", - "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-vote-program", "spl-associated-token-account", "spl-memo", @@ -6443,7 +6550,7 @@ dependencies = [ [[package]] name = "solana-upload-perf" -version = "1.11.6" +version = "1.14.24" dependencies = [ "serde_json", "solana-metrics", @@ -6451,7 +6558,7 @@ dependencies = [ [[package]] name = "solana-validator" -version = "1.11.6" +version = "1.14.24" dependencies = [ "chrono", "clap 2.33.3", @@ -6482,14 +6589,14 @@ dependencies = [ "solana-genesis-utils", "solana-gossip", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-metrics", "solana-net-utils", "solana-perf", "solana-poh", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-send-transaction-service", "solana-storage-bigtable", "solana-streamer", @@ -6502,41 +6609,44 @@ dependencies = [ [[package]] name = "solana-version" -version = "1.11.6" +version = "1.14.24" dependencies = [ "log", "rustc_version 0.4.0", "semver 1.0.10", "serde", "serde_derive", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-sdk 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-sdk 1.14.24", ] [[package]] name = "solana-vote-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", + "itertools", "log", "num-derive", "num-traits", + "rand 0.7.3", "rustc_version 0.4.0", "serde", "serde_derive", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-logger 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-logger 1.14.24", "solana-metrics", "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", + "test-case", "thiserror", ] [[package]] name = "solana-watchtower" -version = "1.11.6" +version = "1.14.24" dependencies = [ "clap 2.33.3", "humantime", @@ -6545,31 +6655,42 @@ dependencies = [ "solana-cli-config", "solana-cli-output", "solana-client", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-metrics", "solana-notifier", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-version", ] [[package]] name = "solana-zk-token-proof-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bytemuck", "getrandom 0.1.16", "num-derive", "num-traits", "solana-program-runtime", - "solana-sdk 1.11.6", - "solana-zk-token-sdk 1.11.6", + "solana-sdk 1.14.24", + "solana-zk-token-sdk 1.14.24", +] + +[[package]] +name = "solana-zk-token-proof-program-tests" +version = "1.14.24" +dependencies = [ + "bytemuck", + "solana-program-runtime", + "solana-program-test", + "solana-sdk 1.14.24", + "solana-zk-token-sdk 1.14.24", ] [[package]] name = "solana-zk-token-sdk" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410ee53a26ac91098c289c983863535d4fbb6604b229ae1159503f48fa4fc90f" +checksum = "7ab38abd096769f79fd8e3fe8465070f04742395db724606a5263c8ebc215567" dependencies = [ "aes-gcm-siv", "arrayref", @@ -6580,6 +6701,7 @@ dependencies = [ "cipher 0.4.3", "curve25519-dalek", "getrandom 0.1.16", + "itertools", "lazy_static", "merlin", "num-derive", @@ -6588,8 +6710,8 @@ dependencies = [ "serde", "serde_json", "sha3 0.9.1", - "solana-program 1.10.33", - "solana-sdk 1.10.33", + "solana-program 1.14.16", + "solana-sdk 1.14.16", "subtle", "thiserror", "zeroize", @@ -6597,7 +6719,7 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "1.11.6" +version = "1.14.24" dependencies = [ "aes-gcm-siv", "arrayref", @@ -6608,6 +6730,7 @@ dependencies = [ "cipher 0.4.3", "curve25519-dalek", "getrandom 0.1.16", + "itertools", "lazy_static", "merlin", "num-derive", @@ -6616,8 +6739,8 @@ dependencies = [ "serde", "serde_json", "sha3 0.9.1", - "solana-program 1.11.6", - "solana-sdk 1.11.6", + "solana-program 1.14.24", + "solana-sdk 1.14.24", "subtle", "thiserror", "zeroize", @@ -6665,13 +6788,18 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "1.0.5" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b013067447a1396303ddfc294f36e3d260a32f8a16c501c295bcdc7de39b490" +checksum = "978dba3bcbe88d0c2c58366c254d9ea41c5f73357e72fc0bdee4d6b5fc99c8f4" dependencies = [ + "assert_matches", "borsh", - "solana-program 1.10.33", + "num-derive", + "num-traits", + "solana-program 1.14.16", "spl-token", + "spl-token-2022", + "thiserror", ] [[package]] @@ -6680,37 +6808,37 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" dependencies = [ - "solana-program 1.10.33", + "solana-program 1.14.16", ] [[package]] name = "spl-token" -version = "3.3.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d05653bed5932064a287340dbc8a3cb298ee717e5c7ec3353d7cdb9f8fb7e1" +checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d" dependencies = [ "arrayref", "bytemuck", "num-derive", "num-traits", "num_enum", - "solana-program 1.10.33", + "solana-program 1.14.16", "thiserror", ] [[package]] name = "spl-token-2022" -version = "0.4.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a97cbf60b91b610c846ccf8eecca96d92a24a19ffbf9fe06cd0c84e76ec45e" +checksum = "0043b590232c400bad5ee9eb983ced003d15163c4c5d56b090ac6d9a57457b47" dependencies = [ "arrayref", "bytemuck", "num-derive", "num-traits", "num_enum", - "solana-program 1.10.33", - "solana-zk-token-sdk 1.10.33", + "solana-program 1.14.16", + "solana-zk-token-sdk 1.14.16", "spl-memo", "spl-token", "thiserror", @@ -6767,10 +6895,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" dependencies = [ "heck 0.4.0", - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "rustversion", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -6798,12 +6926,23 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2 1.0.66", + "quote 1.0.33", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", + "proc-macro2 1.0.66", + "quote 1.0.33", "unicode-ident", ] @@ -6819,9 +6958,9 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", "unicode-xid 0.2.2", ] @@ -6841,7 +6980,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1123645dfaf2b5eac6b6c88addafc359c789b8ef2a770ecaef758c1ddf363ea4" dependencies = [ - "bitflags", + "bitflags 1.3.2", "byteorder", "libc", "thiserror", @@ -6858,16 +6997,10 @@ dependencies = [ "chrono", "lazy_static", "libc", - "nom 7.0.0", + "nom", "winapi 0.3.9", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tar" version = "0.4.38" @@ -6909,23 +7042,22 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi 0.3.9", + "redox_syscall 0.3.5", + "rustix 0.37.5", + "windows-sys 0.45.0", ] [[package]] @@ -6970,9 +7102,9 @@ checksum = "8dd461f47ade621665c9f4e44b20449341769911c253275dc5cb03726cbb852c" dependencies = [ "cfg-if 1.0.0", "proc-macro-error", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -6986,9 +7118,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" @@ -7005,9 +7137,9 @@ version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -7144,9 +7276,9 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -7335,10 +7467,10 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.66", "prost-build 0.9.0", - "quote 1.0.18", - "syn 1.0.98", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -7347,11 +7479,11 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fbcd2800e34e743b9ae795867d5f77b535d3a3be69fd731e39145719752df8c" dependencies = [ - "prettyplease", - "proc-macro2 1.0.41", + "prettyplease 0.1.9", + "proc-macro2 1.0.66", "prost-build 0.11.0", - "quote 1.0.18", - "syn 1.0.98", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -7380,7 +7512,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bytes", "futures-core", "futures-util", @@ -7424,9 +7556,9 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", ] [[package]] @@ -7745,9 +7877,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -7769,7 +7901,7 @@ version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" dependencies = [ - "quote 1.0.18", + "quote 1.0.33", "wasm-bindgen-macro-support", ] @@ -7779,9 +7911,9 @@ version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7911,6 +8043,36 @@ dependencies = [ "windows_x86_64_msvc 0.32.0", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "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", + "windows_x86_64_msvc 0.42.2", +] + +[[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_msvc" version = "0.30.0" @@ -7923,6 +8085,12 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_i686_gnu" version = "0.30.0" @@ -7935,6 +8103,12 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" +[[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_msvc" version = "0.30.0" @@ -7947,6 +8121,12 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_x86_64_gnu" version = "0.30.0" @@ -7959,6 +8139,18 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" +[[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_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_msvc" version = "0.30.0" @@ -7971,6 +8163,12 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "winreg" version = "0.10.1" @@ -7980,12 +8178,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - [[package]] name = "x509-parser" version = "0.14.0" @@ -7997,7 +8189,7 @@ dependencies = [ "data-encoding", "der-parser", "lazy_static", - "nom 7.0.0", + "nom", "oid-registry", "rusticata-macros", "thiserror", @@ -8022,6 +8214,15 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yansi-term" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "yasna" version = "0.5.0" @@ -8046,9 +8247,9 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.18", - "syn 1.0.98", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 1.0.109", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index ef847c1024a17d..2c35cff999771c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,8 +58,10 @@ members = [ "programs/stake", "programs/vote", "programs/zk-token-proof", + "programs/zk-token-proof-tests", "rayon-threadlimit", "rbpf-cli", + "receipt-tree", "remote-wallet", "rpc", "rpc-test", @@ -74,6 +76,7 @@ members = [ "send-transaction-service", "stake-accounts", "storage-bigtable", + "storage-bigtable/build-proto", "storage-proto", "streamer", "sys-tuner", @@ -90,4 +93,8 @@ members = [ exclude = [ "programs/bpf", + "firedancer" ] + +# This prevents a Travis CI error when building for Windows. +resolver = "2" diff --git a/README.md b/README.md index e76cfc43a1d6ca..0e939f533f8d60 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ $ cd solana ## **3. Build.** ```bash -$ cargo build +$ ./cargo build ``` # Testing @@ -56,7 +56,7 @@ $ cargo build **Run the test suite:** ```bash -$ cargo test +$ ./cargo test ``` ### Starting a local testnet diff --git a/account-decoder/Cargo.toml b/account-decoder/Cargo.toml index e37ae94a1c99fe..fbd96b2da432dd 100644 --- a/account-decoder/Cargo.toml +++ b/account-decoder/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-account-decoder" -version = "1.11.6" +version = "1.14.24" description = "Solana account decoder" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -19,11 +19,12 @@ lazy_static = "1.4.0" serde = "1.0.138" serde_derive = "1.0.103" serde_json = "1.0.81" -solana-config-program = { path = "../programs/config", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } -spl-token = { version = "=3.3.1", features = ["no-entrypoint"] } -spl-token-2022 = { version = "=0.4.2", features = ["no-entrypoint"] } +solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.14.24" } +solana-config-program = { path = "../programs/config", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } +spl-token = { version = "=3.5.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "=0.6.1", features = ["no-entrypoint"] } thiserror = "1.0" zstd = "0.11.2" diff --git a/account-decoder/src/lib.rs b/account-decoder/src/lib.rs index 303391d1d89b4e..06c54a761a7e8d 100644 --- a/account-decoder/src/lib.rs +++ b/account-decoder/src/lib.rs @@ -5,6 +5,7 @@ extern crate lazy_static; extern crate serde_derive; pub mod parse_account_data; +pub mod parse_address_lookup_table; pub mod parse_bpf_loader; pub mod parse_config; pub mod parse_nonce; @@ -114,10 +115,7 @@ impl UiAccount { { UiAccountData::Json(parsed_data) } else { - UiAccountData::Binary( - base64::encode(&account.data()), - UiAccountEncoding::Base64, - ) + UiAccountData::Binary(base64::encode(account.data()), UiAccountEncoding::Base64) } } }; diff --git a/account-decoder/src/parse_account_data.rs b/account-decoder/src/parse_account_data.rs index 89d256dce7c28c..1ffdc4c0f0f11f 100644 --- a/account-decoder/src/parse_account_data.rs +++ b/account-decoder/src/parse_account_data.rs @@ -1,5 +1,6 @@ use { crate::{ + parse_address_lookup_table::parse_address_lookup_table, parse_bpf_loader::parse_bpf_upgradeable_loader, parse_config::parse_config, parse_nonce::parse_nonce, @@ -16,6 +17,7 @@ use { }; lazy_static! { + static ref ADDRESS_LOOKUP_PROGRAM_ID: Pubkey = solana_address_lookup_table_program::id(); static ref BPF_UPGRADEABLE_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader_upgradeable::id(); static ref CONFIG_PROGRAM_ID: Pubkey = solana_config_program::id(); static ref STAKE_PROGRAM_ID: Pubkey = stake::program::id(); @@ -24,6 +26,10 @@ lazy_static! { static ref VOTE_PROGRAM_ID: Pubkey = solana_vote_program::id(); pub static ref PARSABLE_PROGRAM_IDS: HashMap = { let mut m = HashMap::new(); + m.insert( + *ADDRESS_LOOKUP_PROGRAM_ID, + ParsableAccount::AddressLookupTable, + ); m.insert( *BPF_UPGRADEABLE_LOADER_PROGRAM_ID, ParsableAccount::BpfUpgradeableLoader, @@ -68,6 +74,7 @@ pub struct ParsedAccount { #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum ParsableAccount { + AddressLookupTable, BpfUpgradeableLoader, Config, Nonce, @@ -94,6 +101,9 @@ pub fn parse_account_data( .ok_or(ParseAccountError::ProgramNotParsable)?; let additional_data = additional_data.unwrap_or_default(); let parsed_json = match program_name { + ParsableAccount::AddressLookupTable => { + serde_json::to_value(parse_address_lookup_table(data)?)? + } ParsableAccount::BpfUpgradeableLoader => { serde_json::to_value(parse_bpf_upgradeable_loader(data)?)? } diff --git a/account-decoder/src/parse_address_lookup_table.rs b/account-decoder/src/parse_address_lookup_table.rs new file mode 100644 index 00000000000000..26955d74a74242 --- /dev/null +++ b/account-decoder/src/parse_address_lookup_table.rs @@ -0,0 +1,117 @@ +use { + crate::parse_account_data::{ParsableAccount, ParseAccountError}, + solana_address_lookup_table_program::state::AddressLookupTable, + solana_sdk::instruction::InstructionError, +}; + +pub fn parse_address_lookup_table( + data: &[u8], +) -> Result { + AddressLookupTable::deserialize(data) + .map(|address_lookup_table| { + LookupTableAccountType::LookupTable(address_lookup_table.into()) + }) + .or_else(|err| match err { + InstructionError::UninitializedAccount => Ok(LookupTableAccountType::Uninitialized), + _ => Err(ParseAccountError::AccountNotParsable( + ParsableAccount::AddressLookupTable, + )), + }) +} + +#[derive(Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase", tag = "type", content = "info")] +pub enum LookupTableAccountType { + Uninitialized, + LookupTable(UiLookupTable), +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct UiLookupTable { + pub deactivation_slot: String, + pub last_extended_slot: String, + pub last_extended_slot_start_index: u8, + #[serde(skip_serializing_if = "Option::is_none")] + pub authority: Option, + pub addresses: Vec, +} + +impl<'a> From> for UiLookupTable { + fn from(address_lookup_table: AddressLookupTable) -> Self { + Self { + deactivation_slot: address_lookup_table.meta.deactivation_slot.to_string(), + last_extended_slot: address_lookup_table.meta.last_extended_slot.to_string(), + last_extended_slot_start_index: address_lookup_table + .meta + .last_extended_slot_start_index, + authority: address_lookup_table + .meta + .authority + .map(|authority| authority.to_string()), + addresses: address_lookup_table + .addresses + .iter() + .map(|address| address.to_string()) + .collect(), + } + } +} + +#[cfg(test)] +mod test { + use { + super::*, + solana_address_lookup_table_program::state::{LookupTableMeta, LOOKUP_TABLE_META_SIZE}, + solana_sdk::pubkey::Pubkey, + std::borrow::Cow, + }; + + #[test] + fn test_parse_address_lookup_table() { + let authority = Pubkey::new_unique(); + let deactivation_slot = 1; + let last_extended_slot = 2; + let last_extended_slot_start_index = 3; + let lookup_table_meta = LookupTableMeta { + deactivation_slot, + last_extended_slot, + last_extended_slot_start_index, + authority: Some(authority), + ..LookupTableMeta::default() + }; + let num_addresses = 42; + let mut addresses = Vec::with_capacity(num_addresses); + addresses.resize_with(num_addresses, Pubkey::new_unique); + let lookup_table = AddressLookupTable { + meta: lookup_table_meta, + addresses: Cow::Owned(addresses), + }; + let lookup_table_data = + AddressLookupTable::serialize_for_tests(lookup_table.clone()).unwrap(); + + let parsing_result = parse_address_lookup_table(&lookup_table_data).unwrap(); + if let LookupTableAccountType::LookupTable(ui_lookup_table) = parsing_result { + assert_eq!( + ui_lookup_table.deactivation_slot, + deactivation_slot.to_string() + ); + assert_eq!( + ui_lookup_table.last_extended_slot, + last_extended_slot.to_string() + ); + assert_eq!( + ui_lookup_table.last_extended_slot_start_index, + last_extended_slot_start_index + ); + assert_eq!(ui_lookup_table.authority, Some(authority.to_string())); + assert_eq!(ui_lookup_table.addresses.len(), num_addresses); + } + + assert_eq!( + parse_address_lookup_table(&[0u8; LOOKUP_TABLE_META_SIZE]).unwrap(), + LookupTableAccountType::Uninitialized + ); + assert!(parse_address_lookup_table(&[]).is_err()); + } +} diff --git a/account-decoder/src/parse_token.rs b/account-decoder/src/parse_token.rs index ab5eb7f95d37c1..8ec2dafec8241b 100644 --- a/account-decoder/src/parse_token.rs +++ b/account-decoder/src/parse_token.rs @@ -6,7 +6,7 @@ use { }, solana_sdk::pubkey::Pubkey, spl_token_2022::{ - extension::StateWithExtensions, + extension::{BaseStateWithExtensions, StateWithExtensions}, generic_token_account::GenericTokenAccount, solana_program::{ program_option::COption, program_pack::Pack, pubkey::Pubkey as SplTokenPubkey, @@ -282,11 +282,9 @@ pub struct UiMultisig { } pub fn get_token_account_mint(data: &[u8]) -> Option { - if Account::valid_account_data(data) { - Some(Pubkey::new(&data[0..32])) - } else { - None - } + Account::valid_account_data(data) + .then(|| Pubkey::try_from(data.get(..32)?).ok()) + .flatten() } #[cfg(test)] @@ -305,8 +303,8 @@ mod test { #[test] fn test_parse_token() { - let mint_pubkey = SplTokenPubkey::new(&[2; 32]); - let owner_pubkey = SplTokenPubkey::new(&[3; 32]); + let mint_pubkey = SplTokenPubkey::new_from_array([2; 32]); + let owner_pubkey = SplTokenPubkey::new_from_array([3; 32]); let mut account_data = vec![0; Account::get_packed_len()]; let mut account = Account::unpack_unchecked(&account_data).unwrap(); account.mint = mint_pubkey; @@ -360,9 +358,9 @@ mod test { }), ); - let signer1 = SplTokenPubkey::new(&[1; 32]); - let signer2 = SplTokenPubkey::new(&[2; 32]); - let signer3 = SplTokenPubkey::new(&[3; 32]); + let signer1 = SplTokenPubkey::new_from_array([1; 32]); + let signer2 = SplTokenPubkey::new_from_array([2; 32]); + let signer3 = SplTokenPubkey::new_from_array([3; 32]); let mut multisig_data = vec![0; Multisig::get_packed_len()]; let mut signers = [SplTokenPubkey::default(); 11]; signers[0] = signer1; @@ -395,14 +393,14 @@ mod test { #[test] fn test_get_token_account_mint() { - let mint_pubkey = SplTokenPubkey::new(&[2; 32]); + let mint_pubkey = SplTokenPubkey::new_from_array([2; 32]); let mut account_data = vec![0; Account::get_packed_len()]; let mut account = Account::unpack_unchecked(&account_data).unwrap(); account.mint = mint_pubkey; account.state = AccountState::Initialized; Account::pack(account, &mut account_data).unwrap(); - let expected_mint_pubkey = Pubkey::new(&[2; 32]); + let expected_mint_pubkey = Pubkey::from([2; 32]); assert_eq!( get_token_account_mint(&account_data), Some(expected_mint_pubkey) @@ -497,8 +495,8 @@ mod test { #[test] fn test_parse_token_account_with_extensions() { - let mint_pubkey = SplTokenPubkey::new(&[2; 32]); - let owner_pubkey = SplTokenPubkey::new(&[3; 32]); + let mint_pubkey = SplTokenPubkey::new_from_array([2; 32]); + let owner_pubkey = SplTokenPubkey::new_from_array([3; 32]); let account_base = Account { mint: mint_pubkey, @@ -588,7 +586,7 @@ mod test { #[test] fn test_parse_token_mint_with_extensions() { - let owner_pubkey = SplTokenPubkey::new(&[3; 32]); + let owner_pubkey = SplTokenPubkey::new_from_array([3; 32]); let mint_size = ExtensionType::get_account_len::(&[ExtensionType::MintCloseAuthority]); let mint_base = Mint { diff --git a/account-decoder/src/parse_token_extension.rs b/account-decoder/src/parse_token_extension.rs index 998250368b0f90..0df45a6b6dab3c 100644 --- a/account-decoder/src/parse_token_extension.rs +++ b/account-decoder/src/parse_token_extension.rs @@ -2,8 +2,9 @@ use { crate::parse_token::UiAccountState, solana_sdk::clock::UnixTimestamp, spl_token_2022::{ - extension::{self, BaseState, ExtensionType, StateWithExtensions}, + extension::{self, BaseState, BaseStateWithExtensions, ExtensionType, StateWithExtensions}, solana_program::pubkey::Pubkey, + solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, }, }; @@ -21,7 +22,10 @@ pub enum UiExtension { MemoTransfer(UiMemoTransfer), NonTransferable, InterestBearingConfig(UiInterestBearingConfig), + CpiGuard(UiCpiGuard), + PermanentDelegate(UiPermanentDelegate), UnparseableExtension, + NonTransferableAccount, } pub fn parse_extension( @@ -64,6 +68,15 @@ pub fn parse_extension( .get_extension::() .map(|&extension| UiExtension::InterestBearingConfig(extension.into())) .unwrap_or(UiExtension::UnparseableExtension), + ExtensionType::CpiGuard => account + .get_extension::() + .map(|&extension| UiExtension::CpiGuard(extension.into())) + .unwrap_or(UiExtension::UnparseableExtension), + ExtensionType::PermanentDelegate => account + .get_extension::() + .map(|&extension| UiExtension::PermanentDelegate(extension.into())) + .unwrap_or(UiExtension::UnparseableExtension), + ExtensionType::NonTransferableAccount => UiExtension::NonTransferableAccount, } } @@ -204,13 +217,42 @@ impl From for UiInteres } } +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct UiCpiGuard { + pub lock_cpi: bool, +} + +impl From for UiCpiGuard { + fn from(cpi_guard: extension::cpi_guard::CpiGuard) -> Self { + Self { + lock_cpi: cpi_guard.lock_cpi.into(), + } + } +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct UiPermanentDelegate { + pub delegate: Option, +} + +impl From for UiPermanentDelegate { + fn from(permanent_delegate: extension::permanent_delegate::PermanentDelegate) -> Self { + let delegate: Option = permanent_delegate.delegate.into(); + Self { + delegate: delegate.map(|pubkey| pubkey.to_string()), + } + } +} + #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct UiConfidentialTransferMint { - pub authority: String, + pub authority: Option, pub auto_approve_new_accounts: bool, - pub auditor_encryption_pubkey: String, - pub withdraw_withheld_authority_encryption_pubkey: String, + pub auditor_encryption_pubkey: Option, + pub withdraw_withheld_authority_encryption_pubkey: Option, pub withheld_amount: String, } @@ -220,17 +262,19 @@ impl From fn from( confidential_transfer_mint: extension::confidential_transfer::ConfidentialTransferMint, ) -> Self { + let authority: Option = confidential_transfer_mint.authority.into(); + let auditor_encryption_pubkey: Option = + confidential_transfer_mint.auditor_encryption_pubkey.into(); + let withdraw_withheld_authority_encryption_pubkey: Option = + confidential_transfer_mint + .withdraw_withheld_authority_encryption_pubkey + .into(); Self { - authority: confidential_transfer_mint.authority.to_string(), + authority: authority.map(|pubkey| pubkey.to_string()), auto_approve_new_accounts: confidential_transfer_mint.auto_approve_new_accounts.into(), - auditor_encryption_pubkey: format!( - "{}", - confidential_transfer_mint.auditor_encryption_pubkey - ), - withdraw_withheld_authority_encryption_pubkey: format!( - "{}", - confidential_transfer_mint.withdraw_withheld_authority_encryption_pubkey - ), + auditor_encryption_pubkey: auditor_encryption_pubkey.map(|pubkey| pubkey.to_string()), + withdraw_withheld_authority_encryption_pubkey: + withdraw_withheld_authority_encryption_pubkey.map(|pubkey| pubkey.to_string()), withheld_amount: format!("{}", confidential_transfer_mint.withheld_amount), } } @@ -245,7 +289,8 @@ pub struct UiConfidentialTransferAccount { pub pending_balance_hi: String, pub available_balance: String, pub decryptable_available_balance: String, - pub allow_balance_credits: bool, + pub allow_confidential_credits: bool, + pub allow_non_confidential_credits: bool, pub pending_balance_credit_counter: u64, pub maximum_pending_balance_credit_counter: u64, pub expected_pending_balance_credit_counter: u64, @@ -269,7 +314,12 @@ impl From "{}", confidential_transfer_account.decryptable_available_balance ), - allow_balance_credits: confidential_transfer_account.allow_balance_credits.into(), + allow_confidential_credits: confidential_transfer_account + .allow_confidential_credits + .into(), + allow_non_confidential_credits: confidential_transfer_account + .allow_non_confidential_credits + .into(), pending_balance_credit_counter: confidential_transfer_account .pending_balance_credit_counter .into(), diff --git a/accounts-bench/Cargo.toml b/accounts-bench/Cargo.toml index 6f483d201f54a8..02b7c968e72e64 100644 --- a/accounts-bench/Cargo.toml +++ b/accounts-bench/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-accounts-bench" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -12,11 +12,11 @@ publish = false clap = "2.33.1" log = "0.4.17" rayon = "1.5.3" -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/accounts-cluster-bench/Cargo.toml b/accounts-cluster-bench/Cargo.toml index a7b45f33f06e87..ac822052297539 100644 --- a/accounts-cluster-bench/Cargo.toml +++ b/accounts-cluster-bench/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-accounts-cluster-bench" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -13,25 +13,25 @@ clap = "2.33.1" log = "0.4.17" rand = "0.7.0" rayon = "1.5.3" -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-faucet = { path = "../faucet", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -spl-token = { version = "=3.3.1", features = ["no-entrypoint"] } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-faucet = { path = "../faucet", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +spl-token = { version = "=3.5.0", features = ["no-entrypoint"] } [dev-dependencies] -solana-core = { path = "../core", version = "=1.11.6" } -solana-local-cluster = { path = "../local-cluster", version = "=1.11.6" } -solana-test-validator = { path = "../test-validator", version = "=1.11.6" } +solana-core = { path = "../core", version = "=1.14.24" } +solana-local-cluster = { path = "../local-cluster", version = "=1.14.24" } +solana-test-validator = { path = "../test-validator", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/banking-bench/Cargo.toml b/banking-bench/Cargo.toml index 38b7de4bf2ed78..830230c182ebd4 100644 --- a/banking-bench/Cargo.toml +++ b/banking-bench/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-banking-bench" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -14,18 +14,18 @@ crossbeam-channel = "0.5" log = "0.4.17" rand = "0.7.0" rayon = "1.5.3" -solana-client = { path = "../client", version = "=1.11.6" } -solana-core = { path = "../core", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-poh = { path = "../poh", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-core = { path = "../core", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-poh = { path = "../poh", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/banking-bench/src/main.rs b/banking-bench/src/main.rs index 51b0042abed374..2806a8a9e05a7a 100644 --- a/banking-bench/src/main.rs +++ b/banking-bench/src/main.rs @@ -214,10 +214,10 @@ fn main() { .help("Number of threads to use in the banking stage"), ) .arg( - Arg::new("tpu_use_quic") - .long("tpu-use-quic") + Arg::new("tpu_disable_quic") + .long("tpu-disable-quic") .takes_value(false) - .help("Forward messages to TPU using QUIC"), + .help("Disable forwarding messages to TPU using QUIC"), ) .get_matches(); diff --git a/banks-client/Cargo.toml b/banks-client/Cargo.toml index fbec24c0104e44..9f64e217924f12 100644 --- a/banks-client/Cargo.toml +++ b/banks-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-banks-client" -version = "1.11.6" +version = "1.14.24" description = "Solana banks client" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -12,17 +12,17 @@ edition = "2021" [dependencies] borsh = "0.9.3" futures = "0.3" -solana-banks-interface = { path = "../banks-interface", version = "=1.11.6" } -solana-program = { path = "../sdk/program", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-banks-interface = { path = "../banks-interface", version = "=1.14.24" } +solana-program = { path = "../sdk/program", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } tarpc = { version = "0.29.0", features = ["full"] } thiserror = "1.0" tokio = { version = "~1.14.1", features = ["full"] } tokio-serde = { version = "0.8", features = ["bincode"] } [dev-dependencies] -solana-banks-server = { path = "../banks-server", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } +solana-banks-server = { path = "../banks-server", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/banks-interface/Cargo.toml b/banks-interface/Cargo.toml index 31a4775ddedebd..66b6a124436891 100644 --- a/banks-interface/Cargo.toml +++ b/banks-interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-banks-interface" -version = "1.11.6" +version = "1.14.24" description = "Solana banks RPC interface" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -11,7 +11,7 @@ edition = "2021" [dependencies] serde = { version = "1.0.138", features = ["derive"] } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } tarpc = { version = "0.29.0", features = ["full"] } [lib] diff --git a/banks-server/Cargo.toml b/banks-server/Cargo.toml index 518340f77d1448..f65be6f48dfa67 100644 --- a/banks-server/Cargo.toml +++ b/banks-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-banks-server" -version = "1.11.6" +version = "1.14.24" description = "Solana banks server" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -13,13 +13,13 @@ edition = "2021" bincode = "1.3.3" crossbeam-channel = "0.5" futures = "0.3" -solana-banks-interface = { path = "../banks-interface", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.11.6" } +solana-banks-interface = { path = "../banks-interface", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.14.24" } tarpc = { version = "0.29.0", features = ["full"] } -tokio = { version = "~1.14.1", features = ["full"] } +tokio = { version = "1", features = ["full"] } tokio-serde = { version = "0.8", features = ["bincode"] } tokio-stream = "0.1" diff --git a/banks-server/src/banks_server.rs b/banks-server/src/banks_server.rs index c73844d2571560..cf0a1c6083d6b1 100644 --- a/banks-server/src/banks_server.rs +++ b/banks-server/src/banks_server.rs @@ -32,7 +32,7 @@ use { convert::TryFrom, io, net::{Ipv4Addr, SocketAddr}, - sync::{Arc, RwLock}, + sync::{atomic::AtomicBool, Arc, RwLock}, thread::Builder, time::Duration, }, @@ -105,7 +105,7 @@ impl BanksServer { } let server_bank_forks = bank_forks.clone(); Builder::new() - .name("solana-bank-forks-client".to_string()) + .name("solBankForksCli".to_string()) .spawn(move || Self::run(server_bank_forks, transaction_receiver)) .unwrap(); Self::new( @@ -395,6 +395,7 @@ pub async fn start_tcp_server( bank_forks: Arc>, block_commitment_cache: Arc>, connection_cache: Arc, + exit: Arc, ) -> io::Result<()> { // Note: These settings are copied straight from the tarpc example. let server = tcp::listen(listen_addr, Bincode::default) @@ -422,6 +423,7 @@ pub async fn start_tcp_server( &connection_cache, 5_000, 0, + exit.clone(), ); let server = BanksServer::new( diff --git a/banks-server/src/rpc_banks_service.rs b/banks-server/src/rpc_banks_service.rs index 822798dd1ffd62..f3224fb64ed87d 100644 --- a/banks-server/src/rpc_banks_service.rs +++ b/banks-server/src/rpc_banks_service.rs @@ -39,6 +39,7 @@ async fn start_abortable_tcp_server( bank_forks.clone(), block_commitment_cache.clone(), connection_cache, + exit.clone(), ) .fuse(); let interval = IntervalStream::new(time::interval(Duration::from_millis(100))).fuse(); @@ -88,7 +89,7 @@ impl RpcBanksService { let connection_cache = connection_cache.clone(); let exit = exit.clone(); let thread_hdl = Builder::new() - .name("solana-rpc-banks".to_string()) + .name("solRpcBanksSvc".to_string()) .spawn(move || { Self::run( listen_addr, diff --git a/bench-streamer/Cargo.toml b/bench-streamer/Cargo.toml index 00286190aa1fc8..07df881ca0e641 100644 --- a/bench-streamer/Cargo.toml +++ b/bench-streamer/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-bench-streamer" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -11,9 +11,9 @@ publish = false [dependencies] clap = { version = "3.1.5", features = ["cargo"] } crossbeam-channel = "0.5" -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/bench-streamer/src/main.rs b/bench-streamer/src/main.rs index f03d29bf7428e1..c4951574f14db6 100644 --- a/bench-streamer/src/main.rs +++ b/bench-streamer/src/main.rs @@ -38,7 +38,7 @@ fn producer(addr: &SocketAddr, exit: Arc) -> JoinHandle<()> { let a = p.meta.socket_addr(); assert!(p.meta.size <= PACKET_DATA_SIZE); let data = p.data(..).unwrap_or_default(); - send.send_to(data, &a).unwrap(); + send.send_to(data, a).unwrap(); num += 1; } assert_eq!(num, 10); diff --git a/bench-tps/Cargo.toml b/bench-tps/Cargo.toml index 6638c36f7bd993..939ba336bcea66 100644 --- a/bench-tps/Cargo.toml +++ b/bench-tps/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-bench-tps" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -15,28 +15,28 @@ log = "0.4.17" rayon = "1.5.3" serde_json = "1.0.81" serde_yaml = "0.8.26" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-core = { path = "../core", version = "=1.11.6" } -solana-faucet = { path = "../faucet", version = "=1.11.6" } -solana-genesis = { path = "../genesis", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-rpc = { path = "../rpc", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-core = { path = "../core", version = "=1.14.24" } +solana-faucet = { path = "../faucet", version = "=1.14.24" } +solana-genesis = { path = "../genesis", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-rpc = { path = "../rpc", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } thiserror = "1.0" [dev-dependencies] serial_test = "0.8.0" -solana-local-cluster = { path = "../local-cluster", version = "=1.11.6" } -solana-test-validator = { path = "../test-validator", version = "=1.11.6" } +solana-local-cluster = { path = "../local-cluster", version = "=1.14.24" } +solana-test-validator = { path = "../test-validator", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/bench-tps/src/bench_tps_client/tpu_client.rs b/bench-tps/src/bench_tps_client/tpu_client.rs index 53b0102a00f11b..a55326ae0f2b0b 100644 --- a/bench-tps/src/bench_tps_client/tpu_client.rs +++ b/bench-tps/src/bench_tps_client/tpu_client.rs @@ -14,9 +14,7 @@ impl BenchTpsClient for TpuClient { Ok(signature) } fn send_batch(&self, transactions: Vec) -> Result<()> { - for transaction in transactions { - BenchTpsClient::send_transaction(self, transaction)?; - } + self.try_send_transaction_batch(&transactions)?; Ok(()) } fn get_latest_blockhash(&self) -> Result { diff --git a/bench-tps/src/cli.rs b/bench-tps/src/cli.rs index e9b4faa848a835..8c5c22ac09d17d 100644 --- a/bench-tps/src/cli.rs +++ b/bench-tps/src/cli.rs @@ -290,10 +290,10 @@ pub fn build_args<'a, 'b>(version: &'b str) -> App<'a, 'b> { .help("Submit transactions with a TpuClient") ) .arg( - Arg::with_name("tpu_use_quic") - .long("tpu-use-quic") + Arg::with_name("tpu_disable_quic") + .long("tpu-disable-quic") .takes_value(false) - .help("Submit transactions via QUIC; only affects ThinClient (default) \ + .help("Do not submit transactions via QUIC; only affects ThinClient (default) \ or TpuClient sends"), ) .arg( @@ -348,8 +348,8 @@ pub fn extract_args(matches: &ArgMatches) -> Config { args.external_client_type = ExternalClientType::RpcClient; } - if matches.is_present("tpu_use_quic") { - args.use_quic = true; + if matches.is_present("tpu_disable_quic") { + args.use_quic = false; } if let Some(v) = matches.value_of("tpu_connection_pool_size") { diff --git a/bloom/Cargo.toml b/bloom/Cargo.toml index e246024488f47b..7a6639e33f6aae 100644 --- a/bloom/Cargo.toml +++ b/bloom/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bloom" -version = "1.11.6" +version = "1.14.24" description = "Solana bloom filter" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -17,9 +17,9 @@ rand = "0.7.0" rayon = "1.5.3" serde = { version = "1.0.138", features = ["rc"] } serde_derive = "1.0.103" -solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-frozen-abi = { path = "../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/bucket_map/Cargo.toml b/bucket_map/Cargo.toml index 55cabef444eba1..81d427409fa6cc 100644 --- a/bucket_map/Cargo.toml +++ b/bucket_map/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bucket-map" -version = "1.11.6" +version = "1.14.24" description = "solana-bucket-map" homepage = "https://solana.com/" documentation = "https://docs.rs/solana-bucket-map" @@ -15,14 +15,14 @@ log = { version = "0.4.17" } memmap2 = "0.5.3" modular-bitfield = "0.11.2" rand = "0.7.0" -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -tempfile = "3.3.0" +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +tempfile = "3.4.0" [dev-dependencies] fs_extra = "1.2.0" rayon = "1.5.3" -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/bucket_map/src/bucket_map.rs b/bucket_map/src/bucket_map.rs index 9df7651da72eb0..02666316f55a4a 100644 --- a/bucket_map/src/bucket_map.rs +++ b/bucket_map/src/bucket_map.rs @@ -105,8 +105,8 @@ impl BucketMap { fn erase_previous_drives(drives: &[PathBuf]) { drives.iter().for_each(|folder| { - let _ = fs::remove_dir_all(&folder); - let _ = fs::create_dir_all(&folder); + let _ = fs::remove_dir_all(folder); + let _ = fs::create_dir_all(folder); }) } diff --git a/ci/buildkite-pipeline.sh b/ci/buildkite-pipeline.sh index 280a4714f4a89e..71bdb01b609e0f 100755 --- a/ci/buildkite-pipeline.sh +++ b/ci/buildkite-pipeline.sh @@ -24,6 +24,12 @@ annotate() { fi } +# Assume everyting needs to be tested when this file or any Dockerfile changes +mandatory_affected_files=() +mandatory_affected_files+=(^ci/buildkite-pipeline.sh) +mandatory_affected_files+=(^ci/docker-rust/Dockerfile) +mandatory_affected_files+=(^ci/docker-rust-nightly/Dockerfile) + # Checks if a CI pull request affects one or more path patterns. Each # pattern argument is checked in series. If one of them found to be affected, # return immediately as such. @@ -42,8 +48,7 @@ affects() { # the worse (affected) return 0 fi - # Assume everyting needs to be tested when any Dockerfile changes - for pattern in ^ci/docker-rust/Dockerfile ^ci/docker-rust-nightly/Dockerfile "$@"; do + for pattern in "${mandatory_affected_files[@]}" "$@"; do if [[ ${pattern:0:1} = "!" ]]; then for file in "${affected_files[@]}"; do if [[ ! $file =~ ${pattern:1} ]]; then @@ -182,7 +187,7 @@ all_test_steps() { timeout_in_minutes: 35 artifact_paths: "bpf-dumps.tar.bz2" agents: - queue: "gcp" + queue: "solana" EOF else annotate --style info \ @@ -234,7 +239,7 @@ EOF cat >> "$output_file" <<"EOF" - command: "scripts/build-downstream-projects.sh" name: "downstream-projects" - timeout_in_minutes: 30 + timeout_in_minutes: 40 agents: queue: "solana" EOF @@ -297,6 +302,29 @@ pull_or_push_steps() { wait_step fi + # Version bump PRs are an edge case that can skip most of the CI steps + if affects .toml$ && affects .lock$ && ! affects_other_than .toml$ .lock$; then + optional_old_version_number=$(git diff origin/"$BUILDKITE_PULL_REQUEST_BASE_BRANCH"..HEAD validator/Cargo.toml | \ + grep -e "^-version" | sed 's/-version = "\(.*\)"/\1/') + echo "optional_old_version_number: ->$optional_old_version_number<-" + new_version_number=$(grep -e "^version = " validator/Cargo.toml | sed 's/version = "\(.*\)"/\1/') + echo "new_version_number: ->$new_version_number<-" + + # Every line in a version bump diff will match one of these patterns. Since we're using grep -v the output is the + # lines that don't match. Any diff that produces output here is not a version bump. + # | cat is a no-op. If this pull request is a version bump then grep will output no lines and have an exit code of 1. + # Piping the output to cat prevents that non-zero exit code from exiting this script + diff_other_than_version_bump=$(git diff origin/"$BUILDKITE_PULL_REQUEST_BASE_BRANCH"..HEAD | \ + grep -vE "^ |^@@ |^--- |^\+\+\+ |^index |^diff |^-( \")?solana.*$optional_old_version_number|^\+( \")?solana.*$new_version_number|^-version|^\+version"|cat) + echo "diff_other_than_version_bump: ->$diff_other_than_version_bump<-" + + if [ -z "$diff_other_than_version_bump" ]; then + echo "Diff only contains version bump." + command_step checks ". ci/rust-version.sh; ci/docker-run.sh \$\$rust_nightly_docker_image ci/test-checks.sh" 20 + exit 0 + fi + fi + # Run the full test suite by default, skipping only if modifications are local # to some particular areas of the tree if affects_other_than ^.buildkite ^.mergify .md$ ^docs/ ^web3.js/ ^explorer/ ^.gitbook; then diff --git a/ci/buildkite-secondary.yml b/ci/buildkite-secondary.yml index ff5a898e50a661..83ebca7d9471a8 100644 --- a/ci/buildkite-secondary.yml +++ b/ci/buildkite-secondary.yml @@ -2,6 +2,12 @@ # Build steps that run after the primary pipeline on pushes and tags. # Pull requests to not run these steps. steps: + - name: "cargo audit" + command: ". ci/rust-version.sh; ci/docker-run.sh $$rust_stable_docker_image ci/do-audit.sh" + agents: + queue: "release-build" + timeout_in_minutes: 10 + - wait - name: "publish tarball (x86_64-unknown-linux-gnu)" command: "ci/publish-tarball.sh" agents: @@ -22,6 +28,9 @@ steps: command: "ci/publish-crate.sh" agents: queue: "release-build" + retry: + manual: + permit_on_passed: true timeout_in_minutes: 240 branches: "!master" - name: "publish tarball (aarch64-apple-darwin)" diff --git a/ci/check-channel-version.sh b/ci/check-channel-version.sh new file mode 100755 index 00000000000000..abd9d10634c6e8 --- /dev/null +++ b/ci/check-channel-version.sh @@ -0,0 +1,27 @@ + # Ensure the current channel version is not equal ("greater") than +# the version of the latest tag +if [[ -z $CI_TAG ]]; then + echo "--- channel version check" + ( + eval "$(ci/channel-info.sh)" + + if [[ -n $CHANNEL_LATEST_TAG ]]; then + source scripts/read-cargo-variable.sh + + version=$(readCargoVariable version "version/Cargo.toml") + echo "latest channel tag: $CHANNEL_LATEST_TAG" + echo "current version: v$version" + + if [[ $CHANNEL_LATEST_TAG = v$version ]]; then + echo -e "\033[31mError:\033[0m A release has been tagged since your feature branch was last rebased. must be greater than .. + Possible solutions (in the order they should be tried): + 1. rebase your feature branch on the base branch + 2. merge the PR: \"Bump Version to ...\" once it has passed ci/checks, then rebase + 3. ask for help in #devops on discord" + exit 1 + fi + else + echo "Skipped. CHANNEL_LATEST_TAG (CHANNEL=$CHANNEL) unset" + fi + ) +fi diff --git a/ci/do-audit.sh b/ci/do-audit.sh index 395218e27d6d2e..a4da2b5b2aa5d6 100755 --- a/ci/do-audit.sh +++ b/ci/do-audit.sh @@ -7,6 +7,16 @@ src_root="$(readlink -f "${here}/..")" cd "${src_root}" +# `cargo-audit` doesn't give us a way to do this nicely, so hammer it is... +dep_tree_filter="grep -Ev '│|└|├|─'" + +while [[ -n $1 ]]; do + if [[ $1 = "--display-dependency-trees" ]]; then + dep_tree_filter="cat" + shift + fi +done + cargo_audit_ignores=( # `net2` crate has been deprecated; use `socket2` instead # @@ -29,5 +39,11 @@ cargo_audit_ignores=( # https://github.com/chronotope/chrono/issues/499 --ignore RUSTSEC-2020-0159 + # tokio: vulnerability affecting named pipes on Windows + # + # Not worth upgrading tokio version on a stable branch + --ignore RUSTSEC-2023-0001 ) -scripts/cargo-for-all-lock-files.sh stable audit "${cargo_audit_ignores[@]}" +scripts/cargo-for-all-lock-files.sh stable audit "${cargo_audit_ignores[@]}" | $dep_tree_filter +# we want the `cargo audit` exit code, not `$dep_tree_filter`'s +exit "${PIPESTATUS[0]}" diff --git a/ci/docker-run.sh b/ci/docker-run.sh index e154de2eefd96d..24ad02d0ee835b 100755 --- a/ci/docker-run.sh +++ b/ci/docker-run.sh @@ -45,14 +45,16 @@ if [[ -n $CI ]]; then # Share the real ~/.cargo between docker containers in CI for speed ARGS+=(--volume "$HOME:/home") - # sccache - ARGS+=( - --env "RUSTC_WRAPPER=/home/.cargo/bin/sccache" - --env AWS_ACCESS_KEY_ID - --env AWS_SECRET_ACCESS_KEY - --env SCCACHE_BUCKET - --env SCCACHE_REGION - ) + if [[ -n $BUILDKITE ]]; then + # sccache + ARGS+=( + --env "RUSTC_WRAPPER=/home/.cargo/bin/sccache" + --env AWS_ACCESS_KEY_ID + --env AWS_SECRET_ACCESS_KEY + --env SCCACHE_BUCKET + --env SCCACHE_REGION + ) + fi else # Avoid sharing ~/.cargo when building locally to avoid a mixed macOS/Linux # ~/.cargo @@ -97,7 +99,7 @@ ARGS+=( # We normalize CI to `1`; but codecov expects it to be `true` to detect Buildkite... # Unfortunately, codecov.io fails sometimes: # curl: (7) Failed to connect to codecov.io port 443: Connection timed out -CODECOV_ENVS=$(CI=true bash <(while ! curl -sS --retry 5 --retry-delay 2 --retry-connrefused https://codecov.io/env; do sleep 10; done)) +CODECOV_ENVS=$(CI=true bash <(while ! curl -sS --retry 5 --retry-delay 2 --retry-connrefused --fail https://codecov.io/env; do sleep 10; done)) if $INTERACTIVE; then if [[ -n $1 ]]; then @@ -112,4 +114,4 @@ fi set -x # shellcheck disable=SC2086 -exec docker run "${ARGS[@]}" $CODECOV_ENVS "$IMAGE" "$@" +exec docker run "${ARGS[@]}" $CODECOV_ENVS -t "$IMAGE" "$@" diff --git a/ci/publish-crate.sh b/ci/publish-crate.sh index 475038c203b283..668643b8b0f34d 100755 --- a/ci/publish-crate.sh +++ b/ci/publish-crate.sh @@ -2,6 +2,7 @@ set -e cd "$(dirname "$0")/.." source ci/semver_bash/semver.sh +export RUST_STABLE_VERSION=1.65.0 source ci/rust-version.sh stable cargo="$(readlink -f ./cargo)" diff --git a/ci/publish-tarball.sh b/ci/publish-tarball.sh index 3b8b7b10df80f8..ef078f6636c0d5 100755 --- a/ci/publish-tarball.sh +++ b/ci/publish-tarball.sh @@ -58,11 +58,6 @@ windows) git config core.symlinks true find . -type l -delete git reset --hard - # The Windows build fails without resolver = "2", but including it in Cargo.toml causes - # other problems (see PR #26555 and Issue #22603). This adds resolver = "2" to - # Cargo.toml before building. Since the build environment does not persist the changes - # are discarded after the build and do not interfere with non-Windows builds. - echo 'resolver = "2"' >> Cargo.toml ) ;; *) diff --git a/ci/shellcheck.sh b/ci/shellcheck.sh index 9612c8390b6794..1c93af633fa84c 100755 --- a/ci/shellcheck.sh +++ b/ci/shellcheck.sh @@ -7,6 +7,6 @@ cd "$(dirname "$0")/.." ( set -x git ls-files -- '*.sh' ':(exclude)ci/semver_bash' \ - | xargs ci/docker-run.sh koalaman/shellcheck@sha256:fe24ab9a9b6b62d3adb162f4a80e006b6a63cae8c6ffafbae45772bab85e7294 --color=always --external-sources --shell=bash + | xargs ci/docker-run.sh koalaman/shellcheck:v0.8.0 --color=always --external-sources --shell=bash ) echo --- ok diff --git a/ci/test-coverage.sh b/ci/test-coverage.sh index 89dc8c7f01da2f..c60972361a04fc 100755 --- a/ci/test-coverage.sh +++ b/ci/test-coverage.sh @@ -35,7 +35,7 @@ else # We normalize CI to `1`; but codecov expects it to be `true` to detect Buildkite... # Unfortunately, codecov.io fails sometimes: # curl: (7) Failed to connect to codecov.io port 443: Connection timed out - CI=true bash <(while ! curl -sS --retry 5 --retry-delay 2 --retry-connrefused https://codecov.io/bash; do sleep 10; done) -Z -X gcov -f target/cov/lcov.info + CI=true bash <(while ! curl -sS --retry 5 --retry-delay 2 --retry-connrefused --fail https://codecov.io/bash; do sleep 10; done) -Z -X gcov -f target/cov/lcov.info annotate --style success --context codecov.io \ "CodeCov report: https://codecov.io/github/solana-labs/solana/commit/${CI_COMMIT:0:9}" diff --git a/ci/test-sanity.sh b/ci/test-sanity.sh index c4dd244bb10e4d..7d482982ef21c4 100755 --- a/ci/test-sanity.sh +++ b/ci/test-sanity.sh @@ -22,32 +22,8 @@ source ci/_ echo +_ ci/check-channel-version.sh _ ci/nits.sh _ ci/check-ssh-keys.sh - -# Ensure the current channel version is not equal ("greater") than -# the version of the latest tag -if [[ -z $CI_TAG ]]; then - echo "--- channel version check" - ( - eval "$(ci/channel-info.sh)" - - if [[ -n $CHANNEL_LATEST_TAG ]]; then - source scripts/read-cargo-variable.sh - - version=$(readCargoVariable version "version/Cargo.toml") - echo "version: v$version" - echo "latest channel tag: $CHANNEL_LATEST_TAG" - - if [[ $CHANNEL_LATEST_TAG = v$version ]]; then - echo "Error: please run ./scripts/increment-cargo-version.sh" - exit 1 - fi - else - echo "Skipped. CHANNEL_LATEST_TAG (CHANNEL=$CHANNEL) unset" - fi - ) -fi - echo --- ok diff --git a/ci/test-stable.sh b/ci/test-stable.sh index 5372183da05939..e3a630599d5402 100755 --- a/ci/test-stable.sh +++ b/ci/test-stable.sh @@ -12,6 +12,12 @@ annotate() { } } +exit_if_error() { + if [[ "$1" -ne 0 ]]; then + exit "$1" + fi +} + # Run the appropriate test based on entrypoint testName=$(basename "$0" .sh) @@ -32,10 +38,35 @@ NPROC=$(nproc) JOBS=$((JOBS>NPROC ? NPROC : JOBS)) +# get channel info +eval "$(ci/channel-info.sh)" + +need_to_generate_test_result() { + local branches=( + "$EDGE_CHANNEL" + "$BETA_CHANNEL" + "$STABLE_CHANNEL" + ) + + for n in "${branches[@]}"; + do + if [[ "$CI_BRANCH" == "$n" ]]; then + return 0 + fi + done + + return 1 +} + echo "Executing $testName" case $testName in test-stable) - _ "$cargo" stable test --jobs "$JOBS" --all --tests --exclude solana-local-cluster ${V:+--verbose} -- --nocapture + if need_to_generate_test_result; then + _ "$cargo" stable test --jobs "$JOBS" --all --tests --exclude solana-local-cluster ${V:+--verbose} -- -Z unstable-options --format json --report-time | tee results.json + exit_if_error "${PIPESTATUS[0]}" + else + _ "$cargo" stable test --jobs "$JOBS" --all --tests --exclude solana-local-cluster ${V:+--verbose} -- --nocapture + fi ;; test-stable-bpf) # Clear the C dependency files, if dependency moves these files are not regenerated @@ -57,9 +88,16 @@ test-stable-bpf) # BPF C program system tests _ make -C programs/bpf/c tests - _ "$cargo" stable test \ - --manifest-path programs/bpf/Cargo.toml \ - --no-default-features --features=bpf_c,bpf_rust -- --nocapture + if need_to_generate_test_result; then + _ "$cargo" stable test \ + --manifest-path programs/bpf/Cargo.toml \ + --no-default-features --features=bpf_c,bpf_rust -- -Z unstable-options --format json --report-time | tee results.json + exit_if_error "${PIPESTATUS[0]}" + else + _ "$cargo" stable test \ + --manifest-path programs/bpf/Cargo.toml \ + --no-default-features --features=bpf_c,bpf_rust -- --nocapture + fi # BPF Rust program unit tests for bpf_test in programs/bpf/rust/*; do @@ -92,10 +130,18 @@ test-stable-bpf) # BPF program instruction count assertion bpf_target_path=programs/bpf/target - _ "$cargo" stable test \ - --manifest-path programs/bpf/Cargo.toml \ - --no-default-features --features=bpf_c,bpf_rust assert_instruction_count \ - -- --nocapture &> "${bpf_target_path}"/deploy/instuction_counts.txt + if need_to_generate_test_result; then + _ "$cargo" stable test \ + --manifest-path programs/bpf/Cargo.toml \ + --no-default-features --features=bpf_c,bpf_rust assert_instruction_count \ + -- -Z unstable-options --format json --report-time |& tee results.json + awk '!/{ "type": .* }/' results.json >"${bpf_target_path}"/deploy/instuction_counts.txt + else + _ "$cargo" stable test \ + --manifest-path programs/bpf/Cargo.toml \ + --no-default-features --features=bpf_c,bpf_rust assert_instruction_count \ + -- --nocapture &> "${bpf_target_path}"/deploy/instuction_counts.txt + fi bpf_dump_archive="bpf-dumps.tar.bz2" rm -f "$bpf_dump_archive" @@ -120,27 +166,52 @@ test-stable-perf) fi _ "$cargo" stable build --bins ${V:+--verbose} - _ "$cargo" stable test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture + if need_to_generate_test_result; then + _ "$cargo" stable test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- -Z unstable-options --format json --report-time | tee results.json + exit_if_error "${PIPESTATUS[0]}" + else + _ "$cargo" stable test --package solana-perf --package solana-ledger --package solana-core --lib ${V:+--verbose} -- --nocapture + fi _ "$cargo" stable run --manifest-path poh-bench/Cargo.toml ${V:+--verbose} -- --hashes-per-tick 10 ;; test-local-cluster) _ "$cargo" stable build --release --bins ${V:+--verbose} - _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster ${V:+--verbose} -- --nocapture --test-threads=1 + if need_to_generate_test_result; then + _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster ${V:+--verbose} -- --test-threads=1 -Z unstable-options --format json --report-time | tee results.json + exit_if_error "${PIPESTATUS[0]}" + else + _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster ${V:+--verbose} -- --nocapture --test-threads=1 + fi exit 0 ;; test-local-cluster-flakey) _ "$cargo" stable build --release --bins ${V:+--verbose} - _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster_flakey ${V:+--verbose} -- --nocapture --test-threads=1 + if need_to_generate_test_result; then + _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster_flakey ${V:+--verbose} -- --test-threads=1 -Z unstable-options --format json --report-time | tee results.json + exit_if_error "${PIPESTATUS[0]}" + else + _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster_flakey ${V:+--verbose} -- --nocapture --test-threads=1 + fi exit 0 ;; test-local-cluster-slow-1) _ "$cargo" stable build --release --bins ${V:+--verbose} - _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster_slow_1 ${V:+--verbose} -- --nocapture --test-threads=1 + if need_to_generate_test_result; then + _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster_slow_1 ${V:+--verbose} -- --test-threads=1 -Z unstable-options --format json --report-time | tee results.json + exit_if_error "${PIPESTATUS[0]}" + else + _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster_slow_1 ${V:+--verbose} -- --nocapture --test-threads=1 + fi exit 0 ;; test-local-cluster-slow-2) _ "$cargo" stable build --release --bins ${V:+--verbose} - _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster_slow_2 ${V:+--verbose} -- --nocapture --test-threads=1 + if need_to_generate_test_result; then + _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster_slow_2 ${V:+--verbose} -- --test-threads=1 -Z unstable-options --format json --report-time | tee results.json + exit_if_error "${PIPESTATUS[0]}" + else + _ "$cargo" stable test --release --package solana-local-cluster --test local_cluster_slow_2 ${V:+--verbose} -- --nocapture --test-threads=1 + fi exit 0 ;; test-wasm) @@ -157,8 +228,13 @@ test-wasm) exit 0 ;; test-docs) - _ "$cargo" stable test --jobs "$JOBS" --all --doc --exclude solana-local-cluster ${V:+--verbose} -- --nocapture - exit 0 + if need_to_generate_test_result; then + _ "$cargo" stable test --jobs "$JOBS" --all --doc --exclude solana-local-cluster ${V:+--verbose} -- -Z unstable-options --format json --report-time | tee results.json + exit "${PIPESTATUS[0]}" + else + _ "$cargo" stable test --jobs "$JOBS" --all --doc --exclude solana-local-cluster ${V:+--verbose} -- --nocapture + exit 0 + fi ;; *) echo "Error: Unknown test: $testName" diff --git a/clap-utils/Cargo.toml b/clap-utils/Cargo.toml index ecae31fa52cfa8..8ee7a3cb81a087 100644 --- a/clap-utils/Cargo.toml +++ b/clap-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-clap-utils" -version = "1.11.6" +version = "1.14.24" description = "Solana utilities for the clap" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -13,16 +13,16 @@ edition = "2021" chrono = "0.4" clap = "2.33.0" rpassword = "6.0" -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-remote-wallet = { path = "../remote-wallet", version = "=1.11.6", default-features = false } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-remote-wallet = { path = "../remote-wallet", version = "=1.14.24", default-features = false } +solana-sdk = { path = "../sdk", version = "=1.14.24" } thiserror = "1.0.31" tiny-bip39 = "0.8.2" uriparse = "0.6.4" url = "2.2.2" [dev-dependencies] -tempfile = "3.3.0" +tempfile = "3.4.0" [lib] name = "solana_clap_utils" diff --git a/clap-utils/src/keypair.rs b/clap-utils/src/keypair.rs index 1634e6bb3e00fe..dd0c29a2e778f0 100644 --- a/clap-utils/src/keypair.rs +++ b/clap-utils/src/keypair.rs @@ -1229,7 +1229,7 @@ mod tests { } if p == absolute_path_str) ); assert!( - matches!(parse_signer_source(&relative_path_str).unwrap(), SignerSource { + matches!(parse_signer_source(relative_path_str).unwrap(), SignerSource { kind: SignerSourceKind::Filepath(p), derivation_path: None, legacy: false, diff --git a/clap-v3-utils/Cargo.toml b/clap-v3-utils/Cargo.toml index bd58206a8bbc8d..0b7b35f4643c77 100644 --- a/clap-v3-utils/Cargo.toml +++ b/clap-v3-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-clap-v3-utils" -version = "1.11.6" +version = "1.14.24" description = "Solana utilities for the clap v3" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -11,18 +11,18 @@ edition = "2021" [dependencies] chrono = "0.4" -clap = { version = "3.1.5", features = ["cargo"] } +clap = { version = "3.2.23", features = ["cargo"] } rpassword = "6.0" -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-remote-wallet = { path = "../remote-wallet", version = "=1.11.6", default-features = false } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-remote-wallet = { path = "../remote-wallet", version = "=1.14.24", default-features = false } +solana-sdk = { path = "../sdk", version = "=1.14.24" } thiserror = "1.0.31" tiny-bip39 = "0.8.2" uriparse = "0.6.4" url = "2.2.2" [dev-dependencies] -tempfile = "3.3.0" +tempfile = "3.4.0" [lib] name = "solana_clap_v3_utils" diff --git a/clap-v3-utils/src/keypair.rs b/clap-v3-utils/src/keypair.rs index fd7dd6d5026c34..d1a92ac6fee2d7 100644 --- a/clap-v3-utils/src/keypair.rs +++ b/clap-v3-utils/src/keypair.rs @@ -789,11 +789,12 @@ pub fn signer_from_path_with_config( *wallet_manager = maybe_wallet_manager()?; } if let Some(wallet_manager) = wallet_manager { + let confirm_key = matches.try_contains_id("confirm_key").unwrap_or(false); Ok(Box::new(generate_remote_keypair( locator, derivation_path.unwrap_or_default(), wallet_manager, - matches.is_present("confirm_key"), + confirm_key, keypair_name, )?)) } else { @@ -917,11 +918,12 @@ pub fn resolve_signer_from_path( *wallet_manager = maybe_wallet_manager()?; } if let Some(wallet_manager) = wallet_manager { + let confirm_key = matches.try_contains_id("confirm_key").unwrap_or(false); let path = generate_remote_keypair( locator, derivation_path.unwrap_or_default(), wallet_manager, - matches.is_present("confirm_key"), + confirm_key, keypair_name, ) .map(|keypair| keypair.path)?; @@ -1229,7 +1231,7 @@ mod tests { } if p == absolute_path_str) ); assert!( - matches!(parse_signer_source(&relative_path_str).unwrap(), SignerSource { + matches!(parse_signer_source(relative_path_str).unwrap(), SignerSource { kind: SignerSourceKind::Filepath(p), derivation_path: None, legacy: false, diff --git a/clap-v3-utils/src/nonce.rs b/clap-v3-utils/src/nonce.rs index 8088edf10efb52..7ea6d5b8fc7284 100644 --- a/clap-v3-utils/src/nonce.rs +++ b/clap-v3-utils/src/nonce.rs @@ -1,5 +1,5 @@ use { - crate::{input_validators::*, offline::BLOCKHASH_ARG, ArgConstant}, + crate::{input_validators::*, ArgConstant}, clap::{Arg, Command}, }; @@ -23,7 +23,6 @@ fn nonce_arg<'a>() -> Arg<'a> { .long(NONCE_ARG.long) .takes_value(true) .value_name("PUBKEY") - .requires(BLOCKHASH_ARG.name) .validator(|s| is_valid_pubkey(s)) .help(NONCE_ARG.help) } diff --git a/cli-config/Cargo.toml b/cli-config/Cargo.toml index bbf5161438dc52..7a0fa4d68da2cc 100644 --- a/cli-config/Cargo.toml +++ b/cli-config/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-cli-config" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -15,8 +15,8 @@ lazy_static = "1.4.0" serde = "1.0.138" serde_derive = "1.0.103" serde_yaml = "0.8.26" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } url = "2.2.2" [dev-dependencies] diff --git a/cli-config/src/config.rs b/cli-config/src/config.rs index bedac8a381db35..d3dc4bc0aacd59 100644 --- a/cli-config/src/config.rs +++ b/cli-config/src/config.rs @@ -18,7 +18,7 @@ lazy_static! { /// [lazy_static]: https://docs.rs/lazy_static pub static ref CONFIG_FILE: Option = { dirs_next::home_dir().map(|mut path| { - path.extend(&[".config", "solana", "cli", "config.yml"]); + path.extend([".config", "solana", "cli", "config.yml"]); path.to_str().unwrap().to_string() }) }; @@ -70,7 +70,7 @@ impl Default for Config { fn default() -> Self { let keypair_path = { let mut keypair_path = dirs_next::home_dir().expect("home directory"); - keypair_path.extend(&[".config", "solana", "id.json"]); + keypair_path.extend([".config", "solana", "id.json"]); keypair_path.to_str().unwrap().to_string() }; let json_rpc_url = "https://api.mainnet-beta.solana.com".to_string(); diff --git a/cli-output/Cargo.toml b/cli-output/Cargo.toml index 88adddaa1a47dd..227e45ff602f0b 100644 --- a/cli-output/Cargo.toml +++ b/cli-output/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-cli-output" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -21,13 +21,13 @@ pretty-hex = "0.3.0" semver = "1.0.10" serde = "1.0.138" serde_json = "1.0.81" -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] } [dev-dependencies] diff --git a/cli-output/src/cli_output.rs b/cli-output/src/cli_output.rs index f45c5713e4af29..5d18d51d81dd10 100644 --- a/cli-output/src/cli_output.rs +++ b/cli-output/src/cli_output.rs @@ -2111,6 +2111,75 @@ impl fmt::Display for CliUpgradeableBuffers { } } +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct CliAddressLookupTable { + pub lookup_table_address: String, + pub authority: Option, + pub deactivation_slot: u64, + pub last_extended_slot: u64, + pub addresses: Vec, +} +impl QuietDisplay for CliAddressLookupTable {} +impl VerboseDisplay for CliAddressLookupTable {} +impl fmt::Display for CliAddressLookupTable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f)?; + writeln_name_value(f, "Lookup Table Address:", &self.lookup_table_address)?; + if let Some(authority) = &self.authority { + writeln_name_value(f, "Authority:", authority)?; + } else { + writeln_name_value(f, "Authority:", "None (frozen)")?; + } + if self.deactivation_slot == u64::MAX { + writeln_name_value(f, "Deactivation Slot:", "None (still active)")?; + } else { + writeln_name_value(f, "Deactivation Slot:", &self.deactivation_slot.to_string())?; + } + if self.last_extended_slot == 0 { + writeln_name_value(f, "Last Extended Slot:", "None (empty)")?; + } else { + writeln_name_value( + f, + "Last Extended Slot:", + &self.last_extended_slot.to_string(), + )?; + } + if self.addresses.is_empty() { + writeln_name_value(f, "Address Table Entries:", "None (empty)")?; + } else { + writeln!(f, "{}", style("Address Table Entries:".to_string()).bold())?; + writeln!(f)?; + writeln!( + f, + "{}", + style(format!(" {:<5} {}", "Index", "Address")).bold() + )?; + for (index, address) in self.addresses.iter().enumerate() { + writeln!(f, " {:<5} {}", index, address)?; + } + } + Ok(()) + } +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CliAddressLookupTableCreated { + pub lookup_table_address: String, + pub signature: String, +} +impl QuietDisplay for CliAddressLookupTableCreated {} +impl VerboseDisplay for CliAddressLookupTableCreated {} +impl fmt::Display for CliAddressLookupTableCreated { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f)?; + writeln_name_value(f, "Signature:", &self.signature)?; + writeln_name_value(f, "Lookup Table Address:", &self.lookup_table_address)?; + Ok(()) + } +} + #[derive(Debug, Default)] pub struct ReturnSignersConfig { pub dump_transaction_message: bool, @@ -2793,10 +2862,10 @@ mod tests { } let present: Box = Box::new(keypair_from_seed(&[2u8; 32]).unwrap()); - let absent: Box = Box::new(NullSigner::new(&Pubkey::new(&[3u8; 32]))); - let bad: Box = Box::new(BadSigner::new(Pubkey::new(&[4u8; 32]))); - let to = Pubkey::new(&[5u8; 32]); - let nonce = Pubkey::new(&[6u8; 32]); + let absent: Box = Box::new(NullSigner::new(&Pubkey::from([3u8; 32]))); + let bad: Box = Box::new(BadSigner::new(Pubkey::from([4u8; 32]))); + let to = Pubkey::from([5u8; 32]); + let nonce = Pubkey::from([6u8; 32]); let from = present.pubkey(); let fee_payer = absent.pubkey(); let nonce_auth = bad.pubkey(); diff --git a/cli-output/src/display.rs b/cli-output/src/display.rs index da4ef31bd28dbb..38a9086f59f89f 100644 --- a/cli-output/src/display.rs +++ b/cli-output/src/display.rs @@ -15,9 +15,10 @@ use { signature::Signature, stake, transaction::{TransactionError, TransactionVersion, VersionedTransaction}, - transaction_context::TransactionReturnData, }, - solana_transaction_status::{Rewards, UiTransactionStatusMeta}, + solana_transaction_status::{ + Rewards, UiReturnDataEncoding, UiTransactionReturnData, UiTransactionStatusMeta, + }, spl_memo::{id as spl_memo_id, v1::id as spl_memo_v1_id}, std::{collections::HashMap, fmt, io}, }; @@ -262,9 +263,14 @@ fn write_transaction( write_status(w, &transaction_status.status, prefix)?; write_fees(w, transaction_status.fee, prefix)?; write_balances(w, transaction_status, prefix)?; - write_log_messages(w, transaction_status.log_messages.as_ref(), prefix)?; - write_return_data(w, transaction_status.return_data.as_ref(), prefix)?; - write_rewards(w, transaction_status.rewards.as_ref(), prefix)?; + write_compute_units_consumed( + w, + transaction_status.compute_units_consumed.clone().into(), + prefix, + )?; + write_log_messages(w, transaction_status.log_messages.as_ref().into(), prefix)?; + write_return_data(w, transaction_status.return_data.as_ref().into(), prefix)?; + write_rewards(w, transaction_status.rewards.as_ref().into(), prefix)?; } else { writeln!(w, "{}Status: Unavailable", prefix)?; } @@ -596,23 +602,43 @@ fn write_balances( fn write_return_data( w: &mut W, - return_data: Option<&TransactionReturnData>, + return_data: Option<&UiTransactionReturnData>, prefix: &str, ) -> io::Result<()> { if let Some(return_data) = return_data { - if !return_data.data.is_empty() { + let (data, encoding) = &return_data.data; + let raw_return_data = match encoding { + UiReturnDataEncoding::Base64 => base64::decode(data).map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("could not parse data as {:?}: {:?}", encoding, err), + ) + })?, + }; + if !raw_return_data.is_empty() { use pretty_hex::*; writeln!( w, "{}Return Data from Program {}:", prefix, return_data.program_id )?; - writeln!(w, "{} {:?}", prefix, return_data.data.hex_dump())?; + writeln!(w, "{} {:?}", prefix, raw_return_data.hex_dump())?; } } Ok(()) } +fn write_compute_units_consumed( + w: &mut W, + compute_units_consumed: Option, + prefix: &str, +) -> io::Result<()> { + if let Some(cus) = compute_units_consumed { + writeln!(w, "{}Compute Units Consumed: {}", prefix, cus)?; + } + Ok(()) +} + fn write_log_messages( w: &mut W, log_messages: Option<&Vec>, @@ -709,6 +735,7 @@ mod test { pubkey::Pubkey, signature::{Keypair, Signer}, transaction::Transaction, + transaction_context::TransactionReturnData, }, solana_transaction_status::{Reward, RewardType, TransactionStatusMeta}, std::io::BufWriter, @@ -791,6 +818,7 @@ mod test { program_id: Pubkey::new_from_array([2u8; 32]), data: vec![1, 2, 3], }), + compute_units_consumed: Some(1234u64), }; let output = { @@ -825,6 +853,7 @@ Status: Ok Fee: ◎0.000005 Account 0 balance: ◎0.000005 -> ◎0 Account 1 balance: ◎0.00001 -> ◎0.0000099 +Compute Units Consumed: 1234 Log Messages: Test message Return Data from Program 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR: @@ -868,6 +897,7 @@ Rewards: program_id: Pubkey::new_from_array([2u8; 32]), data: vec![1, 2, 3], }), + compute_units_consumed: Some(2345u64), }; let output = { @@ -911,6 +941,7 @@ Status: Ok Account 1 balance: ◎0.00001 Account 2 balance: ◎0.000015 -> ◎0.0000149 Account 3 balance: ◎0.00002 +Compute Units Consumed: 2345 Log Messages: Test message Return Data from Program 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR: diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 026b8a27f114f6..9d25f469ae639d 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-cli" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -27,30 +27,31 @@ semver = "1.0.10" serde = "1.0.138" serde_derive = "1.0.103" serde_json = "1.0.81" -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.11.6" } -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-cli-output = { path = "../cli-output", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-config-program = { path = "../programs/config", version = "=1.11.6" } -solana-faucet = { path = "../faucet", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-program-runtime = { path = "../program-runtime", version = "=1.11.6" } -solana-remote-wallet = { path = "../remote-wallet", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.14.24" } +solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.14.24" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-cli-output = { path = "../cli-output", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-config-program = { path = "../programs/config", version = "=1.14.24" } +solana-faucet = { path = "../faucet", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-program-runtime = { path = "../program-runtime", version = "=1.14.24" } +solana-remote-wallet = { path = "../remote-wallet", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } solana_rbpf = "=0.2.31" spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] } thiserror = "1.0.31" tiny-bip39 = "0.8.2" [dev-dependencies] -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-test-validator = { path = "../test-validator", version = "=1.11.6" } -tempfile = "3.3.0" +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-test-validator = { path = "../test-validator", version = "=1.14.24" } +tempfile = "3.4.0" [[bin]] name = "solana" diff --git a/cli/src/address_lookup_table.rs b/cli/src/address_lookup_table.rs new file mode 100644 index 00000000000000..7f0fa9d3137897 --- /dev/null +++ b/cli/src/address_lookup_table.rs @@ -0,0 +1,832 @@ +use { + crate::cli::{CliCommand, CliCommandInfo, CliConfig, CliError, ProcessResult}, + clap::{App, AppSettings, Arg, ArgMatches, SubCommand}, + solana_address_lookup_table_program::{ + instruction::{ + close_lookup_table, create_lookup_table, deactivate_lookup_table, extend_lookup_table, + freeze_lookup_table, + }, + state::AddressLookupTable, + }, + solana_clap_utils::{self, input_parsers::*, input_validators::*, keypair::*}, + solana_cli_output::{CliAddressLookupTable, CliAddressLookupTableCreated, CliSignature}, + solana_client::{rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig}, + solana_remote_wallet::remote_wallet::RemoteWalletManager, + solana_sdk::{ + account::from_account, clock::Clock, commitment_config::CommitmentConfig, message::Message, + pubkey::Pubkey, sysvar, transaction::Transaction, + }, + std::sync::Arc, +}; + +#[derive(Debug, PartialEq, Eq)] +pub enum AddressLookupTableCliCommand { + CreateLookupTable { + authority_signer_index: SignerIndex, + payer_signer_index: SignerIndex, + }, + FreezeLookupTable { + lookup_table_pubkey: Pubkey, + authority_signer_index: SignerIndex, + bypass_warning: bool, + }, + ExtendLookupTable { + lookup_table_pubkey: Pubkey, + authority_signer_index: SignerIndex, + payer_signer_index: SignerIndex, + new_addresses: Vec, + }, + DeactivateLookupTable { + lookup_table_pubkey: Pubkey, + authority_signer_index: SignerIndex, + bypass_warning: bool, + }, + CloseLookupTable { + lookup_table_pubkey: Pubkey, + authority_signer_index: SignerIndex, + recipient_pubkey: Pubkey, + }, + ShowLookupTable { + lookup_table_pubkey: Pubkey, + }, +} + +pub trait AddressLookupTableSubCommands { + fn address_lookup_table_subcommands(self) -> Self; +} + +impl AddressLookupTableSubCommands for App<'_, '_> { + fn address_lookup_table_subcommands(self) -> Self { + self.subcommand( + SubCommand::with_name("address-lookup-table") + .about("Address lookup table management") + .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("create") + .about("Create a lookup table") + .arg( + Arg::with_name("authority") + .long("authority") + .value_name("AUTHORITY_SIGNER") + .takes_value(true) + .validator(is_valid_signer) + .help("Lookup table authority [default: the default configured keypair]") + ) + .arg( + Arg::with_name("payer") + .long("payer") + .value_name("PAYER_SIGNER") + .takes_value(true) + .validator(is_valid_signer) + .help("Account that will pay rent fees for the created lookup table [default: the default configured keypair]") + ) + ) + .subcommand( + SubCommand::with_name("freeze") + .about("Permanently freezes a lookup table") + .arg( + Arg::with_name("lookup_table_address") + .index(1) + .value_name("LOOKUP_TABLE_ADDRESS") + .takes_value(true) + .required(true) + .validator(is_pubkey) + .help("Address of the lookup table") + ) + .arg( + Arg::with_name("authority") + .long("authority") + .value_name("AUTHORITY_SIGNER") + .takes_value(true) + .validator(is_valid_signer) + .help("Lookup table authority [default: the default configured keypair]") + ) + .arg( + Arg::with_name("bypass_warning") + .long("bypass-warning") + .takes_value(false) + .help("Bypass the permanent lookup table freeze warning"), + ), + ) + .subcommand( + SubCommand::with_name("extend") + .about("Append more addresses to a lookup table") + .arg( + Arg::with_name("lookup_table_address") + .index(1) + .value_name("LOOKUP_TABLE_ADDRESS") + .takes_value(true) + .required(true) + .validator(is_pubkey) + .help("Address of the lookup table") + ) + .arg( + Arg::with_name("authority") + .long("authority") + .value_name("AUTHORITY_SIGNER") + .takes_value(true) + .validator(is_valid_signer) + .help("Lookup table authority [default: the default configured keypair]") + ) + .arg( + Arg::with_name("payer") + .long("payer") + .value_name("PAYER_SIGNER") + .takes_value(true) + .validator(is_valid_signer) + .help("Account that will pay rent fees for the extended lookup table [default: the default configured keypair]") + ) + .arg( + Arg::with_name("addresses") + .long("addresses") + .value_name("ADDRESS_1,ADDRESS_2") + .takes_value(true) + .use_delimiter(true) + .required(true) + .validator(is_pubkey) + .help("Comma separated list of addresses to append") + ) + ) + .subcommand( + SubCommand::with_name("deactivate") + .about("Permanently deactivates a lookup table") + .arg( + Arg::with_name("lookup_table_address") + .index(1) + .value_name("LOOKUP_TABLE_ADDRESS") + .takes_value(true) + .required(true) + .help("Address of the lookup table") + ) + .arg( + Arg::with_name("authority") + .long("authority") + .value_name("AUTHORITY_SIGNER") + .takes_value(true) + .validator(is_valid_signer) + .help("Lookup table authority [default: the default configured keypair]") + ) + .arg( + Arg::with_name("bypass_warning") + .long("bypass-warning") + .takes_value(false) + .help("Bypass the permanent lookup table deactivation warning"), + ), + ) + .subcommand( + SubCommand::with_name("close") + .about("Permanently closes a lookup table") + .arg( + Arg::with_name("lookup_table_address") + .index(1) + .value_name("LOOKUP_TABLE_ADDRESS") + .takes_value(true) + .required(true) + .help("Address of the lookup table") + ) + .arg( + Arg::with_name("recipient") + .long("recipient") + .value_name("RECIPIENT_ADDRESS") + .takes_value(true) + .validator(is_pubkey) + .help("Address of the recipient account to deposit the closed account's lamports [default: the default configured keypair]") + ) + .arg( + Arg::with_name("authority") + .long("authority") + .value_name("AUTHORITY_SIGNER") + .takes_value(true) + .validator(is_valid_signer) + .help("Lookup table authority [default: the default configured keypair]") + ) + ) + .subcommand( + SubCommand::with_name("get") + .about("Display information about a lookup table") + .arg( + Arg::with_name("lookup_table_address") + .index(1) + .value_name("LOOKUP_TABLE_ADDRESS") + .takes_value(true) + .help("Address of the lookup table to show") + ) + ) + ) + } +} + +pub fn parse_address_lookup_table_subcommand( + matches: &ArgMatches<'_>, + default_signer: &DefaultSigner, + wallet_manager: &mut Option>, +) -> Result { + let (subcommand, sub_matches) = matches.subcommand(); + + let response = match (subcommand, sub_matches) { + ("create", Some(matches)) => { + let mut bulk_signers = vec![Some( + default_signer.signer_from_path(matches, wallet_manager)?, + )]; + + let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) = + signer_of(matches, "authority", wallet_manager) + { + bulk_signers.push(authority_signer); + Some(authority_pubkey) + } else { + Some( + default_signer + .signer_from_path(matches, wallet_manager)? + .pubkey(), + ) + }; + + let payer_pubkey = if let Ok((payer_signer, Some(payer_pubkey))) = + signer_of(matches, "payer", wallet_manager) + { + bulk_signers.push(payer_signer); + Some(payer_pubkey) + } else { + Some( + default_signer + .signer_from_path(matches, wallet_manager)? + .pubkey(), + ) + }; + + let signer_info = + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; + + CliCommandInfo { + command: CliCommand::AddressLookupTable( + AddressLookupTableCliCommand::CreateLookupTable { + authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(), + payer_signer_index: signer_info.index_of(payer_pubkey).unwrap(), + }, + ), + signers: signer_info.signers, + } + } + ("freeze", Some(matches)) => { + let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap(); + + let mut bulk_signers = vec![Some( + default_signer.signer_from_path(matches, wallet_manager)?, + )]; + + let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) = + signer_of(matches, "authority", wallet_manager) + { + bulk_signers.push(authority_signer); + Some(authority_pubkey) + } else { + Some( + default_signer + .signer_from_path(matches, wallet_manager)? + .pubkey(), + ) + }; + + let signer_info = + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; + + CliCommandInfo { + command: CliCommand::AddressLookupTable( + AddressLookupTableCliCommand::FreezeLookupTable { + lookup_table_pubkey, + authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(), + bypass_warning: matches.is_present("bypass_warning"), + }, + ), + signers: signer_info.signers, + } + } + ("extend", Some(matches)) => { + let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap(); + + let mut bulk_signers = vec![Some( + default_signer.signer_from_path(matches, wallet_manager)?, + )]; + + let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) = + signer_of(matches, "authority", wallet_manager) + { + bulk_signers.push(authority_signer); + Some(authority_pubkey) + } else { + Some( + default_signer + .signer_from_path(matches, wallet_manager)? + .pubkey(), + ) + }; + + let payer_pubkey = if let Ok((payer_signer, Some(payer_pubkey))) = + signer_of(matches, "payer", wallet_manager) + { + bulk_signers.push(payer_signer); + Some(payer_pubkey) + } else { + Some( + default_signer + .signer_from_path(matches, wallet_manager)? + .pubkey(), + ) + }; + + let new_addresses: Vec = values_of(matches, "addresses").unwrap(); + + let signer_info = + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; + + CliCommandInfo { + command: CliCommand::AddressLookupTable( + AddressLookupTableCliCommand::ExtendLookupTable { + lookup_table_pubkey, + authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(), + payer_signer_index: signer_info.index_of(payer_pubkey).unwrap(), + new_addresses, + }, + ), + signers: signer_info.signers, + } + } + ("deactivate", Some(matches)) => { + let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap(); + + let mut bulk_signers = vec![Some( + default_signer.signer_from_path(matches, wallet_manager)?, + )]; + + let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) = + signer_of(matches, "authority", wallet_manager) + { + bulk_signers.push(authority_signer); + Some(authority_pubkey) + } else { + Some( + default_signer + .signer_from_path(matches, wallet_manager)? + .pubkey(), + ) + }; + + let signer_info = + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; + + CliCommandInfo { + command: CliCommand::AddressLookupTable( + AddressLookupTableCliCommand::DeactivateLookupTable { + lookup_table_pubkey, + authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(), + bypass_warning: matches.is_present("bypass_warning"), + }, + ), + signers: signer_info.signers, + } + } + ("close", Some(matches)) => { + let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap(); + + let mut bulk_signers = vec![Some( + default_signer.signer_from_path(matches, wallet_manager)?, + )]; + + let authority_pubkey = if let Ok((authority_signer, Some(authority_pubkey))) = + signer_of(matches, "authority", wallet_manager) + { + bulk_signers.push(authority_signer); + Some(authority_pubkey) + } else { + Some( + default_signer + .signer_from_path(matches, wallet_manager)? + .pubkey(), + ) + }; + + let recipient_pubkey = if let Some(recipient_pubkey) = pubkey_of(matches, "recipient") { + recipient_pubkey + } else { + default_signer + .signer_from_path(matches, wallet_manager)? + .pubkey() + }; + + let signer_info = + default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; + + CliCommandInfo { + command: CliCommand::AddressLookupTable( + AddressLookupTableCliCommand::CloseLookupTable { + lookup_table_pubkey, + authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(), + recipient_pubkey, + }, + ), + signers: signer_info.signers, + } + } + ("get", Some(matches)) => { + let lookup_table_pubkey = pubkey_of(matches, "lookup_table_address").unwrap(); + + CliCommandInfo { + command: CliCommand::AddressLookupTable( + AddressLookupTableCliCommand::ShowLookupTable { + lookup_table_pubkey, + }, + ), + signers: vec![], + } + } + _ => unreachable!(), + }; + Ok(response) +} + +pub fn process_address_lookup_table_subcommand( + rpc_client: Arc, + config: &CliConfig, + subcommand: &AddressLookupTableCliCommand, +) -> ProcessResult { + match subcommand { + AddressLookupTableCliCommand::CreateLookupTable { + authority_signer_index, + payer_signer_index, + } => process_create_lookup_table( + &rpc_client, + config, + *authority_signer_index, + *payer_signer_index, + ), + AddressLookupTableCliCommand::FreezeLookupTable { + lookup_table_pubkey, + authority_signer_index, + bypass_warning, + } => process_freeze_lookup_table( + &rpc_client, + config, + *lookup_table_pubkey, + *authority_signer_index, + *bypass_warning, + ), + AddressLookupTableCliCommand::ExtendLookupTable { + lookup_table_pubkey, + authority_signer_index, + payer_signer_index, + new_addresses, + } => process_extend_lookup_table( + &rpc_client, + config, + *lookup_table_pubkey, + *authority_signer_index, + *payer_signer_index, + new_addresses.to_vec(), + ), + AddressLookupTableCliCommand::DeactivateLookupTable { + lookup_table_pubkey, + authority_signer_index, + bypass_warning, + } => process_deactivate_lookup_table( + &rpc_client, + config, + *lookup_table_pubkey, + *authority_signer_index, + *bypass_warning, + ), + AddressLookupTableCliCommand::CloseLookupTable { + lookup_table_pubkey, + authority_signer_index, + recipient_pubkey, + } => process_close_lookup_table( + &rpc_client, + config, + *lookup_table_pubkey, + *authority_signer_index, + *recipient_pubkey, + ), + AddressLookupTableCliCommand::ShowLookupTable { + lookup_table_pubkey, + } => process_show_lookup_table(&rpc_client, config, *lookup_table_pubkey), + } +} + +fn process_create_lookup_table( + rpc_client: &RpcClient, + config: &CliConfig, + authority_signer_index: usize, + payer_signer_index: usize, +) -> ProcessResult { + let authority_signer = config.signers[authority_signer_index]; + let payer_signer = config.signers[payer_signer_index]; + + let get_clock_result = rpc_client + .get_account_with_commitment(&sysvar::clock::id(), CommitmentConfig::finalized())?; + let clock_account = get_clock_result.value.expect("Clock account doesn't exist"); + let clock: Clock = from_account(&clock_account).ok_or_else(|| { + CliError::RpcRequestError("Failed to deserialize clock sysvar".to_string()) + })?; + + let authority_address = authority_signer.pubkey(); + let payer_address = payer_signer.pubkey(); + let (create_lookup_table_ix, lookup_table_address) = + create_lookup_table(authority_address, payer_address, clock.slot); + + let blockhash = rpc_client.get_latest_blockhash()?; + let mut tx = Transaction::new_unsigned(Message::new( + &[create_lookup_table_ix], + Some(&config.signers[0].pubkey()), + )); + + tx.try_sign( + &[config.signers[0], authority_signer, payer_signer], + blockhash, + )?; + let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( + &tx, + config.commitment, + RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(config.commitment.commitment), + ..RpcSendTransactionConfig::default() + }, + ); + match result { + Err(err) => Err(format!("Create failed: {}", err).into()), + Ok(signature) => Ok(config + .output_format + .formatted_string(&CliAddressLookupTableCreated { + lookup_table_address: lookup_table_address.to_string(), + signature: signature.to_string(), + })), + } +} + +pub const FREEZE_LOOKUP_TABLE_WARNING: &str = "WARNING! \ +Once a lookup table is frozen, it can never be modified or unfrozen again. \ +To proceed with freezing, rerun the `freeze` command with the `--bypass-warning` flag"; + +fn process_freeze_lookup_table( + rpc_client: &RpcClient, + config: &CliConfig, + lookup_table_pubkey: Pubkey, + authority_signer_index: usize, + bypass_warning: bool, +) -> ProcessResult { + let authority_signer = config.signers[authority_signer_index]; + + let get_lookup_table_result = + rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?; + let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| { + format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?") + })?; + if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) { + return Err(format!( + "Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program", + ) + .into()); + } + + if !bypass_warning { + return Err(String::from(FREEZE_LOOKUP_TABLE_WARNING).into()); + } + + let authority_address = authority_signer.pubkey(); + let freeze_lookup_table_ix = freeze_lookup_table(lookup_table_pubkey, authority_address); + + let blockhash = rpc_client.get_latest_blockhash()?; + let mut tx = Transaction::new_unsigned(Message::new( + &[freeze_lookup_table_ix], + Some(&config.signers[0].pubkey()), + )); + + tx.try_sign(&[config.signers[0], authority_signer], blockhash)?; + let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( + &tx, + config.commitment, + RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(config.commitment.commitment), + ..RpcSendTransactionConfig::default() + }, + ); + match result { + Err(err) => Err(format!("Freeze failed: {}", err).into()), + Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature { + signature: signature.to_string(), + })), + } +} + +fn process_extend_lookup_table( + rpc_client: &RpcClient, + config: &CliConfig, + lookup_table_pubkey: Pubkey, + authority_signer_index: usize, + payer_signer_index: usize, + new_addresses: Vec, +) -> ProcessResult { + let authority_signer = config.signers[authority_signer_index]; + let payer_signer = config.signers[payer_signer_index]; + + if new_addresses.is_empty() { + return Err("Lookup tables must be extended by at least one address".into()); + } + + let get_lookup_table_result = + rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?; + let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| { + format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?") + })?; + if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) { + return Err(format!( + "Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program", + ) + .into()); + } + + let authority_address = authority_signer.pubkey(); + let payer_address = payer_signer.pubkey(); + let extend_lookup_table_ix = extend_lookup_table( + lookup_table_pubkey, + authority_address, + Some(payer_address), + new_addresses, + ); + + let blockhash = rpc_client.get_latest_blockhash()?; + let mut tx = Transaction::new_unsigned(Message::new( + &[extend_lookup_table_ix], + Some(&config.signers[0].pubkey()), + )); + + tx.try_sign(&[config.signers[0], authority_signer], blockhash)?; + let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( + &tx, + config.commitment, + RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(config.commitment.commitment), + ..RpcSendTransactionConfig::default() + }, + ); + match result { + Err(err) => Err(format!("Extend failed: {}", err).into()), + Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature { + signature: signature.to_string(), + })), + } +} + +pub const DEACTIVATE_LOOKUP_TABLE_WARNING: &str = "WARNING! \ +Once a lookup table is deactivated, it is no longer usable by transactions. +Deactivated lookup tables may only be closed and cannot be recreated at the same address. \ +To proceed with deactivation, rerun the `deactivate` command with the `--bypass-warning` flag"; + +fn process_deactivate_lookup_table( + rpc_client: &RpcClient, + config: &CliConfig, + lookup_table_pubkey: Pubkey, + authority_signer_index: usize, + bypass_warning: bool, +) -> ProcessResult { + let authority_signer = config.signers[authority_signer_index]; + + let get_lookup_table_result = + rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?; + let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| { + format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?") + })?; + if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) { + return Err(format!( + "Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program", + ) + .into()); + } + + if !bypass_warning { + return Err(String::from(DEACTIVATE_LOOKUP_TABLE_WARNING).into()); + } + + let authority_address = authority_signer.pubkey(); + let deactivate_lookup_table_ix = + deactivate_lookup_table(lookup_table_pubkey, authority_address); + + let blockhash = rpc_client.get_latest_blockhash()?; + let mut tx = Transaction::new_unsigned(Message::new( + &[deactivate_lookup_table_ix], + Some(&config.signers[0].pubkey()), + )); + + tx.try_sign(&[config.signers[0], authority_signer], blockhash)?; + let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( + &tx, + config.commitment, + RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(config.commitment.commitment), + ..RpcSendTransactionConfig::default() + }, + ); + match result { + Err(err) => Err(format!("Deactivate failed: {}", err).into()), + Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature { + signature: signature.to_string(), + })), + } +} + +fn process_close_lookup_table( + rpc_client: &RpcClient, + config: &CliConfig, + lookup_table_pubkey: Pubkey, + authority_signer_index: usize, + recipient_pubkey: Pubkey, +) -> ProcessResult { + let authority_signer = config.signers[authority_signer_index]; + + let get_lookup_table_result = + rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?; + let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| { + format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?") + })?; + if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) { + return Err(format!( + "Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program", + ) + .into()); + } + + let lookup_table_account = AddressLookupTable::deserialize(&lookup_table_account.data)?; + if lookup_table_account.meta.deactivation_slot == u64::MAX { + return Err(format!( + "Lookup table account {lookup_table_pubkey} is not deactivated. Only deactivated lookup tables may be closed", + ) + .into()); + } + + let authority_address = authority_signer.pubkey(); + let close_lookup_table_ix = + close_lookup_table(lookup_table_pubkey, authority_address, recipient_pubkey); + + let blockhash = rpc_client.get_latest_blockhash()?; + let mut tx = Transaction::new_unsigned(Message::new( + &[close_lookup_table_ix], + Some(&config.signers[0].pubkey()), + )); + + tx.try_sign(&[config.signers[0], authority_signer], blockhash)?; + let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( + &tx, + config.commitment, + RpcSendTransactionConfig { + skip_preflight: false, + preflight_commitment: Some(config.commitment.commitment), + ..RpcSendTransactionConfig::default() + }, + ); + match result { + Err(err) => Err(format!("Close failed: {}", err).into()), + Ok(signature) => Ok(config.output_format.formatted_string(&CliSignature { + signature: signature.to_string(), + })), + } +} + +fn process_show_lookup_table( + rpc_client: &RpcClient, + config: &CliConfig, + lookup_table_pubkey: Pubkey, +) -> ProcessResult { + let get_lookup_table_result = + rpc_client.get_account_with_commitment(&lookup_table_pubkey, config.commitment)?; + let lookup_table_account = get_lookup_table_result.value.ok_or_else(|| { + format!("Lookup table account {lookup_table_pubkey} not found, was it already closed?") + })?; + if !solana_address_lookup_table_program::check_id(&lookup_table_account.owner) { + return Err(format!( + "Lookup table account {lookup_table_pubkey} is not owned by the Address Lookup Table program", + ) + .into()); + } + + let lookup_table_account = AddressLookupTable::deserialize(&lookup_table_account.data)?; + Ok(config + .output_format + .formatted_string(&CliAddressLookupTable { + lookup_table_address: lookup_table_pubkey.to_string(), + authority: lookup_table_account + .meta + .authority + .as_ref() + .map(ToString::to_string), + deactivation_slot: lookup_table_account.meta.deactivation_slot, + last_extended_slot: lookup_table_account.meta.last_extended_slot, + addresses: lookup_table_account + .addresses + .iter() + .map(ToString::to_string) + .collect(), + })) +} diff --git a/cli/src/checks.rs b/cli/src/checks.rs index 8c45f6e0483e93..5e75d250b07805 100644 --- a/cli/src/checks.rs +++ b/cli/src/checks.rs @@ -114,7 +114,7 @@ pub fn get_fee_for_messages( ) -> Result { Ok(messages .iter() - .map(|message| rpc_client.get_fee_for_message(message)) + .map(|message| rpc_client.get_fee_for_message(*message)) .collect::, _>>()? .iter() .sum()) @@ -187,8 +187,8 @@ mod tests { }); let pubkey = solana_sdk::pubkey::new_rand(); - let pubkey0 = Pubkey::new(&[0; 32]); - let pubkey1 = Pubkey::new(&[1; 32]); + let pubkey0 = Pubkey::from([0; 32]); + let pubkey1 = Pubkey::from([1; 32]); let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1); let message0 = Message::new(&[ix0], Some(&pubkey0)); @@ -292,8 +292,8 @@ mod tests { assert_eq!(get_fee_for_messages(&rpc_client, &[]).unwrap(), 0); // One message w/ one signature, a fee. - let pubkey0 = Pubkey::new(&[0; 32]); - let pubkey1 = Pubkey::new(&[1; 32]); + let pubkey0 = Pubkey::from([0; 32]); + let pubkey1 = Pubkey::from([1; 32]); let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1); let message0 = Message::new(&[ix0], Some(&pubkey0)); assert_eq!(get_fee_for_messages(&rpc_client, &[&message0]).unwrap(), 1); diff --git a/cli/src/clap_app.rs b/cli/src/clap_app.rs index 3d48ed37160f93..1760b5161783f2 100644 --- a/cli/src/clap_app.rs +++ b/cli/src/clap_app.rs @@ -1,7 +1,7 @@ use { crate::{ - cli::*, cluster_query::*, feature::*, inflation::*, nonce::*, program::*, stake::*, - validator_info::*, vote::*, wallet::*, + address_lookup_table::AddressLookupTableSubCommands, cli::*, cluster_query::*, feature::*, + inflation::*, nonce::*, program::*, stake::*, validator_info::*, vote::*, wallet::*, }, clap::{App, AppSettings, Arg, ArgGroup, SubCommand}, solana_clap_utils::{self, input_validators::*, keypair::*}, @@ -130,6 +130,7 @@ pub fn get_clap_app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> A .inflation_subcommands() .nonce_subcommands() .program_subcommands() + .address_lookup_table_subcommands() .stake_subcommands() .validator_info_subcommands() .vote_subcommands() diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 2a2397efd3e170..7aafeca70c9235 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -1,7 +1,7 @@ use { crate::{ - clap_app::*, cluster_query::*, feature::*, inflation::*, nonce::*, program::*, - spend_utils::*, stake::*, validator_info::*, vote::*, wallet::*, + address_lookup_table::*, clap_app::*, cluster_query::*, feature::*, inflation::*, nonce::*, + program::*, spend_utils::*, stake::*, validator_info::*, vote::*, wallet::*, }, clap::{crate_description, crate_name, value_t_or_exit, ArgMatches, Shell}, log::*, @@ -221,7 +221,7 @@ pub enum CliCommand { nonce_authority: SignerIndex, memo: Option, fee_payer: SignerIndex, - redelegation_stake_account_pubkey: Option, + redelegation_stake_account: Option, compute_unit_price: Option, }, SplitStake { @@ -440,6 +440,8 @@ pub enum CliCommand { StakeMinimumDelegation { use_lamports_unit: bool, }, + // Address lookup table commands + AddressLookupTable(AddressLookupTableCliCommand), } #[derive(Debug, PartialEq)] @@ -687,6 +689,9 @@ pub fn parse_command( ("program", Some(matches)) => { parse_program_subcommand(matches, default_signer, wallet_manager) } + ("address-lookup-table", Some(matches)) => { + parse_address_lookup_table_subcommand(matches, default_signer, wallet_manager) + } ("wait-for-max-stake", Some(matches)) => { let max_stake_percent = value_t_or_exit!(matches, "max_percent", f32); Ok(CliCommandInfo { @@ -1179,7 +1184,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { nonce_authority, memo, fee_payer, - redelegation_stake_account_pubkey, + redelegation_stake_account, compute_unit_price, } => process_delegate_stake( &rpc_client, @@ -1195,7 +1200,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { *nonce_authority, memo.as_ref(), *fee_payer, - redelegation_stake_account_pubkey.as_ref(), + *redelegation_stake_account, compute_unit_price.as_ref(), ), CliCommand::SplitStake { @@ -1627,6 +1632,11 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { derived_address_program_id.as_ref(), compute_unit_price.as_ref(), ), + + // Address Lookup Table Commands + CliCommand::AddressLookupTable(subcommand) => { + process_address_lookup_table_subcommand(rpc_client, config, subcommand) + } } } @@ -2607,7 +2617,7 @@ mod tests { ); //Test Transfer Subcommand, with nonce - let nonce_address = Pubkey::new(&[1u8; 32]); + let nonce_address = Pubkey::from([1u8; 32]); let nonce_address_string = nonce_address.to_string(); let nonce_authority = keypair_from_seed(&[2u8; 32]).unwrap(); let nonce_authority_file = make_tmp_path("nonce_authority_file"); diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index c6b08e3a2862fc..6ebd8f77bc36eb 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -2060,7 +2060,7 @@ pub fn process_transaction_history( Some(status) => format!("{:?}", status), } }, - result.memo.unwrap_or_else(|| "".to_string()), + result.memo.unwrap_or_default(), ); } else { println!("{}", result.signature); diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 85d90869ff41b3..c271990b58b7ce 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -23,6 +23,7 @@ extern crate const_format; extern crate serde_derive; +pub mod address_lookup_table; pub mod checks; pub mod clap_app; pub mod cli; diff --git a/cli/src/memo.rs b/cli/src/memo.rs index 1043415357b338..53a126a450dad2 100644 --- a/cli/src/memo.rs +++ b/cli/src/memo.rs @@ -12,7 +12,7 @@ impl WithMemo for Vec { if let Some(memo) = &memo { let memo = memo.as_ref(); let memo_ix = Instruction { - program_id: Pubkey::new(&id().to_bytes()), + program_id: Pubkey::from(id().to_bytes()), accounts: vec![], data: memo.as_bytes().to_vec(), }; diff --git a/cli/src/nonce.rs b/cli/src/nonce.rs index 1d7324bf30ef36..efe04e242a6b4c 100644 --- a/cli/src/nonce.rs +++ b/cli/src/nonce.rs @@ -1083,7 +1083,7 @@ mod tests { let valid = Account::new_data(1, &data, &system_program::ID); assert!(check_nonce_account(&valid.unwrap(), &nonce_pubkey, &blockhash).is_ok()); - let invalid_owner = Account::new_data(1, &data, &Pubkey::new(&[1u8; 32])); + let invalid_owner = Account::new_data(1, &data, &Pubkey::from([1u8; 32])); if let CliError::InvalidNonce(err) = check_nonce_account(&invalid_owner.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() { @@ -1155,7 +1155,7 @@ mod tests { Err(Error::UnexpectedDataSize), ); - let other_program = Pubkey::new(&[1u8; 32]); + let other_program = Pubkey::from([1u8; 32]); let other_account_no_data = Account::new(1, 0, &other_program); assert_eq!( account_identity_ok(&other_account_no_data), @@ -1169,7 +1169,7 @@ mod tests { assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized)); let durable_nonce = DurableNonce::from_blockhash(&Hash::new(&[42u8; 32])); - let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42); + let data = nonce::state::Data::new(Pubkey::from([1u8; 32]), durable_nonce, 42); nonce_account .set_state(&Versions::new(State::Initialized(data.clone()))) .unwrap(); @@ -1199,7 +1199,7 @@ mod tests { ); let durable_nonce = DurableNonce::from_blockhash(&Hash::new(&[42u8; 32])); - let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42); + let data = nonce::state::Data::new(Pubkey::from([1u8; 32]), durable_nonce, 42); nonce_account .set_state(&Versions::new(State::Initialized(data.clone()))) .unwrap(); diff --git a/cli/src/program.rs b/cli/src/program.rs index 1b3431968c9b81..25fe419a45f826 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -60,6 +60,11 @@ use { }, }; +pub const CLOSE_PROGRAM_WARNING: &str = "WARNING! \ +Closed programs cannot be recreated at the same program id. \ +Once a program is closed, it can never be invoked again. \ +To proceed with closing, rerun the `close` command with the `--bypass-warning` flag"; + #[derive(Debug, PartialEq, Eq)] pub enum ProgramCliCommand { Deploy { @@ -109,6 +114,7 @@ pub enum ProgramCliCommand { recipient_pubkey: Pubkey, authority_index: SignerIndex, use_lamports_unit: bool, + bypass_warning: bool, }, } @@ -131,7 +137,7 @@ impl ProgramSubCommands for App<'_, '_> { ) .subcommand( SubCommand::with_name("deploy") - .about("Deploy a program") + .about("Deploy an upgradeable program") .arg( Arg::with_name("program_location") .index(1) @@ -386,12 +392,18 @@ impl ProgramSubCommands for App<'_, '_> { .long("lamports") .takes_value(false) .help("Display balance in lamports instead of SOL"), + ) + .arg( + Arg::with_name("bypass_warning") + .long("bypass-warning") + .takes_value(false) + .help("Bypass the permanent program closure warning"), ), ) ) .subcommand( SubCommand::with_name("deploy") - .about("Deploy a program") + .about("Deploy a non-upgradeable program. Use `solana program deploy` instead to deploy upgradeable programs") .setting(AppSettings::Hidden) .arg( Arg::with_name("program_location") @@ -674,6 +686,7 @@ pub fn parse_program_subcommand( recipient_pubkey, authority_index: signer_info.index_of(authority_pubkey).unwrap(), use_lamports_unit: matches.is_present("lamports"), + bypass_warning: matches.is_present("bypass_warning"), }), signers: signer_info.signers, } @@ -781,6 +794,7 @@ pub fn process_program_subcommand( recipient_pubkey, authority_index, use_lamports_unit, + bypass_warning, } => process_close( &rpc_client, config, @@ -788,6 +802,7 @@ pub fn process_program_subcommand( *recipient_pubkey, *authority_index, *use_lamports_unit, + *bypass_warning, ), } } @@ -801,7 +816,7 @@ fn get_default_program_keypair(program_location: &Option) -> Keypair { filename.push("-keypair"); keypair_file.set_file_name(filename); keypair_file.set_extension("json"); - if let Ok(keypair) = read_keypair_file(&keypair_file.to_str().unwrap()) { + if let Ok(keypair) = read_keypair_file(keypair_file.to_str().unwrap()) { keypair } else { Keypair::new() @@ -1553,6 +1568,7 @@ fn process_close( recipient_pubkey: Pubkey, authority_index: SignerIndex, use_lamports_unit: bool, + bypass_warning: bool, ) -> ProcessResult { let authority_signer = config.signers[authority_index]; @@ -1615,6 +1631,9 @@ fn process_close( ) .into()) } else { + if !bypass_warning { + return Err(String::from(CLOSE_PROGRAM_WARNING).into()); + } close( rpc_client, config, @@ -3010,6 +3029,30 @@ mod tests { recipient_pubkey: default_keypair.pubkey(), authority_index: 0, use_lamports_unit: false, + bypass_warning: false, + }), + signers: vec![read_keypair_file(&keypair_file).unwrap().into()], + } + ); + + // with bypass-warning + write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap(); + let test_command = test_commands.clone().get_matches_from(vec![ + "test", + "program", + "close", + &buffer_pubkey.to_string(), + "--bypass-warning", + ]); + assert_eq!( + parse_command(&test_command, &default_signer, &mut None).unwrap(), + CliCommandInfo { + command: CliCommand::Program(ProgramCliCommand::Close { + account_pubkey: Some(buffer_pubkey), + recipient_pubkey: default_keypair.pubkey(), + authority_index: 0, + use_lamports_unit: false, + bypass_warning: true, }), signers: vec![read_keypair_file(&keypair_file).unwrap().into()], } @@ -3033,6 +3076,7 @@ mod tests { recipient_pubkey: default_keypair.pubkey(), authority_index: 1, use_lamports_unit: false, + bypass_warning: false, }), signers: vec![ read_keypair_file(&keypair_file).unwrap().into(), @@ -3058,6 +3102,7 @@ mod tests { recipient_pubkey, authority_index: 0, use_lamports_unit: false, + bypass_warning: false, }), signers: vec![read_keypair_file(&keypair_file).unwrap().into(),], } @@ -3079,6 +3124,7 @@ mod tests { recipient_pubkey: default_keypair.pubkey(), authority_index: 0, use_lamports_unit: true, + bypass_warning: false, }), signers: vec![read_keypair_file(&keypair_file).unwrap().into(),], } diff --git a/cli/src/stake.rs b/cli/src/stake.rs index 03425f474ccc5f..08b996a74f77d8 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -390,6 +390,7 @@ impl StakeSubCommands for App<'_, '_> { .value_name("KEYPAIR") .takes_value(true) .validator(is_valid_signer) + .required_unless("new_withdraw_authority") .help("New authorized staker") ) .arg( @@ -398,6 +399,7 @@ impl StakeSubCommands for App<'_, '_> { .value_name("KEYPAIR") .takes_value(true) .validator(is_valid_signer) + .required_unless("new_stake_authority") .help("New authorized withdrawer") ) .arg(stake_authority_arg()) @@ -827,10 +829,13 @@ pub fn parse_stake_delegate_stake( signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?; - let mut bulk_signers = vec![stake_authority, fee_payer, redelegation_stake_account]; + let mut bulk_signers = vec![stake_authority, fee_payer]; if nonce_account.is_some() { bulk_signers.push(nonce_authority); } + if redelegation_stake_account.is_some() { + bulk_signers.push(redelegation_stake_account); + } let signer_info = default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; let compute_unit_price = value_of(matches, COMPUTE_UNIT_PRICE_ARG.name); @@ -848,7 +853,8 @@ pub fn parse_stake_delegate_stake( nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(), memo, fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(), - redelegation_stake_account_pubkey, + redelegation_stake_account: redelegation_stake_account_pubkey + .and_then(|_| signer_info.index_of(redelegation_stake_account_pubkey)), compute_unit_price, }, signers: signer_info.signers, @@ -942,6 +948,11 @@ pub fn parse_stake_authorize( default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?; let compute_unit_price = value_of(matches, COMPUTE_UNIT_PRICE_ARG.name); + if new_authorizations.is_empty() { + return Err(CliError::BadParameter( + "New authorization list must include at least one authority".to_string(), + )); + } let new_authorizations = new_authorizations .into_iter() .map( @@ -2507,25 +2518,26 @@ pub fn process_delegate_stake( nonce_authority: SignerIndex, memo: Option<&String>, fee_payer: SignerIndex, - redelegation_stake_account_pubkey: Option<&Pubkey>, + redelegation_stake_account: Option, compute_unit_price: Option<&u64>, ) -> ProcessResult { check_unique_pubkeys( (&config.signers[0].pubkey(), "cli keypair".to_string()), (stake_account_pubkey, "stake_account_pubkey".to_string()), )?; - if let Some(redelegation_stake_account_pubkey) = &redelegation_stake_account_pubkey { + let redelegation_stake_account = redelegation_stake_account.map(|index| config.signers[index]); + if let Some(redelegation_stake_account) = &redelegation_stake_account { check_unique_pubkeys( (stake_account_pubkey, "stake_account_pubkey".to_string()), ( - redelegation_stake_account_pubkey, + &redelegation_stake_account.pubkey(), "redelegation_stake_account".to_string(), ), )?; check_unique_pubkeys( (&config.signers[0].pubkey(), "cli keypair".to_string()), ( - redelegation_stake_account_pubkey, + &redelegation_stake_account.pubkey(), "redelegation_stake_account".to_string(), ), )?; @@ -2582,12 +2594,12 @@ pub fn process_delegate_stake( let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; - let ixs = if let Some(redelegation_stake_account_pubkey) = &redelegation_stake_account_pubkey { + let ixs = if let Some(redelegation_stake_account) = &redelegation_stake_account { stake_instruction::redelegate( stake_account_pubkey, &stake_authority.pubkey(), vote_account_pubkey, - redelegation_stake_account_pubkey, + &redelegation_stake_account.pubkey(), ) } else { vec![stake_instruction::delegate_stake( @@ -2707,9 +2719,9 @@ mod tests { // stake-authorize subcommand let stake_account_string = stake_account_pubkey.to_string(); - let new_stake_authority = Pubkey::new(&[1u8; 32]); + let new_stake_authority = Pubkey::from([1u8; 32]); let new_stake_string = new_stake_authority.to_string(); - let new_withdraw_authority = Pubkey::new(&[2u8; 32]); + let new_withdraw_authority = Pubkey::from([2u8; 32]); let new_withdraw_string = new_withdraw_authority.to_string(); let test_stake_authorize = test_commands.clone().get_matches_from(vec![ "test", @@ -3558,7 +3570,7 @@ mod tests { let pubkey2 = keypair2.pubkey(); let sig2 = keypair.sign_message(&[0u8]); let signer2 = format!("{}={}", keypair2.pubkey(), sig2); - let nonce_account = Pubkey::new(&[1u8; 32]); + let nonce_account = Pubkey::from([1u8; 32]); let test_authorize = test_commands.clone().get_matches_from(vec![ "test", "stake-authorize", @@ -3935,7 +3947,7 @@ mod tests { assert!(parse_command(&test_create_stake_account, &default_signer, &mut None).is_err()); // CreateStakeAccount offline and nonce - let nonce_account = Pubkey::new(&[1u8; 32]); + let nonce_account = Pubkey::from([1u8; 32]); let nonce_account_string = nonce_account.to_string(); let offline = keypair_from_seed(&[2u8; 32]).unwrap(); let offline_pubkey = offline.pubkey(); @@ -4018,7 +4030,7 @@ mod tests { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }, signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()], @@ -4051,7 +4063,7 @@ mod tests { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }, signers: vec![ @@ -4086,7 +4098,7 @@ mod tests { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }, signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()], @@ -4122,7 +4134,7 @@ mod tests { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }, signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()], @@ -4153,7 +4165,7 @@ mod tests { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }, signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()], @@ -4194,7 +4206,7 @@ mod tests { nonce_authority: 0, memo: None, fee_payer: 1, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }, signers: vec![ @@ -4244,7 +4256,7 @@ mod tests { nonce_authority: 2, memo: None, fee_payer: 1, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }, signers: vec![ @@ -4282,7 +4294,7 @@ mod tests { nonce_authority: 0, memo: None, fee_payer: 1, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }, signers: vec![ @@ -4302,7 +4314,6 @@ mod tests { redelegation_stake_account_tmp_file.as_file_mut(), ) .unwrap(); - let redelegation_stake_account_pubkey = redelegation_stake_account_keypair.pubkey(); let test_redelegate_stake = test_commands.clone().get_matches_from(vec![ "test", @@ -4326,7 +4337,7 @@ mod tests { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: Some(redelegation_stake_account_pubkey), + redelegation_stake_account: Some(1), compute_unit_price: None, }, signers: vec![ @@ -4850,7 +4861,7 @@ mod tests { ); // Split stake offline nonced submission - let nonce_account = Pubkey::new(&[1u8; 32]); + let nonce_account = Pubkey::from([1u8; 32]); let nonce_account_string = nonce_account.to_string(); let nonce_auth = keypair_from_seed(&[2u8; 32]).unwrap(); let nonce_auth_pubkey = nonce_auth.pubkey(); diff --git a/cli/tests/address_lookup_table.rs b/cli/tests/address_lookup_table.rs new file mode 100644 index 00000000000000..5d370d48c4eafd --- /dev/null +++ b/cli/tests/address_lookup_table.rs @@ -0,0 +1,216 @@ +use { + solana_cli::{ + address_lookup_table::{ + AddressLookupTableCliCommand, DEACTIVATE_LOOKUP_TABLE_WARNING, + FREEZE_LOOKUP_TABLE_WARNING, + }, + cli::{process_command, CliCommand, CliConfig}, + }, + solana_cli_output::{CliAddressLookupTable, CliAddressLookupTableCreated, OutputFormat}, + solana_faucet::faucet::run_local_faucet, + solana_sdk::{ + native_token::LAMPORTS_PER_SOL, + pubkey::Pubkey, + signature::{Keypair, Signer}, + }, + solana_streamer::socket::SocketAddrSpace, + solana_test_validator::TestValidator, + std::str::FromStr, +}; + +#[test] +fn test_cli_create_extend_and_freeze_address_lookup_table() { + let mint_keypair = Keypair::new(); + let mint_pubkey = mint_keypair.pubkey(); + let faucet_addr = run_local_faucet(mint_keypair, None); + let test_validator = + TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified); + + let mut config = CliConfig::recent_for_tests(); + let keypair = Keypair::new(); + config.json_rpc_url = test_validator.rpc_url(); + config.signers = vec![&keypair]; + config.output_format = OutputFormat::JsonCompact; + + // Airdrop SOL for transaction fees + config.command = CliCommand::Airdrop { + pubkey: None, + lamports: 10 * LAMPORTS_PER_SOL, + }; + process_command(&config).unwrap(); + + // Create lookup table + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::CreateLookupTable { + authority_signer_index: 0, + payer_signer_index: 0, + }); + let response: CliAddressLookupTableCreated = + serde_json::from_str(&process_command(&config).unwrap()).unwrap(); + let lookup_table_pubkey = Pubkey::from_str(&response.lookup_table_address).unwrap(); + + // Validate created lookup table + { + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable { + lookup_table_pubkey, + }); + let response: CliAddressLookupTable = + serde_json::from_str(&process_command(&config).unwrap()).unwrap(); + assert_eq!( + response, + CliAddressLookupTable { + lookup_table_address: lookup_table_pubkey.to_string(), + authority: Some(keypair.pubkey().to_string()), + deactivation_slot: u64::MAX, + last_extended_slot: 0, + addresses: vec![], + } + ); + } + + // Extend lookup table + let new_addresses: Vec = (0..5).map(|_| Pubkey::new_unique()).collect(); + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ExtendLookupTable { + lookup_table_pubkey, + authority_signer_index: 0, + payer_signer_index: 0, + new_addresses: new_addresses.clone(), + }); + process_command(&config).unwrap(); + + // Validate extended lookup table + { + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable { + lookup_table_pubkey, + }); + let CliAddressLookupTable { + addresses, + last_extended_slot, + .. + } = serde_json::from_str(&process_command(&config).unwrap()).unwrap(); + assert_eq!( + addresses + .into_iter() + .map(|address| Pubkey::from_str(&address).unwrap()) + .collect::>(), + new_addresses + ); + assert!(last_extended_slot > 0); + } + + // Freeze lookup table w/o bypass + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::FreezeLookupTable { + lookup_table_pubkey, + authority_signer_index: 0, + bypass_warning: false, + }); + let process_err = process_command(&config).unwrap_err(); + assert_eq!(process_err.to_string(), FREEZE_LOOKUP_TABLE_WARNING); + + // Freeze lookup table w/ bypass + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::FreezeLookupTable { + lookup_table_pubkey, + authority_signer_index: 0, + bypass_warning: true, + }); + process_command(&config).unwrap(); + + // Validate frozen lookup table + { + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable { + lookup_table_pubkey, + }); + let CliAddressLookupTable { authority, .. } = + serde_json::from_str(&process_command(&config).unwrap()).unwrap(); + assert!(authority.is_none()); + } +} + +#[test] +fn test_cli_create_and_deactivate_address_lookup_table() { + let mint_keypair = Keypair::new(); + let mint_pubkey = mint_keypair.pubkey(); + let faucet_addr = run_local_faucet(mint_keypair, None); + let test_validator = + TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified); + + let mut config = CliConfig::recent_for_tests(); + let keypair = Keypair::new(); + config.json_rpc_url = test_validator.rpc_url(); + config.signers = vec![&keypair]; + config.output_format = OutputFormat::JsonCompact; + + // Airdrop SOL for transaction fees + config.command = CliCommand::Airdrop { + pubkey: None, + lamports: 10 * LAMPORTS_PER_SOL, + }; + process_command(&config).unwrap(); + + // Create lookup table + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::CreateLookupTable { + authority_signer_index: 0, + payer_signer_index: 0, + }); + let response: CliAddressLookupTableCreated = + serde_json::from_str(&process_command(&config).unwrap()).unwrap(); + let lookup_table_pubkey = Pubkey::from_str(&response.lookup_table_address).unwrap(); + + // Validate created lookup table + { + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable { + lookup_table_pubkey, + }); + let response: CliAddressLookupTable = + serde_json::from_str(&process_command(&config).unwrap()).unwrap(); + assert_eq!( + response, + CliAddressLookupTable { + lookup_table_address: lookup_table_pubkey.to_string(), + authority: Some(keypair.pubkey().to_string()), + deactivation_slot: u64::MAX, + last_extended_slot: 0, + addresses: vec![], + } + ); + } + + // Deactivate lookup table w/o bypass + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::DeactivateLookupTable { + lookup_table_pubkey, + authority_signer_index: 0, + bypass_warning: false, + }); + let process_err = process_command(&config).unwrap_err(); + assert_eq!(process_err.to_string(), DEACTIVATE_LOOKUP_TABLE_WARNING); + + // Deactivate lookup table w/ bypass + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::DeactivateLookupTable { + lookup_table_pubkey, + authority_signer_index: 0, + bypass_warning: true, + }); + process_command(&config).unwrap(); + + // Validate deactivated lookup table + { + config.command = + CliCommand::AddressLookupTable(AddressLookupTableCliCommand::ShowLookupTable { + lookup_table_pubkey, + }); + let CliAddressLookupTable { + deactivation_slot, .. + } = serde_json::from_str(&process_command(&config).unwrap()).unwrap(); + assert_ne!(deactivation_slot, u64::MAX); + } +} diff --git a/cli/tests/nonce.rs b/cli/tests/nonce.rs index a5c36197fa1f25..8c66fc52884f9e 100644 --- a/cli/tests/nonce.rs +++ b/cli/tests/nonce.rs @@ -259,7 +259,7 @@ fn test_create_account_with_seed() { let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap(); let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap(); - let to_address = Pubkey::new(&[3u8; 32]); + let to_address = Pubkey::from([3u8; 32]); // Setup accounts let rpc_client = diff --git a/cli/tests/program.rs b/cli/tests/program.rs index 6dbae5cefc812b..315e9213f0442c 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -1,9 +1,10 @@ #![allow(clippy::integer_arithmetic)] + use { serde_json::Value, solana_cli::{ cli::{process_command, CliCommand, CliConfig}, - program::ProgramCliCommand, + program::{ProgramCliCommand, CLOSE_PROGRAM_WARNING}, }, solana_cli_output::OutputFormat, solana_client::rpc_client::RpcClient, @@ -638,13 +639,30 @@ fn test_cli_program_close_program() { let programdata_lamports = close_account.lamports; let recipient_pubkey = Pubkey::new_unique(); config.signers = vec![&keypair, &upgrade_authority]; + + // Close without --bypass-warning flag + config.command = CliCommand::Program(ProgramCliCommand::Close { + account_pubkey: Some(program_keypair.pubkey()), + recipient_pubkey, + authority_index: 1, + use_lamports_unit: false, + bypass_warning: false, + }); + assert_eq!( + process_command(&config).unwrap_err().to_string(), + CLOSE_PROGRAM_WARNING.to_string() + ); + + // Close with --bypass-warning flag config.command = CliCommand::Program(ProgramCliCommand::Close { account_pubkey: Some(program_keypair.pubkey()), recipient_pubkey, authority_index: 1, use_lamports_unit: false, + bypass_warning: true, }); process_command(&config).unwrap(); + rpc_client.get_account(&programdata_pubkey).unwrap_err(); let recipient_account = rpc_client.get_account(&recipient_pubkey).unwrap(); assert_eq!(programdata_lamports, recipient_account.lamports); @@ -902,6 +920,7 @@ fn test_cli_program_write_buffer() { recipient_pubkey, authority_index: 1, use_lamports_unit: false, + bypass_warning: false, }); process_command(&config).unwrap(); rpc_client.get_account(&buffer_pubkey).unwrap_err(); @@ -938,6 +957,7 @@ fn test_cli_program_write_buffer() { recipient_pubkey: keypair.pubkey(), authority_index: 0, use_lamports_unit: false, + bypass_warning: false, }); process_command(&config).unwrap(); rpc_client.get_account(&new_buffer_pubkey).unwrap_err(); diff --git a/cli/tests/stake.rs b/cli/tests/stake.rs index cb7db3666fe759..e7c5a90804a77c 100644 --- a/cli/tests/stake.rs +++ b/cli/tests/stake.rs @@ -155,7 +155,7 @@ fn test_stake_redelegation() { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }; process_command(&config).unwrap(); @@ -219,7 +219,7 @@ fn test_stake_redelegation() { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: Some(stake2_keypair.pubkey()), + redelegation_stake_account: Some(1), compute_unit_price: None, }; process_command(&config).unwrap(); @@ -374,7 +374,7 @@ fn test_stake_delegation_force() { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }; process_command(&config).unwrap_err(); @@ -392,7 +392,7 @@ fn test_stake_delegation_force() { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }; process_command(&config).unwrap(); @@ -471,7 +471,7 @@ fn test_seed_stake_delegation_and_deactivation() { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }; process_command(&config_validator).unwrap(); @@ -563,7 +563,7 @@ fn test_stake_delegation_and_deactivation() { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }; process_command(&config_validator).unwrap(); @@ -679,7 +679,7 @@ fn test_offline_stake_delegation_and_deactivation() { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }; config_offline.output_format = OutputFormat::JsonCompact; @@ -702,7 +702,7 @@ fn test_offline_stake_delegation_and_deactivation() { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }; process_command(&config_payer).unwrap(); @@ -840,7 +840,7 @@ fn test_nonced_stake_delegation_and_deactivation() { nonce_authority: 0, memo: None, fee_payer: 0, - redelegation_stake_account_pubkey: None, + redelegation_stake_account: None, compute_unit_price: None, }; process_command(&config).unwrap(); diff --git a/cli/tests/transfer.rs b/cli/tests/transfer.rs index 33abc5d4e3d937..546792ae7405de 100644 --- a/cli/tests/transfer.rs +++ b/cli/tests/transfer.rs @@ -53,7 +53,7 @@ fn test_transfer() { config.signers = vec![&default_signer]; let sender_pubkey = config.signers[0].pubkey(); - let recipient_pubkey = Pubkey::new(&[1u8; 32]); + let recipient_pubkey = Pubkey::from([1u8; 32]); request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, sol_to_lamports(5.0)) .unwrap(); @@ -339,7 +339,7 @@ fn test_transfer_multisession_signing() { SocketAddrSpace::Unspecified, ); - let to_pubkey = Pubkey::new(&[1u8; 32]); + let to_pubkey = Pubkey::from([1u8; 32]); let offline_from_signer = keypair_from_seed(&[2u8; 32]).unwrap(); let offline_fee_payer_signer = keypair_from_seed(&[3u8; 32]).unwrap(); let from_null_signer = NullSigner::new(&offline_from_signer.pubkey()); @@ -501,7 +501,7 @@ fn test_transfer_all() { config.signers = vec![&default_signer]; let sender_pubkey = config.signers[0].pubkey(); - let recipient_pubkey = Pubkey::new(&[1u8; 32]); + let recipient_pubkey = Pubkey::from([1u8; 32]); request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 500_000).unwrap(); check_balance!(500_000, &rpc_client, &sender_pubkey); @@ -555,7 +555,7 @@ fn test_transfer_unfunded_recipient() { config.signers = vec![&default_signer]; let sender_pubkey = config.signers[0].pubkey(); - let recipient_pubkey = Pubkey::new(&[1u8; 32]); + let recipient_pubkey = Pubkey::from([1u8; 32]); request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap(); check_balance!(50_000, &rpc_client, &sender_pubkey); @@ -610,7 +610,7 @@ fn test_transfer_with_seed() { config.signers = vec![&default_signer]; let sender_pubkey = config.signers[0].pubkey(); - let recipient_pubkey = Pubkey::new(&[1u8; 32]); + let recipient_pubkey = Pubkey::from([1u8; 32]); let derived_address_seed = "seed".to_string(); let derived_address_program_id = stake::program::id(); let derived_address = Pubkey::create_with_seed( diff --git a/client-test/Cargo.toml b/client-test/Cargo.toml index b7166885bdfd40..16546e41ff5a00 100644 --- a/client-test/Cargo.toml +++ b/client-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-client-test" -version = "1.11.6" +version = "1.14.24" description = "Solana RPC Test" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,25 +14,25 @@ publish = false futures-util = "0.3.21" serde_json = "1.0.81" serial_test = "0.8.0" -solana-client = { path = "../client", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-merkle-tree = { path = "../merkle-tree", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.11.6" } -solana-rpc = { path = "../rpc", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-test-validator = { path = "../test-validator", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-merkle-tree = { path = "../merkle-tree", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.14.24" } +solana-rpc = { path = "../rpc", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-test-validator = { path = "../test-validator", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } systemstat = "0.1.11" tokio = { version = "~1.14.1", features = ["full"] } [dev-dependencies] -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/client-test/tests/client.rs b/client-test/tests/client.rs index 9cf1e5ab8538fd..5832a7a146ac0c 100644 --- a/client-test/tests/client.rs +++ b/client-test/tests/client.rs @@ -130,9 +130,11 @@ fn test_account_subscription() { bank_forks.write().unwrap().insert(bank1); let bob = Keypair::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::default())), OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -244,10 +246,12 @@ fn test_block_subscription() { max_complete_transaction_status_slot, ); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); // setup RpcSubscriptions && PubSubService let subscriptions = Arc::new(RpcSubscriptions::new_for_tests_with_blockstore( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, blockstore.clone(), bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::default())), @@ -337,9 +341,11 @@ fn test_program_subscription() { bank_forks.write().unwrap().insert(bank1); let bob = Keypair::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::default())), OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -423,9 +429,11 @@ fn test_root_subscription() { let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1); bank_forks.write().unwrap().insert(bank1); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::default())), OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -474,9 +482,11 @@ fn test_slot_subscription() { let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, Arc::new(RwLock::new(BlockCommitmentCache::default())), optimistically_confirmed_bank, @@ -546,9 +556,11 @@ async fn test_slot_subscription_async() { let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, Arc::new(RwLock::new(BlockCommitmentCache::default())), optimistically_confirmed_bank, diff --git a/client/Cargo.toml b/client/Cargo.toml index 90ffb7ffa6f490..6dc989aefa7aa9 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-client" -version = "1.11.6" +version = "1.14.24" description = "Solana Client" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -38,20 +38,20 @@ semver = "1.0.10" serde = "1.0.138" serde_derive = "1.0.103" serde_json = "1.0.81" -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-faucet = { path = "../faucet", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } -spl-token-2022 = { version = "=0.4.2", features = ["no-entrypoint"] } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-faucet = { path = "../faucet", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } +spl-token-2022 = { version = "=0.6.1", features = ["no-entrypoint"] } thiserror = "1.0" -tokio = { version = "~1.14.1", features = ["full"] } +tokio = { version = "1", features = ["full"] } tokio-stream = "0.1.9" tokio-tungstenite = { version = "0.17.1", features = ["rustls-tls-webpki-roots"] } tungstenite = { version = "0.17.2", features = ["rustls-tls-webpki-roots"] } @@ -61,8 +61,8 @@ url = "2.2.2" anyhow = "1.0.58" assert_matches = "1.5.0" jsonrpc-http-server = "18.0.0" -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/client/src/blockhash_query.rs b/client/src/blockhash_query.rs index 1b630351c94471..98288acbf932c7 100644 --- a/client/src/blockhash_query.rs +++ b/client/src/blockhash_query.rs @@ -205,7 +205,7 @@ mod tests { #[test] fn test_blockhash_query_new_ok() { let blockhash = hash(&[1u8]); - let nonce_pubkey = Pubkey::new(&[1u8; 32]); + let nonce_pubkey = Pubkey::from([1u8; 32]); assert_eq!( BlockhashQuery::new(Some(blockhash), true, None), @@ -246,7 +246,7 @@ mod tests { #[test] #[should_panic] fn test_blockhash_query_new_nonce_fail() { - let nonce_pubkey = Pubkey::new(&[1u8; 32]); + let nonce_pubkey = Pubkey::from([1u8; 32]); BlockhashQuery::new(None, true, Some(nonce_pubkey)); } @@ -287,7 +287,7 @@ mod tests { BlockhashQuery::All(blockhash_query::Source::Cluster), ); - let nonce_pubkey = Pubkey::new(&[1u8; 32]); + let nonce_pubkey = Pubkey::from([1u8; 32]); let nonce_string = nonce_pubkey.to_string(); let matches = test_commands.clone().get_matches_from(vec![ "blockhash_query_test", @@ -341,7 +341,7 @@ mod tests { // We can really only hit this case if the arg requirements // are broken, so unset the requires() to recreate that condition .arg(sign_only_arg().requires("")); - let nonce_pubkey = Pubkey::new(&[1u8; 32]); + let nonce_pubkey = Pubkey::from([1u8; 32]); let nonce_string = nonce_pubkey.to_string(); let matches = test_commands.clone().get_matches_from(vec![ @@ -420,7 +420,7 @@ mod tests { let nonce_blockhash = *durable_nonce.as_hash(); let nonce_fee_calc = FeeCalculator::new(4242); let data = nonce::state::Data { - authority: Pubkey::new(&[3u8; 32]), + authority: Pubkey::from([3u8; 32]), durable_nonce, fee_calculator: nonce_fee_calc, }; @@ -431,7 +431,7 @@ mod tests { &system_program::id(), ) .unwrap(); - let nonce_pubkey = Pubkey::new(&[4u8; 32]); + let nonce_pubkey = Pubkey::from([4u8; 32]); let rpc_nonce_account = UiAccount::encode( &nonce_pubkey, &nonce_account, diff --git a/client/src/connection_cache.rs b/client/src/connection_cache.rs index f0628d3e32b9de..4a716badfabf67 100644 --- a/client/src/connection_cache.rs +++ b/client/src/connection_cache.rs @@ -32,11 +32,13 @@ static MAX_CONNECTIONS: usize = 1024; /// Used to decide whether the TPU and underlying connection cache should use /// QUIC connections. -pub const DEFAULT_TPU_USE_QUIC: bool = false; +pub const DEFAULT_TPU_USE_QUIC: bool = true; /// Default TPU connection pool size per remote address pub const DEFAULT_TPU_CONNECTION_POOL_SIZE: usize = 4; +pub const DEFAULT_TPU_ENABLE_UDP: bool = false; + #[derive(Default)] pub struct ConnectionCacheStats { cache_hits: AtomicU64, @@ -90,6 +92,10 @@ impl ConnectionCacheStats { client_stats.make_connection_ms.load(Ordering::Relaxed), Ordering::Relaxed, ); + self.total_client_stats.send_timeout.fetch_add( + client_stats.send_timeout.load(Ordering::Relaxed), + Ordering::Relaxed, + ); self.sent_packets .fetch_add(num_packets as u64, Ordering::Relaxed); self.total_batches.fetch_add(1, Ordering::Relaxed); @@ -222,6 +228,13 @@ impl ConnectionCacheStats { self.batch_failure.swap(0, Ordering::Relaxed), i64 ), + ( + "send_timeout", + self.total_client_stats + .send_timeout + .swap(0, Ordering::Relaxed), + i64 + ), ); } } @@ -320,7 +333,7 @@ impl ConnectionCache { } } - fn compute_max_parallel_chunks(&self) -> usize { + fn compute_max_parallel_streams(&self) -> usize { let (client_type, stake, total_stake) = self.maybe_client_pubkey .map_or((ConnectionPeerType::Unstaked, 0, 0), |pubkey| { @@ -370,7 +383,7 @@ impl ConnectionCache { BaseTpuConnection::Quic(Arc::new(QuicClient::new( endpoint.as_ref().unwrap().clone(), *addr, - self.compute_max_parallel_chunks(), + self.compute_max_parallel_streams(), ))) }; @@ -683,6 +696,11 @@ mod tests { // be lazy and not connect until first use or handle connection errors somehow // (without crashing, as would be required in a real practical validator) let connection_cache = ConnectionCache::default(); + let port_offset = if connection_cache.use_quic() { + QUIC_PORT_OFFSET + } else { + 0 + }; let addrs = (0..MAX_CONNECTIONS) .into_iter() .map(|_| { @@ -695,18 +713,29 @@ mod tests { let map = connection_cache.map.read().unwrap(); assert!(map.len() == MAX_CONNECTIONS); addrs.iter().for_each(|a| { - let conn = &map.get(a).expect("Address not found").connections[0]; - let conn = conn.new_blocking_connection(*a, connection_cache.stats.clone()); - assert!(a.ip() == conn.tpu_addr().ip()); + let port = a + .port() + .checked_add(port_offset) + .unwrap_or_else(|| a.port()); + let addr = &SocketAddr::new(a.ip(), port); + + let conn = &map.get(addr).expect("Address not found").connections[0]; + let conn = conn.new_blocking_connection(*addr, connection_cache.stats.clone()); + assert!(addr.ip() == conn.tpu_addr().ip()); }); } - let addr = get_addr(&mut rng); - connection_cache.get_connection(&addr); + let addr = &get_addr(&mut rng); + connection_cache.get_connection(addr); + let port = addr + .port() + .checked_add(port_offset) + .unwrap_or_else(|| addr.port()); + let addr_with_quic_port = SocketAddr::new(addr.ip(), port); let map = connection_cache.map.read().unwrap(); assert!(map.len() == MAX_CONNECTIONS); - let _conn = map.get(&addr).expect("Address not found"); + let _conn = map.get(&addr_with_quic_port).expect("Address not found"); } #[test] @@ -714,7 +743,7 @@ mod tests { solana_logger::setup(); let mut connection_cache = ConnectionCache::default(); assert_eq!( - connection_cache.compute_max_parallel_chunks(), + connection_cache.compute_max_parallel_streams(), QUIC_MAX_UNSTAKED_CONCURRENT_STREAMS ); @@ -722,13 +751,13 @@ mod tests { let pubkey = Pubkey::new_unique(); connection_cache.set_staked_nodes(&staked_nodes, &pubkey); assert_eq!( - connection_cache.compute_max_parallel_chunks(), + connection_cache.compute_max_parallel_streams(), QUIC_MAX_UNSTAKED_CONCURRENT_STREAMS ); staked_nodes.write().unwrap().total_stake = 10000; assert_eq!( - connection_cache.compute_max_parallel_chunks(), + connection_cache.compute_max_parallel_streams(), QUIC_MAX_UNSTAKED_CONCURRENT_STREAMS ); @@ -738,7 +767,7 @@ mod tests { .pubkey_stake_map .insert(pubkey, 1); assert_eq!( - connection_cache.compute_max_parallel_chunks(), + connection_cache.compute_max_parallel_streams(), QUIC_MIN_STAKED_CONCURRENT_STREAMS ); @@ -753,7 +782,7 @@ mod tests { .pubkey_stake_map .insert(pubkey, 1000); assert_ne!( - connection_cache.compute_max_parallel_chunks(), + connection_cache.compute_max_parallel_streams(), QUIC_MIN_STAKED_CONCURRENT_STREAMS ); } diff --git a/client/src/http_sender.rs b/client/src/http_sender.rs index 84e1418d7a0fe9..18caad62d6d403 100644 --- a/client/src/http_sender.rs +++ b/client/src/http_sender.rs @@ -59,6 +59,7 @@ impl HttpSender { reqwest::Client::builder() .default_headers(default_headers) .timeout(timeout) + .pool_idle_timeout(timeout) .build() .expect("build rpc client"), ); diff --git a/client/src/lib.rs b/client/src/lib.rs index 2cef3aa9665f71..ccdd55941d8d24 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -29,6 +29,7 @@ pub mod tpu_client; pub mod tpu_connection; pub mod transaction_executor; pub mod udp_client; +pub mod version_req; pub mod mock_sender_for_cli { /// Magic `SIGNATURE` value used by `solana-cli` unit tests. diff --git a/client/src/mock_sender.rs b/client/src/mock_sender.rs index bfef4350404458..057dbed5c84c38 100644 --- a/client/src/mock_sender.rs +++ b/client/src/mock_sender.rs @@ -9,8 +9,8 @@ use { Response, RpcAccountBalance, RpcBlockProduction, RpcBlockProductionRange, RpcBlockhash, RpcConfirmedTransactionStatusWithSignature, RpcContactInfo, RpcFees, RpcIdentity, RpcInflationGovernor, RpcInflationRate, RpcInflationReward, RpcKeyedAccount, - RpcPerfSample, RpcResponseContext, RpcSimulateTransactionResult, RpcSnapshotSlotInfo, - RpcStakeActivation, RpcSupply, RpcVersionInfo, RpcVoteAccountInfo, + RpcPerfSample, RpcPrioritizationFee, RpcResponseContext, RpcSimulateTransactionResult, + RpcSnapshotSlotInfo, RpcStakeActivation, RpcSupply, RpcVersionInfo, RpcVoteAccountInfo, RpcVoteAccountStatus, StakeActivationState, }, rpc_sender::*, @@ -31,7 +31,8 @@ use { transaction::{self, Transaction, TransactionError, TransactionVersion}, }, solana_transaction_status::{ - EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, EncodedTransaction, + option_serializer::OptionSerializer, EncodedConfirmedBlock, + EncodedConfirmedTransactionWithStatusMeta, EncodedTransaction, EncodedTransactionWithStatusMeta, Rewards, TransactionBinaryEncoding, TransactionConfirmationStatus, TransactionStatus, UiCompiledInstruction, UiMessage, UiRawMessage, UiTransaction, UiTransactionStatusMeta, @@ -223,13 +224,14 @@ impl RpcSender for MockSender { fee: 0, pre_balances: vec![499999999999999950, 50, 1], post_balances: vec![499999999999999950, 50, 1], - inner_instructions: None, - log_messages: None, - pre_token_balances: None, - post_token_balances: None, - rewards: None, - loaded_addresses: None, - return_data: None, + inner_instructions: OptionSerializer::None, + log_messages: OptionSerializer::None, + pre_token_balances: OptionSerializer::None, + post_token_balances: OptionSerializer::None, + rewards: OptionSerializer::None, + loaded_addresses: OptionSerializer::Skip, + return_data: OptionSerializer::Skip, + compute_units_consumed: OptionSerializer::Skip, }), }, block_time: Some(1628633791), @@ -416,6 +418,10 @@ impl RpcSender for MockSender { num_slots: 123, sample_period_secs: 60, }])?, + "getRecentPrioritizationFees" => serde_json::to_value(vec![RpcPrioritizationFee { + slot: 123_456_789, + prioritization_fee: 10_000, + }])?, "getIdentity" => serde_json::to_value(RpcIdentity { identity: PUBKEY.to_string(), })?, diff --git a/client/src/nonblocking/blockhash_query.rs b/client/src/nonblocking/blockhash_query.rs new file mode 100644 index 00000000000000..922ff15a19198a --- /dev/null +++ b/client/src/nonblocking/blockhash_query.rs @@ -0,0 +1,433 @@ +use { + crate::nonblocking::{nonce_utils, rpc_client::RpcClient}, + clap::ArgMatches, + solana_clap_utils::{ + input_parsers::{pubkey_of, value_of}, + nonce::*, + offline::*, + }, + solana_sdk::{commitment_config::CommitmentConfig, hash::Hash, pubkey::Pubkey}, +}; + +#[derive(Debug, PartialEq, Eq)] +pub enum Source { + Cluster, + NonceAccount(Pubkey), +} + +impl Source { + pub async fn get_blockhash( + &self, + rpc_client: &RpcClient, + commitment: CommitmentConfig, + ) -> Result> { + match self { + Self::Cluster => { + let (blockhash, _) = rpc_client + .get_latest_blockhash_with_commitment(commitment) + .await?; + Ok(blockhash) + } + Self::NonceAccount(ref pubkey) => { + #[allow(clippy::redundant_closure)] + let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment) + .await + .and_then(|ref a| nonce_utils::data_from_account(a))?; + Ok(data.blockhash()) + } + } + } + + pub async fn is_blockhash_valid( + &self, + rpc_client: &RpcClient, + blockhash: &Hash, + commitment: CommitmentConfig, + ) -> Result> { + Ok(match self { + Self::Cluster => rpc_client.is_blockhash_valid(blockhash, commitment).await?, + Self::NonceAccount(ref pubkey) => { + #[allow(clippy::redundant_closure)] + let _ = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment) + .await + .and_then(|ref a| nonce_utils::data_from_account(a))?; + true + } + }) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum BlockhashQuery { + Static(Hash), + Validated(Source, Hash), + Rpc(Source), +} + +impl BlockhashQuery { + pub fn new(blockhash: Option, sign_only: bool, nonce_account: Option) -> Self { + let source = nonce_account + .map(Source::NonceAccount) + .unwrap_or(Source::Cluster); + match blockhash { + Some(hash) if sign_only => Self::Static(hash), + Some(hash) if !sign_only => Self::Validated(source, hash), + None if !sign_only => Self::Rpc(source), + _ => panic!("Cannot resolve blockhash"), + } + } + + pub fn new_from_matches(matches: &ArgMatches<'_>) -> Self { + let blockhash = value_of(matches, BLOCKHASH_ARG.name); + let sign_only = matches.is_present(SIGN_ONLY_ARG.name); + let nonce_account = pubkey_of(matches, NONCE_ARG.name); + BlockhashQuery::new(blockhash, sign_only, nonce_account) + } + + pub async fn get_blockhash( + &self, + rpc_client: &RpcClient, + commitment: CommitmentConfig, + ) -> Result> { + match self { + BlockhashQuery::Static(hash) => Ok(*hash), + BlockhashQuery::Validated(source, hash) => { + if !source + .is_blockhash_valid(rpc_client, hash, commitment) + .await? + { + return Err(format!("Hash has expired {:?}", hash).into()); + } + Ok(*hash) + } + BlockhashQuery::Rpc(source) => source.get_blockhash(rpc_client, commitment).await, + } + } +} + +impl Default for BlockhashQuery { + fn default() -> Self { + BlockhashQuery::Rpc(Source::Cluster) + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + crate::{ + nonblocking::blockhash_query, + rpc_request::RpcRequest, + rpc_response::{Response, RpcBlockhash, RpcResponseContext}, + }, + clap::App, + serde_json::{self, json}, + solana_account_decoder::{UiAccount, UiAccountEncoding}, + solana_sdk::{ + account::Account, + fee_calculator::FeeCalculator, + hash::hash, + nonce::{self, state::DurableNonce}, + system_program, + }, + std::collections::HashMap, + }; + + #[test] + fn test_blockhash_query_new_ok() { + let blockhash = hash(&[1u8]); + let nonce_pubkey = Pubkey::from([1u8; 32]); + + assert_eq!( + BlockhashQuery::new(Some(blockhash), true, None), + BlockhashQuery::Static(blockhash), + ); + assert_eq!( + BlockhashQuery::new(Some(blockhash), false, None), + BlockhashQuery::Validated(blockhash_query::Source::Cluster, blockhash), + ); + assert_eq!( + BlockhashQuery::new(None, false, None), + BlockhashQuery::Rpc(blockhash_query::Source::Cluster) + ); + + assert_eq!( + BlockhashQuery::new(Some(blockhash), true, Some(nonce_pubkey)), + BlockhashQuery::Static(blockhash), + ); + assert_eq!( + BlockhashQuery::new(Some(blockhash), false, Some(nonce_pubkey)), + BlockhashQuery::Validated( + blockhash_query::Source::NonceAccount(nonce_pubkey), + blockhash + ), + ); + assert_eq!( + BlockhashQuery::new(None, false, Some(nonce_pubkey)), + BlockhashQuery::Rpc(blockhash_query::Source::NonceAccount(nonce_pubkey)), + ); + } + + #[test] + #[should_panic] + fn test_blockhash_query_new_no_nonce_fail() { + BlockhashQuery::new(None, true, None); + } + + #[test] + #[should_panic] + fn test_blockhash_query_new_nonce_fail() { + let nonce_pubkey = Pubkey::from([1u8; 32]); + BlockhashQuery::new(None, true, Some(nonce_pubkey)); + } + + #[test] + fn test_blockhash_query_new_from_matches_ok() { + let test_commands = App::new("blockhash_query_test") + .nonce_args(false) + .offline_args(); + let blockhash = hash(&[1u8]); + let blockhash_string = blockhash.to_string(); + + let matches = test_commands.clone().get_matches_from(vec![ + "blockhash_query_test", + "--blockhash", + &blockhash_string, + "--sign-only", + ]); + assert_eq!( + BlockhashQuery::new_from_matches(&matches), + BlockhashQuery::Static(blockhash), + ); + + let matches = test_commands.clone().get_matches_from(vec![ + "blockhash_query_test", + "--blockhash", + &blockhash_string, + ]); + assert_eq!( + BlockhashQuery::new_from_matches(&matches), + BlockhashQuery::Validated(blockhash_query::Source::Cluster, blockhash), + ); + + let matches = test_commands + .clone() + .get_matches_from(vec!["blockhash_query_test"]); + assert_eq!( + BlockhashQuery::new_from_matches(&matches), + BlockhashQuery::Rpc(blockhash_query::Source::Cluster), + ); + + let nonce_pubkey = Pubkey::from([1u8; 32]); + let nonce_string = nonce_pubkey.to_string(); + let matches = test_commands.clone().get_matches_from(vec![ + "blockhash_query_test", + "--blockhash", + &blockhash_string, + "--sign-only", + "--nonce", + &nonce_string, + ]); + assert_eq!( + BlockhashQuery::new_from_matches(&matches), + BlockhashQuery::Static(blockhash), + ); + + let matches = test_commands.clone().get_matches_from(vec![ + "blockhash_query_test", + "--blockhash", + &blockhash_string, + "--nonce", + &nonce_string, + ]); + assert_eq!( + BlockhashQuery::new_from_matches(&matches), + BlockhashQuery::Validated( + blockhash_query::Source::NonceAccount(nonce_pubkey), + blockhash + ), + ); + } + + #[test] + #[should_panic] + fn test_blockhash_query_new_from_matches_without_nonce_fail() { + let test_commands = App::new("blockhash_query_test") + .arg(blockhash_arg()) + // We can really only hit this case if the arg requirements + // are broken, so unset the requires() to recreate that condition + .arg(sign_only_arg().requires("")); + + let matches = test_commands + .clone() + .get_matches_from(vec!["blockhash_query_test", "--sign-only"]); + BlockhashQuery::new_from_matches(&matches); + } + + #[test] + #[should_panic] + fn test_blockhash_query_new_from_matches_with_nonce_fail() { + let test_commands = App::new("blockhash_query_test") + .arg(blockhash_arg()) + // We can really only hit this case if the arg requirements + // are broken, so unset the requires() to recreate that condition + .arg(sign_only_arg().requires("")); + let nonce_pubkey = Pubkey::from([1u8; 32]); + let nonce_string = nonce_pubkey.to_string(); + + let matches = test_commands.clone().get_matches_from(vec![ + "blockhash_query_test", + "--sign-only", + "--nonce", + &nonce_string, + ]); + BlockhashQuery::new_from_matches(&matches); + } + + #[tokio::test] + async fn test_blockhash_query_get_blockhash() { + let test_blockhash = hash(&[0u8]); + let rpc_blockhash = hash(&[1u8]); + + let get_latest_blockhash_response = json!(Response { + context: RpcResponseContext { + slot: 1, + api_version: None + }, + value: json!(RpcBlockhash { + blockhash: rpc_blockhash.to_string(), + last_valid_block_height: 42, + }), + }); + + let is_blockhash_valid_response = json!(Response { + context: RpcResponseContext { + slot: 1, + api_version: None + }, + value: true + }); + + let mut mocks = HashMap::new(); + mocks.insert( + RpcRequest::GetLatestBlockhash, + get_latest_blockhash_response.clone(), + ); + let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); + assert_eq!( + BlockhashQuery::default() + .get_blockhash(&rpc_client, CommitmentConfig::default()) + .await + .unwrap(), + rpc_blockhash, + ); + + let mut mocks = HashMap::new(); + mocks.insert( + RpcRequest::GetLatestBlockhash, + get_latest_blockhash_response.clone(), + ); + mocks.insert( + RpcRequest::IsBlockhashValid, + is_blockhash_valid_response.clone(), + ); + let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); + assert_eq!( + BlockhashQuery::Validated(Source::Cluster, test_blockhash) + .get_blockhash(&rpc_client, CommitmentConfig::default()) + .await + .unwrap(), + test_blockhash, + ); + + let mut mocks = HashMap::new(); + mocks.insert( + RpcRequest::GetLatestBlockhash, + get_latest_blockhash_response.clone(), + ); + let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); + assert_eq!( + BlockhashQuery::Static(test_blockhash) + .get_blockhash(&rpc_client, CommitmentConfig::default()) + .await + .unwrap(), + test_blockhash, + ); + + let rpc_client = RpcClient::new_mock("fails".to_string()); + assert!(BlockhashQuery::default() + .get_blockhash(&rpc_client, CommitmentConfig::default()) + .await + .is_err()); + + let durable_nonce = DurableNonce::from_blockhash(&Hash::new(&[2u8; 32])); + let nonce_blockhash = *durable_nonce.as_hash(); + let nonce_fee_calc = FeeCalculator::new(4242); + let data = nonce::state::Data { + authority: Pubkey::from([3u8; 32]), + durable_nonce, + fee_calculator: nonce_fee_calc, + }; + let nonce_account = Account::new_data_with_space( + 42, + &nonce::state::Versions::new(nonce::State::Initialized(data)), + nonce::State::size(), + &system_program::id(), + ) + .unwrap(); + let nonce_pubkey = Pubkey::from([4u8; 32]); + let rpc_nonce_account = UiAccount::encode( + &nonce_pubkey, + &nonce_account, + UiAccountEncoding::Base64, + None, + None, + ); + let get_account_response = json!(Response { + context: RpcResponseContext { + slot: 1, + api_version: None + }, + value: json!(Some(rpc_nonce_account)), + }); + + let mut mocks = HashMap::new(); + mocks.insert(RpcRequest::GetAccountInfo, get_account_response.clone()); + let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); + assert_eq!( + BlockhashQuery::Rpc(Source::NonceAccount(nonce_pubkey)) + .get_blockhash(&rpc_client, CommitmentConfig::default()) + .await + .unwrap(), + nonce_blockhash, + ); + + let mut mocks = HashMap::new(); + mocks.insert(RpcRequest::GetAccountInfo, get_account_response.clone()); + let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); + assert_eq!( + BlockhashQuery::Validated(Source::NonceAccount(nonce_pubkey), nonce_blockhash) + .get_blockhash(&rpc_client, CommitmentConfig::default()) + .await + .unwrap(), + nonce_blockhash, + ); + + let mut mocks = HashMap::new(); + mocks.insert(RpcRequest::GetAccountInfo, get_account_response); + let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); + assert_eq!( + BlockhashQuery::Static(nonce_blockhash) + .get_blockhash(&rpc_client, CommitmentConfig::default()) + .await + .unwrap(), + nonce_blockhash, + ); + + let rpc_client = RpcClient::new_mock("fails".to_string()); + assert!(BlockhashQuery::Rpc(Source::NonceAccount(nonce_pubkey)) + .get_blockhash(&rpc_client, CommitmentConfig::default()) + .await + .is_err()); + } +} diff --git a/client/src/nonblocking/mod.rs b/client/src/nonblocking/mod.rs index 844811c35657a8..7ba96d3994f443 100644 --- a/client/src/nonblocking/mod.rs +++ b/client/src/nonblocking/mod.rs @@ -1,3 +1,5 @@ +pub mod blockhash_query; +pub mod nonce_utils; pub mod pubsub_client; pub mod quic_client; pub mod rpc_client; diff --git a/client/src/nonblocking/nonce_utils.rs b/client/src/nonblocking/nonce_utils.rs new file mode 100644 index 00000000000000..fe0d2216d59fb3 --- /dev/null +++ b/client/src/nonblocking/nonce_utils.rs @@ -0,0 +1,247 @@ +//! Durable transaction nonce helpers. + +use { + crate::nonblocking::rpc_client::RpcClient, + solana_sdk::{ + account::{Account, ReadableAccount}, + account_utils::StateMut, + commitment_config::CommitmentConfig, + hash::Hash, + nonce::{ + state::{Data, Versions}, + State, + }, + pubkey::Pubkey, + system_program, + }, +}; + +#[derive(Debug, thiserror::Error, PartialEq, Eq)] +pub enum Error { + #[error("invalid account owner")] + InvalidAccountOwner, + #[error("invalid account data")] + InvalidAccountData, + #[error("unexpected account data size")] + UnexpectedDataSize, + #[error("provided hash ({provided}) does not match nonce hash ({expected})")] + InvalidHash { provided: Hash, expected: Hash }, + #[error("provided authority ({provided}) does not match nonce authority ({expected})")] + InvalidAuthority { provided: Pubkey, expected: Pubkey }, + #[error("invalid state for requested operation")] + InvalidStateForOperation, + #[error("client error: {0}")] + Client(String), +} + +/// Get a nonce account from the network. +/// +/// This is like [`RpcClient::get_account`] except: +/// +/// - it returns this module's [`Error`] type, +/// - it returns an error if any of the checks from [`account_identity_ok`] fail. +pub async fn get_account(rpc_client: &RpcClient, nonce_pubkey: &Pubkey) -> Result { + get_account_with_commitment(rpc_client, nonce_pubkey, CommitmentConfig::default()).await +} + +/// Get a nonce account from the network. +/// +/// This is like [`RpcClient::get_account_with_commitment`] except: +/// +/// - it returns this module's [`Error`] type, +/// - it returns an error if the account does not exist, +/// - it returns an error if any of the checks from [`account_identity_ok`] fail. +pub async fn get_account_with_commitment( + rpc_client: &RpcClient, + nonce_pubkey: &Pubkey, + commitment: CommitmentConfig, +) -> Result { + rpc_client + .get_account_with_commitment(nonce_pubkey, commitment) + .await + .map_err(|e| Error::Client(format!("{}", e))) + .and_then(|result| { + result + .value + .ok_or_else(|| Error::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey))) + }) + .and_then(|a| account_identity_ok(&a).map(|()| a)) +} + +/// Perform basic checks that an account has nonce-like properties. +/// +/// # Errors +/// +/// Returns [`Error::InvalidAccountOwner`] if the account is not owned by the +/// system program. Returns [`Error::UnexpectedDataSize`] if the account +/// contains no data. +pub fn account_identity_ok(account: &T) -> Result<(), Error> { + if account.owner() != &system_program::id() { + Err(Error::InvalidAccountOwner) + } else if account.data().is_empty() { + Err(Error::UnexpectedDataSize) + } else { + Ok(()) + } +} + +/// Deserialize the state of a durable transaction nonce account. +/// +/// # Errors +/// +/// Returns an error if the account is not owned by the system program or +/// contains no data. +/// +/// # Examples +/// +/// Determine if a nonce account is initialized: +/// +/// ```no_run +/// use solana_client::nonblocking::{ +/// rpc_client::RpcClient, +/// nonce_utils, +/// }; +/// use solana_sdk::{ +/// nonce::State, +/// pubkey::Pubkey, +/// }; +/// use anyhow::Result; +/// +/// futures::executor::block_on(async { +/// async fn is_nonce_initialized( +/// client: &RpcClient, +/// nonce_account_pubkey: &Pubkey, +/// ) -> Result { +/// +/// // Sign the tx with nonce_account's `blockhash` instead of the +/// // network's latest blockhash. +/// let nonce_account = client.get_account(nonce_account_pubkey).await?; +/// let nonce_state = nonce_utils::state_from_account(&nonce_account)?; +/// +/// Ok(!matches!(nonce_state, State::Uninitialized)) +/// } +/// # +/// # let client = RpcClient::new(String::new()); +/// # let nonce_account_pubkey = Pubkey::new_unique(); +/// # is_nonce_initialized(&client, &nonce_account_pubkey).await?; +/// # Ok::<(), anyhow::Error>(()) +/// # })?; +/// # Ok::<(), anyhow::Error>(()) +/// ``` +pub fn state_from_account>( + account: &T, +) -> Result { + account_identity_ok(account)?; + let versions = StateMut::::state(account).map_err(|_| Error::InvalidAccountData)?; + Ok(State::from(versions)) +} + +/// Deserialize the state data of a durable transaction nonce account. +/// +/// # Errors +/// +/// Returns an error if the account is not owned by the system program or +/// contains no data. Returns an error if the account state is uninitialized or +/// fails to deserialize. +/// +/// # Examples +/// +/// Create and sign a transaction with a durable nonce: +/// +/// ```no_run +/// use solana_client::nonblocking::{ +/// rpc_client::RpcClient, +/// nonce_utils, +/// }; +/// use solana_sdk::{ +/// message::Message, +/// pubkey::Pubkey, +/// signature::{Keypair, Signer}, +/// system_instruction, +/// transaction::Transaction, +/// }; +/// use std::path::Path; +/// use anyhow::Result; +/// # use anyhow::anyhow; +/// +/// futures::executor::block_on(async { +/// async fn create_transfer_tx_with_nonce( +/// client: &RpcClient, +/// nonce_account_pubkey: &Pubkey, +/// payer: &Keypair, +/// receiver: &Pubkey, +/// amount: u64, +/// tx_path: &Path, +/// ) -> Result<()> { +/// +/// let instr_transfer = system_instruction::transfer( +/// &payer.pubkey(), +/// receiver, +/// amount, +/// ); +/// +/// // In this example, `payer` is `nonce_account_pubkey`'s authority +/// let instr_advance_nonce_account = system_instruction::advance_nonce_account( +/// nonce_account_pubkey, +/// &payer.pubkey(), +/// ); +/// +/// // The `advance_nonce_account` instruction must be the first issued in +/// // the transaction. +/// let message = Message::new( +/// &[ +/// instr_advance_nonce_account, +/// instr_transfer +/// ], +/// Some(&payer.pubkey()), +/// ); +/// +/// let mut tx = Transaction::new_unsigned(message); +/// +/// // Sign the tx with nonce_account's `blockhash` instead of the +/// // network's latest blockhash. +/// let nonce_account = client.get_account(nonce_account_pubkey).await?; +/// let nonce_data = nonce_utils::data_from_account(&nonce_account)?; +/// let blockhash = nonce_data.blockhash(); +/// +/// tx.try_sign(&[payer], blockhash)?; +/// +/// // Save the signed transaction locally for later submission. +/// save_tx_to_file(&tx_path, &tx)?; +/// +/// Ok(()) +/// } +/// # +/// # fn save_tx_to_file(path: &Path, tx: &Transaction) -> Result<()> { +/// # Ok(()) +/// # } +/// # +/// # let client = RpcClient::new(String::new()); +/// # let nonce_account_pubkey = Pubkey::new_unique(); +/// # let payer = Keypair::new(); +/// # let receiver = Pubkey::new_unique(); +/// # create_transfer_tx_with_nonce(&client, &nonce_account_pubkey, &payer, &receiver, 1024, Path::new("new_tx")).await?; +/// # +/// # Ok::<(), anyhow::Error>(()) +/// # })?; +/// # Ok::<(), anyhow::Error>(()) +/// ``` +pub fn data_from_account>( + account: &T, +) -> Result { + account_identity_ok(account)?; + state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone())) +} + +/// Get the nonce data from its [`State`] value. +/// +/// # Errors +/// +/// Returns [`Error::InvalidStateForOperation`] if `state` is +/// [`State::Uninitialized`]. +pub fn data_from_state(state: &State) -> Result<&Data, Error> { + match state { + State::Uninitialized => Err(Error::InvalidStateForOperation), + State::Initialized(data) => Ok(data), + } +} diff --git a/client/src/nonblocking/quic_client.rs b/client/src/nonblocking/quic_client.rs index 153937203a7a66..d2f1e5dd3d4f08 100644 --- a/client/src/nonblocking/quic_client.rs +++ b/client/src/nonblocking/quic_client.rs @@ -263,21 +263,21 @@ pub struct QuicClient { connection: Arc>>, addr: SocketAddr, stats: Arc, - num_chunks: usize, + chunk_size: usize, } impl QuicClient { pub fn new( endpoint: Arc, addr: SocketAddr, - num_chunks: usize, + chunk_size: usize, ) -> Self { Self { endpoint, connection: Arc::new(Mutex::new(None)), addr, stats: Arc::new(ClientStats::default()), - num_chunks, + chunk_size, } } @@ -443,21 +443,6 @@ impl QuicClient { Ok(()) } - fn compute_chunk_length(num_buffers_to_chunk: usize, num_chunks: usize) -> usize { - // The function is equivalent to checked div_ceil() - // Also, if num_chunks == 0 || num_buffers_to_chunk == 0, return 1 - num_buffers_to_chunk - .checked_div(num_chunks) - .map_or(1, |value| { - if num_buffers_to_chunk.checked_rem(num_chunks).unwrap_or(0) != 0 { - value.saturating_add(1) - } else { - value - } - }) - .max(1) - } - pub async fn send_batch( &self, buffers: &[T], @@ -489,8 +474,7 @@ impl QuicClient { // by just getting a reference to the NewConnection once let connection_ref: &NewConnection = &connection; - let chunk_len = Self::compute_chunk_length(buffers.len() - 1, self.num_chunks); - let chunks = buffers[1..buffers.len()].iter().chunks(chunk_len); + let chunks = buffers[1..buffers.len()].iter().chunks(self.chunk_size); let futures: Vec<_> = chunks .into_iter() @@ -528,6 +512,10 @@ impl QuicTpuConnection { self.client.stats() } + pub fn connection_stats(&self) -> Arc { + self.connection_stats.clone() + } + pub fn new( endpoint: Arc, addr: SocketAddr, @@ -596,29 +584,3 @@ impl TpuConnection for QuicTpuConnection { Ok(()) } } - -#[cfg(test)] -mod tests { - use crate::nonblocking::quic_client::QuicClient; - - #[test] - fn test_transaction_batch_chunking() { - assert_eq!(QuicClient::compute_chunk_length(0, 0), 1); - assert_eq!(QuicClient::compute_chunk_length(10, 0), 1); - assert_eq!(QuicClient::compute_chunk_length(0, 10), 1); - assert_eq!(QuicClient::compute_chunk_length(usize::MAX, usize::MAX), 1); - assert_eq!(QuicClient::compute_chunk_length(10, usize::MAX), 1); - assert!(QuicClient::compute_chunk_length(usize::MAX, 10) == (usize::MAX / 10) + 1); - assert_eq!(QuicClient::compute_chunk_length(10, 1), 10); - assert_eq!(QuicClient::compute_chunk_length(10, 2), 5); - assert_eq!(QuicClient::compute_chunk_length(10, 3), 4); - assert_eq!(QuicClient::compute_chunk_length(10, 4), 3); - assert_eq!(QuicClient::compute_chunk_length(10, 5), 2); - assert_eq!(QuicClient::compute_chunk_length(10, 6), 2); - assert_eq!(QuicClient::compute_chunk_length(10, 7), 2); - assert_eq!(QuicClient::compute_chunk_length(10, 8), 2); - assert_eq!(QuicClient::compute_chunk_length(10, 9), 2); - assert_eq!(QuicClient::compute_chunk_length(10, 10), 1); - assert_eq!(QuicClient::compute_chunk_length(10, 11), 1); - } -} diff --git a/client/src/nonblocking/rpc_client.rs b/client/src/nonblocking/rpc_client.rs index c6f0098d71eb64..e47908016fd157 100644 --- a/client/src/nonblocking/rpc_client.rs +++ b/client/src/nonblocking/rpc_client.rs @@ -17,7 +17,10 @@ use { client_error::{ClientError, ClientErrorKind, Result as ClientResult}, http_sender::HttpSender, mock_sender::MockSender, - rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClientConfig}, + rpc_client::{ + GetConfirmedSignaturesForAddress2Config, RpcClientConfig, SerializableMessage, + SerializableTransaction, + }, rpc_config::{RpcAccountInfoConfig, *}, rpc_filter::{self, RpcFilterType}, rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter}, @@ -40,10 +43,9 @@ use { epoch_schedule::EpochSchedule, fee_calculator::{FeeCalculator, FeeRateGovernor}, hash::Hash, - message::Message, pubkey::Pubkey, signature::Signature, - transaction::{self, uses_durable_nonce, Transaction}, + transaction, }, solana_transaction_status::{ EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, @@ -656,7 +658,7 @@ impl RpcClient { /// ``` pub async fn send_and_confirm_transaction( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, ) -> ClientResult { const SEND_RETRIES: usize = 1; const GET_STATUS_RETRIES: usize = usize::MAX; @@ -664,13 +666,13 @@ impl RpcClient { 'sending: for _ in 0..SEND_RETRIES { let signature = self.send_transaction(transaction).await?; - let recent_blockhash = if uses_durable_nonce(transaction).is_some() { + let recent_blockhash = if transaction.uses_durable_nonce() { let (recent_blockhash, ..) = self .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .await?; recent_blockhash } else { - transaction.message.recent_blockhash + *transaction.get_recent_blockhash() }; for status_retry in 0..GET_STATUS_RETRIES { @@ -708,7 +710,7 @@ impl RpcClient { pub async fn send_and_confirm_transaction_with_spinner( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, ) -> ClientResult { self.send_and_confirm_transaction_with_spinner_and_commitment( transaction, @@ -719,7 +721,7 @@ impl RpcClient { pub async fn send_and_confirm_transaction_with_spinner_and_commitment( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, commitment: CommitmentConfig, ) -> ClientResult { self.send_and_confirm_transaction_with_spinner_and_config( @@ -735,16 +737,16 @@ impl RpcClient { pub async fn send_and_confirm_transaction_with_spinner_and_config( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, commitment: CommitmentConfig, config: RpcSendTransactionConfig, ) -> ClientResult { - let recent_blockhash = if uses_durable_nonce(transaction).is_some() { + let recent_blockhash = if transaction.uses_durable_nonce() { self.get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .await? .0 } else { - transaction.message.recent_blockhash + *transaction.get_recent_blockhash() }; let signature = self .send_transaction_with_config(transaction, config) @@ -826,7 +828,10 @@ impl RpcClient { /// # })?; /// # Ok::<(), ClientError>(()) /// ``` - pub async fn send_transaction(&self, transaction: &Transaction) -> ClientResult { + pub async fn send_transaction( + &self, + transaction: &impl SerializableTransaction, + ) -> ClientResult { self.send_transaction_with_config( transaction, RpcSendTransactionConfig { @@ -924,7 +929,7 @@ impl RpcClient { /// ``` pub async fn send_transaction_with_config( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, config: RpcSendTransactionConfig, ) -> ClientResult { let encoding = if let Some(encoding) = config.encoding { @@ -941,7 +946,7 @@ impl RpcClient { preflight_commitment: Some(preflight_commitment.commitment), ..config }; - let serialized_encoded = serialize_and_encode::(transaction, encoding)?; + let serialized_encoded = serialize_and_encode(transaction, encoding)?; let signature_base58_str: String = match self .send( RpcRequest::SendTransaction, @@ -981,14 +986,15 @@ impl RpcClient { // should not be passed along to confirmation methods. The transaction may or may // not have been submitted to the cluster, so callers should verify the success of // the correct transaction signature independently. - if signature != transaction.signatures[0] { + if signature != *transaction.get_signature() { Err(RpcError::RpcRequestError(format!( "RPC node returned mismatched signature {:?}, expected {:?}", - signature, transaction.signatures[0] + signature, + transaction.get_signature() )) .into()) } else { - Ok(transaction.signatures[0]) + Ok(*transaction.get_signature()) } } @@ -1290,7 +1296,7 @@ impl RpcClient { /// ``` pub async fn simulate_transaction( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, ) -> RpcResult { self.simulate_transaction_with_config( transaction, @@ -1377,7 +1383,7 @@ impl RpcClient { /// ``` pub async fn simulate_transaction_with_config( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, config: RpcSimulateTransactionConfig, ) -> RpcResult { let encoding = if let Some(encoding) = config.encoding { @@ -1392,7 +1398,7 @@ impl RpcClient { commitment: Some(commitment), ..config }; - let serialized_encoded = serialize_and_encode::(transaction, encoding)?; + let serialized_encoded = serialize_and_encode(transaction, encoding)?; self.send( RpcRequest::SimulateTransaction, json!([serialized_encoded, config]), @@ -3597,6 +3603,51 @@ impl RpcClient { .await } + /// Returns a list of minimum prioritization fees from recent blocks. + /// Takes an optional vector of addresses; if any addresses are provided, the response will + /// reflect the minimum prioritization fee to land a transaction locking all of the provided + /// accounts as writable. + /// + /// Currently, a node's prioritization-fee cache stores data from up to 150 blocks. + /// + /// # RPC Reference + /// + /// This method corresponds directly to the [`getRecentPrioritizationFees`] RPC method. + /// + /// [`getRecentPrioritizationFees`]: https://docs.solana.com/developing/clients/jsonrpc-api#getrecentprioritizationfees + /// + /// # Examples + /// + /// ``` + /// # use solana_client::{ + /// # client_error::ClientError, + /// # nonblocking::rpc_client::RpcClient, + /// # }; + /// # use solana_sdk::signature::{Keypair, Signer}; + /// # futures::executor::block_on(async { + /// # let rpc_client = RpcClient::new_mock("succeeds".to_string()); + /// # let alice = Keypair::new(); + /// # let bob = Keypair::new(); + /// let addresses = vec![alice.pubkey(), bob.pubkey()]; + /// let prioritization_fees = rpc_client.get_recent_prioritization_fees( + /// &addresses, + /// ).await?; + /// # Ok::<(), ClientError>(()) + /// # })?; + /// # Ok::<(), ClientError>(()) + /// ``` + pub async fn get_recent_prioritization_fees( + &self, + addresses: &[Pubkey], + ) -> ClientResult> { + let addresses: Vec<_> = addresses + .iter() + .map(|address| address.to_string()) + .collect(); + self.send(RpcRequest::GetRecentPrioritizationFees, json!([addresses])) + .await + } + /// Returns the identity pubkey for the current node. /// /// # RPC Reference @@ -4512,12 +4563,14 @@ impl RpcClient { if let Some(filters) = config.filters { config.filters = Some(self.maybe_map_filters(filters).await?); } - let accounts: Vec = self - .send( + + let accounts = self + .send::>>( RpcRequest::GetProgramAccounts, json!([pubkey.to_string(), config]), ) - .await?; + .await? + .parse_value(); parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts) } @@ -5347,28 +5400,20 @@ impl RpcClient { } #[allow(deprecated)] - pub async fn get_fee_for_message(&self, message: &Message) -> ClientResult { - if self.get_node_version().await? < semver::Version::new(1, 9, 0) { - let fee_calculator = self - .get_fee_calculator_for_blockhash(&message.recent_blockhash) - .await? - .ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()))?; - Ok(fee_calculator - .lamports_per_signature - .saturating_mul(message.header.num_required_signatures as u64)) - } else { - let serialized_encoded = - serialize_and_encode::(message, UiTransactionEncoding::Base64)?; - let result = self - .send::>>( - RpcRequest::GetFeeForMessage, - json!([serialized_encoded, self.commitment()]), - ) - .await?; - result - .value - .ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()).into()) - } + pub async fn get_fee_for_message( + &self, + message: &impl SerializableMessage, + ) -> ClientResult { + let serialized_encoded = serialize_and_encode(message, UiTransactionEncoding::Base64)?; + let result = self + .send::>>( + RpcRequest::GetFeeForMessage, + json!([serialized_encoded, self.commitment()]), + ) + .await?; + result + .value + .ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()).into()) } pub async fn get_new_latest_blockhash(&self, blockhash: &Hash) -> ClientResult { diff --git a/client/src/nonblocking/tpu_client.rs b/client/src/nonblocking/tpu_client.rs index 4dd39c7685407b..27ed331334141c 100644 --- a/client/src/nonblocking/tpu_client.rs +++ b/client/src/nonblocking/tpu_client.rs @@ -77,6 +77,15 @@ async fn send_wire_transaction_to_addr( conn.send_wire_transaction(wire_transaction.clone()).await } +async fn send_wire_transaction_batch_to_addr( + connection_cache: &ConnectionCache, + addr: &SocketAddr, + wire_transactions: &[Vec], +) -> TransportResult<()> { + let conn = connection_cache.get_nonblocking_connection(addr); + conn.send_wire_transaction_batch(wire_transactions).await +} + impl TpuClient { /// Serialize and send transaction to the current and upcoming leader TPUs according to fanout /// size @@ -140,6 +149,50 @@ impl TpuClient { } } + /// Send a batch of wire transactions to the current and upcoming leader TPUs according to + /// fanout size + /// Returns the last error if all sends fail + pub async fn try_send_wire_transaction_batch( + &self, + wire_transactions: Vec>, + ) -> TransportResult<()> { + let leaders = self + .leader_tpu_service + .leader_tpu_sockets(self.fanout_slots); + let futures = leaders + .iter() + .map(|addr| { + send_wire_transaction_batch_to_addr( + &self.connection_cache, + addr, + &wire_transactions, + ) + }) + .collect::>(); + let results: Vec> = join_all(futures).await; + + let mut last_error: Option = None; + let mut some_success = false; + for result in results { + if let Err(e) = result { + if last_error.is_none() { + last_error = Some(e); + } + } else { + some_success = true; + } + } + if !some_success { + Err(if let Some(err) = last_error { + err + } else { + std::io::Error::new(std::io::ErrorKind::Other, "No sends attempted").into() + }) + } else { + Ok(()) + } + } + /// Create a new client that disconnects when dropped pub async fn new( rpc_client: Arc, @@ -289,7 +342,7 @@ impl TpuClient { } } - transactions = pending_transactions.into_iter().map(|(_k, v)| v).collect(); + transactions = pending_transactions.into_values().collect(); progress_bar.println(format!( "Blockhash expired. {} retries remaining", expired_blockhash_retries diff --git a/client/src/nonce_utils.rs b/client/src/nonce_utils.rs index 9d797771559cd8..b00ef416c5b27f 100644 --- a/client/src/nonce_utils.rs +++ b/client/src/nonce_utils.rs @@ -1,39 +1,13 @@ //! Durable transaction nonce helpers. +pub use crate::nonblocking::nonce_utils::{ + account_identity_ok, data_from_account, data_from_state, state_from_account, Error, +}; use { crate::rpc_client::RpcClient, - solana_sdk::{ - account::{Account, ReadableAccount}, - account_utils::StateMut, - commitment_config::CommitmentConfig, - hash::Hash, - nonce::{ - state::{Data, Versions}, - State, - }, - pubkey::Pubkey, - system_program, - }, + solana_sdk::{account::Account, commitment_config::CommitmentConfig, pubkey::Pubkey}, }; -#[derive(Debug, thiserror::Error, PartialEq, Eq)] -pub enum Error { - #[error("invalid account owner")] - InvalidAccountOwner, - #[error("invalid account data")] - InvalidAccountData, - #[error("unexpected account data size")] - UnexpectedDataSize, - #[error("provided hash ({provided}) does not match nonce hash ({expected})")] - InvalidHash { provided: Hash, expected: Hash }, - #[error("provided authority ({provided}) does not match nonce authority ({expected})")] - InvalidAuthority { provided: Pubkey, expected: Pubkey }, - #[error("invalid state for requested operation")] - InvalidStateForOperation, - #[error("client error: {0}")] - Client(String), -} - /// Get a nonce account from the network. /// /// This is like [`RpcClient::get_account`] except: @@ -66,176 +40,3 @@ pub fn get_account_with_commitment( }) .and_then(|a| account_identity_ok(&a).map(|()| a)) } - -/// Perform basic checks that an account has nonce-like properties. -/// -/// # Errors -/// -/// Returns [`Error::InvalidAccountOwner`] if the account is not owned by the -/// system program. Returns [`Error::UnexpectedDataSize`] if the account -/// contains no data. -pub fn account_identity_ok(account: &T) -> Result<(), Error> { - if account.owner() != &system_program::id() { - Err(Error::InvalidAccountOwner) - } else if account.data().is_empty() { - Err(Error::UnexpectedDataSize) - } else { - Ok(()) - } -} - -/// Deserialize the state of a durable transaction nonce account. -/// -/// # Errors -/// -/// Returns an error if the account is not owned by the system program or -/// contains no data. -/// -/// # Examples -/// -/// Determine if a nonce account is initialized: -/// -/// ```no_run -/// use solana_client::{ -/// rpc_client::RpcClient, -/// nonce_utils, -/// }; -/// use solana_sdk::{ -/// nonce::State, -/// pubkey::Pubkey, -/// }; -/// use anyhow::Result; -/// -/// fn is_nonce_initialized( -/// client: &RpcClient, -/// nonce_account_pubkey: &Pubkey, -/// ) -> Result { -/// -/// // Sign the tx with nonce_account's `blockhash` instead of the -/// // network's latest blockhash. -/// let nonce_account = client.get_account(nonce_account_pubkey)?; -/// let nonce_state = nonce_utils::state_from_account(&nonce_account)?; -/// -/// Ok(!matches!(nonce_state, State::Uninitialized)) -/// } -/// # -/// # let client = RpcClient::new(String::new()); -/// # let nonce_account_pubkey = Pubkey::new_unique(); -/// # is_nonce_initialized(&client, &nonce_account_pubkey)?; -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -pub fn state_from_account>( - account: &T, -) -> Result { - account_identity_ok(account)?; - let versions = StateMut::::state(account).map_err(|_| Error::InvalidAccountData)?; - Ok(State::from(versions)) -} - -/// Deserialize the state data of a durable transaction nonce account. -/// -/// # Errors -/// -/// Returns an error if the account is not owned by the system program or -/// contains no data. Returns an error if the account state is uninitialized or -/// fails to deserialize. -/// -/// # Examples -/// -/// Create and sign a transaction with a durable nonce: -/// -/// ```no_run -/// use solana_client::{ -/// rpc_client::RpcClient, -/// nonce_utils, -/// }; -/// use solana_sdk::{ -/// message::Message, -/// pubkey::Pubkey, -/// signature::{Keypair, Signer}, -/// system_instruction, -/// transaction::Transaction, -/// }; -/// use std::path::Path; -/// use anyhow::Result; -/// # use anyhow::anyhow; -/// -/// fn create_transfer_tx_with_nonce( -/// client: &RpcClient, -/// nonce_account_pubkey: &Pubkey, -/// payer: &Keypair, -/// receiver: &Pubkey, -/// amount: u64, -/// tx_path: &Path, -/// ) -> Result<()> { -/// -/// let instr_transfer = system_instruction::transfer( -/// &payer.pubkey(), -/// receiver, -/// amount, -/// ); -/// -/// // In this example, `payer` is `nonce_account_pubkey`'s authority -/// let instr_advance_nonce_account = system_instruction::advance_nonce_account( -/// nonce_account_pubkey, -/// &payer.pubkey(), -/// ); -/// -/// // The `advance_nonce_account` instruction must be the first issued in -/// // the transaction. -/// let message = Message::new( -/// &[ -/// instr_advance_nonce_account, -/// instr_transfer -/// ], -/// Some(&payer.pubkey()), -/// ); -/// -/// let mut tx = Transaction::new_unsigned(message); -/// -/// // Sign the tx with nonce_account's `blockhash` instead of the -/// // network's latest blockhash. -/// let nonce_account = client.get_account(nonce_account_pubkey)?; -/// let nonce_data = nonce_utils::data_from_account(&nonce_account)?; -/// let blockhash = nonce_data.blockhash(); -/// -/// tx.try_sign(&[payer], blockhash)?; -/// -/// // Save the signed transaction locally for later submission. -/// save_tx_to_file(&tx_path, &tx)?; -/// -/// Ok(()) -/// } -/// # -/// # fn save_tx_to_file(path: &Path, tx: &Transaction) -> Result<()> { -/// # Ok(()) -/// # } -/// # -/// # let client = RpcClient::new(String::new()); -/// # let nonce_account_pubkey = Pubkey::new_unique(); -/// # let payer = Keypair::new(); -/// # let receiver = Pubkey::new_unique(); -/// # create_transfer_tx_with_nonce(&client, &nonce_account_pubkey, &payer, &receiver, 1024, Path::new("new_tx"))?; -/// # -/// # Ok::<(), anyhow::Error>(()) -/// ``` -pub fn data_from_account>( - account: &T, -) -> Result { - account_identity_ok(account)?; - state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone())) -} - -/// Get the nonce data from its [`State`] value. -/// -/// # Errors -/// -/// Returns [`Error::InvalidStateForOperation`] if `state` is -/// [`State::Uninitialized`]. -pub fn data_from_state(state: &State) -> Result<&Data, Error> { - match state { - State::Uninitialized => Err(Error::InvalidStateForOperation), - State::Initialized(data) => Ok(data), - } -} diff --git a/client/src/quic_client.rs b/client/src/quic_client.rs index 71775b4a4b7ad4..bcd7f03090ac07 100644 --- a/client/src/quic_client.rs +++ b/client/src/quic_client.rs @@ -11,15 +11,66 @@ use { }, tpu_connection::TpuConnection as NonblockingTpuConnection, }, - tpu_connection::TpuConnection, + tpu_connection::{ClientStats, TpuConnection}, }, lazy_static::lazy_static, - solana_sdk::transport::Result as TransportResult, - std::{net::SocketAddr, sync::Arc}, - tokio::runtime::Runtime, + log::*, + solana_sdk::transport::{Result as TransportResult, TransportError}, + std::{ + net::SocketAddr, + sync::{atomic::Ordering, Arc, Condvar, Mutex, MutexGuard}, + time::Duration, + }, + tokio::{runtime::Runtime, time::timeout}, }; +const MAX_OUTSTANDING_TASK: u64 = 2000; +const SEND_TRANSACTION_TIMEOUT_MS: u64 = 10000; + +/// A semaphore used for limiting the number of asynchronous tasks spawn to the +/// runtime. Before spawnning a task, use acquire. After the task is done (be it +/// succsess or failure), call release. +struct AsyncTaskSemaphore { + /// Keep the counter info about the usage + counter: Mutex, + /// Conditional variable for signaling when counter is decremented + cond_var: Condvar, + /// The maximum usage allowed by this semaphore. + permits: u64, +} + +impl AsyncTaskSemaphore { + fn new(permits: u64) -> Self { + Self { + counter: Mutex::new(0), + cond_var: Condvar::new(), + permits, + } + } + + /// When returned, the lock has been locked and usage count has been + /// incremented. When the returned MutexGuard is dropped the lock is dropped + /// without decrementing the usage count. + fn acquire(&self) -> MutexGuard { + let mut count = self.counter.lock().unwrap(); + *count += 1; + while *count > self.permits { + count = self.cond_var.wait(count).unwrap(); + } + count + } + + /// Acquire the lock and decrement the usage count + fn release(&self) { + let mut count = self.counter.lock().unwrap(); + *count -= 1; + self.cond_var.notify_one(); + } +} + lazy_static! { + static ref ASYNC_TASK_SEMAPHORE: AsyncTaskSemaphore = + AsyncTaskSemaphore::new(MAX_OUTSTANDING_TASK); static ref RUNTIME: Runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() @@ -55,6 +106,54 @@ impl QuicTpuConnection { } } +async fn send_wire_transaction_async( + connection: Arc, + wire_transaction: Vec, +) -> TransportResult<()> { + let result = timeout( + Duration::from_millis(SEND_TRANSACTION_TIMEOUT_MS), + connection.send_wire_transaction(wire_transaction), + ) + .await; + ASYNC_TASK_SEMAPHORE.release(); + handle_send_result(result, connection) +} + +async fn send_wire_transaction_batch_async( + connection: Arc, + buffers: Vec>, +) -> TransportResult<()> { + let time_out = SEND_TRANSACTION_TIMEOUT_MS * buffers.len() as u64; + + let result = timeout( + Duration::from_millis(time_out), + connection.send_wire_transaction_batch(&buffers), + ) + .await; + ASYNC_TASK_SEMAPHORE.release(); + handle_send_result(result, connection) +} + +/// Check the send result and update stats if timedout. Returns the checked result. +fn handle_send_result( + result: Result, tokio::time::error::Elapsed>, + connection: Arc, +) -> Result<(), TransportError> { + match result { + Ok(result) => result, + Err(_err) => { + let client_stats = ClientStats::default(); + client_stats.send_timeout.fetch_add(1, Ordering::Relaxed); + let stats = connection.connection_stats(); + stats.add_client_stats(&client_stats, 0, false); + info!("Timedout sending transaction {:?}", connection.tpu_addr()); + Err(TransportError::Custom( + "Timedout sending transaction".to_string(), + )) + } + } +} + impl TpuConnection for QuicTpuConnection { fn tpu_addr(&self) -> &SocketAddr { self.inner.tpu_addr() @@ -69,16 +168,19 @@ impl TpuConnection for QuicTpuConnection { } fn send_wire_transaction_async(&self, wire_transaction: Vec) -> TransportResult<()> { + let _lock = ASYNC_TASK_SEMAPHORE.acquire(); let inner = self.inner.clone(); - //drop and detach the task - let _ = RUNTIME.spawn(async move { inner.send_wire_transaction(wire_transaction).await }); + + let _ = RUNTIME + .spawn(async move { send_wire_transaction_async(inner, wire_transaction).await }); Ok(()) } fn send_wire_transaction_batch_async(&self, buffers: Vec>) -> TransportResult<()> { + let _lock = ASYNC_TASK_SEMAPHORE.acquire(); let inner = self.inner.clone(); - //drop and detach the task - let _ = RUNTIME.spawn(async move { inner.send_wire_transaction_batch(&buffers).await }); + let _ = + RUNTIME.spawn(async move { send_wire_transaction_batch_async(inner, buffers).await }); Ok(()) } } diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 1aba5a0c53f047..bac070ce6fcd3d 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -20,6 +20,7 @@ use { rpc_response::*, rpc_sender::*, }, + serde::Serialize, serde_json::Value, solana_account_decoder::{ parse_token::{UiTokenAccount, UiTokenAmount}, @@ -33,10 +34,10 @@ use { epoch_schedule::EpochSchedule, fee_calculator::{FeeCalculator, FeeRateGovernor}, hash::Hash, - message::Message, + message::{v0, Message as LegacyMessage}, pubkey::Pubkey, signature::Signature, - transaction::{self, Transaction}, + transaction::{self, uses_durable_nonce, Transaction, VersionedTransaction}, }, solana_transaction_status::{ EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, @@ -60,6 +61,42 @@ impl RpcClientConfig { } } +/// Trait used to add support for versioned messages to RPC APIs while +/// retaining backwards compatibility +pub trait SerializableMessage: Serialize {} +impl SerializableMessage for LegacyMessage {} +impl SerializableMessage for v0::Message {} + +/// Trait used to add support for versioned transactions to RPC APIs while +/// retaining backwards compatibility +pub trait SerializableTransaction: Serialize { + fn get_signature(&self) -> &Signature; + fn get_recent_blockhash(&self) -> &Hash; + fn uses_durable_nonce(&self) -> bool; +} +impl SerializableTransaction for Transaction { + fn get_signature(&self) -> &Signature { + &self.signatures[0] + } + fn get_recent_blockhash(&self) -> &Hash { + &self.message.recent_blockhash + } + fn uses_durable_nonce(&self) -> bool { + uses_durable_nonce(self).is_some() + } +} +impl SerializableTransaction for VersionedTransaction { + fn get_signature(&self) -> &Signature { + &self.signatures[0] + } + fn get_recent_blockhash(&self) -> &Hash { + self.message.recent_blockhash() + } + fn uses_durable_nonce(&self) -> bool { + self.uses_durable_nonce() + } +} + #[derive(Debug, Default)] pub struct GetConfirmedSignaturesForAddress2Config { pub before: Option, @@ -176,7 +213,7 @@ impl RpcClient { rpc_client: nonblocking::rpc_client::RpcClient::new_sender(sender, config), runtime: Some( tokio::runtime::Builder::new_current_thread() - .thread_name("rpc-client") + .thread_name("solRpcClient") .enable_io() .enable_time() .build() @@ -622,14 +659,14 @@ impl RpcClient { /// ``` pub fn send_and_confirm_transaction( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, ) -> ClientResult { self.invoke(self.rpc_client.send_and_confirm_transaction(transaction)) } pub fn send_and_confirm_transaction_with_spinner( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, ) -> ClientResult { self.invoke( self.rpc_client @@ -639,7 +676,7 @@ impl RpcClient { pub fn send_and_confirm_transaction_with_spinner_and_commitment( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, commitment: CommitmentConfig, ) -> ClientResult { self.invoke( @@ -650,7 +687,7 @@ impl RpcClient { pub fn send_and_confirm_transaction_with_spinner_and_config( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, commitment: CommitmentConfig, config: RpcSendTransactionConfig, ) -> ClientResult { @@ -734,7 +771,10 @@ impl RpcClient { /// let signature = rpc_client.send_transaction(&tx)?; /// # Ok::<(), ClientError>(()) /// ``` - pub fn send_transaction(&self, transaction: &Transaction) -> ClientResult { + pub fn send_transaction( + &self, + transaction: &impl SerializableTransaction, + ) -> ClientResult { self.invoke(self.rpc_client.send_transaction(transaction)) } @@ -819,7 +859,7 @@ impl RpcClient { /// ``` pub fn send_transaction_with_config( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, config: RpcSendTransactionConfig, ) -> ClientResult { self.invoke( @@ -1025,7 +1065,7 @@ impl RpcClient { /// ``` pub fn simulate_transaction( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, ) -> RpcResult { self.invoke(self.rpc_client.simulate_transaction(transaction)) } @@ -1102,7 +1142,7 @@ impl RpcClient { /// ``` pub fn simulate_transaction_with_config( &self, - transaction: &Transaction, + transaction: &impl SerializableTransaction, config: RpcSimulateTransactionConfig, ) -> RpcResult { self.invoke( @@ -2983,6 +3023,43 @@ impl RpcClient { self.invoke(self.rpc_client.get_recent_performance_samples(limit)) } + /// Returns a list of minimum prioritization fees from recent blocks. + /// Takes an optional vector of addresses; if any addresses are provided, the response will + /// reflect the minimum prioritization fee to land a transaction locking all of the provided + /// accounts as writable. + /// + /// Currently, a node's prioritization-fee cache stores data from up to 150 blocks. + /// + /// # RPC Reference + /// + /// This method corresponds directly to the [`getRecentPrioritizationFees`] RPC method. + /// + /// [`getRecentPrioritizationFees`]: https://docs.solana.com/developing/clients/jsonrpc-api#getrecentprioritizationfees + /// + /// # Examples + /// + /// ``` + /// # use solana_client::{ + /// # client_error::ClientError, + /// # rpc_client::RpcClient, + /// # }; + /// # use solana_sdk::signature::{Keypair, Signer}; + /// # let rpc_client = RpcClient::new_mock("succeeds".to_string()); + /// # let alice = Keypair::new(); + /// # let bob = Keypair::new(); + /// let addresses = vec![alice.pubkey(), bob.pubkey()]; + /// let prioritization_fees = rpc_client.get_recent_prioritization_fees( + /// &addresses, + /// )?; + /// # Ok::<(), ClientError>(()) + /// ``` + pub fn get_recent_prioritization_fees( + &self, + addresses: &[Pubkey], + ) -> ClientResult> { + self.invoke(self.rpc_client.get_recent_prioritization_fees(addresses)) + } + /// Returns the identity pubkey for the current node. /// /// # RPC Reference @@ -4089,7 +4166,7 @@ impl RpcClient { } #[allow(deprecated)] - pub fn get_fee_for_message(&self, message: &Message) -> ClientResult { + pub fn get_fee_for_message(&self, message: &impl SerializableMessage) -> ClientResult { self.invoke(self.rpc_client.get_fee_for_message(message)) } @@ -4391,4 +4468,83 @@ mod tests { assert_eq!(expected_minimum_delegation, actual_minimum_delegation); } } + + #[test] + fn test_get_program_accounts_with_config() { + let program_id = Pubkey::new_unique(); + let pubkey = Pubkey::new_unique(); + let account = Account { + lamports: 1_000_000, + data: vec![], + owner: program_id, + executable: false, + rent_epoch: 0, + }; + let keyed_account = RpcKeyedAccount { + pubkey: pubkey.to_string(), + account: UiAccount::encode(&pubkey, &account, UiAccountEncoding::Base64, None, None), + }; + let expected_result = vec![(pubkey, account)]; + // Test: without context + { + let mocks: Mocks = [( + RpcRequest::GetProgramAccounts, + serde_json::to_value(OptionalContext::NoContext(vec![keyed_account.clone()])) + .unwrap(), + )] + .into_iter() + .collect(); + let rpc_client = RpcClient::new_mock_with_mocks("mock_client".to_string(), mocks); + let result = rpc_client + .get_program_accounts_with_config( + &program_id, + RpcProgramAccountsConfig { + filters: None, + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + data_slice: None, + commitment: None, + min_context_slot: None, + }, + with_context: None, + }, + ) + .unwrap(); + assert_eq!(expected_result, result); + } + + // Test: with context + { + let mocks: Mocks = [( + RpcRequest::GetProgramAccounts, + serde_json::to_value(OptionalContext::Context(Response { + context: RpcResponseContext { + slot: 1, + api_version: None, + }, + value: vec![keyed_account], + })) + .unwrap(), + )] + .into_iter() + .collect(); + let rpc_client = RpcClient::new_mock_with_mocks("mock_client".to_string(), mocks); + let result = rpc_client + .get_program_accounts_with_config( + &program_id, + RpcProgramAccountsConfig { + filters: None, + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + data_slice: None, + commitment: None, + min_context_slot: None, + }, + with_context: Some(true), + }, + ) + .unwrap(); + assert_eq!(expected_result, result); + } + } } diff --git a/client/src/rpc_custom_error.rs b/client/src/rpc_custom_error.rs index 95ef60293cae18..f0fb19e21da228 100644 --- a/client/src/rpc_custom_error.rs +++ b/client/src/rpc_custom_error.rs @@ -195,7 +195,12 @@ impl From for Error { }, RpcCustomError::UnsupportedTransactionVersion(version) => Self { code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION), - message: format!("Transaction version ({}) is not supported", version), + message: format!( + "Transaction version ({0}) is not supported by the requesting client. \ + Please try the request again with the following configuration parameter: \ + \"maxSupportedTransactionVersion\": {0}", + version + ), data: None, }, RpcCustomError::MinContextSlotNotReached { context_slot } => Self { diff --git a/client/src/rpc_filter.rs b/client/src/rpc_filter.rs index 1f6548c80a822c..ebccd067f9e7ee 100644 --- a/client/src/rpc_filter.rs +++ b/client/src/rpc_filter.rs @@ -1,5 +1,6 @@ #![allow(deprecated)] use { + crate::version_req::VersionReq, solana_sdk::account::{AccountSharedData, ReadableAccount}, spl_token_2022::{generic_token_account::GenericTokenAccount, state::Account}, std::borrow::Cow, @@ -57,7 +58,7 @@ impl RpcFilterType { if bytes.len() > MAX_DATA_BASE64_SIZE { return Err(RpcFilterError::DataTooLarge); } - let bytes = base64::decode(&bytes)?; + let bytes = base64::decode(bytes)?; if bytes.len() > MAX_DATA_SIZE { Err(RpcFilterError::DataTooLarge) } else { @@ -263,7 +264,11 @@ pub(crate) fn maybe_map_filters( node_version: Option, filters: &mut [RpcFilterType], ) -> Result<(), String> { - if node_version.is_none() || node_version.unwrap() < semver::Version::new(1, 11, 2) { + let version_reqs = VersionReq::from_strs(&["<1.11.2", "~1.13"])?; + let needs_mapping = node_version + .map(|version| version_reqs.matches_any(&version)) + .unwrap_or(true); + if needs_mapping { for filter in filters.iter_mut() { if let RpcFilterType::Memcmp(memcmp) = filter { match &memcmp.bytes { diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index d3f0ceb1c0ad54..42cd79686b76d3 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -79,6 +79,7 @@ pub enum RpcRequest { )] GetRecentBlockhash, GetRecentPerformanceSamples, + GetRecentPrioritizationFees, GetHighestSnapshotSlot, #[deprecated( since = "1.9.0", @@ -157,6 +158,7 @@ impl fmt::Display for RpcRequest { RpcRequest::GetProgramAccounts => "getProgramAccounts", RpcRequest::GetRecentBlockhash => "getRecentBlockhash", RpcRequest::GetRecentPerformanceSamples => "getRecentPerformanceSamples", + RpcRequest::GetRecentPrioritizationFees => "getRecentPrioritizationFees", RpcRequest::GetHighestSnapshotSlot => "getHighestSnapshotSlot", RpcRequest::GetSnapshotSlot => "getSnapshotSlot", RpcRequest::GetSignaturesForAddress => "getSignaturesForAddress", diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index b54feb3a777139..557ec2891c0f8e 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -8,15 +8,34 @@ use { hash::Hash, inflation::Inflation, transaction::{Result, TransactionError}, - transaction_context::TransactionReturnData, }, solana_transaction_status::{ ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock, + UiTransactionReturnData, }, std::{collections::HashMap, fmt, net::SocketAddr, str::FromStr}, thiserror::Error, }; +/// Wrapper for rpc return types of methods that provide responses both with and without context. +/// Main purpose of this is to fix methods that lack context information in their return type, +/// without breaking backwards compatibility. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum OptionalContext { + Context(Response), + NoContext(T), +} + +impl OptionalContext { + pub fn parse_value(self) -> T { + match self { + Self::Context(response) => response.value, + Self::NoContext(value) => value, + } + } +} + pub type RpcResult = client_error::Result>; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -399,29 +418,7 @@ pub struct RpcSimulateTransactionResult { pub logs: Option>, pub accounts: Option>>, pub units_consumed: Option, - pub return_data: Option, -} - -#[derive(Serialize, Deserialize, Clone, Debug)] -#[serde(rename_all = "camelCase")] -pub struct RpcTransactionReturnData { - pub program_id: String, - pub data: (String, ReturnDataEncoding), -} - -impl From for RpcTransactionReturnData { - fn from(return_data: TransactionReturnData) -> Self { - Self { - program_id: return_data.program_id.to_string(), - data: (base64::encode(return_data.data), ReturnDataEncoding::Base64), - } - } -} - -#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[serde(rename_all = "camelCase")] -pub enum ReturnDataEncoding { - Base64, + pub return_data: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -544,3 +541,10 @@ pub struct RpcSnapshotSlotInfo { pub full: Slot, pub incremental: Option, } + +#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct RpcPrioritizationFee { + pub slot: Slot, + pub prioritization_fee: u64, +} diff --git a/client/src/tpu_client.rs b/client/src/tpu_client.rs index 5e94098e2624b9..acb52ceed6a58c 100644 --- a/client/src/tpu_client.rs +++ b/client/src/tpu_client.rs @@ -11,6 +11,7 @@ use { }, bincode::serialize, log::*, + rayon::iter::{IntoParallelIterator, ParallelIterator}, solana_sdk::{ clock::Slot, commitment_config::CommitmentConfig, @@ -111,6 +112,17 @@ impl TpuClient { self.try_send_wire_transaction(wire_transaction) } + /// Serialize and send a batch of transactions to the current and upcoming leader TPUs according + /// to fanout size + /// Returns the last error if all sends fail + pub fn try_send_transaction_batch(&self, transactions: &[Transaction]) -> TransportResult<()> { + let wire_transactions = transactions + .into_par_iter() + .map(|tx| bincode::serialize(&tx).expect("serialize Transaction in send_batch")) + .collect::>(); + self.try_send_wire_transaction_batch(wire_transactions) + } + /// Send a wire transaction to the current and upcoming leader TPUs according to fanout size /// Returns the last error if all sends fail fn try_send_wire_transaction(&self, wire_transaction: Vec) -> TransportResult<()> { @@ -140,6 +152,39 @@ impl TpuClient { } } + /// Send a batch of wire transactions to the current and upcoming leader TPUs according to + /// fanout size + /// Returns the last error if all sends fail + fn try_send_wire_transaction_batch( + &self, + wire_transactions: Vec>, + ) -> TransportResult<()> { + let mut last_error: Option = None; + let mut some_success = false; + + for tpu_address in self + .leader_tpu_service + .leader_tpu_sockets(self.fanout_slots) + { + let conn = self.connection_cache.get_connection(&tpu_address); + let result = conn.send_wire_transaction_batch_async(wire_transactions.clone()); + if let Err(err) = result { + last_error = Some(err); + } else { + some_success = true; + } + } + if !some_success { + Err(if let Some(err) = last_error { + err + } else { + std::io::Error::new(std::io::ErrorKind::Other, "No sends attempted").into() + }) + } else { + Ok(()) + } + } + /// Create a new client that disconnects when dropped pub fn new( rpc_client: Arc, @@ -363,11 +408,15 @@ impl LeaderTpuCache { // Get the TPU sockets for the current leader and upcoming leaders according to fanout size pub(crate) fn get_leader_sockets( &self, - current_slot: Slot, + estimated_current_slot: Slot, fanout_slots: u64, ) -> Vec { let mut leader_set = HashSet::new(); let mut leader_sockets = Vec::new(); + // `first_slot` might have been advanced since caller last read the `estimated_current_slot` + // value. Take the greater of the two values to ensure we are reading from the latest + // leader schedule. + let current_slot = std::cmp::max(estimated_current_slot, self.first_slot); for leader_slot in current_slot..current_slot + fanout_slots { if let Some(leader) = self.get_slot_leader(leader_slot) { if let Some(tpu_socket) = self.leader_tpu_map.get(leader) { diff --git a/client/src/tpu_connection.rs b/client/src/tpu_connection.rs index 9f02319379c942..0825c06987dd58 100644 --- a/client/src/tpu_connection.rs +++ b/client/src/tpu_connection.rs @@ -21,6 +21,7 @@ pub struct ClientStats { pub tx_data_blocked: MovingStat, pub tx_acks: MovingStat, pub make_connection_ms: AtomicU64, + pub send_timeout: AtomicU64, } #[enum_dispatch] diff --git a/client/src/transaction_executor.rs b/client/src/transaction_executor.rs index 56f7a8002275e4..89a70d7ee596e8 100644 --- a/client/src/transaction_executor.rs +++ b/client/src/transaction_executor.rs @@ -91,7 +91,7 @@ impl TransactionExecutor { let exit = exit.clone(); let cleared = cleared.clone(); Builder::new() - .name("sig_clear".to_string()) + .name("solSigClear".to_string()) .spawn(move || { let client = RpcClient::new_socket_with_commitment( entrypoint_addr, diff --git a/client/src/version_req.rs b/client/src/version_req.rs new file mode 100644 index 00000000000000..515f85b359eed4 --- /dev/null +++ b/client/src/version_req.rs @@ -0,0 +1,20 @@ +pub(crate) struct VersionReq(Vec); + +impl VersionReq { + pub(crate) fn from_strs(versions: &[T]) -> Result + where + T: AsRef + std::fmt::Debug, + { + let mut version_reqs = vec![]; + for version in versions { + let version_req = semver::VersionReq::parse(version.as_ref()) + .map_err(|err| format!("Could not parse version {:?}: {:?}", version, err))?; + version_reqs.push(version_req); + } + Ok(Self(version_reqs)) + } + + pub(crate) fn matches_any(&self, version: &semver::Version) -> bool { + self.0.iter().any(|r| r.matches(version)) + } +} diff --git a/core/Cargo.toml b/core/Cargo.toml index d8b6bae0133bf9..70d6c82eb8ecbc 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "solana-core" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" homepage = "https://solana.com/" documentation = "https://docs.rs/solana-core" readme = "../README.md" @@ -30,37 +30,38 @@ lazy_static = "1.4.0" log = "0.4.17" lru = "0.7.7" min-max-heap = "1.3.0" +num_enum = "0.5.7" rand = "0.7.0" rand_chacha = "0.2.2" rayon = "1.5.3" serde = "1.0.138" serde_derive = "1.0.103" -solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.11.6" } -solana-bloom = { path = "../bloom", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.11.6" } -solana-geyser-plugin-manager = { path = "../geyser-plugin-manager", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-poh = { path = "../poh", version = "=1.11.6" } -solana-program-runtime = { path = "../program-runtime", version = "=1.11.6" } -solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.11.6" } -solana-rpc = { path = "../rpc", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } +solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.14.24" } +solana-bloom = { path = "../bloom", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-frozen-abi = { path = "../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.14.24" } +solana-geyser-plugin-manager = { path = "../geyser-plugin-manager", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-poh = { path = "../poh", version = "=1.14.24" } +solana-program-runtime = { path = "../program-runtime", version = "=1.14.24" } +solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.14.24" } +solana-rpc = { path = "../rpc", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } sys-info = "0.9.1" -tempfile = "3.3.0" +tempfile = "3.4.0" thiserror = "1.0" tokio = { version = "~1.14.1", features = ["full"] } trees = "0.4.2" @@ -70,9 +71,9 @@ matches = "0.1.9" raptorq = "1.7.0" serde_json = "1.0.81" serial_test = "0.8.0" -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-program-runtime = { path = "../program-runtime", version = "=1.11.6" } -solana-stake-program = { path = "../programs/stake", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-program-runtime = { path = "../program-runtime", version = "=1.14.24" } +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } static_assertions = "1.1.0" systemstat = "0.1.11" test-case = "2.1.0" diff --git a/core/benches/banking_stage.rs b/core/benches/banking_stage.rs index fb506be8c5626f..9905172a6c7f4a 100644 --- a/core/benches/banking_stage.rs +++ b/core/benches/banking_stage.rs @@ -98,7 +98,6 @@ fn bench_consume_buffered(bencher: &mut Bencher) { &recorder, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), &mut LeaderSlotMetricsTracker::new(0), - 10, None, ); }); diff --git a/core/benches/cluster_info.rs b/core/benches/cluster_info.rs index ea12f7d08959ce..2c6df51cca75b5 100644 --- a/core/benches/cluster_info.rs +++ b/core/benches/cluster_info.rs @@ -12,7 +12,7 @@ use { }, solana_gossip::{ cluster_info::{ClusterInfo, Node}, - contact_info::ContactInfo, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_ledger::{ genesis_utils::{create_genesis_config, GenesisConfigInfo}, diff --git a/core/benches/cluster_nodes.rs b/core/benches/cluster_nodes.rs index 10900c0bea54b3..2201a73f2f7adb 100644 --- a/core/benches/cluster_nodes.rs +++ b/core/benches/cluster_nodes.rs @@ -8,7 +8,7 @@ use { cluster_nodes::{make_test_cluster, new_cluster_nodes, ClusterNodes}, retransmit_stage::RetransmitStage, }, - solana_gossip::contact_info::ContactInfo, + solana_gossip::legacy_contact_info::LegacyContactInfo as ContactInfo, solana_ledger::{ genesis_utils::{create_genesis_config, GenesisConfigInfo}, shred::{Shred, ShredFlags}, @@ -36,7 +36,7 @@ fn get_retransmit_peers_deterministic( root_bank: &Bank, num_simulated_shreds: usize, ) { - let parent_offset = if slot == 0 { 0 } else { 1 }; + let parent_offset = u16::from(slot != 0); for i in 0..num_simulated_shreds { let index = i as u32; let shred = Shred::new_from_data( @@ -49,7 +49,7 @@ fn get_retransmit_peers_deterministic( 0, 0, ); - let (_root_distance, _neighbors, _children) = cluster_nodes.get_retransmit_peers( + let _retransmit_peers = cluster_nodes.get_retransmit_peers( slot_leader, &shred.id(), root_bank, diff --git a/core/benches/retransmit_stage.rs b/core/benches/retransmit_stage.rs index 1b460ea03a522e..ff647525dfc2d9 100644 --- a/core/benches/retransmit_stage.rs +++ b/core/benches/retransmit_stage.rs @@ -10,12 +10,12 @@ use { solana_entry::entry::Entry, solana_gossip::{ cluster_info::{ClusterInfo, Node}, - contact_info::ContactInfo, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_ledger::{ genesis_utils::{create_genesis_config, GenesisConfigInfo}, leader_schedule_cache::LeaderScheduleCache, - shred::{ProcessShredsStats, Shredder}, + shred::{ProcessShredsStats, ReedSolomonCache, Shredder}, }, solana_measure::measure::Measure, solana_runtime::{bank::Bank, bank_forks::BankForks}, @@ -106,6 +106,8 @@ fn bench_retransmitter(bencher: &mut Bencher) { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); diff --git a/core/benches/shredder.rs b/core/benches/shredder.rs index 68c3243efd1512..fa98d4049ef1c0 100644 --- a/core/benches/shredder.rs +++ b/core/benches/shredder.rs @@ -8,8 +8,8 @@ use { raptorq::{Decoder, Encoder}, solana_entry::entry::{create_ticks, Entry}, solana_ledger::shred::{ - max_entries_per_n_shred, max_ticks_per_n_shreds, ProcessShredsStats, Shred, ShredFlags, - Shredder, LEGACY_SHRED_DATA_CAPACITY, MAX_DATA_SHREDS_PER_FEC_BLOCK, + max_entries_per_n_shred, max_ticks_per_n_shreds, ProcessShredsStats, ReedSolomonCache, + Shred, ShredFlags, Shredder, DATA_SHREDS_PER_FEC_BLOCK, LEGACY_SHRED_DATA_CAPACITY, }, solana_perf::test_tx, solana_sdk::{hash::Hash, packet::PACKET_DATA_SIZE, signature::Keypair}, @@ -49,9 +49,11 @@ fn make_shreds(num_shreds: usize) -> Vec { let (data_shreds, _) = shredder.entries_to_shreds( &Keypair::new(), &entries, - true, // is_last_in_slot - 0, // next_shred_index - 0, // next_code_index + true, // is_last_in_slot + 0, // next_shred_index + 0, // next_code_index + false, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); assert!(data_shreds.len() >= num_shreds); @@ -77,6 +79,7 @@ fn bench_shredder_ticks(bencher: &mut Bencher) { // ~1Mb let num_ticks = max_ticks_per_n_shreds(1, Some(LEGACY_SHRED_DATA_CAPACITY)) * num_shreds as u64; let entries = create_ticks(num_ticks, 0, Hash::default()); + let reed_solomon_cache = ReedSolomonCache::default(); bencher.iter(|| { let shredder = Shredder::new(1, 0, 0, 0).unwrap(); shredder.entries_to_shreds( @@ -85,6 +88,8 @@ fn bench_shredder_ticks(bencher: &mut Bencher) { true, 0, 0, + true, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); }) @@ -102,6 +107,7 @@ fn bench_shredder_large_entries(bencher: &mut Bencher) { Some(shred_size), ); let entries = make_large_unchained_entries(txs_per_entry, num_entries); + let reed_solomon_cache = ReedSolomonCache::default(); // 1Mb bencher.iter(|| { let shredder = Shredder::new(1, 0, 0, 0).unwrap(); @@ -111,6 +117,8 @@ fn bench_shredder_large_entries(bencher: &mut Bencher) { true, 0, 0, + true, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); }) @@ -131,6 +139,8 @@ fn bench_deshredder(bencher: &mut Bencher) { true, 0, 0, + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); bencher.iter(|| { @@ -153,13 +163,14 @@ fn bench_deserialize_hdr(bencher: &mut Bencher) { #[bench] fn bench_shredder_coding(bencher: &mut Bencher) { - let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize; + let symbol_count = DATA_SHREDS_PER_FEC_BLOCK; let data_shreds = make_shreds(symbol_count); + let reed_solomon_cache = ReedSolomonCache::default(); bencher.iter(|| { Shredder::generate_coding_shreds( &data_shreds[..symbol_count], - true, // is_last_in_slot - 0, // next_code_index + 0, // next_code_index + &reed_solomon_cache, ) .len(); }) @@ -167,32 +178,33 @@ fn bench_shredder_coding(bencher: &mut Bencher) { #[bench] fn bench_shredder_decoding(bencher: &mut Bencher) { - let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize; + let symbol_count = DATA_SHREDS_PER_FEC_BLOCK; let data_shreds = make_shreds(symbol_count); + let reed_solomon_cache = ReedSolomonCache::default(); let coding_shreds = Shredder::generate_coding_shreds( &data_shreds[..symbol_count], - true, // is_last_in_slot - 0, // next_code_index + 0, // next_code_index + &reed_solomon_cache, ); bencher.iter(|| { - Shredder::try_recovery(coding_shreds[..].to_vec()).unwrap(); + Shredder::try_recovery(coding_shreds[..].to_vec(), &reed_solomon_cache).unwrap(); }) } #[bench] fn bench_shredder_coding_raptorq(bencher: &mut Bencher) { - let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK; - let data = make_concatenated_shreds(symbol_count as usize); + let symbol_count = DATA_SHREDS_PER_FEC_BLOCK; + let data = make_concatenated_shreds(symbol_count); bencher.iter(|| { let encoder = Encoder::with_defaults(&data, VALID_SHRED_DATA_LEN as u16); - encoder.get_encoded_packets(symbol_count); + encoder.get_encoded_packets(symbol_count as u32); }) } #[bench] fn bench_shredder_decoding_raptorq(bencher: &mut Bencher) { - let symbol_count = MAX_DATA_SHREDS_PER_FEC_BLOCK; - let data = make_concatenated_shreds(symbol_count as usize); + let symbol_count = DATA_SHREDS_PER_FEC_BLOCK; + let data = make_concatenated_shreds(symbol_count); let encoder = Encoder::with_defaults(&data, VALID_SHRED_DATA_LEN as u16); let mut packets = encoder.get_encoded_packets(symbol_count as u32); packets.shuffle(&mut rand::thread_rng()); diff --git a/core/src/accounts_hash_verifier.rs b/core/src/accounts_hash_verifier.rs index ae8f0dbe780aae..70e967f7387dad 100644 --- a/core/src/accounts_hash_verifier.rs +++ b/core/src/accounts_hash_verifier.rs @@ -50,7 +50,7 @@ impl AccountsHashVerifier { let exit = exit.clone(); let cluster_info = cluster_info.clone(); let t_accounts_hash_verifier = Builder::new() - .name("solana-hash-accounts".to_string()) + .name("solAcctHashVer".to_string()) .spawn(move || { let mut hashes = vec![]; loop { @@ -190,6 +190,7 @@ impl AccountsHashVerifier { accounts_package.snapshot_links.path(), accounts_package.slot, &accounts_hash, + None, ); datapoint_info!( "accounts_hash_verifier", @@ -292,7 +293,7 @@ impl AccountsHashVerifier { accounts_hashes.iter().any(|(slot, hash)| { if let Some(reference_hash) = slot_to_hash.get(slot) { if *hash != *reference_hash { - error!("Known validator {} produced conflicting hashes for slot: {} ({} != {})", + error!("Fatal! Exiting! Known validator {} produced conflicting hashes for slot: {} ({} != {})", known_validator, slot, hash, @@ -333,7 +334,10 @@ impl AccountsHashVerifier { mod tests { use { super::*, - solana_gossip::{cluster_info::make_accounts_hashes_message, contact_info::ContactInfo}, + solana_gossip::{ + cluster_info::make_accounts_hashes_message, + legacy_contact_info::LegacyContactInfo as ContactInfo, + }, solana_runtime::{ rent_collector::RentCollector, snapshot_utils::{ArchiveFormat, SnapshotVersion}, diff --git a/core/src/admin_rpc_post_init.rs b/core/src/admin_rpc_post_init.rs new file mode 100644 index 00000000000000..71d88f2b758d45 --- /dev/null +++ b/core/src/admin_rpc_post_init.rs @@ -0,0 +1,13 @@ +use { + solana_gossip::cluster_info::ClusterInfo, + solana_runtime::bank_forks::BankForks, + solana_sdk::pubkey::Pubkey, + std::sync::{Arc, RwLock}, +}; + +#[derive(Clone)] +pub struct AdminRpcRequestMetadataPostInit { + pub cluster_info: Arc, + pub bank_forks: Arc>, + pub vote_account: Pubkey, +} diff --git a/core/src/ancestor_hashes_service.rs b/core/src/ancestor_hashes_service.rs index cc142168cbb260..c9e0d3f001fcb4 100644 --- a/core/src/ancestor_hashes_service.rs +++ b/core/src/ancestor_hashes_service.rs @@ -4,29 +4,34 @@ use { duplicate_repair_status::{DeadSlotAncestorRequestStatus, DuplicateAncestorDecision}, outstanding_requests::OutstandingRequests, packet_threshold::DynamicPacketToProcessThreshold, - repair_response::{self}, repair_service::{DuplicateSlotsResetSender, RepairInfo, RepairStatsGroup}, replay_stage::DUPLICATE_THRESHOLD, result::{Error, Result}, - serve_repair::{AncestorHashesRepairType, ServeRepair}, + serve_repair::{ + AncestorHashesRepairType, AncestorHashesResponse, RepairProtocol, ServeRepair, + }, }, + bincode::serialize, crossbeam_channel::{unbounded, Receiver, Sender}, dashmap::{mapref::entry::Entry::Occupied, DashMap}, - solana_ledger::{blockstore::Blockstore, shred::SIZE_OF_NONCE}, + solana_gossip::{cluster_info::ClusterInfo, ping_pong::Pong}, + solana_ledger::blockstore::Blockstore, solana_perf::{ - packet::{Packet, PacketBatch}, + packet::{deserialize_from_with_limit, Packet, PacketBatch}, recycler::Recycler, }, solana_runtime::bank::Bank, solana_sdk::{ clock::{Slot, SLOT_MS}, pubkey::Pubkey, + signature::Signable, signer::keypair::Keypair, timing::timestamp, }, solana_streamer::streamer::{self, PacketBatchReceiver, StreamerReceiveStats}, std::{ collections::HashSet, + io::{Cursor, Read}, net::UdpSocket, sync::{ atomic::{AtomicBool, Ordering}, @@ -62,27 +67,25 @@ type RetryableSlotsReceiver = Receiver; type OutstandingAncestorHashesRepairs = OutstandingRequests; #[derive(Default)] -pub struct AncestorHashesResponsesStats { - pub total_packets: usize, - pub dropped_packets: usize, - pub invalid_packets: usize, - pub processed: usize, +struct AncestorHashesResponsesStats { + total_packets: usize, + processed: usize, + dropped_packets: usize, + invalid_packets: usize, + ping_count: usize, + ping_err_verify_count: usize, } impl AncestorHashesResponsesStats { fn report(&mut self) { - inc_new_counter_info!( - "ancestor_hashes_responses-total_packets", - self.total_packets - ); - inc_new_counter_info!("ancestor_hashes_responses-processed", self.processed); - inc_new_counter_info!( - "ancestor_hashes_responses-dropped_packets", - self.dropped_packets - ); - inc_new_counter_info!( - "ancestor_hashes_responses-invalid_packets", - self.invalid_packets + datapoint_info!( + "ancestor_hashes_responses", + ("total_packets", self.total_packets, i64), + ("processed", self.processed, i64), + ("dropped_packets", self.dropped_packets, i64), + ("invalid_packets", self.invalid_packets, i64), + ("ping_count", self.ping_count, i64), + ("ping_err_verify_count", self.ping_err_verify_count, i64), ); *self = AncestorHashesResponsesStats::default(); } @@ -174,6 +177,8 @@ impl AncestorHashesService { exit.clone(), repair_info.duplicate_slots_reset_sender.clone(), retryable_slots_sender, + repair_info.cluster_info.clone(), + ancestor_hashes_request_socket.clone(), ); // Generate ancestor requests for dead slots that are repairable @@ -206,14 +211,17 @@ impl AncestorHashesService { exit: Arc, duplicate_slots_reset_sender: DuplicateSlotsResetSender, retryable_slots_sender: RetryableSlotsSender, + cluster_info: Arc, + ancestor_socket: Arc, ) -> JoinHandle<()> { Builder::new() - .name("solana-ancestor-hashes-responses-service".to_string()) + .name("solAncHashesSvc".to_string()) .spawn(move || { let mut last_stats_report = Instant::now(); let mut stats = AncestorHashesResponsesStats::default(); let mut packet_threshold = DynamicPacketToProcessThreshold::default(); loop { + let keypair = cluster_info.keypair().clone(); let result = Self::process_new_packets_from_channel( &ancestor_hashes_request_statuses, &response_receiver, @@ -223,6 +231,8 @@ impl AncestorHashesService { &mut packet_threshold, &duplicate_slots_reset_sender, &retryable_slots_sender, + &keypair, + &ancestor_socket, ); match result { Err(Error::RecvTimeout(_)) | Ok(_) => {} @@ -241,6 +251,7 @@ impl AncestorHashesService { } /// Process messages from the network + #[allow(clippy::too_many_arguments)] fn process_new_packets_from_channel( ancestor_hashes_request_statuses: &DashMap, response_receiver: &PacketBatchReceiver, @@ -250,6 +261,8 @@ impl AncestorHashesService { packet_threshold: &mut DynamicPacketToProcessThreshold, duplicate_slots_reset_sender: &DuplicateSlotsResetSender, retryable_slots_sender: &RetryableSlotsSender, + keypair: &Keypair, + ancestor_socket: &UdpSocket, ) -> Result<()> { let timeout = Duration::new(1, 0); let mut packet_batches = vec![response_receiver.recv_timeout(timeout)?]; @@ -278,6 +291,8 @@ impl AncestorHashesService { blockstore, duplicate_slots_reset_sender, retryable_slots_sender, + keypair, + ancestor_socket, ); } packet_threshold.update(total_packets, timer.elapsed()); @@ -292,6 +307,8 @@ impl AncestorHashesService { blockstore: &Blockstore, duplicate_slots_reset_sender: &DuplicateSlotsResetSender, retryable_slots_sender: &RetryableSlotsSender, + keypair: &Keypair, + ancestor_socket: &UdpSocket, ) { packet_batch.iter().for_each(|packet| { let decision = Self::verify_and_process_ancestor_response( @@ -300,6 +317,8 @@ impl AncestorHashesService { stats, outstanding_requests, blockstore, + keypair, + ancestor_socket, ); if let Some((slot, decision)) = decision { Self::handle_ancestor_request_decision( @@ -321,55 +340,104 @@ impl AncestorHashesService { stats: &mut AncestorHashesResponsesStats, outstanding_requests: &RwLock, blockstore: &Blockstore, + keypair: &Keypair, + ancestor_socket: &UdpSocket, ) -> Option<(Slot, DuplicateAncestorDecision)> { let from_addr = packet.meta.socket_addr(); - let ancestor_hashes_response = packet - .deserialize_slice(..packet.meta.size.saturating_sub(SIZE_OF_NONCE)) - .ok()?; - - // Verify the response - let request_slot = repair_response::nonce(packet).and_then(|nonce| { - outstanding_requests.write().unwrap().register_response( - nonce, - &ancestor_hashes_response, - timestamp(), - // If the response is valid, return the slot the request - // was for - |ancestor_hashes_request| ancestor_hashes_request.0, - ) - }); + let packet_data = match packet.data(..) { + Some(data) => data, + None => { + stats.invalid_packets += 1; + return None; + } + }; + let mut cursor = Cursor::new(packet_data); + let response = match deserialize_from_with_limit(&mut cursor) { + Ok(response) => response, + Err(_) => { + stats.invalid_packets += 1; + return None; + } + }; - if request_slot.is_none() { - stats.invalid_packets += 1; - return None; - } + match response { + AncestorHashesResponse::Hashes(ref hashes) => { + // deserialize trailing nonce + let nonce = match deserialize_from_with_limit(&mut cursor) { + Ok(nonce) => nonce, + Err(_) => { + stats.invalid_packets += 1; + return None; + } + }; - // If was a valid response, there must be a valid `request_slot` - let request_slot = request_slot.unwrap(); - stats.processed += 1; + // verify that packet does not contain extraneous data + if cursor.bytes().next().is_some() { + stats.invalid_packets += 1; + return None; + } - if let Occupied(mut ancestor_hashes_status_ref) = - ancestor_hashes_request_statuses.entry(request_slot) - { - let decision = ancestor_hashes_status_ref.get_mut().add_response( - &from_addr, - ancestor_hashes_response.into_slot_hashes(), - blockstore, - ); - if decision.is_some() { - // Once a request is completed, remove it from the map so that new - // requests for the same slot can be made again if necessary. It's - // important to hold the `write` lock here via - // `ancestor_hashes_status_ref` so that we don't race with deletion + - // insertion from the `t_ancestor_requests` thread, which may - // 1) Remove expired statuses from `ancestor_hashes_request_statuses` - // 2) Insert another new one via `manage_ancestor_requests()`. - // In which case we wouldn't want to delete the newly inserted entry here. - ancestor_hashes_status_ref.remove(); + let request_slot = outstanding_requests.write().unwrap().register_response( + nonce, + &response, + timestamp(), + // If the response is valid, return the slot the request + // was for + |ancestor_hashes_request| ancestor_hashes_request.0, + ); + + if request_slot.is_none() { + stats.invalid_packets += 1; + return None; + } + + // If was a valid response, there must be a valid `request_slot` + let request_slot = request_slot.unwrap(); + stats.processed += 1; + + if let Occupied(mut ancestor_hashes_status_ref) = + ancestor_hashes_request_statuses.entry(request_slot) + { + let decision = ancestor_hashes_status_ref.get_mut().add_response( + &from_addr, + hashes.clone(), + blockstore, + ); + if decision.is_some() { + // Once a request is completed, remove it from the map so that new + // requests for the same slot can be made again if necessary. It's + // important to hold the `write` lock here via + // `ancestor_hashes_status_ref` so that we don't race with deletion + + // insertion from the `t_ancestor_requests` thread, which may + // 1) Remove expired statuses from `ancestor_hashes_request_statuses` + // 2) Insert another new one via `manage_ancestor_requests()`. + // In which case we wouldn't want to delete the newly inserted entry here. + ancestor_hashes_status_ref.remove(); + } + decision.map(|decision| (request_slot, decision)) + } else { + None + } + } + AncestorHashesResponse::Ping(ping) => { + // verify that packet does not contain extraneous data + if cursor.bytes().next().is_some() { + stats.invalid_packets += 1; + return None; + } + if !ping.verify() { + stats.ping_err_verify_count += 1; + return None; + } + stats.ping_count += 1; + if let Ok(pong) = Pong::new(&ping, keypair) { + let pong = RepairProtocol::Pong(pong); + if let Ok(pong_bytes) = serialize(&pong) { + let _ignore = ancestor_socket.send_to(&pong_bytes[..], from_addr); + } + } + None } - decision.map(|decision| (request_slot, decision)) - } else { - None } } @@ -465,7 +533,7 @@ impl AncestorHashesService { // to MAX_ANCESTOR_HASHES_SLOT_REQUESTS_PER_SECOND/second let mut request_throttle = vec![]; Builder::new() - .name("solana-manage-ancestor-requests".to_string()) + .name("solManAncReqs".to_string()) .spawn(move || loop { if exit.load(Ordering::Relaxed) { return; @@ -570,7 +638,6 @@ impl AncestorHashesService { repair_stats, outstanding_requests, identity_keypair, - &root_bank, ) { request_throttle.push(timestamp()); repairable_dead_slot_pool.take(&slot).unwrap(); @@ -646,7 +713,6 @@ impl AncestorHashesService { repair_stats: &mut AncestorRepairRequestsStats, outstanding_requests: &RwLock, identity_keypair: &Keypair, - root_bank: &Bank, ) -> bool { let sampled_validators = serve_repair.repair_request_ancestor_hashes_sample_peers( duplicate_slot, @@ -665,7 +731,6 @@ impl AncestorHashesService { .add_request(AncestorHashesRepairType(duplicate_slot), timestamp()); let request_bytes = serve_repair.ancestor_repair_request_bytes( identity_keypair, - root_bank, pubkey, duplicate_slot, nonce, @@ -695,7 +760,7 @@ mod test { use { super::*, crate::{ - cluster_slot_state_verifier::DuplicateSlotsToRepair, + cluster_slot_state_verifier::{DuplicateSlotsToRepair, PurgeRepairSlotCounter}, repair_service::DuplicateSlotsResetReceiver, replay_stage::{ tests::{replay_blockstore_components, ReplayBlockstoreComponents}, @@ -706,11 +771,14 @@ mod test { }, solana_gossip::{ cluster_info::{ClusterInfo, Node}, - contact_info::ContactInfo, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, - solana_ledger::{blockstore::make_many_slot_entries, get_tmp_ledger_path}, + solana_ledger::{blockstore::make_many_slot_entries, get_tmp_ledger_path, shred::Nonce}, solana_runtime::{accounts_background_service::AbsRequestSender, bank_forks::BankForks}, - solana_sdk::{hash::Hash, signature::Keypair}, + solana_sdk::{ + hash::Hash, + signature::{Keypair, Signer}, + }, solana_streamer::socket::SocketAddrSpace, std::collections::HashMap, trees::tr, @@ -894,10 +962,11 @@ mod test { fn new(slot_to_query: Slot) -> Self { assert!(slot_to_query >= MAX_ANCESTOR_RESPONSES as Slot); let vote_simulator = VoteSimulator::new(3); - let responder_node = Node::new_localhost(); + let keypair = Keypair::new(); + let responder_node = Node::new_localhost_with_pubkey(&keypair.pubkey()); let cluster_info = ClusterInfo::new( responder_node.info.clone(), - Arc::new(Keypair::new()), + Arc::new(keypair), SocketAddrSpace::Unspecified, ); let responder_serve_repair = @@ -979,9 +1048,10 @@ mod test { let ancestor_hashes_request_statuses = Arc::new(DashMap::new()); let ancestor_hashes_request_socket = Arc::new(UdpSocket::bind("0.0.0.0:0").unwrap()); let epoch_schedule = *bank_forks.read().unwrap().root_bank().epoch_schedule(); + let keypair = Keypair::new(); let requester_cluster_info = Arc::new(ClusterInfo::new( - Node::new_localhost().info, - Arc::new(Keypair::new()), + Node::new_localhost_with_pubkey(&keypair.pubkey()).info, + Arc::new(keypair), SocketAddrSpace::Unspecified, )); let requester_serve_repair = @@ -1062,6 +1132,26 @@ mod test { replay_blockstore_components } + fn send_ancestor_repair_request( + requester_serve_repair: &ServeRepair, + requester_cluster_info: &ClusterInfo, + responder_info: &ContactInfo, + ancestor_hashes_request_socket: &UdpSocket, + dead_slot: Slot, + nonce: Nonce, + ) { + let request_bytes = requester_serve_repair.ancestor_repair_request_bytes( + &requester_cluster_info.keypair(), + &responder_info.id, + dead_slot, + nonce, + ); + if let Ok(request_bytes) = request_bytes { + let _ = + ancestor_hashes_request_socket.send_to(&request_bytes, responder_info.serve_repair); + } + } + #[test] fn test_ancestor_hashes_service_initiate_ancestor_hashes_requests_for_duplicate_slot() { let dead_slot = MAX_ANCESTOR_RESPONSES as Slot; @@ -1091,7 +1181,6 @@ mod test { } = ManageAncestorHashesState::new(vote_simulator.bank_forks); let RepairInfo { - bank_forks, cluster_info: requester_cluster_info, cluster_slots, repair_validators, @@ -1108,10 +1197,36 @@ mod test { &mut repair_stats, &outstanding_requests, &requester_cluster_info.keypair(), - &bank_forks.read().unwrap().root_bank(), ); assert!(ancestor_hashes_request_statuses.is_empty()); + // Send a request to generate a ping + send_ancestor_repair_request( + &requester_serve_repair, + &requester_cluster_info, + responder_info, + &ancestor_hashes_request_socket, + dead_slot, + /*nonce*/ 123, + ); + // Should have received valid response + let mut response_packet = response_receiver + .recv_timeout(Duration::from_millis(10_000)) + .unwrap(); + let packet = &mut response_packet[0]; + packet.meta.set_socket_addr(&responder_info.serve_repair); + let decision = AncestorHashesService::verify_and_process_ancestor_response( + packet, + &ancestor_hashes_request_statuses, + &mut AncestorHashesResponsesStats::default(), + &outstanding_requests, + &requester_blockstore, + &requester_cluster_info.keypair(), + &ancestor_hashes_request_socket, + ); + // should have processed a ping packet + assert_eq!(decision, None); + // Add the responder to the eligible list for requests let responder_id = responder_info.id; cluster_slots.insert_node_id(dead_slot, responder_id); @@ -1127,7 +1242,6 @@ mod test { &mut repair_stats, &outstanding_requests, &requester_cluster_info.keypair(), - &bank_forks.read().unwrap().root_bank(), ); assert_eq!(ancestor_hashes_request_statuses.len(), 1); @@ -1145,6 +1259,8 @@ mod test { &mut AncestorHashesResponsesStats::default(), &outstanding_requests, &requester_blockstore, + &requester_cluster_info.keypair(), + &ancestor_hashes_request_socket, ) .unwrap(); @@ -1385,7 +1501,9 @@ mod test { let ManageAncestorHashesState { ancestor_hashes_request_statuses, + ancestor_hashes_request_socket, outstanding_requests, + repair_info, .. } = ManageAncestorHashesState::new(bank_forks); @@ -1402,6 +1520,8 @@ mod test { &mut AncestorHashesResponsesStats::default(), &outstanding_requests, &blockstore, + &repair_info.cluster_info.keypair(), + &ancestor_hashes_request_socket, ) .is_none()); } @@ -1456,6 +1576,33 @@ mod test { cluster_slots.insert_node_id(dead_slot, responder_id); requester_cluster_info.insert_info(responder_info.clone()); + // Send a request to generate a ping + send_ancestor_repair_request( + &requester_serve_repair, + requester_cluster_info, + responder_info, + &ancestor_hashes_request_socket, + dead_slot, + /*nonce*/ 123, + ); + // Should have received valid response + let mut response_packet = response_receiver + .recv_timeout(Duration::from_millis(10_000)) + .unwrap(); + let packet = &mut response_packet[0]; + packet.meta.set_socket_addr(&responder_info.serve_repair); + let decision = AncestorHashesService::verify_and_process_ancestor_response( + packet, + &ancestor_hashes_request_statuses, + &mut AncestorHashesResponsesStats::default(), + &outstanding_requests, + &requester_blockstore, + &requester_cluster_info.keypair(), + &ancestor_hashes_request_socket, + ); + // Should have processed a ping packet + assert_eq!(decision, None); + // Simulate getting duplicate confirmed dead slot ancestor_hashes_replay_update_sender .send(AncestorHashesReplayUpdate::DeadDuplicateConfirmed( @@ -1474,6 +1621,7 @@ mod test { &bank_forks, &requester_blockstore, None, + &mut PurgeRepairSlotCounter::default(), ); // Simulate making a request @@ -1506,6 +1654,8 @@ mod test { &mut AncestorHashesResponsesStats::default(), &outstanding_requests, &requester_blockstore, + &requester_cluster_info.keypair(), + &ancestor_hashes_request_socket, ) .unwrap(); diff --git a/core/src/banking_stage.rs b/core/src/banking_stage.rs index b3cd01f828b8aa..53a8faaff58be5 100644 --- a/core/src/banking_stage.rs +++ b/core/src/banking_stage.rs @@ -1,6 +1,7 @@ //! The `banking_stage` processes Transaction messages. It is intended to be used //! to construct a software pipeline. The stage uses all available CPU cores and //! can do its processing in parallel with signature verification on the GPU. + use { crate::{ forward_packet_batches_by_accounts::ForwardPacketBatchesByAccounts, @@ -8,6 +9,7 @@ use { leader_slot_banking_stage_timing_metrics::{ LeaderExecuteAndCommitTimings, RecordTransactionsTimings, }, + multi_iterator_scanner::{MultiIteratorScanner, ProcessingDecision}, qos_service::QosService, sigverify::SigverifyTracerPacketStats, tracer_packet_stats::TracerPacketStats, @@ -22,8 +24,12 @@ use { min_max_heap::MinMaxHeap, solana_client::{connection_cache::ConnectionCache, tpu_connection::TpuConnection}, solana_entry::entry::hash_transactions, - solana_gossip::{cluster_info::ClusterInfo, contact_info::ContactInfo}, - solana_ledger::blockstore_processor::TransactionStatusSender, + solana_gossip::{ + cluster_info::ClusterInfo, legacy_contact_info::LegacyContactInfo as ContactInfo, + }, + solana_ledger::{ + blockstore_processor::TransactionStatusSender, token_balances::collect_token_balances, + }, solana_measure::{measure, measure::Measure}, solana_metrics::inc_new_counter_info, solana_perf::{ @@ -46,6 +52,7 @@ use { vote_sender_types::ReplayVoteSender, }, solana_sdk::{ + hash::{Hash, hashv}, clock::{ Slot, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE, MAX_TRANSACTION_FORWARDING_DELAY, MAX_TRANSACTION_FORWARDING_DELAY_GPU, @@ -57,12 +64,10 @@ use { transport::TransportError, }, solana_streamer::sendmmsg::batch_send, - solana_transaction_status::token_balances::{ - collect_token_balances, TransactionTokenBalancesSet, - }, + solana_transaction_status::token_balances::TransactionTokenBalancesSet, std::{ cmp, - collections::HashMap, + collections::{HashMap, HashSet}, env, net::{SocketAddr, UdpSocket}, rc::Rc, @@ -89,7 +94,6 @@ const MAX_NUM_TRANSACTIONS_PER_BATCH: usize = 64; const NUM_VOTE_PROCESSING_THREADS: u32 = 2; const MIN_THREADS_BANKING: u32 = 1; const MIN_TOTAL_THREADS: u32 = NUM_VOTE_PROCESSING_THREADS + MIN_THREADS_BANKING; -const UNPROCESSED_BUFFER_STEP_SIZE: usize = 128; const SLOT_BOUNDARY_CHECK_PERIOD: Duration = Duration::from_millis(10); pub type BankingPacketBatch = (Vec, Option); @@ -383,6 +387,16 @@ pub struct FilterForwardingResults { total_forwardable_tracer_packets: usize, } +/// Convenient wrapper for shared-state between banking stage processing and the +/// multi-iterator checking function. +struct ConsumeScannerPayload<'a> { + reached_end_of_slot: bool, + write_accounts: HashSet, + sanitized_transactions: Vec, + buffered_packet_batches: &'a mut UnprocessedPacketBatches, + slot_metrics_tracker: &'a mut LeaderSlotMetricsTracker, +} + impl BankingStage { /// Create the stage using `bank`. Exit when `verified_receiver` is dropped. #[allow(clippy::new_ret_no_self)] @@ -464,7 +478,7 @@ impl BankingStage { let connection_cache = connection_cache.clone(); let bank_forks = bank_forks.clone(); Builder::new() - .name(format!("solana-banking-stage-tx-{}", i)) + .name(format!("solBanknStgTx{:02}", i)) .spawn(move || { Self::process_loop( &verified_receiver, @@ -657,7 +671,6 @@ impl BankingStage { recorder: &TransactionRecorder, qos_service: &QosService, slot_metrics_tracker: &mut LeaderSlotMetricsTracker, - num_packets_to_process_per_iteration: usize, log_messages_bytes_limit: Option, ) { let mut rebuffered_packet_count = 0; @@ -665,126 +678,171 @@ impl BankingStage { let buffered_packets_len = buffered_packet_batches.len(); let mut proc_start = Measure::start("consume_buffered_process"); let mut reached_end_of_slot = false; + let original_capacity = buffered_packet_batches.capacity(); let mut retryable_packets = { - let capacity = buffered_packet_batches.capacity(); std::mem::replace( &mut buffered_packet_batches.packet_priority_queue, - MinMaxHeap::with_capacity(capacity), + MinMaxHeap::new(), ) }; - let retryable_packets: MinMaxHeap> = retryable_packets - .drain_desc() - .chunks(num_packets_to_process_per_iteration) - .into_iter() - .flat_map(|packets_to_process| { - let packets_to_process = packets_to_process.into_iter().collect_vec(); - // TODO: Right now we iterate through buffer and try the highest weighted transaction once - // but we should retry the highest weighted transactions more often. - let (bank_start, poh_recorder_lock_time) = measure!( - poh_recorder.read().unwrap().bank_start(), - "poh_recorder.read", - ); - slot_metrics_tracker.increment_consume_buffered_packets_poh_recorder_lock_us( - poh_recorder_lock_time.as_us(), + + let (bank_start, poh_recorder_lock_time) = measure!( + poh_recorder.read().unwrap().bank_start(), + "poh_recorder.read", + ); + slot_metrics_tracker.increment_consume_buffered_packets_poh_recorder_lock_us( + poh_recorder_lock_time.as_us(), + ); + + let new_retryable_packets: MinMaxHeap<_> = if let Some(BankStart { + working_bank, + bank_creation_time, + }) = bank_start + { + // Collect all packets into a priority-ordered vector + // Use multi-iterator to iterate with non-conflicting batches + let all_packets_to_process = retryable_packets.drain_desc().collect_vec(); + let mut new_retryable_packets = MinMaxHeap::with_capacity(original_capacity); + const SCANNER_BATCH_SIZE: usize = 64; + + let scanner_payload = ConsumeScannerPayload { + reached_end_of_slot, + write_accounts: HashSet::new(), + sanitized_transactions: Vec::with_capacity(SCANNER_BATCH_SIZE), + buffered_packet_batches, + slot_metrics_tracker, + }; + + let should_process_packet = + |packet: &Rc, payload: &mut ConsumeScannerPayload| { + Self::consume_scan_should_process_packet( + &working_bank, + banking_stage_stats, + packet, + payload, + ) + }; + + let mut scanner = MultiIteratorScanner::new( + &all_packets_to_process, + SCANNER_BATCH_SIZE, + scanner_payload, + should_process_packet, + ); + + while let Some((packets_to_process, payload)) = scanner.iterate() { + if payload.reached_end_of_slot { + new_retryable_packets.extend(packets_to_process.iter().map(|p| (*p).clone())); + continue; + } + + let packets_to_process = packets_to_process + .iter() + .map(|p| (*p).clone()) + .collect_vec(); + assert_eq!( + packets_to_process.len(), + payload.sanitized_transactions.len() ); - let packets_to_process_len = packets_to_process.len(); - if let Some(BankStart { - working_bank, - bank_creation_time, - }) = bank_start - { - let (process_transactions_summary, process_packets_transactions_time) = - measure!( - Self::process_packets_transactions( - &working_bank, - &bank_creation_time, - recorder, - packets_to_process.iter().map(|p| &**p), - transaction_status_sender.clone(), - gossip_vote_sender, - banking_stage_stats, - qos_service, - slot_metrics_tracker, - log_messages_bytes_limit - ), - "process_packets_transactions", - ); - slot_metrics_tracker.increment_process_packets_transactions_us( + let (process_transactions_summary, process_packets_transactions_time) = + measure!(Self::process_packets_transactions( + &working_bank, + &bank_creation_time, + recorder, + &payload.sanitized_transactions, + transaction_status_sender.clone(), + gossip_vote_sender, + banking_stage_stats, + qos_service, + payload.slot_metrics_tracker, + log_messages_bytes_limit + )); + payload + .slot_metrics_tracker + .increment_process_packets_transactions_us( process_packets_transactions_time.as_us(), ); - let ProcessTransactionsSummary { - reached_max_poh_height, - retryable_transaction_indexes, - .. - } = process_transactions_summary; + // reset batch locks and transactions for next iterate call + payload.write_accounts.clear(); + payload.sanitized_transactions.clear(); - if reached_max_poh_height - || !Bank::should_bank_still_be_processing_txs( - &bank_creation_time, - max_tx_ingestion_ns, - ) - { - reached_end_of_slot = true; - } + let ProcessTransactionsSummary { + reached_max_poh_height, + retryable_transaction_indexes, + .. + } = process_transactions_summary; - // The difference between all transactions passed to execution and the ones that - // are retryable were the ones that were either: - // 1) Committed into the block - // 2) Dropped without being committed because they had some fatal error (too old, - // duplicate signature, etc.) - // - // Note: This assumes that every packet deserializes into one transaction! - consumed_buffered_packets_count += - packets_to_process_len.saturating_sub(retryable_transaction_indexes.len()); - - // Out of the buffered packets just retried, collect any still unprocessed - // transactions in this batch for forwarding - rebuffered_packet_count += retryable_transaction_indexes.len(); - if let Some(test_fn) = &test_fn { - test_fn(); - } + if reached_max_poh_height + || !Bank::should_bank_still_be_processing_txs( + &bank_creation_time, + max_tx_ingestion_ns, + ) + { + payload.reached_end_of_slot = true; + } - slot_metrics_tracker.increment_retryable_packets_count( - retryable_transaction_indexes.len() as u64, - ); + // The difference between all transactions passed to execution and the ones that + // are retryable were the ones that were either: + // 1) Committed into the block + // 2) Dropped without being committed because they had some fatal error (too old, + // duplicate signature, etc.) + // + // Note: This assumes that every packet deserializes into one transaction! + consumed_buffered_packets_count += packets_to_process + .len() + .saturating_sub(retryable_transaction_indexes.len()); + + // Out of the buffered packets just retried, collect any still unprocessed + // transactions in this batch for forwarding + rebuffered_packet_count += retryable_transaction_indexes.len(); + if let Some(test_fn) = &test_fn { + test_fn(); + } + + payload + .slot_metrics_tracker + .increment_retryable_packets_count(retryable_transaction_indexes.len() as u64); - let result = retryable_transaction_indexes + let batch_result = retryable_transaction_indexes + .iter() + .map(|i| packets_to_process[*i].clone()) + .collect_vec(); + + // Remove the non-retryable packets, packets that were either: + // 1) Successfully processed + // 2) Failed but not retryable + Self::filter_processed_packets( + retryable_transaction_indexes .iter() - .map(|i| packets_to_process[*i].clone()) - .collect_vec(); - - // Remove the non-retryable packets, packets that were either: - // 1) Successfully processed - // 2) Failed but not retryable - Self::filter_processed_packets( - retryable_transaction_indexes - .iter() - .chain(std::iter::once(&packets_to_process.len())), - |start, end| { - for processed_packet in &packets_to_process[start..end] { - buffered_packet_batches - .message_hash_to_transaction - .remove(processed_packet.message_hash()); - } - }, - ); + .chain(std::iter::once(&packets_to_process.len())), + |start, end| { + for processed_packet in &packets_to_process[start..end] { + payload + .buffered_packet_batches + .message_hash_to_transaction + .remove(processed_packet.message_hash()); + } + }, + ); + new_retryable_packets.extend(batch_result); + } - result - } else if reached_end_of_slot { - packets_to_process - } else { - // mark as end-of-slot to avoid aggressively lock poh for the remaining for - // packet batches in buffer - reached_end_of_slot = true; + let ConsumeScannerPayload { + reached_end_of_slot: new_reached_end_of_slot, + .. + } = scanner.finalize(); - packets_to_process - } - }) - .collect(); + reached_end_of_slot = new_reached_end_of_slot; + + new_retryable_packets + } else { + reached_end_of_slot = true; + retryable_packets + }; - buffered_packet_batches.packet_priority_queue = retryable_packets; + buffered_packet_batches.packet_priority_queue = new_retryable_packets; if reached_end_of_slot { slot_metrics_tracker @@ -804,7 +862,8 @@ impl BankingStage { (consumed_buffered_packets_count as f32) / (proc_start.as_s()) ); - // Assert unprocessed queue is still consistent + // Assert unprocessed queue is still consistent and maintains original capacity + assert_eq!(buffered_packet_batches.capacity(), original_capacity); assert_eq!( buffered_packet_batches.packet_priority_queue.len(), buffered_packet_batches.message_hash_to_transaction.len() @@ -820,6 +879,91 @@ impl BankingStage { .fetch_add(consumed_buffered_packets_count, Ordering::Relaxed); } + /// Checks if a packet should be processed for the multi-iterator scanner + /// Returns true if the packet should be processed, false otherwise + #[allow(clippy::too_many_arguments)] + fn consume_scan_should_process_packet( + working_bank: &Bank, + banking_stage_stats: &BankingStageStats, + packet: &ImmutableDeserializedPacket, + payload: &mut ConsumeScannerPayload, + ) -> ProcessingDecision { + // If end of the slot, return should process (quick loop after reached end of slot) + if payload.reached_end_of_slot { + return ProcessingDecision::Now; + } + + // Before sanitization, let's quickly check the static keys (performance optimization) + let message = &packet.transaction().get_message().message; + let static_keys = message.static_account_keys(); + for key in static_keys.iter().enumerate().filter_map(|(idx, key)| { + if message.is_maybe_writable(idx) { + Some(key) + } else { + None + } + }) { + if payload.write_accounts.contains(key) { + return ProcessingDecision::Later; + } + } + + // Try to deserialize the packet + let (maybe_sanitized_transaction, sanitization_time) = measure!( + unprocessed_packet_batches::transaction_from_deserialized_packet( + packet, + &working_bank.feature_set, + working_bank.vote_only_bank(), + working_bank, + ) + ); + + let sanitization_time_us = sanitization_time.as_us(); + payload + .slot_metrics_tracker + .increment_transactions_from_packets_us(sanitization_time_us); + banking_stage_stats + .packet_conversion_elapsed + .fetch_add(sanitization_time_us, Ordering::Relaxed); + + if let Some(sanitized_transaction) = maybe_sanitized_transaction { + let message = sanitized_transaction.message(); + + let conflicts_with_batch = + message.account_keys().iter().enumerate().any(|(idx, key)| { + if message.is_writable(idx) { + payload.write_accounts.contains(key) + } else { + false + } + }); + + if conflicts_with_batch { + ProcessingDecision::Later + } else { + message + .account_keys() + .iter() + .enumerate() + .for_each(|(idx, key)| { + if message.is_writable(idx) { + payload.write_accounts.insert(*key); + } + }); + + payload.sanitized_transactions.push(sanitized_transaction); + ProcessingDecision::Now + } + } else { + // Remove the packet from map + payload + .buffered_packet_batches + .message_hash_to_transaction + .remove(packet.message_hash()); + ProcessingDecision::Never + } + } + fn consume_or_forward_packets( my_pubkey: &Pubkey, leader_pubkey: Option, @@ -930,7 +1074,6 @@ impl BankingStage { recorder, qos_service, slot_metrics_tracker, - UNPROCESSED_BUFFER_STEP_SIZE, log_messages_bytes_limit ), "consume_buffered_packets", @@ -1262,7 +1405,7 @@ impl BankingStage { "collect_balances", ); execute_and_commit_timings.collect_balances_us = collect_balances_time.as_us(); - + let msg_hashes: Vec = batch.sanitized_transactions().iter().map(|txn| *txn.message_hash()).collect(); let (load_and_execute_transactions_output, load_execute_time) = measure!( bank.load_and_execute_transactions( batch, @@ -1304,7 +1447,12 @@ impl BankingStage { .collect(), "execution_results_to_transactions", ); - + let statuses: Vec = execution_results.iter().map(|result| result.was_executed_successfully()).collect(); + let receipts: Vec<(Hash,Hash)> = msg_hashes.into_iter().zip(statuses).map(|rec_data| { + let status_code: u8 = if rec_data.1 {1}else {0}; + let hashed = hashv(&[rec_data.0.as_ref(),status_code.to_be_bytes().as_ref()]); + (rec_data.0,hashed) + }).collect(); let (last_blockhash, lamports_per_signature) = bank.last_blockhash_and_lamports_per_signature(); let (freeze_lock, freeze_lock_time) = measure!(bank.freeze_lock(), "freeze_lock"); @@ -1359,6 +1507,7 @@ impl BankingStage { sanitized_txs, &mut loaded_transactions, execution_results, + receipts, last_blockhash, lamports_per_signature, CommitTransactionCounts { @@ -1647,8 +1796,8 @@ impl BankingStage { let (units, times): (Vec<_>, Vec<_>) = execute_timings .details .per_program_timings - .iter() - .map(|(_program_id, program_timings)| { + .values() + .map(|program_timings| { ( program_timings.accumulated_units, program_timings.accumulated_us, @@ -1805,29 +1954,23 @@ impl BankingStage { mask } - // This function returns a vector containing index of all valid transactions. A valid - // transaction has result Ok() as the value - fn filter_valid_transaction_indexes( - valid_txs: &[TransactionCheckResult], - transaction_indexes: &[usize], - ) -> Vec { + /// This function returns a vector containing index of all valid transactions. A valid + /// transaction has result Ok() as the value + fn filter_valid_transaction_indexes(valid_txs: &[TransactionCheckResult]) -> Vec { valid_txs .iter() .enumerate() .filter_map(|(index, (x, _h))| if x.is_ok() { Some(index) } else { None }) - .map(|x| transaction_indexes[x]) .collect_vec() } /// This function filters pending packets that are still valid /// # Arguments /// * `transactions` - a batch of transactions deserialized from packets - /// * `transaction_to_packet_indexes` - maps each transaction to a packet index /// * `pending_indexes` - identifies which indexes in the `transactions` list are still pending fn filter_pending_packets_from_pending_txs( bank: &Arc, transactions: &[SanitizedTransaction], - transaction_to_packet_indexes: &[usize], pending_indexes: &[usize], ) -> Vec { let filter = @@ -1855,7 +1998,7 @@ impl BankingStage { &mut error_counters, ); - Self::filter_valid_transaction_indexes(&results, transaction_to_packet_indexes) + Self::filter_valid_transaction_indexes(&results) } fn filter_processed_packets<'a, F>( @@ -1882,7 +2025,7 @@ impl BankingStage { bank: &'a Arc, bank_creation_time: &Instant, poh: &'a TransactionRecorder, - deserialized_packets: impl Iterator, + sanitized_transactions: &[SanitizedTransaction], transaction_status_sender: Option, gossip_vote_sender: &'a ReplayVoteSender, banking_stage_stats: &'a BankingStageStats, @@ -1890,39 +2033,12 @@ impl BankingStage { slot_metrics_tracker: &'a mut LeaderSlotMetricsTracker, log_messages_bytes_limit: Option, ) -> ProcessTransactionsSummary { - // Convert packets to transactions - let ((transactions, transaction_to_packet_indexes), packet_conversion_time): ( - (Vec, Vec), - _, - ) = measure!( - deserialized_packets - .enumerate() - .filter_map(|(i, deserialized_packet)| { - unprocessed_packet_batches::transaction_from_deserialized_packet( - deserialized_packet, - &bank.feature_set, - bank.vote_only_bank(), - bank.as_ref(), - ) - .map(|transaction| (transaction, i)) - }) - .unzip(), - "packet_conversion", - ); - - let packet_conversion_us = packet_conversion_time.as_us(); - slot_metrics_tracker.increment_transactions_from_packets_us(packet_conversion_us); - banking_stage_stats - .packet_conversion_elapsed - .fetch_add(packet_conversion_us, Ordering::Relaxed); - inc_new_counter_info!("banking_stage-packet_conversion", 1); - // Process transactions let (mut process_transactions_summary, process_transactions_time) = measure!( Self::process_transactions( bank, bank_creation_time, - &transactions, + sanitized_transactions, poh, transaction_status_sender, gossip_vote_sender, @@ -1953,12 +2069,12 @@ impl BankingStage { let (filtered_retryable_transaction_indexes, filter_retryable_packets_time) = measure!( Self::filter_pending_packets_from_pending_txs( bank, - &transactions, - &transaction_to_packet_indexes, - retryable_transaction_indexes, + sanitized_transactions, + retryable_transaction_indexes ), - "filter_pending_packets_time", + "filter_retryable_packets_time" ); + let filter_retryable_packets_us = filter_retryable_packets_time.as_us(); slot_metrics_tracker .increment_filter_retryable_packets_us(filter_retryable_packets_us as u64); @@ -1998,26 +2114,26 @@ impl BankingStage { packet_count_upperbound: usize, ) -> Result<(Vec, Option), RecvTimeoutError> { let start = Instant::now(); - let mut aggregated_tracer_packet_stats_option: Option = None; - let (mut packet_batches, new_tracer_packet_stats_option) = + let (mut packet_batches, mut aggregated_tracer_packet_stats_option) = verified_receiver.recv_timeout(recv_timeout)?; - if let Some(new_tracer_packet_stats) = &new_tracer_packet_stats_option { - if let Some(aggregated_tracer_packet_stats) = &mut aggregated_tracer_packet_stats_option - { - aggregated_tracer_packet_stats.aggregate(new_tracer_packet_stats); - } else { - aggregated_tracer_packet_stats_option = new_tracer_packet_stats_option; - } - } - let mut num_packets_received: usize = packet_batches.iter().map(|batch| batch.len()).sum(); - while let Ok((packet_batch, _tracer_packet_stats_option)) = verified_receiver.try_recv() { + while let Ok((packet_batch, tracer_packet_stats_option)) = verified_receiver.try_recv() { trace!("got more packet batches in banking stage"); let (packets_received, packet_count_overflowed) = num_packets_received .overflowing_add(packet_batch.iter().map(|batch| batch.len()).sum()); packet_batches.extend(packet_batch); + if let Some(tracer_packet_stats) = &tracer_packet_stats_option { + if let Some(aggregated_tracer_packet_stats) = + &mut aggregated_tracer_packet_stats_option + { + aggregated_tracer_packet_stats.aggregate(tracer_packet_stats); + } else { + aggregated_tracer_packet_stats_option = tracer_packet_stats_option; + } + } + // Spend any leftover receive time budget to greedily receive more packet batches, // until the upperbound of the packet count is reached. if start.elapsed() >= recv_timeout @@ -2201,7 +2317,7 @@ mod tests { crossbeam_channel::{unbounded, Receiver}, solana_address_lookup_table_program::state::{AddressLookupTable, LookupTableMeta}, solana_entry::entry::{next_entry, next_versioned_entry, Entry, EntrySlice}, - solana_gossip::{cluster_info::Node, contact_info::ContactInfo}, + solana_gossip::cluster_info::Node, solana_ledger::{ blockstore::{entries_to_test_shreds, Blockstore}, genesis_utils::{create_genesis_config, GenesisConfigInfo}, @@ -2233,7 +2349,6 @@ mod tests { solana_transaction_status::{TransactionStatusMeta, VersionedTransactionWithStatusMeta}, std::{ borrow::Cow, - collections::HashSet, path::Path, sync::atomic::{AtomicBool, Ordering}, thread::sleep, @@ -2709,33 +2824,27 @@ mod tests { #[test] fn test_bank_filter_valid_transaction_indexes() { assert_eq!( - BankingStage::filter_valid_transaction_indexes( - &[ - (Err(TransactionError::BlockhashNotFound), None), - (Err(TransactionError::BlockhashNotFound), None), - (Ok(()), None), - (Err(TransactionError::BlockhashNotFound), None), - (Ok(()), None), - (Ok(()), None), - ], - &[2, 4, 5, 9, 11, 13] - ), - [5, 11, 13] + BankingStage::filter_valid_transaction_indexes(&[ + (Err(TransactionError::BlockhashNotFound), None), + (Err(TransactionError::BlockhashNotFound), None), + (Ok(()), None), + (Err(TransactionError::BlockhashNotFound), None), + (Ok(()), None), + (Ok(()), None), + ]), + [2, 4, 5] ); assert_eq!( - BankingStage::filter_valid_transaction_indexes( - &[ - (Ok(()), None), - (Err(TransactionError::BlockhashNotFound), None), - (Err(TransactionError::BlockhashNotFound), None), - (Ok(()), None), - (Ok(()), None), - (Ok(()), None), - ], - &[1, 6, 7, 9, 31, 43] - ), - [1, 9, 31, 43] + BankingStage::filter_valid_transaction_indexes(&[ + (Ok(()), None), + (Err(TransactionError::BlockhashNotFound), None), + (Err(TransactionError::BlockhashNotFound), None), + (Ok(()), None), + (Ok(()), None), + (Ok(()), None), + ]), + [0, 3, 4, 5] ); } @@ -3673,7 +3782,14 @@ mod tests { poh_recorder.write().unwrap().set_bank(&bank, false); - let shreds = entries_to_test_shreds(&entries, bank.slot(), 0, true, 0); + let shreds = entries_to_test_shreds( + &entries, + bank.slot(), + 0, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.set_roots(std::iter::once(&bank.slot())).unwrap(); @@ -3835,7 +3951,14 @@ mod tests { poh_recorder.write().unwrap().set_bank(&bank, false); - let shreds = entries_to_test_shreds(&entries, bank.slot(), 0, true, 0); + let shreds = entries_to_test_shreds( + &entries, + bank.slot(), + 0, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.set_roots(std::iter::once(&bank.slot())).unwrap(); @@ -3881,6 +4004,7 @@ mod tests { post_token_balances: Some(vec![]), rewards: Some(vec![]), loaded_addresses: sanitized_tx.get_loaded_addresses(), + compute_units_consumed: Some(0), ..TransactionStatusMeta::default() } ); @@ -3984,36 +4108,27 @@ mod tests { &recorder, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), &mut LeaderSlotMetricsTracker::new(0), - num_conflicting_transactions, None, ); assert_eq!(buffered_packet_batches.len(), num_conflicting_transactions); - // When the poh recorder has a bank, should process all non conflicting buffered packets. - // Processes one packet per iteration of the loop - let num_packets_to_process_per_iteration = num_conflicting_transactions; - for num_expected_unprocessed in (0..num_conflicting_transactions).rev() { - poh_recorder.write().unwrap().set_bank(&bank, false); - BankingStage::consume_buffered_packets( - &Pubkey::default(), - max_tx_processing_ns, - &poh_recorder, - &mut buffered_packet_batches, - None, - &gossip_vote_sender, - None::>, - &BankingStageStats::default(), - &recorder, - &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), - &mut LeaderSlotMetricsTracker::new(0), - num_packets_to_process_per_iteration, - None, - ); - if num_expected_unprocessed == 0 { - assert!(buffered_packet_batches.is_empty()) - } else { - assert_eq!(buffered_packet_batches.len(), num_expected_unprocessed); - } - } + // When the working bank in poh_recorder is Some, all packets should be processed. + // Multi-Iterator will process them 1-by-1 if all txs are conflicting. + poh_recorder.write().unwrap().set_bank(&bank, false); + BankingStage::consume_buffered_packets( + &Pubkey::default(), + max_tx_processing_ns, + &poh_recorder, + &mut buffered_packet_batches, + None, + &gossip_vote_sender, + None::>, + &BankingStageStats::default(), + &recorder, + &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), + &mut LeaderSlotMetricsTracker::new(0), + None, + ); + assert!(buffered_packet_batches.is_empty()); poh_recorder .read() .unwrap() @@ -4037,11 +4152,8 @@ mod tests { finished_packet_sender.send(()).unwrap(); continue_receiver.recv().unwrap(); }); - // When the poh recorder has a bank, it should process all non conflicting buffered packets. - // Because each conflicting transaction is in it's own `Packet` within a `PacketBatch`, then - // each iteration of this loop will process one element of the batch per iteration of the - // loop. - let interrupted_iteration = 1; + // When the poh recorder has a bank, it should process all buffered packets. + let num_conflicting_transactions = transactions.len(); poh_recorder.write().unwrap().set_bank(&bank, false); let poh_recorder_ = poh_recorder.clone(); let recorder = poh_recorder_.read().unwrap().recorder(); @@ -4057,16 +4169,11 @@ mod tests { ) .unwrap(); assert_eq!(deserialized_packets.len(), num_conflicting_transactions); - let num_packets_to_process_per_iteration = 1; let mut buffered_packet_batches: UnprocessedPacketBatches = UnprocessedPacketBatches::from_iter( - deserialized_packets.clone().into_iter(), + deserialized_packets.into_iter(), num_conflicting_transactions, ); - let all_packet_message_hashes: HashSet = buffered_packet_batches - .iter() - .map(|packet| *packet.immutable_section().message_hash()) - .collect(); BankingStage::consume_buffered_packets( &Pubkey::default(), std::u128::MAX, @@ -4079,26 +4186,19 @@ mod tests { &recorder, &QosService::new(Arc::new(RwLock::new(CostModel::default())), 1), &mut LeaderSlotMetricsTracker::new(0), - num_packets_to_process_per_iteration, None, ); - // Check everything is correct. All indexes after `interrupted_iteration` - // should still be unprocessed - assert_eq!( - buffered_packet_batches.len(), - deserialized_packets[interrupted_iteration + 1..].len() - ); - for packet in buffered_packet_batches.iter() { - assert!(all_packet_message_hashes - .contains(packet.immutable_section().message_hash())); - } + // Check everything is correct. All valid packets should be processed. + assert!(buffered_packet_batches.is_empty()); }) .unwrap(); - for i in 0..=interrupted_iteration { + // Should be calling `test_fn` for each non-conflicting batch. + // In this case each batch is of size 1. + for i in 0..num_conflicting_transactions { finished_packet_receiver.recv().unwrap(); - if i == interrupted_iteration { + if i == num_conflicting_transactions { poh_recorder .write() .unwrap() @@ -4119,6 +4219,7 @@ mod tests { } #[test] + #[ignore] fn test_forwarder_budget() { solana_logger::setup(); // Create `PacketBatch` with 1 unprocessed packet @@ -4206,6 +4307,7 @@ mod tests { } #[test] + #[ignore] fn test_handle_forwarding() { solana_logger::setup(); // packets are deserialized upon receiving, failed packets will not be diff --git a/core/src/broadcast_stage.rs b/core/src/broadcast_stage.rs index 18ab25a0b914c9..3609569b7ca85e 100644 --- a/core/src/broadcast_stage.rs +++ b/core/src/broadcast_stage.rs @@ -14,7 +14,10 @@ use { }, crossbeam_channel::{unbounded, Receiver, RecvError, RecvTimeoutError, Sender}, itertools::Itertools, - solana_gossip::cluster_info::{ClusterInfo, ClusterInfoError, DATA_PLANE_FANOUT}, + solana_gossip::{ + cluster_info::{ClusterInfo, ClusterInfoError}, + legacy_contact_info::LegacyContactInfo as ContactInfo, + }, solana_ledger::{blockstore::Blockstore, shred::Shred}, solana_measure::measure::Measure, solana_metrics::{inc_new_counter_error, inc_new_counter_info}, @@ -32,7 +35,6 @@ use { }, std::{ collections::{HashMap, HashSet}, - iter::repeat, net::UdpSocket, sync::{ atomic::{AtomicBool, Ordering}, @@ -253,7 +255,7 @@ impl BroadcastStage { let blockstore = blockstore.clone(); let cluster_info = cluster_info.clone(); Builder::new() - .name("solana-broadcaster".to_string()) + .name("solBroadcast".to_string()) .spawn(move || { let _finalizer = Finalizer::new(exit); Self::run( @@ -275,7 +277,7 @@ impl BroadcastStage { let cluster_info = cluster_info.clone(); let bank_forks = bank_forks.clone(); let t = Builder::new() - .name("solana-broadcaster-transmit".to_string()) + .name("solBroadcastTx".to_string()) .spawn(move || loop { let res = bs_transmit.transmit(&socket_receiver, &cluster_info, &sock, &bank_forks); @@ -293,7 +295,7 @@ impl BroadcastStage { let mut bs_record = broadcast_stage_run.clone(); let btree = blockstore.clone(); let t = Builder::new() - .name("solana-broadcaster-record".to_string()) + .name("solBroadcastRec".to_string()) .spawn(move || loop { let res = bs_record.record(&blockstore_receiver, &btree); let res = Self::handle_error(res, "solana-broadcaster-record"); @@ -306,7 +308,7 @@ impl BroadcastStage { } let retransmit_thread = Builder::new() - .name("solana-broadcaster-retransmit".to_string()) + .name("solBroadcastRtx".to_string()) .spawn(move || loop { if let Some(res) = Self::handle_error( Self::check_retransmit_signals( @@ -379,19 +381,12 @@ fn update_peer_stats( last_datapoint_submit: &AtomicInterval, ) { if last_datapoint_submit.should_update(1000) { - let now = timestamp(); - let num_live_peers = cluster_nodes.num_peers_live(now); - let broadcast_len = cluster_nodes.num_peers() + 1; - datapoint_info!( - "cluster_info-num_nodes", - ("live_count", num_live_peers, i64), - ("broadcast_count", broadcast_len, i64) - ); + cluster_nodes.submit_metrics("cluster_nodes_broadcast", timestamp()); } } -/// broadcast messages from the leader to layer 1 nodes -/// # Remarks +/// Broadcasts shreds from the leader (i.e. this node) to the root of the +/// turbine retransmit tree for each shred. pub fn broadcast_shreds( s: &UdpSocket, shreds: &[Shred], @@ -416,14 +411,10 @@ pub fn broadcast_shreds( let cluster_nodes = cluster_nodes_cache.get(slot, &root_bank, &working_bank, cluster_info); update_peer_stats(&cluster_nodes, last_datapoint_submit); - let root_bank = root_bank.clone(); shreds.flat_map(move |shred| { - repeat(shred.payload()).zip(cluster_nodes.get_broadcast_addrs( - &shred.id(), - &root_bank, - DATA_PLANE_FANOUT, - socket_addr_space, - )) + let node = cluster_nodes.get_broadcast_peer(&shred.id())?; + ContactInfo::is_valid_address(&node.tvu, socket_addr_space) + .then(|| (shred.payload(), node.tvu)) }) }) .collect(); @@ -452,7 +443,7 @@ pub mod test { blockstore::Blockstore, genesis_utils::{create_genesis_config, GenesisConfigInfo}, get_tmp_ledger_path, - shred::{max_ticks_per_n_shreds, ProcessShredsStats, Shredder}, + shred::{max_ticks_per_n_shreds, ProcessShredsStats, ReedSolomonCache, Shredder}, }, solana_runtime::bank::Bank, solana_sdk::{ @@ -490,6 +481,8 @@ pub mod test { true, // is_last_in_slot 0, // next_shred_index, 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); ( diff --git a/core/src/broadcast_stage/broadcast_duplicates_run.rs b/core/src/broadcast_stage/broadcast_duplicates_run.rs index 741be826c44982..29f901c3ad4e66 100644 --- a/core/src/broadcast_stage/broadcast_duplicates_run.rs +++ b/core/src/broadcast_stage/broadcast_duplicates_run.rs @@ -3,8 +3,8 @@ use { crate::cluster_nodes::ClusterNodesCache, itertools::Itertools, solana_entry::entry::Entry, - solana_gossip::cluster_info::DATA_PLANE_FANOUT, - solana_ledger::shred::{ProcessShredsStats, Shredder}, + solana_gossip::legacy_contact_info::LegacyContactInfo as ContactInfo, + solana_ledger::shred::{ProcessShredsStats, ReedSolomonCache, Shredder}, solana_sdk::{ hash::Hash, signature::{Keypair, Signature, Signer}, @@ -36,6 +36,7 @@ pub(super) struct BroadcastDuplicatesRun { cluster_nodes_cache: Arc>, original_last_data_shreds: Arc>>, partition_last_data_shreds: Arc>>, + reed_solomon_cache: Arc, } impl BroadcastDuplicatesRun { @@ -56,6 +57,7 @@ impl BroadcastDuplicatesRun { cluster_nodes_cache, original_last_data_shreds: Arc::>>::default(), partition_last_data_shreds: Arc::>>::default(), + reed_solomon_cache: Arc::::default(), } } } @@ -163,6 +165,8 @@ impl BroadcastRun for BroadcastDuplicatesRun { last_tick_height == bank.max_tick_height() && last_entries.is_none(), self.next_shred_index, self.next_code_index, + false, // merkle_variant + &self.reed_solomon_cache, &mut ProcessShredsStats::default(), ); @@ -178,6 +182,8 @@ impl BroadcastRun for BroadcastDuplicatesRun { true, self.next_shred_index, self.next_code_index, + false, // merkle_variant + &self.reed_solomon_cache, &mut ProcessShredsStats::default(), ); // Don't mark the last shred as last so that validators won't @@ -189,6 +195,8 @@ impl BroadcastRun for BroadcastDuplicatesRun { true, self.next_shred_index, self.next_code_index, + false, // merkle_variant + &self.reed_solomon_cache, &mut ProcessShredsStats::default(), ); let sigs: Vec<_> = partition_last_data_shred @@ -270,12 +278,6 @@ impl BroadcastRun for BroadcastDuplicatesRun { (bank_forks.root_bank(), bank_forks.working_bank()) }; let self_pubkey = cluster_info.id(); - let nodes: Vec<_> = cluster_info - .all_peers() - .into_iter() - .map(|(node, _)| node) - .collect(); - // Create cluster partition. let cluster_partition: HashSet = { let mut cumilative_stake = 0; @@ -302,24 +304,15 @@ impl BroadcastRun for BroadcastDuplicatesRun { let packets: Vec<_> = shreds .iter() .filter_map(|shred| { - let addr = cluster_nodes - .get_broadcast_addrs( - &shred.id(), - &root_bank, - DATA_PLANE_FANOUT, - socket_addr_space, - ) - .first() - .copied()?; - let node = nodes.iter().find(|node| node.tvu == addr)?; - if !socket_addr_space.check(&node.tvu) { + let node = cluster_nodes.get_broadcast_peer(&shred.id())?; + if !ContactInfo::is_valid_address(&node.tvu, socket_addr_space) { return None; } if self .original_last_data_shreds .lock() .unwrap() - .remove(&shred.signature()) + .remove(shred.signature()) { if cluster_partition.contains(&node.id) { info!( @@ -334,7 +327,7 @@ impl BroadcastRun for BroadcastDuplicatesRun { .partition_last_data_shreds .lock() .unwrap() - .remove(&shred.signature()) + .remove(shred.signature()) { // If the shred is part of the partition, broadcast it directly to the // partition node. This is to account for cases when the partition stake diff --git a/core/src/broadcast_stage/broadcast_fake_shreds_run.rs b/core/src/broadcast_stage/broadcast_fake_shreds_run.rs index 743cc9b072d6d0..f035abf13b90ca 100644 --- a/core/src/broadcast_stage/broadcast_fake_shreds_run.rs +++ b/core/src/broadcast_stage/broadcast_fake_shreds_run.rs @@ -1,7 +1,7 @@ use { super::*, solana_entry::entry::Entry, - solana_ledger::shred::{ProcessShredsStats, Shredder}, + solana_ledger::shred::{ProcessShredsStats, ReedSolomonCache, Shredder}, solana_sdk::{hash::Hash, signature::Keypair}, }; @@ -11,6 +11,7 @@ pub(super) struct BroadcastFakeShredsRun { partition: usize, shred_version: u16, next_code_index: u32, + reed_solomon_cache: Arc, } impl BroadcastFakeShredsRun { @@ -20,6 +21,7 @@ impl BroadcastFakeShredsRun { partition, shred_version, next_code_index: 0, + reed_solomon_cache: Arc::::default(), } } } @@ -60,6 +62,8 @@ impl BroadcastRun for BroadcastFakeShredsRun { last_tick_height == bank.max_tick_height(), next_shred_index, self.next_code_index, + true, // merkle_variant + &self.reed_solomon_cache, &mut ProcessShredsStats::default(), ); @@ -79,6 +83,8 @@ impl BroadcastRun for BroadcastFakeShredsRun { last_tick_height == bank.max_tick_height(), next_shred_index, self.next_code_index, + true, // merkle_variant + &self.reed_solomon_cache, &mut ProcessShredsStats::default(), ); @@ -134,7 +140,7 @@ impl BroadcastRun for BroadcastFakeShredsRun { if fake == (i <= self.partition) { // Send fake shreds to the first N peers data_shreds.iter().for_each(|b| { - sock.send_to(b.payload(), &peer.tvu_forwards).unwrap(); + sock.send_to(b.payload(), peer.tvu_forwards).unwrap(); }); } }); @@ -153,7 +159,7 @@ impl BroadcastRun for BroadcastFakeShredsRun { mod tests { use { super::*, - solana_gossip::contact_info::ContactInfo, + solana_gossip::legacy_contact_info::LegacyContactInfo as ContactInfo, solana_streamer::socket::SocketAddrSpace, std::net::{IpAddr, Ipv4Addr, SocketAddr}, }; diff --git a/core/src/broadcast_stage/broadcast_utils.rs b/core/src/broadcast_stage/broadcast_utils.rs index fd30cfb3b9546d..f9485d59a9ebd8 100644 --- a/core/src/broadcast_stage/broadcast_utils.rs +++ b/core/src/broadcast_stage/broadcast_utils.rs @@ -1,8 +1,9 @@ use { crate::result::Result, + bincode::serialized_size, crossbeam_channel::Receiver, solana_entry::entry::Entry, - solana_ledger::shred::Shred, + solana_ledger::shred::ShredData, solana_poh::poh_recorder::WorkingBankEntry, solana_runtime::bank::Bank, solana_sdk::clock::Slot, @@ -12,9 +13,12 @@ use { }, }; +const ENTRY_COALESCE_DURATION: Duration = Duration::from_millis(50); + pub(super) struct ReceiveResults { pub entries: Vec, pub time_elapsed: Duration, + pub time_coalesced: Duration, pub bank: Arc, pub last_tick_height: u64, } @@ -25,56 +29,69 @@ pub struct UnfinishedSlotInfo { pub(crate) next_code_index: u32, pub slot: Slot, pub parent: Slot, - // Data shreds buffered to make a batch of size - // MAX_DATA_SHREDS_PER_FEC_BLOCK. - pub(crate) data_shreds_buffer: Vec, } -/// This parameter tunes how many entries are received in one iteration of recv loop -/// This will prevent broadcast stage from consuming more entries, that could have led -/// to delays in shredding, and broadcasting shreds to peer validators -const RECEIVE_ENTRY_COUNT_THRESHOLD: usize = 8; - pub(super) fn recv_slot_entries(receiver: &Receiver) -> Result { + let target_serialized_batch_byte_count: u64 = + 32 * ShredData::capacity(/*merkle_proof_size*/ None).unwrap() as u64; let timer = Duration::new(1, 0); let recv_start = Instant::now(); let (mut bank, (entry, mut last_tick_height)) = receiver.recv_timeout(timer)?; - let mut entries = vec![entry]; - let mut slot = bank.slot(); - let mut max_tick_height = bank.max_tick_height(); - - assert!(last_tick_height <= max_tick_height); - - if last_tick_height != max_tick_height { - while let Ok((try_bank, (entry, tick_height))) = receiver.try_recv() { - // If the bank changed, that implies the previous slot was interrupted and we do not have to - // broadcast its entries. - if try_bank.slot() != slot { - warn!("Broadcast for slot: {} interrupted", bank.slot()); - entries.clear(); - bank = try_bank; - slot = bank.slot(); - max_tick_height = bank.max_tick_height(); - } - last_tick_height = tick_height; - entries.push(entry); - - if entries.len() >= RECEIVE_ENTRY_COUNT_THRESHOLD { - break; - } - - assert!(last_tick_height <= max_tick_height); - if last_tick_height == max_tick_height { - break; - } + assert!(last_tick_height <= bank.max_tick_height()); + + // Drain channel + while last_tick_height != bank.max_tick_height() { + let (try_bank, (entry, tick_height)) = match receiver.try_recv() { + Ok(working_bank_entry) => working_bank_entry, + Err(_) => break, + }; + // If the bank changed, that implies the previous slot was interrupted and we do not have to + // broadcast its entries. + if try_bank.slot() != bank.slot() { + warn!("Broadcast for slot: {} interrupted", bank.slot()); + entries.clear(); + bank = try_bank; + } + last_tick_height = tick_height; + entries.push(entry); + assert!(last_tick_height <= bank.max_tick_height()); + } + + let mut serialized_batch_byte_count = serialized_size(&entries)?; + + // Wait up to `ENTRY_COALESCE_DURATION` to try to coalesce entries into a 32 shred batch + let mut coalesce_start = Instant::now(); + while last_tick_height != bank.max_tick_height() + && serialized_batch_byte_count < target_serialized_batch_byte_count + { + let (try_bank, (entry, tick_height)) = + match receiver.recv_deadline(coalesce_start + ENTRY_COALESCE_DURATION) { + Ok(working_bank_entry) => working_bank_entry, + Err(_) => break, + }; + // If the bank changed, that implies the previous slot was interrupted and we do not have to + // broadcast its entries. + if try_bank.slot() != bank.slot() { + warn!("Broadcast for slot: {} interrupted", bank.slot()); + entries.clear(); + serialized_batch_byte_count = 8; // Vec len + bank = try_bank; + coalesce_start = Instant::now(); } + last_tick_height = tick_height; + let entry_bytes = serialized_size(&entry)?; + serialized_batch_byte_count += entry_bytes; + entries.push(entry); + assert!(last_tick_height <= bank.max_tick_height()); } + let time_coalesced = coalesce_start.elapsed(); let time_elapsed = recv_start.elapsed(); Ok(ReceiveResults { entries, time_elapsed, + time_coalesced, bank, last_tick_height, }) diff --git a/core/src/broadcast_stage/fail_entry_verification_broadcast_run.rs b/core/src/broadcast_stage/fail_entry_verification_broadcast_run.rs index c8fd4acc9e1cfc..e7b899ab0fdbbb 100644 --- a/core/src/broadcast_stage/fail_entry_verification_broadcast_run.rs +++ b/core/src/broadcast_stage/fail_entry_verification_broadcast_run.rs @@ -1,7 +1,7 @@ use { super::*, crate::cluster_nodes::ClusterNodesCache, - solana_ledger::shred::{ProcessShredsStats, Shredder}, + solana_ledger::shred::{ProcessShredsStats, ReedSolomonCache, Shredder}, solana_sdk::{hash::Hash, signature::Keypair}, std::{thread::sleep, time::Duration}, }; @@ -17,6 +17,7 @@ pub(super) struct FailEntryVerificationBroadcastRun { next_shred_index: u32, next_code_index: u32, cluster_nodes_cache: Arc>, + reed_solomon_cache: Arc, } impl FailEntryVerificationBroadcastRun { @@ -32,6 +33,7 @@ impl FailEntryVerificationBroadcastRun { next_shred_index: 0, next_code_index: 0, cluster_nodes_cache, + reed_solomon_cache: Arc::::default(), } } } @@ -91,6 +93,8 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun { last_tick_height == bank.max_tick_height() && last_entries.is_none(), self.next_shred_index, self.next_code_index, + true, // merkle_variant + &self.reed_solomon_cache, &mut ProcessShredsStats::default(), ); @@ -105,6 +109,8 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun { true, self.next_shred_index, self.next_code_index, + true, // merkle_variant + &self.reed_solomon_cache, &mut ProcessShredsStats::default(), ); // Don't mark the last shred as last so that validators won't know @@ -116,6 +122,8 @@ impl BroadcastRun for FailEntryVerificationBroadcastRun { false, self.next_shred_index, self.next_code_index, + true, // merkle_variant + &self.reed_solomon_cache, &mut ProcessShredsStats::default(), ); self.next_shred_index += 1; diff --git a/core/src/broadcast_stage/standard_broadcast_run.rs b/core/src/broadcast_stage/standard_broadcast_run.rs index 3acb1dcc5784ac..fceaa86d8bd97c 100644 --- a/core/src/broadcast_stage/standard_broadcast_run.rs +++ b/core/src/broadcast_stage/standard_broadcast_run.rs @@ -9,10 +9,12 @@ use { broadcast_stage::broadcast_utils::UnfinishedSlotInfo, cluster_nodes::ClusterNodesCache, }, solana_entry::entry::Entry, - solana_ledger::shred::{ - ProcessShredsStats, Shred, ShredFlags, Shredder, MAX_DATA_SHREDS_PER_FEC_BLOCK, + solana_ledger::{ + blockstore, + shred::{shred_code, ProcessShredsStats, ReedSolomonCache, Shred, ShredFlags, Shredder}, }, solana_sdk::{ + genesis_config::ClusterType, signature::Keypair, timing::{duration_as_us, AtomicInterval}, }, @@ -31,6 +33,12 @@ pub struct StandardBroadcastRun { last_datapoint_submit: Arc, num_batches: usize, cluster_nodes_cache: Arc>, + reed_solomon_cache: Arc, +} + +#[derive(Debug)] +enum BroadcastError { + TooManyShreds, } impl StandardBroadcastRun { @@ -50,6 +58,7 @@ impl StandardBroadcastRun { last_datapoint_submit: Arc::default(), num_batches: 0, cluster_nodes_cache, + reed_solomon_cache: Arc::::default(), } } @@ -60,6 +69,7 @@ impl StandardBroadcastRun { &mut self, keypair: &Keypair, max_ticks_in_slot: u8, + cluster_type: ClusterType, stats: &mut ProcessShredsStats, ) -> Vec { const SHRED_TICK_REFERENCE_MASK: u8 = ShredFlags::SHRED_TICK_REFERENCE_MASK.bits(); @@ -68,52 +78,55 @@ impl StandardBroadcastRun { None => Vec::default(), Some(ref state) if state.slot == current_slot => Vec::default(), Some(ref mut state) => { - let parent_offset = state.slot - state.parent; let reference_tick = max_ticks_in_slot & SHRED_TICK_REFERENCE_MASK; - let fec_set_offset = state - .data_shreds_buffer - .first() - .map(Shred::index) - .unwrap_or(state.next_shred_index); - let fec_set_index = Shredder::fec_set_index(state.next_shred_index, fec_set_offset); - let mut shred = Shred::new_from_data( - state.slot, - state.next_shred_index, - parent_offset as u16, - &[], // data - ShredFlags::LAST_SHRED_IN_SLOT, - reference_tick, - self.shred_version, - fec_set_index.unwrap(), - ); - shred.sign(keypair); - state.data_shreds_buffer.push(shred.clone()); - let mut shreds = make_coding_shreds( + let shredder = + Shredder::new(state.slot, state.parent, reference_tick, self.shred_version) + .unwrap(); + let merkle_variant = should_use_merkle_variant(state.slot, cluster_type); + let (mut shreds, coding_shreds) = shredder.entries_to_shreds( keypair, - &mut self.unfinished_slot, - true, // is_last_in_slot + &[], // entries + true, // is_last_in_slot, + state.next_shred_index, + state.next_code_index, + merkle_variant, + &self.reed_solomon_cache, stats, ); - shreds.insert(0, shred); + if merkle_variant { + stats.num_merkle_data_shreds += shreds.len(); + stats.num_merkle_coding_shreds += coding_shreds.len(); + } self.report_and_reset_stats(true); self.unfinished_slot = None; + shreds.extend(coding_shreds); shreds } } } - fn entries_to_data_shreds( + #[allow(clippy::too_many_arguments)] + fn entries_to_shreds( &mut self, keypair: &Keypair, entries: &[Entry], blockstore: &Blockstore, reference_tick: u8, is_slot_end: bool, + cluster_type: ClusterType, process_stats: &mut ProcessShredsStats, - ) -> Vec { + max_data_shreds_per_slot: u32, + max_code_shreds_per_slot: u32, + ) -> std::result::Result< + ( + Vec, // data shreds + Vec, // coding shreds + ), + BroadcastError, + > { let (slot, parent_slot) = self.current_slot_and_parent.unwrap(); - let next_shred_index = match &self.unfinished_slot { - Some(state) => state.next_shred_index, + let (next_shred_index, next_code_index) = match &self.unfinished_slot { + Some(state) => (state.next_shred_index, state.next_code_index), None => { // If the blockstore has shreds for the slot, it should not // recreate the slot: @@ -123,46 +136,51 @@ impl StandardBroadcastRun { process_stats.num_extant_slots += 1; // This is a faulty situation that should not happen. // Refrain from generating shreds for the slot. - return Vec::default(); + return Ok((Vec::default(), Vec::default())); } } - 0u32 + (0u32, 0u32) } }; - let data_shreds = Shredder::new(slot, parent_slot, reference_tick, self.shred_version) - .unwrap() - .entries_to_data_shreds( - keypair, - entries, - is_slot_end, - next_shred_index, - 0, // fec_set_offset - process_stats, - ); - let mut data_shreds_buffer = match &mut self.unfinished_slot { - Some(state) => { - assert_eq!(state.slot, slot); - std::mem::take(&mut state.data_shreds_buffer) - } - None => Vec::default(), - }; - data_shreds_buffer.extend(data_shreds.clone()); + let shredder = + Shredder::new(slot, parent_slot, reference_tick, self.shred_version).unwrap(); + let merkle_variant = should_use_merkle_variant(slot, cluster_type); + let (data_shreds, coding_shreds) = shredder.entries_to_shreds( + keypair, + entries, + is_slot_end, + next_shred_index, + next_code_index, + merkle_variant, + &self.reed_solomon_cache, + process_stats, + ); + if merkle_variant { + process_stats.num_merkle_data_shreds += data_shreds.len(); + process_stats.num_merkle_coding_shreds += coding_shreds.len(); + } let next_shred_index = match data_shreds.iter().map(Shred::index).max() { Some(index) => index + 1, None => next_shred_index, }; - let next_code_index = match &self.unfinished_slot { - Some(state) => state.next_code_index, - None => 0, + + if next_shred_index > max_data_shreds_per_slot { + return Err(BroadcastError::TooManyShreds); + } + let next_code_index = match coding_shreds.iter().map(Shred::index).max() { + Some(index) => index + 1, + None => next_code_index, }; + if next_code_index > max_code_shreds_per_slot { + return Err(BroadcastError::TooManyShreds); + } self.unfinished_slot = Some(UnfinishedSlotInfo { next_shred_index, next_code_index, slot, parent: parent_slot, - data_shreds_buffer, }); - data_shreds + Ok((data_shreds, coding_shreds)) } #[cfg(test)] @@ -199,6 +217,7 @@ impl StandardBroadcastRun { receive_results: ReceiveResults, ) -> Result<()> { let mut receive_elapsed = receive_results.time_elapsed; + let mut coalesce_elapsed = receive_results.time_coalesced; let num_entries = receive_results.entries.len(); let bank = receive_results.bank.clone(); let last_tick_height = receive_results.last_tick_height; @@ -215,27 +234,38 @@ impl StandardBroadcastRun { self.current_slot_and_parent = Some((slot, parent_slot)); receive_elapsed = Duration::new(0, 0); + coalesce_elapsed = Duration::new(0, 0); } let mut process_stats = ProcessShredsStats::default(); let mut to_shreds_time = Measure::start("broadcast_to_shreds"); + let cluster_type = bank.cluster_type(); // 1) Check if slot was interrupted - let prev_slot_shreds = - self.finish_prev_slot(keypair, bank.ticks_per_slot() as u8, &mut process_stats); + let prev_slot_shreds = self.finish_prev_slot( + keypair, + bank.ticks_per_slot() as u8, + cluster_type, + &mut process_stats, + ); // 2) Convert entries to shreds and coding shreds let is_last_in_slot = last_tick_height == bank.max_tick_height(); let reference_tick = bank.tick_height() % bank.ticks_per_slot(); - let data_shreds = self.entries_to_data_shreds( - keypair, - &receive_results.entries, - blockstore, - reference_tick as u8, - is_last_in_slot, - &mut process_stats, - ); + let (data_shreds, coding_shreds) = self + .entries_to_shreds( + keypair, + &receive_results.entries, + blockstore, + reference_tick as u8, + is_last_in_slot, + cluster_type, + &mut process_stats, + blockstore::MAX_DATA_SHREDS_PER_SLOT as u32, + shred_code::MAX_CODE_SHREDS_PER_SLOT as u32, + ) + .unwrap(); // Insert the first data shred synchronously so that blockstore stores // that the leader started this block. This must be done before the // blocks are sent out over the wire. By contrast Self::insert skips @@ -300,13 +330,7 @@ impl StandardBroadcastRun { socket_sender.send((data_shreds.clone(), batch_info.clone()))?; blockstore_sender.send((data_shreds, batch_info.clone()))?; - // Create and send coding shreds - let coding_shreds = make_coding_shreds( - keypair, - &mut self.unfinished_slot, - is_last_in_slot, - &mut process_stats, - ); + // Send coding shreds let coding_shreds = Arc::new(coding_shreds); debug_assert!(coding_shreds .iter() @@ -319,6 +343,7 @@ impl StandardBroadcastRun { process_stats.shredding_elapsed = to_shreds_time.as_us(); process_stats.get_leader_schedule_elapsed = get_leader_schedule_time.as_us(); process_stats.receive_elapsed = duration_as_us(&receive_elapsed); + process_stats.coalesce_elapsed = duration_as_us(&coalesce_elapsed); process_stats.coding_send_elapsed = coding_send_time.as_us(); self.process_shreds_stats += process_stats; @@ -418,7 +443,8 @@ impl StandardBroadcastRun { self.process_shreds_stats.submit( "broadcast-process-shreds-interrupted-stats", unfinished_slot.slot, - unfinished_slot.next_shred_index, // num_data_shreds, + unfinished_slot.next_shred_index, // num_data_shreds + unfinished_slot.next_code_index, // num_coding_shreds None, // slot_broadcast_time ); } else { @@ -426,56 +452,14 @@ impl StandardBroadcastRun { self.process_shreds_stats.submit( "broadcast-process-shreds-stats", unfinished_slot.slot, - unfinished_slot.next_shred_index, // num_data_shreds, + unfinished_slot.next_shred_index, // num_data_shreds + unfinished_slot.next_code_index, // num_coding_shreds Some(slot_broadcast_time), ); } } } -// Consumes data_shreds_buffer returning corresponding coding shreds. -fn make_coding_shreds( - keypair: &Keypair, - unfinished_slot: &mut Option, - is_slot_end: bool, - stats: &mut ProcessShredsStats, -) -> Vec { - let unfinished_slot = match unfinished_slot { - None => return Vec::default(), - Some(state) => state, - }; - let data_shreds: Vec<_> = { - let size = unfinished_slot.data_shreds_buffer.len(); - // Consume a multiple of 32, unless this is the slot end. - let offset = if is_slot_end { - 0 - } else { - size % MAX_DATA_SHREDS_PER_FEC_BLOCK as usize - }; - unfinished_slot - .data_shreds_buffer - .drain(0..size - offset) - .collect() - }; - let shreds = Shredder::data_shreds_to_coding_shreds( - keypair, - &data_shreds, - is_slot_end, - unfinished_slot.next_code_index, - stats, - ) - .unwrap(); - if let Some(index) = shreds - .iter() - .filter(|shred| shred.is_code()) - .map(Shred::index) - .max() - { - unfinished_slot.next_code_index = unfinished_slot.next_code_index.max(index + 1); - } - shreds -} - impl BroadcastRun for StandardBroadcastRun { fn run( &mut self, @@ -513,6 +497,13 @@ impl BroadcastRun for StandardBroadcastRun { } } +fn should_use_merkle_variant(slot: Slot, cluster_type: ClusterType) -> bool { + match cluster_type { + ClusterType::Testnet | ClusterType::Devnet | ClusterType::Development => true, + ClusterType::MainnetBeta => (slot % 19) < 10, + } +} + #[cfg(test)] mod test { use { @@ -589,7 +580,6 @@ mod test { next_code_index: 17, slot, parent, - data_shreds_buffer: Vec::default(), }); run.slot_broadcast_start = Some(Instant::now()); @@ -597,7 +587,12 @@ mod test { run.current_slot_and_parent = Some((4, 2)); // Slot 2 interrupted slot 1 - let shreds = run.finish_prev_slot(&keypair, 0, &mut ProcessShredsStats::default()); + let shreds = run.finish_prev_slot( + &keypair, + 0, + ClusterType::Devnet, + &mut ProcessShredsStats::default(), + ); let shred = shreds .get(0) .expect("Expected a shred that signals an interrupt"); @@ -622,6 +617,7 @@ mod test { let receive_results = ReceiveResults { entries: ticks0.clone(), time_elapsed: Duration::new(3, 0), + time_coalesced: Duration::new(2, 0), bank: bank0.clone(), last_tick_height: (ticks0.len() - 1) as u64, }; @@ -690,6 +686,7 @@ mod test { let receive_results = ReceiveResults { entries: ticks1.clone(), time_elapsed: Duration::new(2, 0), + time_coalesced: Duration::new(1, 0), bank: bank2, last_tick_height: (ticks1.len() - 1) as u64, }; @@ -754,6 +751,7 @@ mod test { let receive_results = ReceiveResults { entries: ticks, time_elapsed: Duration::new(1, 0), + time_coalesced: Duration::new(0, 0), bank: bank.clone(), last_tick_height, }; @@ -774,19 +772,15 @@ mod test { while let Ok((recv_shreds, _)) = brecv.recv_timeout(Duration::from_secs(1)) { shreds.extend(recv_shreds.deref().clone()); } - assert!(shreds.len() < 32, "shreds.len(): {}", shreds.len()); - assert!(shreds.iter().all(|shred| shred.is_data())); + // At least as many coding shreds as data shreds. + assert!(shreds.len() >= 29 * 2); + assert_eq!(shreds.iter().filter(|shred| shred.is_data()).count(), 30); process_ticks(75); while let Ok((recv_shreds, _)) = brecv.recv_timeout(Duration::from_secs(1)) { shreds.extend(recv_shreds.deref().clone()); } - assert!(shreds.len() > 64, "shreds.len(): {}", shreds.len()); - let num_coding_shreds = shreds.iter().filter(|shred| shred.is_code()).count(); - assert_eq!( - num_coding_shreds, 32, - "num coding shreds: {}", - num_coding_shreds - ); + assert!(shreds.len() >= 33 * 2); + assert_eq!(shreds.iter().filter(|shred| shred.is_data()).count(), 34); } #[test] @@ -801,6 +795,7 @@ mod test { let receive_results = ReceiveResults { entries: ticks.clone(), time_elapsed: Duration::new(3, 0), + time_coalesced: Duration::new(2, 0), bank: bank0, last_tick_height: ticks.len() as u64, }; @@ -818,4 +813,50 @@ mod test { .unwrap(); assert!(standard_broadcast_run.unfinished_slot.is_none()) } + + #[test] + fn entries_to_shreds_max() { + solana_logger::setup(); + let mut bs = StandardBroadcastRun::new(0); + bs.current_slot_and_parent = Some((1, 0)); + let keypair = Keypair::new(); + let entries = create_ticks(10_000, 1, solana_sdk::hash::Hash::default()); + + let ledger_path = get_tmp_ledger_path!(); + let blockstore = Arc::new( + Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"), + ); + let mut stats = ProcessShredsStats::default(); + + let (data, coding) = bs + .entries_to_shreds( + &keypair, + &entries[0..entries.len() - 2], + &blockstore, + 0, + false, + ClusterType::Development, + &mut stats, + 1000, + 1000, + ) + .unwrap(); + info!("{} {}", data.len(), coding.len()); + assert!(!data.is_empty()); + assert!(!coding.is_empty()); + + let r = bs.entries_to_shreds( + &keypair, + &entries, + &blockstore, + 0, + false, + ClusterType::Development, + &mut stats, + 10, + 10, + ); + info!("{:?}", r); + assert_matches!(r, Err(BroadcastError::TooManyShreds)); + } } diff --git a/core/src/cache_block_meta_service.rs b/core/src/cache_block_meta_service.rs index 98069f253aa66d..a8da9ac0964459 100644 --- a/core/src/cache_block_meta_service.rs +++ b/core/src/cache_block_meta_service.rs @@ -31,7 +31,7 @@ impl CacheBlockMetaService { ) -> Self { let exit = exit.clone(); let thread_hdl = Builder::new() - .name("solana-cache-block-time".to_string()) + .name("solCacheBlkTime".to_string()) .spawn(move || loop { if exit.load(Ordering::Relaxed) { break; diff --git a/core/src/cluster_info_vote_listener.rs b/core/src/cluster_info_vote_listener.rs index ed94724824ff2d..ef06d9806b81f3 100644 --- a/core/src/cluster_info_vote_listener.rs +++ b/core/src/cluster_info_vote_listener.rs @@ -252,7 +252,7 @@ impl ClusterInfoVoteListener { let exit = exit.clone(); let bank_forks = bank_forks.clone(); Builder::new() - .name("solana-cluster_info_vote_listener".to_string()) + .name("solCiVoteLstnr".to_string()) .spawn(move || { let _ = Self::recv_loop( exit, @@ -265,20 +265,22 @@ impl ClusterInfoVoteListener { .unwrap() }; let exit_ = exit.clone(); + let bank_forks_clone = bank_forks.clone(); let bank_send_thread = Builder::new() - .name("solana-cluster_info_bank_send".to_string()) + .name("solCiBankSend".to_string()) .spawn(move || { let _ = Self::bank_send_loop( exit_, verified_vote_label_packets_receiver, poh_recorder, &verified_packets_sender, + bank_forks_clone, ); }) .unwrap(); let send_thread = Builder::new() - .name("solana-cluster_info_process_votes".to_string()) + .name("solCiProcVotes".to_string()) .spawn(move || { let _ = Self::process_votes_loop( exit, @@ -377,6 +379,7 @@ impl ClusterInfoVoteListener { verified_vote_label_packets_receiver: VerifiedLabelVotePacketsReceiver, poh_recorder: Arc>, verified_packets_sender: &BankingPacketSender, + bank_forks: Arc>, ) -> Result<()> { let mut verified_vote_packets = VerifiedVotePackets::default(); let mut time_since_lock = Instant::now(); @@ -391,11 +394,7 @@ impl ClusterInfoVoteListener { .read() .unwrap() .would_be_leader(3 * slot_hashes::MAX_ENTRIES as u64 * DEFAULT_TICKS_PER_SLOT); - let feature_set = poh_recorder - .read() - .unwrap() - .bank() - .map(|bank| bank.feature_set.clone()); + let feature_set = Some(bank_forks.read().unwrap().root_bank().feature_set.clone()); if let Err(e) = verified_vote_packets.receive_and_process_vote_packets( &verified_vote_label_packets_receiver, @@ -1443,9 +1442,11 @@ mod tests { let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, Arc::new(RwLock::new(BlockCommitmentCache::default())), optimistically_confirmed_bank, @@ -1557,9 +1558,11 @@ mod tests { let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, Arc::new(RwLock::new(BlockCommitmentCache::default())), optimistically_confirmed_bank, diff --git a/core/src/cluster_nodes.rs b/core/src/cluster_nodes.rs index f83175a9946f8d..3e13c821c30bdb 100644 --- a/core/src/cluster_nodes.rs +++ b/core/src/cluster_nodes.rs @@ -5,11 +5,11 @@ use { rand::{seq::SliceRandom, Rng, SeedableRng}, rand_chacha::ChaChaRng, solana_gossip::{ - cluster_info::{compute_retransmit_peers, ClusterInfo}, - contact_info::ContactInfo, + cluster_info::{compute_retransmit_peers, ClusterInfo, DATA_PLANE_FANOUT}, crds::GossipRoute, crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS, crds_value::{CrdsData, CrdsValue}, + legacy_contact_info::LegacyContactInfo as ContactInfo, weighted_shuffle::WeightedShuffle, }, solana_ledger::shred::ShredId, @@ -17,6 +17,7 @@ use { solana_sdk::{ clock::{Epoch, Slot}, feature_set, + native_token::LAMPORTS_PER_SOL, pubkey::Pubkey, signature::Keypair, timing::timestamp, @@ -26,15 +27,24 @@ use { any::TypeId, cmp::Reverse, collections::HashMap, - iter::{once, repeat_with}, + iter::repeat_with, marker::PhantomData, net::SocketAddr, ops::Deref, sync::{Arc, Mutex}, time::{Duration, Instant}, }, + thiserror::Error, }; +pub(crate) const MAX_NUM_TURBINE_HOPS: usize = 4; + +#[derive(Debug, Error)] +pub enum Error { + #[error("Loopback from slot leader: {leader}, shred: {shred:?}")] + Loopback { leader: Pubkey, shred: ShredId }, +} + #[allow(clippy::large_enum_variant)] enum NodeId { // TVU node obtained through gossip (staked or not). @@ -68,6 +78,16 @@ pub struct ClusterNodesCache { ttl: Duration, // Time to live. } +pub struct RetransmitPeers<'a> { + root_distance: usize, // distance from the root node + neighbors: Vec<&'a Node>, + children: Vec<&'a Node>, + // Maps from tvu/tvu_forwards addresses to the first node + // in the shuffle with the same address. + addrs: HashMap, // tvu addresses + frwds: HashMap, // tvu_forwards addresses +} + impl Node { #[inline] fn pubkey(&self) -> Pubkey { @@ -87,25 +107,44 @@ impl Node { } impl ClusterNodes { - pub(crate) fn num_peers(&self) -> usize { - self.nodes.len().saturating_sub(1) - } - - // A peer is considered live if they generated their contact info recently. - pub(crate) fn num_peers_live(&self, now: u64) -> usize { - self.nodes - .iter() - .filter(|node| node.pubkey() != self.pubkey) - .filter_map(|node| node.contact_info()) - .filter(|node| { - let elapsed = if node.wallclock < now { - now - node.wallclock - } else { - node.wallclock - now - }; - elapsed < CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS - }) - .count() + pub(crate) fn submit_metrics(&self, name: &'static str, now: u64) { + let mut epoch_stakes = 0; + let mut num_nodes_dead = 0; + let mut num_nodes_staked = 0; + let mut num_nodes_stale = 0; + let mut stake_dead = 0; + let mut stake_stale = 0; + for node in &self.nodes { + epoch_stakes += node.stake; + if node.stake != 0u64 { + num_nodes_staked += 1; + } + match node.contact_info() { + None => { + num_nodes_dead += 1; + stake_dead += node.stake; + } + Some(&ContactInfo { wallclock, .. }) => { + let age = now.saturating_sub(wallclock); + if age > CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS { + num_nodes_stale += 1; + stake_stale += node.stake; + } + } + } + } + num_nodes_stale += num_nodes_dead; + stake_stale += stake_dead; + datapoint_info!( + name, + ("epoch_stakes", epoch_stakes / LAMPORTS_PER_SOL, i64), + ("num_nodes", self.nodes.len(), i64), + ("num_nodes_dead", num_nodes_dead, i64), + ("num_nodes_staked", num_nodes_staked, i64), + ("num_nodes_stale", num_nodes_stale, i64), + ("stake_dead", stake_dead / LAMPORTS_PER_SOL, i64), + ("stake_stale", stake_stale / LAMPORTS_PER_SOL, i64), + ); } } @@ -114,62 +153,11 @@ impl ClusterNodes { new_cluster_nodes(cluster_info, stakes) } - pub(crate) fn get_broadcast_addrs( - &self, - shred: &ShredId, - root_bank: &Bank, - fanout: usize, - socket_addr_space: &SocketAddrSpace, - ) -> Vec { - const MAX_CONTACT_INFO_AGE: Duration = Duration::from_secs(2 * 60); + pub(crate) fn get_broadcast_peer(&self, shred: &ShredId) -> Option<&ContactInfo> { let shred_seed = shred.seed(&self.pubkey); let mut rng = ChaChaRng::from_seed(shred_seed); - let index = match self.weighted_shuffle.first(&mut rng) { - None => return Vec::default(), - Some(index) => index, - }; - if let Some(node) = self.nodes[index].contact_info() { - let now = timestamp(); - let age = Duration::from_millis(now.saturating_sub(node.wallclock)); - if age < MAX_CONTACT_INFO_AGE - && ContactInfo::is_valid_address(&node.tvu, socket_addr_space) - { - return vec![node.tvu]; - } - } - let mut rng = ChaChaRng::from_seed(shred_seed); - let nodes: Vec<&Node> = self - .weighted_shuffle - .clone() - .shuffle(&mut rng) - .map(|index| &self.nodes[index]) - .collect(); - if nodes.is_empty() { - return Vec::default(); - } - if drop_redundant_turbine_path(shred.slot(), root_bank) { - let peers = once(nodes[0]).chain(get_retransmit_peers(fanout, 0, &nodes)); - let addrs = peers.filter_map(Node::contact_info).map(|peer| peer.tvu); - return addrs - .filter(|addr| ContactInfo::is_valid_address(addr, socket_addr_space)) - .collect(); - } - let (neighbors, children) = compute_retransmit_peers(fanout, 0, &nodes); - neighbors[..1] - .iter() - .filter_map(|node| Some(node.contact_info()?.tvu)) - .chain( - neighbors[1..] - .iter() - .filter_map(|node| Some(node.contact_info()?.tvu_forwards)), - ) - .chain( - children - .iter() - .filter_map(|node| Some(node.contact_info()?.tvu)), - ) - .filter(|addr| ContactInfo::is_valid_address(addr, socket_addr_space)) - .collect() + let index = self.weighted_shuffle.first(&mut rng)?; + self.nodes[index].contact_info() } } @@ -180,34 +168,49 @@ impl ClusterNodes { shred: &ShredId, root_bank: &Bank, fanout: usize, - ) -> (/*root_distance:*/ usize, Vec) { - let (root_distance, neighbors, children) = - self.get_retransmit_peers(slot_leader, shred, root_bank, fanout); + ) -> Result<(/*root_distance:*/ usize, Vec), Error> { + let RetransmitPeers { + root_distance, + neighbors, + children, + addrs, + frwds, + } = self.get_retransmit_peers(slot_leader, shred, root_bank, fanout)?; if neighbors.is_empty() { - let peers = children.into_iter().filter_map(Node::contact_info); - let addrs = peers.map(|peer| peer.tvu).collect(); - return (root_distance, addrs); + let peers = children + .into_iter() + .filter_map(Node::contact_info) + .filter(|node| addrs.get(&node.tvu) == Some(&node.id)) + .map(|node| node.tvu) + .collect(); + return Ok((root_distance, peers)); } // If the node is on the critical path (i.e. the first node in each // neighborhood), it should send the packet to tvu socket of its // children and also tvu_forward socket of its neighbors. Otherwise it // should only forward to tvu_forwards socket of its children. if neighbors[0].pubkey() != self.pubkey { - let addrs = children - .iter() - .filter_map(|node| Some(node.contact_info()?.tvu_forwards)); - return (root_distance, addrs.collect()); + let peers = children + .into_iter() + .filter_map(Node::contact_info) + .filter(|node| frwds.get(&node.tvu_forwards) == Some(&node.id)) + .map(|node| node.tvu_forwards); + return Ok((root_distance, peers.collect())); } // First neighbor is this node itself, so skip it. - let addrs = neighbors[1..] + let peers = neighbors[1..] .iter() - .filter_map(|node| Some(node.contact_info()?.tvu_forwards)) + .filter_map(|node| node.contact_info()) + .filter(|node| frwds.get(&node.tvu_forwards) == Some(&node.id)) + .map(|node| node.tvu_forwards) .chain( children - .iter() - .filter_map(|node| Some(node.contact_info()?.tvu)), + .into_iter() + .filter_map(Node::contact_info) + .filter(|node| addrs.get(&node.tvu) == Some(&node.id)) + .map(|node| node.tvu), ); - (root_distance, addrs.collect()) + Ok((root_distance, peers.collect())) } pub fn get_retransmit_peers( @@ -216,51 +219,78 @@ impl ClusterNodes { shred: &ShredId, root_bank: &Bank, fanout: usize, - ) -> ( - usize, // distance from the root node - Vec<&Node>, // neighbors - Vec<&Node>, // children - ) { + ) -> Result { let shred_seed = shred.seed(slot_leader); let mut weighted_shuffle = self.weighted_shuffle.clone(); // Exclude slot leader from list of nodes. if slot_leader == &self.pubkey { - error!("retransmit from slot leader: {}", slot_leader); - } else if let Some(index) = self.index.get(slot_leader) { + return Err(Error::Loopback { + leader: *slot_leader, + shred: *shred, + }); + } + if let Some(index) = self.index.get(slot_leader) { weighted_shuffle.remove_index(*index); - }; + } + let mut addrs = HashMap::::with_capacity(self.nodes.len()); + let mut frwds = HashMap::::with_capacity(self.nodes.len()); let mut rng = ChaChaRng::from_seed(shred_seed); + let drop_redundant_turbine_path = drop_redundant_turbine_path(shred.slot(), root_bank); let nodes: Vec<_> = weighted_shuffle .shuffle(&mut rng) .map(|index| &self.nodes[index]) + .inspect(|node| { + if let Some(node) = node.contact_info() { + addrs.entry(node.tvu).or_insert(node.id); + if !drop_redundant_turbine_path { + frwds.entry(node.tvu_forwards).or_insert(node.id); + } + } + }) .collect(); let self_index = nodes .iter() .position(|node| node.pubkey() == self.pubkey) .unwrap(); - if drop_redundant_turbine_path(shred.slot(), root_bank) { + if drop_redundant_turbine_path { let root_distance = if self_index == 0 { 0 } else if self_index <= fanout { 1 - } else { + } else if self_index <= fanout.saturating_add(1).saturating_mul(fanout) { 2 + } else { + 3 // If changed, update MAX_NUM_TURBINE_HOPS. }; let peers = get_retransmit_peers(fanout, self_index, &nodes); - return (root_distance, Vec::default(), peers.collect()); + return Ok(RetransmitPeers { + root_distance, + neighbors: Vec::default(), + children: peers.collect(), + addrs, + frwds, + }); } let root_distance = if self_index == 0 { 0 } else if self_index < fanout { 1 - } else { + } else if self_index < fanout.saturating_add(1).saturating_mul(fanout) { 2 + } else { + 3 // If changed, update MAX_NUM_TURBINE_HOPS. }; let (neighbors, children) = compute_retransmit_peers(fanout, self_index, &nodes); // Assert that the node itself is included in the set of neighbors, at // the right offset. debug_assert_eq!(neighbors[self_index % fanout].pubkey(), self.pubkey); - (root_distance, neighbors, children) + Ok(RetransmitPeers { + root_distance, + neighbors, + children, + addrs, + frwds, + }) } } @@ -403,7 +433,7 @@ impl ClusterNodesCache { .iter() .find_map(|bank| bank.epoch_staked_nodes(epoch)); if epoch_staked_nodes.is_none() { - inc_new_counter_info!("cluster_nodes-unknown_epoch_staked_nodes", 1); + inc_new_counter_debug!("cluster_nodes-unknown_epoch_staked_nodes", 1); if epoch != root_bank.get_leader_schedule_epoch(root_bank.slot()) { return self.get(root_bank.slot(), root_bank, working_bank, cluster_info); } @@ -467,7 +497,7 @@ pub fn make_test_cluster( let mut gossip_crds = cluster_info.gossip.crds.write().unwrap(); // First node is pushed to crds table by ClusterInfo constructor. for node in nodes.iter().skip(1) { - let node = CrdsData::ContactInfo(node.clone()); + let node = CrdsData::LegacyContactInfo(node.clone()); let node = CrdsValue::new_unsigned(node); assert_eq!( gossip_crds.insert(node, now, GossipRoute::LocalMessage), @@ -478,11 +508,52 @@ pub fn make_test_cluster( (nodes, stakes, cluster_info) } +pub(crate) fn get_data_plane_fanout(shred_slot: Slot, root_bank: &Bank) -> usize { + if enable_turbine_fanout_experiments(shred_slot, root_bank) { + // Allocate ~2% of slots to turbine fanout experiments. + match shred_slot % 359 { + 11 => 64, + 61 => 768, + 111 => 128, + 161 => 640, + 211 => 256, + 261 => 512, + 311 => 384, + _ => DATA_PLANE_FANOUT, + } + } else { + DATA_PLANE_FANOUT + } +} + fn drop_redundant_turbine_path(shred_slot: Slot, root_bank: &Bank) -> bool { - let feature_slot = root_bank - .feature_set - .activated_slot(&feature_set::drop_redundant_turbine_path::id()); - match feature_slot { + check_feature_activation( + &feature_set::drop_redundant_turbine_path::id(), + shred_slot, + root_bank, + ) +} + +fn enable_turbine_fanout_experiments(shred_slot: Slot, root_bank: &Bank) -> bool { + check_feature_activation( + &feature_set::enable_turbine_fanout_experiments::id(), + shred_slot, + root_bank, + ) && !check_feature_activation( + &feature_set::disable_turbine_fanout_experiments::id(), + shred_slot, + root_bank, + ) +} + +// Returns true if the feature is effective for the shred slot. +#[must_use] +pub(crate) fn check_feature_activation( + feature: &Pubkey, + shred_slot: Slot, + root_bank: &Bank, +) -> bool { + match root_bank.feature_set.activated_slot(feature) { None => false, Some(feature_slot) => { let epoch_schedule = root_bank.epoch_schedule(); diff --git a/core/src/cluster_slot_state_verifier.rs b/core/src/cluster_slot_state_verifier.rs index 24828b7568a0ce..a9e5be9d9256d1 100644 --- a/core/src/cluster_slot_state_verifier.rs +++ b/core/src/cluster_slot_state_verifier.rs @@ -11,6 +11,7 @@ use { pub(crate) type DuplicateSlotsTracker = BTreeSet; pub(crate) type DuplicateSlotsToRepair = HashMap; +pub(crate) type PurgeRepairSlotCounter = BTreeMap; pub(crate) type EpochSlotsFrozenSlots = BTreeMap; pub(crate) type GossipDuplicateConfirmedSlots = BTreeMap; @@ -694,6 +695,7 @@ fn apply_state_changes( duplicate_slots_to_repair: &mut DuplicateSlotsToRepair, blockstore: &Blockstore, ancestor_hashes_replay_update_sender: &AncestorHashesReplayUpdateSender, + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, state_changes: Vec, ) { // Handle cases where the bank is frozen, but not duplicate confirmed @@ -728,6 +730,7 @@ fn apply_state_changes( ) .unwrap(); duplicate_slots_to_repair.remove(&slot); + purge_repair_slot_counter.remove(&slot); } ResultingStateChange::SendAncestorHashesReplayUpdate(ancestor_hashes_replay_update) => { let _ = ancestor_hashes_replay_update_sender.send(ancestor_hashes_replay_update); @@ -750,6 +753,7 @@ pub(crate) fn check_slot_agrees_with_cluster( fork_choice: &mut HeaviestSubtreeForkChoice, duplicate_slots_to_repair: &mut DuplicateSlotsToRepair, ancestor_hashes_replay_update_sender: &AncestorHashesReplayUpdateSender, + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, slot_state_update: SlotStateUpdate, ) { info!( @@ -763,11 +767,33 @@ pub(crate) fn check_slot_agrees_with_cluster( // Needs to happen before the bank_frozen_hash.is_none() check below to account for duplicate // signals arriving before the bank is constructed in replay. - if matches!(slot_state_update, SlotStateUpdate::Duplicate(_)) { + if let SlotStateUpdate::Duplicate(ref state) = slot_state_update { // If this slot has already been processed before, return if !duplicate_slots_tracker.insert(slot) { return; } + + datapoint_info!( + "duplicate_slot", + ("slot", slot, i64), + ( + "duplicate_confirmed_hash", + state + .duplicate_confirmed_hash + .unwrap_or_default() + .to_string(), + String + ), + ( + "my_hash", + state + .bank_status + .bank_hash() + .unwrap_or_default() + .to_string(), + String + ), + ); } // Avoid duplicate work from multiple of the same DuplicateConfirmed signal. This can @@ -778,6 +804,25 @@ pub(crate) fn check_slot_agrees_with_cluster( return; } } + + datapoint_info!( + "duplicate_confirmed_slot", + ("slot", slot, i64), + ( + "duplicate_confirmed_hash", + state.duplicate_confirmed_hash.to_string(), + String + ), + ( + "my_hash", + state + .bank_status + .bank_hash() + .unwrap_or_default() + .to_string(), + String + ), + ); } if let SlotStateUpdate::EpochSlotsFrozen(epoch_slots_frozen_state) = &slot_state_update { @@ -798,6 +843,7 @@ pub(crate) fn check_slot_agrees_with_cluster( duplicate_slots_to_repair, blockstore, ancestor_hashes_replay_update_sender, + purge_repair_slot_counter, state_changes, ); } @@ -1355,6 +1401,7 @@ mod test { } = setup(); let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); // MarkSlotDuplicate should mark progress map and remove // the slot from fork choice @@ -1373,6 +1420,7 @@ mod test { &mut duplicate_slots_to_repair, &blockstore, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, vec![ResultingStateChange::MarkSlotDuplicate(duplicate_slot_hash)], ); assert!(!heaviest_subtree_fork_choice @@ -1395,6 +1443,7 @@ mod test { ); } assert!(duplicate_slots_to_repair.is_empty()); + assert!(purge_repair_slot_counter.is_empty()); // Simulate detecting another hash that is the correct version, // RepairDuplicateConfirmedVersion should add the slot to repair @@ -1407,6 +1456,7 @@ mod test { &mut duplicate_slots_to_repair, &blockstore, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, vec![ResultingStateChange::RepairDuplicateConfirmedVersion( correct_hash, )], @@ -1416,6 +1466,7 @@ mod test { *duplicate_slots_to_repair.get(&duplicate_slot).unwrap(), correct_hash ); + assert!(purge_repair_slot_counter.is_empty()); } #[test] @@ -1429,6 +1480,7 @@ mod test { } = setup(); let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); let duplicate_slot = bank_forks.read().unwrap().root() + 1; let duplicate_slot_hash = bank_forks @@ -1449,6 +1501,7 @@ mod test { &mut duplicate_slots_to_repair, &blockstore, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, vec![ResultingStateChange::BankFrozen(duplicate_slot_hash)], ); assert_eq!( @@ -1472,6 +1525,7 @@ mod test { &mut duplicate_slots_to_repair, &blockstore, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, vec![ResultingStateChange::BankFrozen(new_bank_hash)], ); assert_eq!( @@ -1494,6 +1548,7 @@ mod test { } = setup(); let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); let duplicate_slot = bank_forks.read().unwrap().root() + 1; let our_duplicate_slot_hash = bank_forks @@ -1505,6 +1560,7 @@ mod test { // Setup and check the state that is about to change. duplicate_slots_to_repair.insert(duplicate_slot, Hash::new_unique()); + purge_repair_slot_counter.insert(duplicate_slot, 1); assert!(blockstore.get_bank_hash(duplicate_slot).is_none()); assert!(!blockstore.is_duplicate_confirmed(duplicate_slot)); @@ -1512,6 +1568,7 @@ mod test { // 1) Re-enable fork choice // 2) Clear any pending repairs from `duplicate_slots_to_repair` since we have the // right version now + // 3) Clear the slot from `purge_repair_slot_counter` // 3) Set the status to duplicate confirmed in Blockstore let mut state_changes = vec![ResultingStateChange::DuplicateConfirmedSlotMatchesCluster( our_duplicate_slot_hash, @@ -1525,6 +1582,7 @@ mod test { &mut duplicate_slots_to_repair, &blockstore, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, state_changes, ); for child_slot in descendants @@ -1544,6 +1602,7 @@ mod test { .is_candidate(&(duplicate_slot, our_duplicate_slot_hash)) .unwrap()); assert!(duplicate_slots_to_repair.is_empty()); + assert!(purge_repair_slot_counter.is_empty()); assert_eq!( blockstore.get_bank_hash(duplicate_slot).unwrap(), our_duplicate_slot_hash @@ -1586,6 +1645,7 @@ mod test { let gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default(); let mut epoch_slots_frozen_slots = EpochSlotsFrozenSlots::default(); let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); let duplicate_slot = 2; let duplicate_state = DuplicateState::new_from_state( duplicate_slot, @@ -1605,6 +1665,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::Duplicate(duplicate_state), ); assert!(duplicate_slots_tracker.contains(&duplicate_slot)); @@ -1640,6 +1701,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::BankFrozen(bank_frozen_state), ); @@ -1689,6 +1751,7 @@ mod test { ); let root = 0; let mut duplicate_slots_tracker = DuplicateSlotsTracker::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default(); // Mark slot 2 as duplicate confirmed @@ -1710,6 +1773,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut DuplicateSlotsToRepair::default(), &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), ); assert!(heaviest_subtree_fork_choice @@ -1747,6 +1811,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut DuplicateSlotsToRepair::default(), &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::Duplicate(duplicate_state), ); assert!(duplicate_slots_tracker.contains(&3)); @@ -1796,6 +1861,7 @@ mod test { let root = 0; let mut duplicate_slots_tracker = DuplicateSlotsTracker::default(); let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); // Mark 2 as duplicate let slot2_hash = bank_forks.read().unwrap().get(2).unwrap().hash(); @@ -1817,6 +1883,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut DuplicateSlotsToRepair::default(), &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::Duplicate(duplicate_state), ); assert!(duplicate_slots_tracker.contains(&2)); @@ -1852,6 +1919,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut DuplicateSlotsToRepair::default(), &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), ); for slot in 0..=3 { @@ -1913,6 +1981,7 @@ mod test { let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default(); let mut epoch_slots_frozen_slots = EpochSlotsFrozenSlots::default(); let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); // Mark 3 as duplicate confirmed gossip_duplicate_confirmed_slots.insert(3, slot3_hash); @@ -1932,6 +2001,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), ); verify_all_slots_duplicate_confirmed(&bank_forks, &heaviest_subtree_fork_choice, 3, true); @@ -1960,6 +2030,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::Duplicate(duplicate_state), ); assert!(duplicate_slots_tracker.contains(&1)); @@ -1991,6 +2062,7 @@ mod test { let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default(); let mut epoch_slots_frozen_slots = EpochSlotsFrozenSlots::default(); let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); // Mark 3 as only epoch slots frozen, matching our `slot3_hash`, should not duplicate // confirm the slot @@ -2014,6 +2086,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::EpochSlotsFrozen(epoch_slots_frozen_state), ); verify_all_slots_duplicate_confirmed( @@ -2043,6 +2116,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), ); assert_eq!(*epoch_slots_frozen_slots.get(&3).unwrap(), slot3_hash); @@ -2079,6 +2153,7 @@ mod test { let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default(); let mut epoch_slots_frozen_slots = EpochSlotsFrozenSlots::default(); let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); // Mark 3 as only epoch slots frozen with different hash than the our // locally replayed `slot3_hash`. This should not duplicate confirm the slot, @@ -2104,6 +2179,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::EpochSlotsFrozen(epoch_slots_frozen_state), ); assert_eq!(*duplicate_slots_to_repair.get(&3).unwrap(), mismatched_hash); @@ -2134,6 +2210,7 @@ mod test { &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), ); assert!(duplicate_slots_to_repair.is_empty()); diff --git a/core/src/cluster_slots.rs b/core/src/cluster_slots.rs index 6f43a2ac8d2092..be22ed7024fa3a 100644 --- a/core/src/cluster_slots.rs +++ b/core/src/cluster_slots.rs @@ -1,7 +1,8 @@ use { itertools::Itertools, solana_gossip::{ - cluster_info::ClusterInfo, contact_info::ContactInfo, crds::Cursor, epoch_slots::EpochSlots, + cluster_info::ClusterInfo, crds::Cursor, epoch_slots::EpochSlots, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_runtime::{bank::Bank, epoch_stakes::NodeIdToVoteAccounts}, solana_sdk::{ diff --git a/core/src/cluster_slots_service.rs b/core/src/cluster_slots_service.rs index 119f6081cff0e4..f867981f6afb9b 100644 --- a/core/src/cluster_slots_service.rs +++ b/core/src/cluster_slots_service.rs @@ -48,7 +48,7 @@ impl ClusterSlotsService { Self::initialize_lowest_slot(&blockstore, &cluster_info); Self::initialize_epoch_slots(&bank_forks, &cluster_info); let t_cluster_slots_service = Builder::new() - .name("solana-cluster-slots-service".to_string()) + .name("solClusterSlots".to_string()) .spawn(move || { Self::run( blockstore, diff --git a/core/src/commitment_service.rs b/core/src/commitment_service.rs index 8a882e50560ae5..8ef3f29482f0e3 100644 --- a/core/src/commitment_service.rs +++ b/core/src/commitment_service.rs @@ -69,7 +69,7 @@ impl AggregateCommitmentService { sender, Self { t_commitment: Builder::new() - .name("solana-aggregate-stake-lockouts".to_string()) + .name("solAggCommitSvc".to_string()) .spawn(move || loop { if exit_.load(Ordering::Relaxed) { break; diff --git a/core/src/completed_data_sets_service.rs b/core/src/completed_data_sets_service.rs index 08b561b8aca12f..ff11dfa1fb454a 100644 --- a/core/src/completed_data_sets_service.rs +++ b/core/src/completed_data_sets_service.rs @@ -31,7 +31,7 @@ impl CompletedDataSetsService { ) -> Self { let exit = exit.clone(); let thread_hdl = Builder::new() - .name("completed-data-set-service".to_string()) + .name("solComplDataSet".to_string()) .spawn(move || loop { if exit.load(Ordering::Relaxed) { break; diff --git a/core/src/consensus.rs b/core/src/consensus.rs index 37c80014aeaa94..06d0f2a761e6d6 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -169,7 +169,7 @@ impl TowerVersions { } } -#[frozen_abi(digest = "8Y9r3XAwXwmrVGMCyTuy4Kbdotnt1V6N8J6NEniBFD9x")] +#[frozen_abi(digest = "F2LpNgXygZAqWwGUCn8rTWTYGd8LhQS7tT1e5FYDWCDj")] #[derive(Clone, Serialize, Deserialize, Debug, PartialEq, AbiExample)] pub struct Tower { pub node_pubkey: Pubkey, @@ -182,7 +182,8 @@ pub struct Tower { // blockhash of the voted block itself, depending if the vote slot was refreshed. // For instance, a vote for slot 5, may be refreshed/resubmitted for inclusion in // block 10, in which case `last_vote_tx_blockhash` equals the blockhash of 10, not 5. - last_vote_tx_blockhash: Hash, + // For non voting validators this is None + last_vote_tx_blockhash: Option, last_timestamp: BlockTimestamp, #[serde(skip)] // Restored last voted slot which cannot be found in SlotHistory at replayed root @@ -205,7 +206,7 @@ impl Default for Tower { vote_state: VoteState::default(), last_vote: VoteTransaction::from(VoteStateUpdate::default()), last_timestamp: BlockTimestamp::default(), - last_vote_tx_blockhash: Hash::default(), + last_vote_tx_blockhash: None, stray_restored_slot: Option::default(), last_switch_threshold_check: Option::default(), }; @@ -417,12 +418,51 @@ impl Tower { self.vote_state.tower() } - pub fn last_vote_tx_blockhash(&self) -> Hash { + pub fn last_vote_tx_blockhash(&self) -> Option { self.last_vote_tx_blockhash } + pub fn refresh_last_vote_timestamp(&mut self, heaviest_slot_on_same_fork: Slot) { + let timestamp = if let Some(last_vote_timestamp) = self.last_vote.timestamp() { + // To avoid a refreshed vote tx getting caught in deduplication filters, + // we need to update timestamp. Increment by smallest amount to avoid skewing + // the Timestamp Oracle. + last_vote_timestamp.saturating_add(1) + } else { + // If the previous vote did not send a timestamp due to clock error, + // use the last good timestamp + 1 + datapoint_info!( + "refresh-timestamp-missing", + ("heaviest-slot", heaviest_slot_on_same_fork, i64), + ("last-timestamp", self.last_timestamp.timestamp, i64), + ("last-slot", self.last_timestamp.slot, i64), + ); + self.last_timestamp.timestamp.saturating_add(1) + }; + + if let Some(last_voted_slot) = self.last_vote.last_voted_slot() { + if heaviest_slot_on_same_fork <= last_voted_slot { + warn!( + "Trying to refresh timestamp for vote on {last_voted_slot} + using smaller heaviest bank {heaviest_slot_on_same_fork}" + ); + return; + } + self.last_timestamp = BlockTimestamp { + slot: last_voted_slot, + timestamp, + }; + self.last_vote.set_timestamp(Some(timestamp)); + } else { + warn!( + "Trying to refresh timestamp for last vote on heaviest bank on same fork + {heaviest_slot_on_same_fork}, but there is no vote to refresh" + ); + } + } + pub fn refresh_last_vote_tx_blockhash(&mut self, new_vote_tx_blockhash: Hash) { - self.last_vote_tx_blockhash = new_vote_tx_blockhash; + self.last_vote_tx_blockhash = Some(new_vote_tx_blockhash); } // Returns true if we have switched the new vote instruction that directly sets vote state @@ -558,6 +598,13 @@ impl Tower { timestamp, }; return Some(timestamp); + } else { + datapoint_info!( + "backwards-timestamp", + ("slot", current_slot, i64), + ("timestamp", timestamp, i64), + ("last-timestamp", self.last_timestamp.timestamp, i64), + ) } } None @@ -654,285 +701,302 @@ impl Tower { latest_validator_votes_for_frozen_banks: &LatestValidatorVotesForFrozenBanks, heaviest_subtree_fork_choice: &HeaviestSubtreeForkChoice, ) -> SwitchForkDecision { - self.last_voted_slot_hash() - .map(|(last_voted_slot, last_voted_hash)| { - let root = self.root(); - let empty_ancestors = HashSet::default(); - let empty_ancestors_due_to_minor_unsynced_ledger = || { - // This condition (stale stray last vote) shouldn't occur under normal validator - // operation, indicating something unusual happened. - // This condition could be introduced by manual ledger mishandling, - // validator SEGV, OS/HW crash, or plain No Free Space FS error. - - // However, returning empty ancestors as a fallback here shouldn't result in - // slashing by itself (Note that we couldn't fully preclude any kind of slashing if - // the failure was OS or HW level). - - // Firstly, lockout is ensured elsewhere. - - // Also, there is no risk of optimistic conf. violation. Although empty ancestors - // could result in incorrect (= more than actual) locked_out_stake and - // false-positive SwitchProof later in this function, there should be no such a - // heavier fork candidate, first of all, if the last vote (or any of its - // unavailable ancestors) were already optimistically confirmed. - // The only exception is that other validator is already violating it... - if self.is_first_switch_check() && switch_slot < last_voted_slot { - // `switch < last` is needed not to warn! this message just because of using - // newer snapshots on validator restart - let message = format!( + let (last_voted_slot, last_voted_hash) = match self.last_voted_slot_hash() { + None => return SwitchForkDecision::SameFork, + Some(slot_hash) => slot_hash, + }; + let root = self.root(); + let empty_ancestors = HashSet::default(); + let empty_ancestors_due_to_minor_unsynced_ledger = || { + // This condition (stale stray last vote) shouldn't occur under normal validator + // operation, indicating something unusual happened. + // This condition could be introduced by manual ledger mishandling, + // validator SEGV, OS/HW crash, or plain No Free Space FS error. + + // However, returning empty ancestors as a fallback here shouldn't result in + // slashing by itself (Note that we couldn't fully preclude any kind of slashing if + // the failure was OS or HW level). + + // Firstly, lockout is ensured elsewhere. + + // Also, there is no risk of optimistic conf. violation. Although empty ancestors + // could result in incorrect (= more than actual) locked_out_stake and + // false-positive SwitchProof later in this function, there should be no such a + // heavier fork candidate, first of all, if the last vote (or any of its + // unavailable ancestors) were already optimistically confirmed. + // The only exception is that other validator is already violating it... + if self.is_first_switch_check() && switch_slot < last_voted_slot { + // `switch < last` is needed not to warn! this message just because of using + // newer snapshots on validator restart + let message = format!( "bank_forks doesn't have corresponding data for the stray restored \ last vote({}), meaning some inconsistency between saved tower and ledger.", last_voted_slot ); - warn!("{}", message); - datapoint_warn!("tower_warn", ("warn", message, String)); - } - &empty_ancestors - }; + warn!("{}", message); + datapoint_warn!("tower_warn", ("warn", message, String)); + } + &empty_ancestors + }; - let suspended_decision_due_to_major_unsynced_ledger = || { - // This peculiar corner handling is needed mainly for a tower which is newer than - // blockstore. (Yeah, we tolerate it for ease of maintaining validator by operators) - // This condition could be introduced by manual ledger mishandling, - // validator SEGV, OS/HW crash, or plain No Free Space FS error. - - // When we're in this clause, it basically means validator is badly running - // with a future tower while replaying past slots, especially problematic is - // last_voted_slot. - // So, don't re-vote on it by returning pseudo FailedSwitchThreshold, otherwise - // there would be slashing because of double vote on one of last_vote_ancestors. - // (Well, needless to say, re-creating the duplicate block must be handled properly - // at the banking stage: https://github.com/solana-labs/solana/issues/8232) - // - // To be specific, the replay stage is tricked into a false perception where - // last_vote_ancestors is AVAILABLE for descendant-of-`switch_slot`, stale, and - // stray slots (which should always be empty_ancestors). - // - // This is covered by test_future_tower_* in local_cluster - SwitchForkDecision::FailedSwitchThreshold(0, total_stake) - }; + let suspended_decision_due_to_major_unsynced_ledger = || { + // This peculiar corner handling is needed mainly for a tower which is newer than + // blockstore. (Yeah, we tolerate it for ease of maintaining validator by operators) + // This condition could be introduced by manual ledger mishandling, + // validator SEGV, OS/HW crash, or plain No Free Space FS error. + + // When we're in this clause, it basically means validator is badly running + // with a future tower while replaying past slots, especially problematic is + // last_voted_slot. + // So, don't re-vote on it by returning pseudo FailedSwitchThreshold, otherwise + // there would be slashing because of double vote on one of last_vote_ancestors. + // (Well, needless to say, re-creating the duplicate block must be handled properly + // at the banking stage: https://github.com/solana-labs/solana/issues/8232) + // + // To be specific, the replay stage is tricked into a false perception where + // last_vote_ancestors is AVAILABLE for descendant-of-`switch_slot`, stale, and + // stray slots (which should always be empty_ancestors). + // + // This is covered by test_future_tower_* in local_cluster + SwitchForkDecision::FailedSwitchThreshold(0, total_stake) + }; - let rollback_due_to_to_to_duplicate_ancestor = |latest_duplicate_ancestor| { - SwitchForkDecision::FailedSwitchDuplicateRollback(latest_duplicate_ancestor) - }; + let rollback_due_to_to_to_duplicate_ancestor = |latest_duplicate_ancestor| { + SwitchForkDecision::FailedSwitchDuplicateRollback(latest_duplicate_ancestor) + }; - // `heaviest_subtree_fork_choice` entries are not cleaned by duplicate block purging/rollback logic, - // so this is safe to check here. We return here if the last voted slot was rolled back/purged due to - // being a duplicate because `ancestors`/`descendants`/`progress` structurs may be missing this slot due - // to duplicate purging. This would cause many of the `unwrap()` checks below to fail. - // - // TODO: Handle if the last vote is on a dupe, and then we restart. The dupe won't be in - // heaviest_subtree_fork_choice, so `heaviest_subtree_fork_choice.latest_invalid_ancestor()` will return - // None, but the last vote will be persisted in tower. - let switch_hash = progress - .get_hash(switch_slot) - .expect("Slot we're trying to switch to must exist AND be frozen in progress map"); - if let Some(latest_duplicate_ancestor) = heaviest_subtree_fork_choice - .latest_invalid_ancestor(&(last_voted_slot, last_voted_hash)) - { - // We're rolling back because one of the ancestors of the last vote was a duplicate. In this - // case, it's acceptable if the switch candidate is one of ancestors of the previous vote, - // just fail the switch check because there's no point in voting on an ancestor. ReplayStage - // should then have a special case continue building an alternate fork from this ancestor, NOT - // the `last_voted_slot`. This is in contrast to usual SwitchFailure where ReplayStage continues to build blocks - // on latest vote. See `ReplayStage::select_vote_and_reset_forks()` for more details. - if heaviest_subtree_fork_choice.is_strict_ancestor( - &(switch_slot, switch_hash), - &(last_voted_slot, last_voted_hash), - ) { - return rollback_due_to_to_to_duplicate_ancestor(latest_duplicate_ancestor); - } else if progress - .get_hash(last_voted_slot) - .map(|current_slot_hash| current_slot_hash != last_voted_hash) - .unwrap_or(true) - { - // Our last vote slot was purged because it was on a duplicate fork, don't continue below - // where checks may panic. We allow a freebie vote here that may violate switching - // thresholds - // TODO: Properly handle this case - info!( - "Allowing switch vote on {:?} because last vote {:?} was rolled back", - (switch_slot, switch_hash), - (last_voted_slot, last_voted_hash) - ); - return SwitchForkDecision::SwitchProof(Hash::default()); - } - } + // `heaviest_subtree_fork_choice` entries are not cleaned by duplicate block purging/rollback logic, + // so this is safe to check here. We return here if the last voted slot was rolled back/purged due to + // being a duplicate because `ancestors`/`descendants`/`progress` structurs may be missing this slot due + // to duplicate purging. This would cause many of the `unwrap()` checks below to fail. + // + // TODO: Handle if the last vote is on a dupe, and then we restart. The dupe won't be in + // heaviest_subtree_fork_choice, so `heaviest_subtree_fork_choice.latest_invalid_ancestor()` will return + // None, but the last vote will be persisted in tower. + let switch_hash = progress + .get_hash(switch_slot) + .expect("Slot we're trying to switch to must exist AND be frozen in progress map"); + if let Some(latest_duplicate_ancestor) = heaviest_subtree_fork_choice + .latest_invalid_ancestor(&(last_voted_slot, last_voted_hash)) + { + // We're rolling back because one of the ancestors of the last vote was a duplicate. In this + // case, it's acceptable if the switch candidate is one of ancestors of the previous vote, + // just fail the switch check because there's no point in voting on an ancestor. ReplayStage + // should then have a special case continue building an alternate fork from this ancestor, NOT + // the `last_voted_slot`. This is in contrast to usual SwitchFailure where ReplayStage continues to build blocks + // on latest vote. See `ReplayStage::select_vote_and_reset_forks()` for more details. + if heaviest_subtree_fork_choice.is_strict_ancestor( + &(switch_slot, switch_hash), + &(last_voted_slot, last_voted_hash), + ) { + return rollback_due_to_to_to_duplicate_ancestor(latest_duplicate_ancestor); + } else if progress + .get_hash(last_voted_slot) + .map(|current_slot_hash| current_slot_hash != last_voted_hash) + .unwrap_or(true) + { + // Our last vote slot was purged because it was on a duplicate fork, don't continue below + // where checks may panic. We allow a freebie vote here that may violate switching + // thresholds + // TODO: Properly handle this case + info!( + "Allowing switch vote on {:?} because last vote {:?} was rolled back", + (switch_slot, switch_hash), + (last_voted_slot, last_voted_hash) + ); + return SwitchForkDecision::SwitchProof(Hash::default()); + } + } - let last_vote_ancestors = ancestors.get(&last_voted_slot).unwrap_or_else(|| { - if self.is_stray_last_vote() { - // Unless last vote is stray and stale, ancestors.get(last_voted_slot) must - // return Some(_), justifying to panic! here. - // Also, adjust_lockouts_after_replay() correctly makes last_voted_slot None, - // if all saved votes are ancestors of replayed_root_slot. So this code shouldn't be - // touched in that case as well. - // In other words, except being stray, all other slots have been voted on while - // this validator has been running, so we must be able to fetch ancestors for - // all of them. - empty_ancestors_due_to_minor_unsynced_ledger() - } else { - panic!("no ancestors found with slot: {}", last_voted_slot); - } - }); + let last_vote_ancestors = ancestors.get(&last_voted_slot).unwrap_or_else(|| { + if self.is_stray_last_vote() { + // Unless last vote is stray and stale, ancestors.get(last_voted_slot) must + // return Some(_), justifying to panic! here. + // Also, adjust_lockouts_after_replay() correctly makes last_voted_slot None, + // if all saved votes are ancestors of replayed_root_slot. So this code shouldn't be + // touched in that case as well. + // In other words, except being stray, all other slots have been voted on while + // this validator has been running, so we must be able to fetch ancestors for + // all of them. + empty_ancestors_due_to_minor_unsynced_ledger() + } else { + panic!("no ancestors found with slot: {}", last_voted_slot); + } + }); - let switch_slot_ancestors = ancestors.get(&switch_slot).unwrap(); + let switch_slot_ancestors = ancestors.get(&switch_slot).unwrap(); - if switch_slot == last_voted_slot || switch_slot_ancestors.contains(&last_voted_slot) { - // If the `switch_slot is a descendant of the last vote, - // no switching proof is necessary - return SwitchForkDecision::SameFork; - } + if switch_slot == last_voted_slot || switch_slot_ancestors.contains(&last_voted_slot) { + // If the `switch_slot is a descendant of the last vote, + // no switching proof is necessary + return SwitchForkDecision::SameFork; + } - if last_vote_ancestors.contains(&switch_slot) { - if self.is_stray_last_vote() { - return suspended_decision_due_to_major_unsynced_ledger(); - } else { - panic!( + if last_vote_ancestors.contains(&switch_slot) { + if self.is_stray_last_vote() { + return suspended_decision_due_to_major_unsynced_ledger(); + } else { + panic!( "Should never consider switching to ancestor ({}) of last vote: {}, ancestors({:?})", switch_slot, last_voted_slot, last_vote_ancestors, ); - } + } + } + + // By this point, we know the `switch_slot` is on a different fork + // (is neither an ancestor nor descendant of `last_vote`), so a + // switching proof is necessary + let switch_proof = Hash::default(); + let mut locked_out_stake = 0; + let mut locked_out_vote_accounts = HashSet::new(); + for (candidate_slot, descendants) in descendants.iter() { + // 1) Don't consider any banks that haven't been frozen yet + // because the needed stats are unavailable + // 2) Only consider lockouts at the latest `frozen` bank + // on each fork, as that bank will contain all the + // lockout intervals for ancestors on that fork as well. + // 3) Don't consider lockouts on the `last_vote` itself + // 4) Don't consider lockouts on any descendants of + // `last_vote` + // 5) Don't consider any banks before the root because + // all lockouts must be ancestors of `last_vote` + if !progress + .get_fork_stats(*candidate_slot) + .map(|stats| stats.computed) + .unwrap_or(false) + || { + // If any of the descendants have the `computed` flag set, then there must be a more + // recent frozen bank on this fork to use, so we can ignore this one. Otherwise, + // even if this bank has descendants, if they have not yet been frozen / stats computed, + // then use this bank as a representative for the fork. + descendants.iter().any(|d| { + progress + .get_fork_stats(*d) + .map(|stats| stats.computed) + .unwrap_or(false) + }) } + || *candidate_slot == last_voted_slot + || { + // Ignore if the `candidate_slot` is a descendant of the `last_voted_slot`, since we do not + // want to count votes on the same fork. + Self::is_candidate_slot_descendant_of_last_vote( + *candidate_slot, + last_voted_slot, + ancestors, + ) + .expect("exists in descendants map, so must exist in ancestors map") + } + || *candidate_slot <= root + { + continue; + } - // By this point, we know the `switch_slot` is on a different fork - // (is neither an ancestor nor descendant of `last_vote`), so a - // switching proof is necessary - let switch_proof = Hash::default(); - let mut locked_out_stake = 0; - let mut locked_out_vote_accounts = HashSet::new(); - for (candidate_slot, descendants) in descendants.iter() { - // 1) Don't consider any banks that haven't been frozen yet - // because the needed stats are unavailable - // 2) Only consider lockouts at the latest `frozen` bank - // on each fork, as that bank will contain all the - // lockout intervals for ancestors on that fork as well. - // 3) Don't consider lockouts on the `last_vote` itself - // 4) Don't consider lockouts on any descendants of - // `last_vote` - // 5) Don't consider any banks before the root because - // all lockouts must be ancestors of `last_vote` - if !progress.get_fork_stats(*candidate_slot).map(|stats| stats.computed).unwrap_or(false) - // If any of the descendants have the `computed` flag set, then there must be a more - // recent frozen bank on this fork to use, so we can ignore this one. Otherwise, - // even if this bank has descendants, if they have not yet been frozen / stats computed, - // then use this bank as a representative for the fork. - || descendants.iter().any(|d| progress.get_fork_stats(*d).map(|stats| stats.computed).unwrap_or(false)) - || *candidate_slot == last_voted_slot - // Ignore if the `candidate_slot` is a descendant of the `last_voted_slot`, since we do not - // want to count votes on the same fork. - || Self::is_candidate_slot_descendant_of_last_vote(*candidate_slot, last_voted_slot, ancestors).expect("exists in descendants map, so must exist in ancestors map") - || *candidate_slot <= root - { + // By the time we reach here, any ancestors of the `last_vote`, + // should have been filtered out, as they all have a descendant, + // namely the `last_vote` itself. + assert!(!last_vote_ancestors.contains(candidate_slot)); + + // Evaluate which vote accounts in the bank are locked out + // in the interval candidate_slot..last_vote, which means + // finding any lockout intervals in the `lockout_intervals` tree + // for this bank that contain `last_vote`. + let lockout_intervals = &progress + .get(candidate_slot) + .unwrap() + .fork_stats + .lockout_intervals; + // Find any locked out intervals for vote accounts in this bank with + // `lockout_interval_end` >= `last_vote`, which implies they are locked out at + // `last_vote` on another fork. + for (_lockout_interval_end, intervals_keyed_by_end) in + lockout_intervals.range((Included(last_voted_slot), Unbounded)) + { + for (lockout_interval_start, vote_account_pubkey) in intervals_keyed_by_end { + if locked_out_vote_accounts.contains(vote_account_pubkey) { continue; } - // By the time we reach here, any ancestors of the `last_vote`, - // should have been filtered out, as they all have a descendant, - // namely the `last_vote` itself. - assert!(!last_vote_ancestors.contains(candidate_slot)); - - // Evaluate which vote accounts in the bank are locked out - // in the interval candidate_slot..last_vote, which means - // finding any lockout intervals in the `lockout_intervals` tree - // for this bank that contain `last_vote`. - let lockout_intervals = &progress - .get(candidate_slot) - .unwrap() - .fork_stats - .lockout_intervals; - // Find any locked out intervals for vote accounts in this bank with - // `lockout_interval_end` >= `last_vote`, which implies they are locked out at - // `last_vote` on another fork. - for (_lockout_interval_end, intervals_keyed_by_end) in - lockout_intervals.range((Included(last_voted_slot), Unbounded)) - { - for (lockout_interval_start, vote_account_pubkey) in intervals_keyed_by_end { - if locked_out_vote_accounts.contains(vote_account_pubkey) { - continue; - } - - // Only count lockouts on slots that are: - // 1) Not ancestors of `last_vote`, meaning being on different fork - // 2) Not from before the current root as we can't determine if - // anything before the root was an ancestor of `last_vote` or not - if !last_vote_ancestors.contains(lockout_interval_start) - // Given a `lockout_interval_start` < root that appears in a - // bank for a `candidate_slot`, it must be that `lockout_interval_start` - // is an ancestor of the current root, because `candidate_slot` is a - // descendant of the current root - && *lockout_interval_start > root - { - let stake = epoch_vote_accounts - .get(vote_account_pubkey) - .map(|(stake, _)| *stake) - .unwrap_or(0); - locked_out_stake += stake; - if (locked_out_stake as f64 / total_stake as f64) - > SWITCH_FORK_THRESHOLD - { - return SwitchForkDecision::SwitchProof(switch_proof); - } - locked_out_vote_accounts.insert(vote_account_pubkey); - } - } - } + // Only count lockouts on slots that are: + // 1) Not ancestors of `last_vote`, meaning being on different fork + // 2) Not from before the current root as we can't determine if + // anything before the root was an ancestor of `last_vote` or not + if !last_vote_ancestors.contains(lockout_interval_start) && { + // Given a `lockout_interval_start` < root that appears in a + // bank for a `candidate_slot`, it must be that `lockout_interval_start` + // is an ancestor of the current root, because `candidate_slot` is a + // descendant of the current root + *lockout_interval_start > root + } { + let stake = epoch_vote_accounts + .get(vote_account_pubkey) + .map(|(stake, _)| *stake) + .unwrap_or(0); + locked_out_stake += stake; + if (locked_out_stake as f64 / total_stake as f64) > SWITCH_FORK_THRESHOLD { + return SwitchForkDecision::SwitchProof(switch_proof); + } + locked_out_vote_accounts.insert(vote_account_pubkey); + } } + } + } - // Check the latest votes for potentially gossip votes that haven't landed yet - for ( - vote_account_pubkey, - (candidate_latest_frozen_vote, _candidate_latest_frozen_vote_hash), - ) in latest_validator_votes_for_frozen_banks.max_gossip_frozen_votes() - { - if locked_out_vote_accounts.contains(&vote_account_pubkey) { - continue; - } + // Check the latest votes for potentially gossip votes that haven't landed yet + for ( + vote_account_pubkey, + (candidate_latest_frozen_vote, _candidate_latest_frozen_vote_hash), + ) in latest_validator_votes_for_frozen_banks.max_gossip_frozen_votes() + { + if locked_out_vote_accounts.contains(&vote_account_pubkey) { + continue; + } - if *candidate_latest_frozen_vote > last_voted_slot - && - // Because `candidate_latest_frozen_vote` is the last vote made by some validator - // in the cluster for a frozen bank `B` observed through gossip, we may have cleared - // that frozen bank `B` because we `set_root(root)` for a `root` on a different fork, - // like so: - // - // |----------X ------candidate_latest_frozen_vote (frozen) - // old root - // |----------new root ----last_voted_slot - // - // In most cases, because `last_voted_slot` must be a descendant of `root`, then - // if `candidate_latest_frozen_vote` is not found in the ancestors/descendants map (recall these - // directly reflect the state of BankForks), this implies that `B` was pruned from BankForks - // because it was on a different fork than `last_voted_slot`, and thus this vote for `candidate_latest_frozen_vote` - // should be safe to count towards the switching proof: - // - // However, there is also the possibility that `last_voted_slot` is a stray, in which - // case we cannot make this conclusion as we do not know the ancestors/descendants - // of strays. Hence we err on the side of caution here and ignore this vote. This - // is ok because validators voting on different unrooted forks should eventually vote - // on some descendant of the root, at which time they can be included in switching proofs. - !Self::is_candidate_slot_descendant_of_last_vote( - *candidate_latest_frozen_vote, last_voted_slot, ancestors) - .unwrap_or(true) - { - let stake = epoch_vote_accounts - .get(vote_account_pubkey) - .map(|(stake, _)| *stake) - .unwrap_or(0); - locked_out_stake += stake; - if (locked_out_stake as f64 / total_stake as f64) > SWITCH_FORK_THRESHOLD { - return SwitchForkDecision::SwitchProof(switch_proof); - } - locked_out_vote_accounts.insert(vote_account_pubkey); - } + if *candidate_latest_frozen_vote > last_voted_slot && { + // Because `candidate_latest_frozen_vote` is the last vote made by some validator + // in the cluster for a frozen bank `B` observed through gossip, we may have cleared + // that frozen bank `B` because we `set_root(root)` for a `root` on a different fork, + // like so: + // + // |----------X ------candidate_latest_frozen_vote (frozen) + // old root + // |----------new root ----last_voted_slot + // + // In most cases, because `last_voted_slot` must be a descendant of `root`, then + // if `candidate_latest_frozen_vote` is not found in the ancestors/descendants map (recall these + // directly reflect the state of BankForks), this implies that `B` was pruned from BankForks + // because it was on a different fork than `last_voted_slot`, and thus this vote for `candidate_latest_frozen_vote` + // should be safe to count towards the switching proof: + // + // However, there is also the possibility that `last_voted_slot` is a stray, in which + // case we cannot make this conclusion as we do not know the ancestors/descendants + // of strays. Hence we err on the side of caution here and ignore this vote. This + // is ok because validators voting on different unrooted forks should eventually vote + // on some descendant of the root, at which time they can be included in switching proofs. + !Self::is_candidate_slot_descendant_of_last_vote( + *candidate_latest_frozen_vote, + last_voted_slot, + ancestors, + ) + .unwrap_or(true) + } { + let stake = epoch_vote_accounts + .get(vote_account_pubkey) + .map(|(stake, _)| *stake) + .unwrap_or(0); + locked_out_stake += stake; + if (locked_out_stake as f64 / total_stake as f64) > SWITCH_FORK_THRESHOLD { + return SwitchForkDecision::SwitchProof(switch_proof); } + locked_out_vote_accounts.insert(vote_account_pubkey); + } + } - // We have not detected sufficient lockout past the last voted slot to generate - // a switching proof - SwitchForkDecision::FailedSwitchThreshold(locked_out_stake, total_stake) - }) - .unwrap_or(SwitchForkDecision::SameFork) + // We have not detected sufficient lockout past the last voted slot to generate + // a switching proof + SwitchForkDecision::FailedSwitchThreshold(locked_out_stake, total_stake) } #[allow(clippy::too_many_arguments)] @@ -2558,6 +2622,44 @@ pub mod test { assert!(tower.maybe_timestamp(3).is_none()); // slot 3 gets no timestamp } + #[test] + fn test_refresh_last_vote_timestamp() { + let mut tower = Tower::default(); + + // Tower has no vote or timestamp + tower.last_vote.set_timestamp(None); + tower.refresh_last_vote_timestamp(5); + assert_eq!(tower.last_vote.timestamp(), None); + assert_eq!(tower.last_timestamp.slot, 0); + assert_eq!(tower.last_timestamp.timestamp, 0); + + // Tower has vote no timestamp, but is greater than heaviest_bank + tower.last_vote = + VoteTransaction::from(VoteStateUpdate::from(vec![(0, 3), (1, 2), (6, 1)])); + assert_eq!(tower.last_vote.timestamp(), None); + tower.refresh_last_vote_timestamp(5); + assert_eq!(tower.last_vote.timestamp(), None); + assert_eq!(tower.last_timestamp.slot, 0); + assert_eq!(tower.last_timestamp.timestamp, 0); + + // Tower has vote with no timestamp + tower.last_vote = + VoteTransaction::from(VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)])); + assert_eq!(tower.last_vote.timestamp(), None); + tower.refresh_last_vote_timestamp(5); + assert_eq!(tower.last_vote.timestamp(), Some(1)); + assert_eq!(tower.last_timestamp.slot, 2); + assert_eq!(tower.last_timestamp.timestamp, 1); + + // Vote has timestamp + tower.last_vote = + VoteTransaction::from(VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)])); + tower.refresh_last_vote_timestamp(5); + assert_eq!(tower.last_vote.timestamp(), Some(2)); + assert_eq!(tower.last_timestamp.slot, 2); + assert_eq!(tower.last_timestamp.timestamp, 2); + } + fn run_test_load_tower_snapshot( modify_original: F, modify_serialized: G, @@ -2823,7 +2925,7 @@ pub mod test { OpenOptions::new() .write(true) .truncate(true) - .open(&path) + .open(path) .unwrap_or_else(|_| panic!("Failed to truncate file: {:?}", path)); }, ); @@ -2848,11 +2950,11 @@ pub mod test { { let blockstore = Blockstore::open(&blockstore_path).unwrap(); - let (shreds, _) = make_slot_entries(1, 0, 42); + let (shreds, _) = make_slot_entries(1, 0, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); - let (shreds, _) = make_slot_entries(3, 1, 42); + let (shreds, _) = make_slot_entries(3, 1, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); - let (shreds, _) = make_slot_entries(4, 1, 42); + let (shreds, _) = make_slot_entries(4, 1, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); assert!(!blockstore.is_root(0)); assert!(!blockstore.is_root(1)); @@ -2886,11 +2988,11 @@ pub mod test { { let blockstore = Blockstore::open(&blockstore_path).unwrap(); - let (shreds, _) = make_slot_entries(1, 0, 42); + let (shreds, _) = make_slot_entries(1, 0, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); - let (shreds, _) = make_slot_entries(3, 1, 42); + let (shreds, _) = make_slot_entries(3, 1, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); - let (shreds, _) = make_slot_entries(4, 1, 42); + let (shreds, _) = make_slot_entries(4, 1, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.set_roots(std::iter::once(&3)).unwrap(); assert!(!blockstore.is_root(0)); @@ -2917,9 +3019,9 @@ pub mod test { { let blockstore = Blockstore::open(&blockstore_path).unwrap(); - let (shreds, _) = make_slot_entries(1, 0, 42); + let (shreds, _) = make_slot_entries(1, 0, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); - let (shreds, _) = make_slot_entries(3, 1, 42); + let (shreds, _) = make_slot_entries(3, 1, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); assert!(!blockstore.is_root(0)); assert!(!blockstore.is_root(1)); diff --git a/core/src/cost_update_service.rs b/core/src/cost_update_service.rs index cf1a55365f444e..8f5038c0c91633 100644 --- a/core/src/cost_update_service.rs +++ b/core/src/cost_update_service.rs @@ -75,7 +75,7 @@ impl CostUpdateService { cost_update_receiver: CostUpdateReceiver, ) -> Self { let thread_hdl = Builder::new() - .name("solana-cost-update-service".to_string()) + .name("solCostUpdtSvc".to_string()) .spawn(move || { Self::service_loop(blockstore, cost_model, cost_update_receiver); }) diff --git a/core/src/drop_bank_service.rs b/core/src/drop_bank_service.rs index aac1a02ee0344d..0321643d6aab68 100644 --- a/core/src/drop_bank_service.rs +++ b/core/src/drop_bank_service.rs @@ -15,7 +15,7 @@ pub struct DropBankService { impl DropBankService { pub fn new(bank_receiver: Receiver>>) -> Self { let thread_hdl = Builder::new() - .name("sol-drop-b-service".to_string()) + .name("solDropBankSrvc".to_string()) .spawn(move || { for banks in bank_receiver.iter() { let len = banks.len(); diff --git a/core/src/fetch_stage.rs b/core/src/fetch_stage.rs index c041739d7c8d4e..4ea12395c1c3af 100644 --- a/core/src/fetch_stage.rs +++ b/core/src/fetch_stage.rs @@ -6,6 +6,7 @@ use { result::{Error, Result}, }, crossbeam_channel::{unbounded, RecvTimeoutError}, + solana_client::connection_cache::DEFAULT_TPU_ENABLE_UDP, solana_metrics::{inc_new_counter_debug, inc_new_counter_info}, solana_perf::{packet::PacketBatchRecycler, recycler::Recycler}, solana_poh::poh_recorder::PohRecorder, @@ -57,6 +58,7 @@ impl FetchStage { poh_recorder, coalesce_ms, None, + DEFAULT_TPU_ENABLE_UDP, ), receiver, vote_receiver, @@ -76,6 +78,7 @@ impl FetchStage { poh_recorder: &Arc>, coalesce_ms: u64, in_vote_only_mode: Option>, + tpu_enable_udp: bool, ) -> Self { let tx_sockets = sockets.into_iter().map(Arc::new).collect(); let tpu_forwards_sockets = tpu_forwards_sockets.into_iter().map(Arc::new).collect(); @@ -92,6 +95,7 @@ impl FetchStage { poh_recorder, coalesce_ms, in_vote_only_mode, + tpu_enable_udp, ) } @@ -150,42 +154,52 @@ impl FetchStage { poh_recorder: &Arc>, coalesce_ms: u64, in_vote_only_mode: Option>, + tpu_enable_udp: bool, ) -> Self { let recycler: PacketBatchRecycler = Recycler::warmed(1000, 1024); let tpu_stats = Arc::new(StreamerReceiveStats::new("tpu_receiver")); - let tpu_threads: Vec<_> = tpu_sockets - .into_iter() - .map(|socket| { - streamer::receiver( - socket, - exit.clone(), - sender.clone(), - recycler.clone(), - tpu_stats.clone(), - coalesce_ms, - true, - in_vote_only_mode.clone(), - ) - }) - .collect(); + + let tpu_threads: Vec<_> = if tpu_enable_udp { + tpu_sockets + .into_iter() + .map(|socket| { + streamer::receiver( + socket, + exit.clone(), + sender.clone(), + recycler.clone(), + tpu_stats.clone(), + coalesce_ms, + true, + in_vote_only_mode.clone(), + ) + }) + .collect() + } else { + Vec::default() + }; let tpu_forward_stats = Arc::new(StreamerReceiveStats::new("tpu_forwards_receiver")); - let tpu_forwards_threads: Vec<_> = tpu_forwards_sockets - .into_iter() - .map(|socket| { - streamer::receiver( - socket, - exit.clone(), - forward_sender.clone(), - recycler.clone(), - tpu_forward_stats.clone(), - coalesce_ms, - true, - in_vote_only_mode.clone(), - ) - }) - .collect(); + let tpu_forwards_threads: Vec<_> = if tpu_enable_udp { + tpu_forwards_sockets + .into_iter() + .map(|socket| { + streamer::receiver( + socket, + exit.clone(), + forward_sender.clone(), + recycler.clone(), + tpu_forward_stats.clone(), + coalesce_ms, + true, + in_vote_only_mode.clone(), + ) + }) + .collect() + } else { + Vec::default() + }; let tpu_vote_stats = Arc::new(StreamerReceiveStats::new("tpu_vote_receiver")); let tpu_vote_threads: Vec<_> = tpu_vote_sockets @@ -208,7 +222,7 @@ impl FetchStage { let poh_recorder = poh_recorder.clone(); let fwd_thread_hdl = Builder::new() - .name("solana-fetch-stage-fwd-rcvr".to_string()) + .name("solFetchStgFwRx".to_string()) .spawn(move || loop { if let Err(e) = Self::handle_forwarded_packets(&forward_receiver, &sender, &poh_recorder) @@ -226,7 +240,7 @@ impl FetchStage { let exit = exit.clone(); let metrics_thread_hdl = Builder::new() - .name("solana-fetch-stage-metrics".to_string()) + .name("solFetchStgMetr".to_string()) .spawn(move || loop { sleep(Duration::from_secs(1)); diff --git a/core/src/find_packet_sender_stake_stage.rs b/core/src/find_packet_sender_stake_stage.rs index d62d6afe7c8dac..53f1d033669a34 100644 --- a/core/src/find_packet_sender_stake_stage.rs +++ b/core/src/find_packet_sender_stake_stage.rs @@ -84,7 +84,7 @@ impl FindPacketSenderStakeStage { ) -> Self { let mut stats = FindPacketSenderStakeStats::default(); let thread_hdl = Builder::new() - .name("find-packet-sender-stake".to_string()) + .name("solPktStake".to_string()) .spawn(move || loop { match streamer::recv_packet_batches(&packet_receiver) { Ok((mut batches, num_packets, recv_duration)) => { diff --git a/core/src/forward_packet_batches_by_accounts.rs b/core/src/forward_packet_batches_by_accounts.rs index 14fcfe486fa8d6..caeda3fae2bfb1 100644 --- a/core/src/forward_packet_batches_by_accounts.rs +++ b/core/src/forward_packet_batches_by_accounts.rs @@ -187,14 +187,12 @@ impl ForwardPacketBatchesByAccounts { mod tests { use { super::*, - crate::{ - transaction_priority_details::TransactionPriorityDetails, - unprocessed_packet_batches::DeserializedPacket, - }, + crate::unprocessed_packet_batches::DeserializedPacket, solana_runtime::{ bank::Bank, bank_forks::BankForks, genesis_utils::{create_genesis_config, GenesisConfigInfo}, + transaction_priority_details::TransactionPriorityDetails, }, solana_sdk::{hash::Hash, signature::Keypair, system_transaction}, std::sync::RwLock, diff --git a/core/src/heaviest_subtree_fork_choice.rs b/core/src/heaviest_subtree_fork_choice.rs index 96167eebe73967..f9c97f4adb893b 100644 --- a/core/src/heaviest_subtree_fork_choice.rs +++ b/core/src/heaviest_subtree_fork_choice.rs @@ -858,37 +858,32 @@ impl HeaviestSubtreeForkChoice { tower .last_voted_slot_hash() .and_then(|last_voted_slot_hash| { - let heaviest_slot_hash_on_same_voted_fork = self.best_slot(&last_voted_slot_hash); - if heaviest_slot_hash_on_same_voted_fork.is_none() { - if !tower.is_stray_last_vote() { - // Unless last vote is stray and stale, self.bast_slot(last_voted_slot) must return - // Some(_), justifying to panic! here. - // Also, adjust_lockouts_after_replay() correctly makes last_voted_slot None, - // if all saved votes are ancestors of replayed_root_slot. So this code shouldn't be - // touched in that case as well. - // In other words, except being stray, all other slots have been voted on while this - // validator has been running, so we must be able to fetch best_slots for all of - // them. - panic!( - "a bank at last_voted_slot({:?}) is a frozen bank so must have been \ - added to heaviest_subtree_fork_choice at time of freezing", - last_voted_slot_hash, - ) - } else { - // fork_infos doesn't have corresponding data for the stale stray last vote, - // meaning some inconsistency between saved tower and ledger. - // (newer snapshot, or only a saved tower is moved over to new setup?) - return None; + match self.is_candidate(&last_voted_slot_hash) { + Some(true) => self.best_slot(&last_voted_slot_hash), + Some(false) => None, + None => { + if !tower.is_stray_last_vote() { + // Unless last vote is stray and stale, self.is_candidate(last_voted_slot_hash) must return + // Some(_), justifying to panic! here. + // Also, adjust_lockouts_after_replay() correctly makes last_voted_slot None, + // if all saved votes are ancestors of replayed_root_slot. So this code shouldn't be + // touched in that case as well. + // In other words, except being stray, all other slots have been voted on while this + // validator has been running, so we must be able to fetch best_slots for all of + // them. + panic!( + "a bank at last_voted_slot({:?}) is a frozen bank so must have been \ + added to heaviest_subtree_fork_choice at time of freezing", + last_voted_slot_hash, + ) + } else { + // fork_infos doesn't have corresponding data for the stale stray last vote, + // meaning some inconsistency between saved tower and ledger. + // (newer snapshot, or only a saved tower is moved over to new setup?) + None + } } } - let heaviest_slot_hash_on_same_voted_fork = - heaviest_slot_hash_on_same_voted_fork.unwrap(); - - if heaviest_slot_hash_on_same_voted_fork == last_voted_slot_hash { - None - } else { - Some(heaviest_slot_hash_on_same_voted_fork) - } }) } @@ -2957,9 +2952,10 @@ mod test { // After marking the last vote in the tower as invalid, `heaviest_slot_on_same_voted_fork()` // should disregard all descendants of that invalid vote - assert!(heaviest_subtree_fork_choice - .heaviest_slot_on_same_voted_fork(&tower) - .is_none()); + assert_eq!( + heaviest_subtree_fork_choice.heaviest_slot_on_same_voted_fork(&tower), + None + ); // Adding another descendant to the invalid candidate won't // update the best slot, even if it contains votes diff --git a/core/src/immutable_deserialized_packet.rs b/core/src/immutable_deserialized_packet.rs new file mode 100644 index 00000000000000..0a12fcad44e17d --- /dev/null +++ b/core/src/immutable_deserialized_packet.rs @@ -0,0 +1,137 @@ +use { + solana_perf::packet::Packet, + solana_runtime::transaction_priority_details::{ + GetTransactionPriorityDetails, TransactionPriorityDetails, + }, + solana_sdk::{ + hash::Hash, + message::Message, + sanitize::SanitizeError, + short_vec::decode_shortu16_len, + signature::Signature, + transaction::{SanitizedVersionedTransaction, VersionedTransaction}, + }, + std::{cmp::Ordering, mem::size_of}, + thiserror::Error, +}; + +#[derive(Debug, Error)] +pub enum DeserializedPacketError { + #[error("ShortVec Failed to Deserialize")] + // short_vec::decode_shortu16_len() currently returns () on error + ShortVecError(()), + #[error("Deserialization Error: {0}")] + DeserializationError(#[from] bincode::Error), + #[error("overflowed on signature size {0}")] + SignatureOverflowed(usize), + #[error("packet failed sanitization {0}")] + SanitizeError(#[from] SanitizeError), + #[error("transaction failed prioritization")] + PrioritizationFailure, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct ImmutableDeserializedPacket { + original_packet: Packet, + transaction: SanitizedVersionedTransaction, + message_hash: Hash, + is_simple_vote: bool, + priority_details: TransactionPriorityDetails, +} + +impl ImmutableDeserializedPacket { + pub fn new( + packet: Packet, + priority_details: Option, + ) -> Result { + let versioned_transaction: VersionedTransaction = packet.deserialize_slice(..)?; + let sanitized_transaction = SanitizedVersionedTransaction::try_from(versioned_transaction)?; + let message_bytes = packet_message(&packet)?; + let message_hash = Message::hash_raw_message(message_bytes); + let is_simple_vote = packet.meta.is_simple_vote_tx(); + + // drop transaction if prioritization fails. + let priority_details = priority_details + .or_else(|| sanitized_transaction.get_transaction_priority_details()) + .ok_or(DeserializedPacketError::PrioritizationFailure)?; + + Ok(Self { + original_packet: packet, + transaction: sanitized_transaction, + message_hash, + is_simple_vote, + priority_details, + }) + } + + pub fn original_packet(&self) -> &Packet { + &self.original_packet + } + + pub fn transaction(&self) -> &SanitizedVersionedTransaction { + &self.transaction + } + + pub fn message_hash(&self) -> &Hash { + &self.message_hash + } + + pub fn is_simple_vote(&self) -> bool { + self.is_simple_vote + } + + pub fn priority(&self) -> u64 { + self.priority_details.priority + } + + pub fn compute_unit_limit(&self) -> u64 { + self.priority_details.compute_unit_limit + } +} + +impl PartialOrd for ImmutableDeserializedPacket { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ImmutableDeserializedPacket { + fn cmp(&self, other: &Self) -> Ordering { + self.priority().cmp(&other.priority()) + } +} + +/// Read the transaction message from packet data +fn packet_message(packet: &Packet) -> Result<&[u8], DeserializedPacketError> { + let (sig_len, sig_size) = packet + .data(..) + .and_then(|bytes| decode_shortu16_len(bytes).ok()) + .ok_or(DeserializedPacketError::ShortVecError(()))?; + sig_len + .checked_mul(size_of::()) + .and_then(|v| v.checked_add(sig_size)) + .and_then(|msg_start| packet.data(msg_start..)) + .ok_or(DeserializedPacketError::SignatureOverflowed(sig_size)) +} + +#[cfg(test)] +mod tests { + use { + super::*, + solana_sdk::{signature::Keypair, system_transaction}, + }; + + #[test] + fn simple_deserialized_packet() { + let tx = system_transaction::transfer( + &Keypair::new(), + &solana_sdk::pubkey::new_rand(), + 1, + Hash::new_unique(), + ); + let packet = Packet::from_data(None, &tx).unwrap(); + let deserialized_packet = ImmutableDeserializedPacket::new(packet, None); + + assert!(matches!(deserialized_packet, Ok(_))); + } +} diff --git a/core/src/leader_slot_banking_stage_metrics.rs b/core/src/leader_slot_banking_stage_metrics.rs index 06e767ed358ae5..ed556991e1d560 100644 --- a/core/src/leader_slot_banking_stage_metrics.rs +++ b/core/src/leader_slot_banking_stage_metrics.rs @@ -104,6 +104,16 @@ struct LeaderSlotPacketCountMetrics { // then hit the age limit after failing to be comitted. executed_transactions_failed_commit_count: u64, + // total number of transactions that were excluded from the block because there were concurrent write locks active. + // These transactions are added back to the buffered queue and are already counted in + // `self.retrayble_errored_transaction_count`. + account_lock_throttled_transactions_count: u64, + + // total number of transactions that were excluded from the block because their write + // account locks exceed the limit. + // These transactions are not retried. + account_locks_limit_throttled_transactions_count: u64, + // total number of transactions that were excluded from the block because they were too expensive // according to the cost model. These transactions are added back to the buffered queue and are // already counted in `self.retrayble_errored_transaction_count`. @@ -197,6 +207,16 @@ impl LeaderSlotPacketCountMetrics { self.executed_transactions_failed_commit_count as i64, i64 ), + ( + "account_lock_throttled_transactions_count", + self.account_lock_throttled_transactions_count as i64, + i64 + ), + ( + "account_locks_limit_throttled_transactions_count", + self.account_locks_limit_throttled_transactions_count as i64, + i64 + ), ( "cost_model_throttled_transactions_count", self.cost_model_throttled_transactions_count as i64, @@ -394,6 +414,7 @@ impl LeaderSlotMetricsTracker { cost_model_throttled_transactions_count, cost_model_us, ref execute_and_commit_timings, + error_counters, .. } = process_transactions_summary; @@ -441,6 +462,20 @@ impl LeaderSlotMetricsTracker { .saturating_sub(retryable_transaction_indexes.len()) as u64 ); + saturating_add_assign!( + leader_slot_metrics + .packet_count_metrics + .account_lock_throttled_transactions_count, + error_counters.account_in_use as u64 + ); + + saturating_add_assign!( + leader_slot_metrics + .packet_count_metrics + .account_locks_limit_throttled_transactions_count, + error_counters.too_many_account_locks as u64 + ); + saturating_add_assign!( leader_slot_metrics .packet_count_metrics diff --git a/core/src/ledger_cleanup_service.rs b/core/src/ledger_cleanup_service.rs index 6607ba8ed594b5..160b8721f45b6e 100644 --- a/core/src/ledger_cleanup_service.rs +++ b/core/src/ledger_cleanup_service.rs @@ -76,7 +76,7 @@ impl LedgerCleanupService { let blockstore_compact = blockstore.clone(); let t_cleanup = Builder::new() - .name("sol-led-cleanup".to_string()) + .name("solLedgerClean".to_string()) .spawn(move || loop { if exit.load(Ordering::Relaxed) { break; @@ -98,7 +98,7 @@ impl LedgerCleanupService { .unwrap(); let t_compact = Builder::new() - .name("sol-led-compact".to_string()) + .name("solLedgerComp".to_string()) .spawn(move || loop { if exit_compact.load(Ordering::Relaxed) { break; @@ -124,28 +124,25 @@ impl LedgerCleanupService { /// A helper function to `cleanup_ledger` which returns a tuple of the /// following four elements suggesting whether to clean up the ledger: /// - /// Return value (bool, Slot, Slot, u64): + /// Return value (bool, Slot, u64): /// - `slots_to_clean` (bool): a boolean value indicating whether there /// are any slots to clean. If true, then `cleanup_ledger` function /// will then proceed with the ledger cleanup. - /// - `first_slot_to_purge` (Slot): the first slot to purge. - /// - `lowest_slot_to_puerge` (Slot): the lowest slot to purge. Together - /// with `first_slot_to_purge`, the two Slot values represent the - /// range of the clean up. + /// - `lowest_slot_to_purge` (Slot): the lowest slot to purge. Any + /// slot which is older or equal to `lowest_slot_to_purge` will be + /// cleaned up. /// - `total_shreds` (u64): the total estimated number of shreds before the /// `root`. fn find_slots_to_clean( blockstore: &Arc, root: Slot, max_ledger_shreds: u64, - ) -> (bool, Slot, Slot, u64) { + ) -> (bool, Slot, u64) { let mut total_slots = Vec::new(); let mut iterate_time = Measure::start("iterate_time"); let mut total_shreds = 0; - let mut first_slot = 0; for (i, (slot, meta)) in blockstore.slot_meta_iterator(0).unwrap().enumerate() { if i == 0 { - first_slot = slot; debug!("purge: searching from slot: {}", slot); } // Not exact since non-full slots will have holes @@ -157,15 +154,14 @@ impl LedgerCleanupService { } iterate_time.stop(); info!( - "first_slot={} total_slots={} total_shreds={} max_ledger_shreds={}, {}", - first_slot, + "total_slots={} total_shreds={} max_ledger_shreds={}, {}", total_slots.len(), total_shreds, max_ledger_shreds, iterate_time ); if (total_shreds as u64) < max_ledger_shreds { - return (false, 0, 0, total_shreds); + return (false, 0, total_shreds); } let mut num_shreds_to_clean = 0; let mut lowest_cleanup_slot = total_slots[0].0; @@ -177,7 +173,7 @@ impl LedgerCleanupService { } } - (true, first_slot, lowest_cleanup_slot, total_shreds) + (true, lowest_cleanup_slot, total_shreds) } fn receive_new_roots(new_root_receiver: &Receiver) -> Result { @@ -233,7 +229,7 @@ impl LedgerCleanupService { *last_purge_slot = root; - let (slots_to_clean, purge_first_slot, lowest_cleanup_slot, total_shreds) = + let (slots_to_clean, lowest_cleanup_slot, total_shreds) = Self::find_slots_to_clean(blockstore, root, max_ledger_shreds); if slots_to_clean { @@ -242,24 +238,18 @@ impl LedgerCleanupService { let purge_complete1 = purge_complete.clone(); let last_compact_slot1 = last_compact_slot.clone(); let _t_purge = Builder::new() - .name("solana-ledger-purge".to_string()) + .name("solLedgerPurge".to_string()) .spawn(move || { let mut slot_update_time = Measure::start("slot_update"); *blockstore.lowest_cleanup_slot.write().unwrap() = lowest_cleanup_slot; slot_update_time.stop(); - info!( - "purging data from slots {} to {}", - purge_first_slot, lowest_cleanup_slot - ); + info!("purging data older than {}", lowest_cleanup_slot); let mut purge_time = Measure::start("purge_slots"); - blockstore.purge_slots( - purge_first_slot, - lowest_cleanup_slot, - PurgeType::CompactionFilter, - ); + // purge any slots older than lowest_cleanup_slot. + blockstore.purge_slots(0, lowest_cleanup_slot, PurgeType::CompactionFilter); // Update only after purge operation. // Safety: This value can be used by compaction_filters shared via Arc. // Compactions are async and run as a multi-threaded background job. However, this diff --git a/core/src/ledger_metric_report_service.rs b/core/src/ledger_metric_report_service.rs index 8d0b96d28c3500..1f8636bff6a546 100644 --- a/core/src/ledger_metric_report_service.rs +++ b/core/src/ledger_metric_report_service.rs @@ -26,7 +26,7 @@ impl LedgerMetricReportService { pub fn new(blockstore: Arc, exit: &Arc) -> Self { let exit_signal = exit.clone(); let t_cf_metric = Builder::new() - .name("metric_report_rocksdb_cf_metrics".to_string()) + .name("solRocksCfMtrcs".to_string()) .spawn(move || loop { if exit_signal.load(Ordering::Relaxed) { break; diff --git a/core/src/lib.rs b/core/src/lib.rs index 9be6ab5b9cc4ba..d6a22a5a68a639 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -9,6 +9,7 @@ //! pub mod accounts_hash_verifier; +pub mod admin_rpc_post_init; pub mod ancestor_hashes_service; pub mod banking_stage; pub mod broadcast_stage; @@ -35,9 +36,9 @@ pub mod leader_slot_banking_stage_metrics; pub mod leader_slot_banking_stage_timing_metrics; pub mod ledger_cleanup_service; pub mod ledger_metric_report_service; +pub mod multi_iterator_scanner; pub mod optimistic_confirmation_verifier; pub mod outstanding_requests; -mod packet_hasher; pub mod packet_threshold; pub mod poh_timing_report_service; pub mod poh_timing_reporter; @@ -68,7 +69,6 @@ mod tower1_7_14; pub mod tower_storage; pub mod tpu; pub mod tracer_packet_stats; -pub mod transaction_priority_details; pub mod tree_diff; pub mod tvu; pub mod unfrozen_gossip_verified_vote_hashes; diff --git a/core/src/multi_iterator_scanner.rs b/core/src/multi_iterator_scanner.rs new file mode 100644 index 00000000000000..d2d639293e3186 --- /dev/null +++ b/core/src/multi_iterator_scanner.rs @@ -0,0 +1,351 @@ +//! Provides an iterator interface that create non-conflicting batches of elements to process. +//! +//! The problem that this structure is targetting is as following: +//! We have a slice of transactions we want to process in batches where transactions +//! in the same batch do not conflict with each other. This allows us process them in +//! parallel. The original slice is ordered by priority, and it is often the case +//! that transactions with high-priority are conflicting with each other. This means +//! we cannot simply grab chunks of transactions. +//! The solution is to create a MultiIteratorScanner that will use multiple iterators, up +//! to the desired batch size, and create batches of transactions that do not conflict with +//! each other. The MultiIteratorScanner stores state for the current positions of each iterator, +//! as well as which transactions have already been handled. If a transaction is invalid it can +//! also be skipped without being considered for future batches. +//! + +/// Output from the element checker used in `MultiIteratorScanner::iterate`. +pub enum ProcessingDecision { + /// Should be processed by the scanner. + Now, + /// Should be skipped by the scanner on this pass - process later. + Later, + /// Should be skipped and marked as handled so we don't try processing it again. + Never, +} + +/// Iterates over a slice creating valid non-self-conflicting batches of elements to process, +/// elements between batches are not guaranteed to be non-conflicting. +/// Conflicting elements are guaranteed to be processed in the order they appear in the slice, +/// as long as the `should_process` function is appropriately marking resources as used. +/// It is also guaranteed that elements within the batch are in the order they appear in +/// the slice. The batch size is not guaranteed to be `max_iterators` - it can be smaller. +/// +/// # Example: +/// +/// Assume transactions with same letter conflict with each other. A typical priority ordered +/// buffer might look like: +/// +/// // [A, A, B, A, C, D, B, C, D] +/// +/// If we want to have batches of size 4, the MultiIteratorScanner will proceed as follows: +/// +/// // [A, A, B, A, C, D, B, C, D] +/// // ^ ^ ^ ^ +/// +/// // [A, A, B, A, C, D, B, C, D] +/// // ^ ^ ^ ^ +/// +/// // [A, A, B, A, C, D, B, C, D] +/// // ^ +/// The iterator will iterate with batches: +/// +/// // [[A, B, C, D], [A, B, C, D], [A]] +/// +pub struct MultiIteratorScanner<'a, T, U, F> +where + F: FnMut(&T, &mut U) -> ProcessingDecision, +{ + /// Maximum number of iterators to use. + max_iterators: usize, + /// Slice that we're iterating over + slice: &'a [T], + /// Payload - used to store shared mutable state between scanner and the processing function. + payload: U, + /// Function that checks if an element should be processed. This function is also responsible + /// for marking resources, such as locks, as used. + should_process: F, + /// Store whether an element has already been handled + already_handled: Vec, + /// Current indices inside `slice` for multiple iterators + current_positions: Vec, + /// Container to store items for iteration - Should only be used in `get_current_items()` + current_items: Vec<&'a T>, + /// Initialized + initialized: bool, +} + +impl<'a, T, U, F> MultiIteratorScanner<'a, T, U, F> +where + F: FnMut(&T, &mut U) -> ProcessingDecision, +{ + pub fn new(slice: &'a [T], max_iterators: usize, payload: U, should_process: F) -> Self { + assert!(max_iterators > 0); + Self { + max_iterators, + slice, + payload, + should_process, + already_handled: vec![false; slice.len()], + current_positions: Vec::with_capacity(max_iterators), + current_items: Vec::with_capacity(max_iterators), + initialized: false, + } + } + + /// Returns a slice of the item references at the current positions of the iterators + /// and a mutable reference to the payload. + /// + /// Returns None if the scanner is done iterating. + pub fn iterate(&mut self) -> Option<(&[&'a T], &mut U)> { + if !self.initialized { + self.initialized = true; + self.initialize_current_positions(); + } else { + self.advance_current_positions(); + } + self.get_current_items() + } + + /// Consume the iterator and return the payload. + pub fn finalize(self) -> U { + self.payload + } + + /// Initialize the `current_positions` vector for the first batch. + fn initialize_current_positions(&mut self) { + let mut last_index = 0; + for _iterator_index in 0..self.max_iterators { + match self.march_iterator(last_index) { + Some(index) => { + self.current_positions.push(index); + last_index = index.saturating_add(1); + } + None => break, + } + } + } + + /// March iterators forward to find the next batch of items. + fn advance_current_positions(&mut self) { + if let Some(first_position) = self.current_positions.first() { + let mut prev_position = *first_position; + for iterator_index in 0..self.current_positions.len() { + // If the previous iterator has passed this iterator, we should start + // at it's position + 1 to avoid duplicate re-traversal. + let start_index = (self.current_positions[iterator_index].saturating_add(1)) + .max(prev_position.saturating_add(1)); + match self.march_iterator(start_index) { + Some(index) => { + self.current_positions[iterator_index] = index; + prev_position = index; + } + None => { + // Drop current positions that go past the end of the slice + self.current_positions.truncate(iterator_index); + break; + } + } + } + } + } + + /// Get the current items from the slice using `self.current_positions`. + /// Returns `None` if there are no more items. + fn get_current_items(&mut self) -> Option<(&[&'a T], &mut U)> { + self.current_items.clear(); + for index in &self.current_positions { + self.current_items.push(&self.slice[*index]); + } + if self.current_items.is_empty() { + None + } else { + Some((&self.current_items, &mut self.payload)) + } + } + + /// Moves the iterator to its' next position. If we've reached the end of the slice, we return None + fn march_iterator(&mut self, starting_index: usize) -> Option { + let mut found = None; + for index in starting_index..self.slice.len() { + if !self.already_handled[index] { + match (self.should_process)(&self.slice[index], &mut self.payload) { + ProcessingDecision::Now => { + self.already_handled[index] = true; + found = Some(index); + break; + } + ProcessingDecision::Later => { + // Do nothing - iterator will try this element in a future batch + } + ProcessingDecision::Never => { + self.already_handled[index] = true; + } + } + } + } + + found + } +} + +#[cfg(test)] +mod tests { + use {super::MultiIteratorScanner, crate::multi_iterator_scanner::ProcessingDecision}; + + struct TestScannerPayload { + locks: Vec, + } + + fn test_scanner_locking_should_process( + item: &i32, + payload: &mut TestScannerPayload, + ) -> ProcessingDecision { + if payload.locks[*item as usize] { + ProcessingDecision::Later + } else { + payload.locks[*item as usize] = true; + ProcessingDecision::Now + } + } + + #[test] + fn test_multi_iterator_scanner_empty() { + let slice: Vec = vec![]; + let mut scanner = MultiIteratorScanner::new(&slice, 2, (), |_, _| ProcessingDecision::Now); + assert!(scanner.iterate().is_none()); + } + + #[test] + fn test_multi_iterator_scanner_iterate() { + let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; + let should_process = |_item: &i32, _payload: &mut ()| ProcessingDecision::Now; + + let mut scanner = MultiIteratorScanner::new(&slice, 2, (), should_process); + let mut actual_batches = vec![]; + while let Some((batch, _payload)) = scanner.iterate() { + actual_batches.push(batch.to_vec()); + } + + // Batch 1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + // ^ ^ + // Batch 2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + // ^ ^ + // Batch 3: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + // ^ ^ + // Batch 4: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + // ^ ^ + // Batch 5: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + // ^ ^ + // Batch 6: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + // ^ + let expected_batches = vec![ + vec![&1, &2], + vec![&3, &4], + vec![&5, &6], + vec![&7, &8], + vec![&9, &10], + vec![&11], + ]; + assert_eq!(actual_batches, expected_batches); + } + + #[test] + fn test_multi_iterator_scanner_iterate_with_gaps() { + let slice = [0, 0, 0, 1, 2, 3, 1]; + + let payload = TestScannerPayload { + locks: vec![false; 4], + }; + + let mut scanner = + MultiIteratorScanner::new(&slice, 2, payload, test_scanner_locking_should_process); + let mut actual_batches = vec![]; + while let Some((batch, payload)) = scanner.iterate() { + // free the resources + for item in batch { + payload.locks[**item as usize] = false; + } + + actual_batches.push(batch.to_vec()); + } + + // Batch 1: [0, 0, 0, 1, 2, 3, 4] + // ^ ^ + // Batch 2: [0, 0, 0, 1, 2, 3, 4] + // ^ ^ + // Batch 3: [0, 0, 0, 1, 2, 3, 4] + // ^ ^ + // Batch 4: [0, 0, 0, 1, 2, 3, 4] + // -----------^ (--- indicates where the 0th iterator marched from) + let expected_batches = vec![vec![&0, &1], vec![&0, &2], vec![&0, &3], vec![&1]]; + assert_eq!(actual_batches, expected_batches); + + let TestScannerPayload { locks } = scanner.finalize(); + assert_eq!(locks, vec![false; 4]); + } + + #[test] + fn test_multi_iterator_scanner_iterate_conflicts_not_at_front() { + let slice = [1, 2, 3, 0, 0, 0, 3, 2, 1]; + + let payload = TestScannerPayload { + locks: vec![false; 4], + }; + + let mut scanner = + MultiIteratorScanner::new(&slice, 2, payload, test_scanner_locking_should_process); + let mut actual_batches = vec![]; + while let Some((batch, payload)) = scanner.iterate() { + // free the resources + for item in batch { + payload.locks[**item as usize] = false; + } + + actual_batches.push(batch.to_vec()); + } + + // Batch 1: [1, 2, 3, 0, 0, 0, 3, 2, 1] + // ^ ^ + // Batch 2: [1, 2, 3, 0, 0, 0, 3, 2, 1] + // ^ ^ + // Batch 3: [1, 2, 3, 0, 0, 0, 3, 2, 1] + // ^ ^ + // Batch 4: [1, 2, 3, 0, 0, 0, 3, 2, 1] + // ^ ^ + // Batch 5: [1, 2, 3, 0, 0, 0, 3, 2, 1] + // ^ + let expected_batches = vec![ + vec![&1, &2], + vec![&3, &0], + vec![&0, &3], + vec![&0, &2], + vec![&1], + ]; + assert_eq!(actual_batches, expected_batches); + + let TestScannerPayload { locks } = scanner.finalize(); + assert_eq!(locks, vec![false; 4]); + } + + #[test] + fn test_multi_iterator_scanner_iterate_with_never_process() { + let slice = [0, 4, 1, 2]; + let should_process = |item: &i32, _payload: &mut ()| match item { + 4 => ProcessingDecision::Never, + _ => ProcessingDecision::Now, + }; + + let mut scanner = MultiIteratorScanner::new(&slice, 2, (), should_process); + let mut actual_batches = vec![]; + while let Some((batch, _payload)) = scanner.iterate() { + actual_batches.push(batch.to_vec()); + } + + // Batch 1: [0, 4, 1, 2] + // ^ ^ + // Batch 2: [0, 4, 1, 2] + // ^ + let expected_batches = vec![vec![&0, &1], vec![&2]]; + assert_eq!(actual_batches, expected_batches); + } +} diff --git a/core/src/packet_hasher.rs b/core/src/packet_hasher.rs deleted file mode 100644 index 58d40ec4ba238d..00000000000000 --- a/core/src/packet_hasher.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Get a unique hash value for a packet -// Used in retransmit and shred fetch to prevent dos with same packet data. - -use { - ahash::AHasher, - rand::{thread_rng, Rng}, - solana_perf::packet::Packet, - std::hash::Hasher, -}; - -#[derive(Clone)] -pub(crate) struct PacketHasher { - seed1: u128, - seed2: u128, -} - -impl Default for PacketHasher { - fn default() -> Self { - Self { - seed1: thread_rng().gen::(), - seed2: thread_rng().gen::(), - } - } -} - -impl PacketHasher { - pub(crate) fn hash_packet(&self, packet: &Packet) -> u64 { - self.hash_data(packet.data(..).unwrap_or_default()) - } - - pub(crate) fn hash_shred(&self, shred: &[u8]) -> u64 { - self.hash_data(shred) - } - - fn hash_data(&self, data: &[u8]) -> u64 { - let mut hasher = AHasher::new_with_keys(self.seed1, self.seed2); - hasher.write(data); - hasher.finish() - } - - pub(crate) fn reset(&mut self) { - *self = Self::default(); - } -} diff --git a/core/src/poh_timing_report_service.rs b/core/src/poh_timing_report_service.rs index 175b3cdc83e50d..bc84176525ef6e 100644 --- a/core/src/poh_timing_report_service.rs +++ b/core/src/poh_timing_report_service.rs @@ -28,7 +28,7 @@ impl PohTimingReportService { let exit_signal = exit.clone(); let mut poh_timing_reporter = PohTimingReporter::default(); let t_poh_timing = Builder::new() - .name("poh_timing_report".to_string()) + .name("solPohTimingRpt".to_string()) .spawn(move || loop { if exit_signal.load(Ordering::Relaxed) { break; diff --git a/core/src/progress_map.rs b/core/src/progress_map.rs index fc069750508839..fb297f2d9f8589 100644 --- a/core/src/progress_map.rs +++ b/core/src/progress_map.rs @@ -161,26 +161,22 @@ impl ValidatorStakeInfo { pub const RETRANSMIT_BASE_DELAY_MS: u64 = 5_000; pub const RETRANSMIT_BACKOFF_CAP: u32 = 6; -#[derive(Debug, Default)] +#[derive(Debug)] pub struct RetransmitInfo { - pub retry_time: Option, - pub retry_iteration: u32, + pub(crate) retry_time: Instant, + pub(crate) retry_iteration: u32, } impl RetransmitInfo { pub fn reached_retransmit_threshold(&self) -> bool { let backoff = std::cmp::min(self.retry_iteration, RETRANSMIT_BACKOFF_CAP); let backoff_duration_ms = (1_u64 << backoff) * RETRANSMIT_BASE_DELAY_MS; - self.retry_time - .map(|time| time.elapsed().as_millis() > backoff_duration_ms.into()) - .unwrap_or(true) + self.retry_time.elapsed().as_millis() > u128::from(backoff_duration_ms) } pub fn increment_retry_iteration(&mut self) { - if self.retry_time.is_some() { - self.retry_iteration += 1; - } - self.retry_time = Some(Instant::now()); + self.retry_iteration = self.retry_iteration.saturating_add(1); + self.retry_time = Instant::now(); } } @@ -248,7 +244,10 @@ impl ForkProgress { total_epoch_stake, ..PropagatedStats::default() }, - retransmit_info: RetransmitInfo::default(), + retransmit_info: RetransmitInfo { + retry_time: Instant::now(), + retry_iteration: 0u32, + }, } } diff --git a/core/src/qos_service.rs b/core/src/qos_service.rs index fb493f674343bb..9b54e2a302c46d 100644 --- a/core/src/qos_service.rs +++ b/core/src/qos_service.rs @@ -72,7 +72,7 @@ impl QosService { let metrics_clone = Arc::clone(&metrics); let reporting_thread = Some( Builder::new() - .name("solana-qos-service-metrics-repoting".to_string()) + .name("solQosSvcMetr".to_string()) .spawn(move || { Self::reporting_loop(running_flag_clone, metrics_clone, report_receiver); }) diff --git a/core/src/repair_generic_traversal.rs b/core/src/repair_generic_traversal.rs index 03cd9d7329ab2c..d150c18b52fb00 100644 --- a/core/src/repair_generic_traversal.rs +++ b/core/src/repair_generic_traversal.rs @@ -307,6 +307,7 @@ pub mod test { parent.unwrap_or(slot), is_slot_complete, 0, + true, // merkle_variant ); // remove next to last shred diff --git a/core/src/repair_service.rs b/core/src/repair_service.rs index 018824c7935e9e..854a81854460f4 100644 --- a/core/src/repair_service.rs +++ b/core/src/repair_service.rs @@ -212,7 +212,7 @@ impl RepairService { let exit = exit.clone(); let repair_info = repair_info.clone(); Builder::new() - .name("solana-repair-service".to_string()) + .name("solRepairSvc".to_string()) .spawn(move || { Self::run( &blockstore, @@ -272,9 +272,6 @@ impl RepairService { let mut add_votes_elapsed; let root_bank = repair_info.bank_forks.read().unwrap().root_bank(); - let sign_repair_requests_feature_epoch = - ServeRepair::sign_repair_requests_activated_epoch(&root_bank); - let repairs = { let new_root = root_bank.slot(); @@ -331,16 +328,6 @@ impl RepairService { repairs .iter() .filter_map(|repair_request| { - let sign_repair_request = ServeRepair::should_sign_repair_request( - repair_request.slot(), - &root_bank, - sign_repair_requests_feature_epoch, - ); - let maybe_keypair = if sign_repair_request { - Some(identity_keypair) - } else { - None - }; let (to, req) = serve_repair .repair_request( &repair_info.cluster_slots, @@ -349,7 +336,7 @@ impl RepairService { &mut repair_stats, &repair_info.repair_validators, &mut outstanding_requests, - maybe_keypair, + identity_keypair, ) .ok()?; Some((req, to)) @@ -393,14 +380,7 @@ impl RepairService { .chain(repair_stats.highest_shred.slot_pubkeys.iter()) .chain(repair_stats.orphan.slot_pubkeys.iter()) .map(|(slot, slot_repairs)| { - ( - slot, - slot_repairs - .pubkey_repairs - .iter() - .map(|(_key, count)| count) - .sum::(), - ) + (slot, slot_repairs.pubkey_repairs.values().sum::()) }) .collect(); info!("repair_stats: {:?}", slot_to_count); @@ -624,6 +604,7 @@ impl RepairService { repair_socket: &UdpSocket, repair_validators: &Option>, outstanding_requests: &RwLock, + identity_keypair: &Keypair, ) { duplicate_slot_repair_statuses.retain(|slot, status| { Self::update_duplicate_slot_repair_addr( @@ -648,6 +629,7 @@ impl RepairService { serve_repair, repair_stats, nonce, + identity_keypair, ) { info!( "repair req send_to {} ({}) error {:?}", @@ -674,13 +656,14 @@ impl RepairService { serve_repair: &ServeRepair, repair_stats: &mut RepairStats, nonce: Nonce, + identity_keypair: &Keypair, ) -> Result<()> { let req = serve_repair.map_repair_request( repair_type, repair_pubkey, repair_stats, nonce, - None, + identity_keypair, )?; repair_socket.send_to(&req, to)?; Ok(()) @@ -744,7 +727,9 @@ impl RepairService { mod test { use { super::*, - solana_gossip::{cluster_info::Node, contact_info::ContactInfo}, + solana_gossip::{ + cluster_info::Node, legacy_contact_info::LegacyContactInfo as ContactInfo, + }, solana_ledger::{ blockstore::{ make_chaining_slot_entries, make_many_slot_entries, make_slot_entries, Blockstore, @@ -774,8 +759,8 @@ mod test { let blockstore = Blockstore::open(&blockstore_path).unwrap(); // Create some orphan slots - let (mut shreds, _) = make_slot_entries(1, 0, 1); - let (shreds2, _) = make_slot_entries(5, 2, 1); + let (mut shreds, _) = make_slot_entries(1, 0, 1, /*merkle_variant:*/ true); + let (shreds2, _) = make_slot_entries(5, 2, 1, /*merkle_variant:*/ true); shreds.extend(shreds2); blockstore.insert_shreds(shreds, None, false).unwrap(); let mut repair_weight = RepairWeight::new(0); @@ -808,7 +793,7 @@ mod test { { let blockstore = Blockstore::open(&blockstore_path).unwrap(); - let (shreds, _) = make_slot_entries(2, 0, 1); + let (shreds, _) = make_slot_entries(2, 0, 1, /*merkle_variant:*/ true); // Write this shred to slot 2, should chain to slot 0, which we haven't received // any shreds for @@ -854,7 +839,11 @@ mod test { let mut missing_indexes_per_slot = vec![]; for i in (0..num_shreds).rev() { let index = i % num_shreds_per_slot; - if index % nth == 0 { + // get_best_repair_shreds only returns missing shreds in + // between shreds received; So this should either insert the + // last shred in each slot, or exclude missing shreds after the + // last inserted shred from expected repairs. + if index % nth == 0 || index + 1 == num_shreds_per_slot { shreds_to_write.insert(0, shreds.remove(i as usize)); } else if i < num_shreds_per_slot { missing_indexes_per_slot.insert(0, index); @@ -918,7 +907,12 @@ mod test { let num_entries_per_slot = 100; // Create some shreds - let (mut shreds, _) = make_slot_entries(0, 0, num_entries_per_slot as u64); + let (mut shreds, _) = make_slot_entries( + 0, // slot + 0, // parent_slot + num_entries_per_slot as u64, + true, // merkle_variant + ); let num_shreds_per_slot = shreds.len() as u64; // Remove last shred (which is also last in slot) so that slot is not complete @@ -1014,7 +1008,12 @@ mod test { // Create some shreds in slots 0..num_slots for i in start..start + num_slots { let parent = if i > 0 { i - 1 } else { 0 }; - let (shreds, _) = make_slot_entries(i, parent, num_entries_per_slot as u64); + let (shreds, _) = make_slot_entries( + i, // slot + parent, + num_entries_per_slot as u64, + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); } @@ -1054,7 +1053,12 @@ mod test { // Insert some shreds to create a SlotMeta, should make repairs let num_entries_per_slot = max_ticks_per_n_shreds(1, None) + 1; - let (mut shreds, _) = make_slot_entries(dead_slot, dead_slot - 1, num_entries_per_slot); + let (mut shreds, _) = make_slot_entries( + dead_slot, // slot + dead_slot - 1, // parent_slot + num_entries_per_slot, + true, // merkle_variant + ); blockstore .insert_shreds(shreds[..shreds.len() - 1].to_vec(), None, false) .unwrap(); @@ -1079,10 +1083,9 @@ mod test { let blockstore_path = get_tmp_ledger_path!(); let blockstore = Blockstore::open(&blockstore_path).unwrap(); let cluster_slots = ClusterSlots::default(); - let serve_repair = ServeRepair::new( - Arc::new(new_test_cluster_info(Node::new_localhost().info)), - bank_forks, - ); + let cluster_info = Arc::new(new_test_cluster_info(Node::new_localhost().info)); + let identity_keypair = cluster_info.keypair().clone(); + let serve_repair = ServeRepair::new(cluster_info, bank_forks); let mut duplicate_slot_repair_statuses = HashMap::new(); let dead_slot = 9; let receive_socket = &UdpSocket::bind("0.0.0.0:0").unwrap(); @@ -1094,7 +1097,12 @@ mod test { // Insert some shreds to create a SlotMeta, let num_entries_per_slot = max_ticks_per_n_shreds(1, None) + 1; - let (mut shreds, _) = make_slot_entries(dead_slot, dead_slot - 1, num_entries_per_slot); + let (mut shreds, _) = make_slot_entries( + dead_slot, + dead_slot - 1, + num_entries_per_slot, + true, // merkle_variant + ); blockstore .insert_shreds(shreds[..shreds.len() - 1].to_vec(), None, false) .unwrap(); @@ -1112,6 +1120,7 @@ mod test { &UdpSocket::bind("0.0.0.0:0").unwrap(), &None, &RwLock::new(OutstandingRequests::default()), + &identity_keypair, ); assert!(duplicate_slot_repair_statuses .get(&dead_slot) @@ -1137,6 +1146,7 @@ mod test { &UdpSocket::bind("0.0.0.0:0").unwrap(), &None, &RwLock::new(OutstandingRequests::default()), + &identity_keypair, ); assert_eq!(duplicate_slot_repair_statuses.len(), 1); assert!(duplicate_slot_repair_statuses.get(&dead_slot).is_some()); @@ -1155,6 +1165,7 @@ mod test { &UdpSocket::bind("0.0.0.0:0").unwrap(), &None, &RwLock::new(OutstandingRequests::default()), + &identity_keypair, ); assert!(duplicate_slot_repair_statuses.is_empty()); } diff --git a/core/src/repair_weight.rs b/core/src/repair_weight.rs index b55803bba70ede..f387d4ae219c40 100644 --- a/core/src/repair_weight.rs +++ b/core/src/repair_weight.rs @@ -475,14 +475,9 @@ impl RepairWeight { .get_mut(&orphan_tree_root) .expect("Orphan must exist"); - let num_skip = if parent_tree_root.is_some() { - // Skip the leaf of the parent tree that the - // orphan would merge with later in a call - // to `merge_trees` - 1 - } else { - 0 - }; + // Skip the leaf of the parent tree that the orphan would merge + // with later in a call to `merge_trees` + let num_skip = usize::from(parent_tree_root.is_some()); for ancestor in new_ancestors.iter().skip(num_skip).rev() { self.slot_to_tree.insert(*ancestor, orphan_tree_root); diff --git a/core/src/repair_weighted_traversal.rs b/core/src/repair_weighted_traversal.rs index 426cfd17e8166d..58a2657108335d 100644 --- a/core/src/repair_weighted_traversal.rs +++ b/core/src/repair_weighted_traversal.rs @@ -140,7 +140,10 @@ pub fn get_best_repair_shreds<'a>( pub mod test { use { super::*, - solana_ledger::{get_tmp_ledger_path, shred::Shred}, + solana_ledger::{ + get_tmp_ledger_path, + shred::{Shred, ShredFlags}, + }, solana_runtime::bank_utils, solana_sdk::hash::Hash, trees::tr, @@ -272,15 +275,18 @@ pub mod test { let completed_shreds: Vec = [0, 2, 4, 6] .iter() .map(|slot| { - let mut shred = Shred::new_from_serialized_shred( - blockstore - .get_data_shred(*slot, last_shred - 1) - .unwrap() - .unwrap(), - ) - .unwrap(); - shred.set_index(last_shred as u32); - shred.set_last_in_slot(); + let parent_offset = if *slot == 0 { 0 } else { 1 }; + let shred = Shred::new_from_data( + *slot, + last_shred as u32, // index + parent_offset, + &[0u8; 8], // data + ShredFlags::LAST_SHRED_IN_SLOT, + 8, // reference_tick + 0, // version + last_shred as u32, // fec_set_index + ); + assert!(shred.sanitize().is_ok()); shred }) .collect(); diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index 8d2fec62489c40..c14c2f90003780 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -21,7 +21,7 @@ use { latest_validator_votes_for_frozen_banks::LatestValidatorVotesForFrozenBanks, progress_map::{ForkProgress, ProgressMap, PropagatedStats, ReplaySlotStats}, repair_service::DuplicateSlotsResetReceiver, - rewards_recorder_service::RewardsRecorderSender, + rewards_recorder_service::{RewardsMessage, RewardsRecorderSender}, tower_storage::{SavedTower, SavedTowerVersions, TowerStorage}, unfrozen_gossip_verified_vote_hashes::UnfrozenGossipVerifiedVoteHashes, validator::ProcessBlockStore, @@ -49,7 +49,7 @@ use { solana_poh::poh_recorder::{PohLeaderStatus, PohRecorder, GRACE_TICKS_FACTOR, MAX_GRACE_SLOTS}, solana_program_runtime::timings::ExecuteTimings, solana_rpc::{ - optimistically_confirmed_bank_tracker::{BankNotification, BankNotificationSender}, + optimistically_confirmed_bank_tracker::{BankNotification, BankNotificationSenderConfig}, rpc_subscriptions::RpcSubscriptions, }, solana_runtime::{ @@ -57,6 +57,7 @@ use { bank::{Bank, NewBankOptions}, bank_forks::{BankForks, MAX_ROOT_DISTANCE_FOR_VOTE_ONLY}, commitment::BlockCommitmentCache, + prioritization_fee_cache::PrioritizationFeeCache, vote_sender_types::ReplayVoteSender, }, solana_sdk::{ @@ -70,7 +71,7 @@ use { timing::timestamp, transaction::Transaction, }, - solana_vote_program::vote_state::{CompactVoteStateUpdate, VoteTransaction}, + solana_vote_program::vote_state::VoteTransaction, std::{ collections::{HashMap, HashSet}, result, @@ -93,11 +94,12 @@ const MAX_VOTE_REFRESH_INTERVAL_MILLIS: usize = 5000; // Expect this number to be small enough to minimize thread pool overhead while large enough // to be able to replay all active forks at the same time in most cases. const MAX_CONCURRENT_FORKS_TO_REPLAY: usize = 4; +const MAX_REPAIR_RETRY_LOOP_ATTEMPTS: usize = 10; lazy_static! { static ref PAR_THREAD_POOL: ThreadPool = rayon::ThreadPoolBuilder::new() .num_threads(MAX_CONCURRENT_FORKS_TO_REPLAY) - .thread_name(|ix| format!("replay_{}", ix)) + .thread_name(|ix| format!("solReplay{:02}", ix)) .build() .unwrap(); } @@ -158,7 +160,7 @@ pub struct ReplayStageConfig { pub transaction_status_sender: Option, pub rewards_recorder_sender: Option, pub cache_block_meta_sender: Option, - pub bank_notification_sender: Option, + pub bank_notification_sender: Option, pub wait_for_vote_to_start_leader: bool, pub ancestor_hashes_replay_update_sender: AncestorHashesReplayUpdateSender, pub tower_storage: Arc, @@ -397,6 +399,7 @@ impl ReplayStage { drop_bank_sender: Sender>>, block_metadata_notifier: Option, log_messages_bytes_limit: Option, + prioritization_fee_cache: Arc, ) -> Self { let mut tower = if let Some(process_blockstore) = maybe_process_blockstore { let tower = process_blockstore.process_to_create_tower(); @@ -433,511 +436,564 @@ impl ReplayStage { block_commitment_cache.clone(), rpc_subscriptions.clone(), ); - - #[allow(clippy::cognitive_complexity)] - let t_replay = Builder::new() - .name("solana-replay-stage".to_string()) - .spawn(move || { - let verify_recyclers = VerifyRecyclers::default(); - let _exit = Finalizer::new(exit.clone()); - let mut identity_keypair = cluster_info.keypair().clone(); - let mut my_pubkey = identity_keypair.pubkey(); - let ( - mut progress, - mut heaviest_subtree_fork_choice, - ) = Self::initialize_progress_and_fork_choice_with_locked_bank_forks( + let run_replay = move || { + let verify_recyclers = VerifyRecyclers::default(); + let _exit = Finalizer::new(exit.clone()); + let mut identity_keypair = cluster_info.keypair().clone(); + let mut my_pubkey = identity_keypair.pubkey(); + let (mut progress, mut heaviest_subtree_fork_choice) = + Self::initialize_progress_and_fork_choice_with_locked_bank_forks( &bank_forks, &my_pubkey, &vote_account, ); - let mut current_leader = None; - let mut last_reset = Hash::default(); - let mut partition_exists = false; - let mut skipped_slots_info = SkippedSlotsInfo::default(); - let mut replay_timing = ReplayTiming::default(); - let mut duplicate_slots_tracker = DuplicateSlotsTracker::default(); - let mut gossip_duplicate_confirmed_slots: GossipDuplicateConfirmedSlots = GossipDuplicateConfirmedSlots::default(); - let mut epoch_slots_frozen_slots: EpochSlotsFrozenSlots = EpochSlotsFrozenSlots::default(); - let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); - let mut unfrozen_gossip_verified_vote_hashes: UnfrozenGossipVerifiedVoteHashes = UnfrozenGossipVerifiedVoteHashes::default(); - let mut latest_validator_votes_for_frozen_banks: LatestValidatorVotesForFrozenBanks = LatestValidatorVotesForFrozenBanks::default(); - let mut voted_signatures = Vec::new(); - let mut has_new_vote_been_rooted = !wait_for_vote_to_start_leader; - let mut last_vote_refresh_time = LastVoteRefreshTime { - last_refresh_time: Instant::now(), - last_print_time: Instant::now(), - }; - let (working_bank, in_vote_only_mode) = { - let r_bank_forks = bank_forks.read().unwrap(); - (r_bank_forks.working_bank(), r_bank_forks.get_vote_only_mode_signal()) - }; + let mut current_leader = None; + let mut last_reset = Hash::default(); + let mut partition_exists = false; + let mut skipped_slots_info = SkippedSlotsInfo::default(); + let mut replay_timing = ReplayTiming::default(); + let mut duplicate_slots_tracker = DuplicateSlotsTracker::default(); + let mut gossip_duplicate_confirmed_slots: GossipDuplicateConfirmedSlots = + GossipDuplicateConfirmedSlots::default(); + let mut epoch_slots_frozen_slots: EpochSlotsFrozenSlots = + EpochSlotsFrozenSlots::default(); + let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); + let mut unfrozen_gossip_verified_vote_hashes: UnfrozenGossipVerifiedVoteHashes = + UnfrozenGossipVerifiedVoteHashes::default(); + let mut latest_validator_votes_for_frozen_banks: LatestValidatorVotesForFrozenBanks = + LatestValidatorVotesForFrozenBanks::default(); + let mut voted_signatures = Vec::new(); + let mut has_new_vote_been_rooted = !wait_for_vote_to_start_leader; + let mut last_vote_refresh_time = LastVoteRefreshTime { + last_refresh_time: Instant::now(), + last_print_time: Instant::now(), + }; + let (working_bank, in_vote_only_mode) = { + let r_bank_forks = bank_forks.read().unwrap(); + ( + r_bank_forks.working_bank(), + r_bank_forks.get_vote_only_mode_signal(), + ) + }; - Self::reset_poh_recorder( - &my_pubkey, + Self::reset_poh_recorder( + &my_pubkey, + &blockstore, + &working_bank, + &poh_recorder, + &leader_schedule_cache, + ); + drop(working_bank); + + loop { + // Stop getting entries if we get exit signal + if exit.load(Ordering::Relaxed) { + break; + } + + let mut generate_new_bank_forks_time = + Measure::start("generate_new_bank_forks_time"); + Self::generate_new_bank_forks( &blockstore, - &working_bank, - &poh_recorder, + &bank_forks, &leader_schedule_cache, + &rpc_subscriptions, + &mut progress, + &mut replay_timing, ); + generate_new_bank_forks_time.stop(); - loop { - // Stop getting entries if we get exit signal - if exit.load(Ordering::Relaxed) { - break; - } + let mut tpu_has_bank = poh_recorder.read().unwrap().has_bank(); - let mut generate_new_bank_forks_time = - Measure::start("generate_new_bank_forks_time"); - Self::generate_new_bank_forks( - &blockstore, - &bank_forks, - &leader_schedule_cache, - &rpc_subscriptions, - &mut progress, - &mut replay_timing, - ); - generate_new_bank_forks_time.stop(); + let mut replay_active_banks_time = Measure::start("replay_active_banks_time"); + let mut ancestors = bank_forks.read().unwrap().ancestors(); + let mut descendants = bank_forks.read().unwrap().descendants(); + let did_complete_bank = Self::replay_active_banks( + &blockstore, + &bank_forks, + &my_pubkey, + &vote_account, + &mut progress, + transaction_status_sender.as_ref(), + cache_block_meta_sender.as_ref(), + &verify_recyclers, + &mut heaviest_subtree_fork_choice, + &replay_vote_sender, + &bank_notification_sender, + &rewards_recorder_sender, + &rpc_subscriptions, + &mut duplicate_slots_tracker, + &gossip_duplicate_confirmed_slots, + &mut epoch_slots_frozen_slots, + &mut unfrozen_gossip_verified_vote_hashes, + &mut latest_validator_votes_for_frozen_banks, + &cluster_slots_update_sender, + &cost_update_sender, + &mut duplicate_slots_to_repair, + &ancestor_hashes_replay_update_sender, + block_metadata_notifier.clone(), + &mut replay_timing, + log_messages_bytes_limit, + &prioritization_fee_cache, + &mut purge_repair_slot_counter, + ); + replay_active_banks_time.stop(); - let mut tpu_has_bank = poh_recorder.read().unwrap().has_bank(); + let forks_root = bank_forks.read().unwrap().root(); - let mut replay_active_banks_time = Measure::start("replay_active_banks_time"); - let mut ancestors = bank_forks.read().unwrap().ancestors(); - let mut descendants = bank_forks.read().unwrap().descendants(); - let did_complete_bank = Self::replay_active_banks( + // Reset any dead slots that have been frozen by a sufficient portion of + // the network. Signalled by repair_service. + let mut purge_dead_slots_time = Measure::start("purge_dead_slots"); + Self::process_epoch_slots_frozen_dead_slots( + &my_pubkey, + &blockstore, + &epoch_slots_frozen_receiver, + &mut duplicate_slots_tracker, + &gossip_duplicate_confirmed_slots, + &mut epoch_slots_frozen_slots, + &mut progress, + &mut heaviest_subtree_fork_choice, + &bank_forks, + &mut duplicate_slots_to_repair, + &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, + ); + purge_dead_slots_time.stop(); + + // Check for any newly confirmed slots detected from gossip. + let mut process_gossip_duplicate_confirmed_slots_time = + Measure::start("process_gossip_duplicate_confirmed_slots"); + Self::process_gossip_duplicate_confirmed_slots( + &gossip_duplicate_confirmed_slots_receiver, + &blockstore, + &mut duplicate_slots_tracker, + &mut gossip_duplicate_confirmed_slots, + &mut epoch_slots_frozen_slots, + &bank_forks, + &mut progress, + &mut heaviest_subtree_fork_choice, + &mut duplicate_slots_to_repair, + &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, + ); + process_gossip_duplicate_confirmed_slots_time.stop(); + + // Ingest any new verified votes from gossip. Important for fork choice + // and switching proofs because these may be votes that haven't yet been + // included in a block, so we may not have yet observed these votes just + // by replaying blocks. + let mut process_unfrozen_gossip_verified_vote_hashes_time = + Measure::start("process_gossip_verified_vote_hashes"); + Self::process_gossip_verified_vote_hashes( + &gossip_verified_vote_hash_receiver, + &mut unfrozen_gossip_verified_vote_hashes, + &heaviest_subtree_fork_choice, + &mut latest_validator_votes_for_frozen_banks, + ); + for _ in gossip_verified_vote_hash_receiver.try_iter() {} + process_unfrozen_gossip_verified_vote_hashes_time.stop(); + + // Check to remove any duplicated slots from fork choice + let mut process_duplicate_slots_time = Measure::start("process_duplicate_slots"); + if !tpu_has_bank { + Self::process_duplicate_slots( &blockstore, - &bank_forks, - &my_pubkey, - &vote_account, - &mut progress, - transaction_status_sender.as_ref(), - cache_block_meta_sender.as_ref(), - &verify_recyclers, - &mut heaviest_subtree_fork_choice, - &replay_vote_sender, - &bank_notification_sender, - &rewards_recorder_sender, - &rpc_subscriptions, + &duplicate_slots_receiver, &mut duplicate_slots_tracker, &gossip_duplicate_confirmed_slots, &mut epoch_slots_frozen_slots, - &mut unfrozen_gossip_verified_vote_hashes, - &mut latest_validator_votes_for_frozen_banks, - &cluster_slots_update_sender, - &cost_update_sender, + &bank_forks, + &mut progress, + &mut heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, - block_metadata_notifier.clone(), - &mut replay_timing, - log_messages_bytes_limit + &mut purge_repair_slot_counter, ); - replay_active_banks_time.stop(); + } + process_duplicate_slots_time.stop(); - let forks_root = bank_forks.read().unwrap().root(); + let mut collect_frozen_banks_time = Measure::start("frozen_banks"); + let mut frozen_banks: Vec<_> = bank_forks + .read() + .unwrap() + .frozen_banks() + .into_iter() + .filter(|(slot, _)| *slot >= forks_root) + .map(|(_, bank)| bank) + .collect(); + collect_frozen_banks_time.stop(); - // Reset any dead slots that have been frozen by a sufficient portion of - // the network. Signalled by repair_service. - let mut purge_dead_slots_time = Measure::start("purge_dead_slots"); - Self::process_epoch_slots_frozen_dead_slots( - &my_pubkey, - &blockstore, - &epoch_slots_frozen_receiver, - &mut duplicate_slots_tracker, - &gossip_duplicate_confirmed_slots, - &mut epoch_slots_frozen_slots, - &mut progress, - &mut heaviest_subtree_fork_choice, + let mut compute_bank_stats_time = Measure::start("compute_bank_stats"); + let newly_computed_slot_stats = Self::compute_bank_stats( + &vote_account, + &ancestors, + &mut frozen_banks, + &mut tower, + &mut progress, + &vote_tracker, + &cluster_slots, + &bank_forks, + &mut heaviest_subtree_fork_choice, + &mut latest_validator_votes_for_frozen_banks, + ); + compute_bank_stats_time.stop(); + + let mut compute_slot_stats_time = Measure::start("compute_slot_stats_time"); + for slot in newly_computed_slot_stats { + let fork_stats = progress.get_fork_stats(slot).unwrap(); + let confirmed_forks = Self::confirm_forks( + &tower, + &fork_stats.voted_stakes, + fork_stats.total_stake, + &progress, &bank_forks, - &mut duplicate_slots_to_repair, - &ancestor_hashes_replay_update_sender ); - purge_dead_slots_time.stop(); - // Check for any newly confirmed slots detected from gossip. - let mut process_gossip_duplicate_confirmed_slots_time = Measure::start("process_gossip_duplicate_confirmed_slots"); - Self::process_gossip_duplicate_confirmed_slots( - &gossip_duplicate_confirmed_slots_receiver, + Self::mark_slots_confirmed( + &confirmed_forks, &blockstore, - &mut duplicate_slots_tracker, - &mut gossip_duplicate_confirmed_slots, - &mut epoch_slots_frozen_slots, &bank_forks, &mut progress, + &mut duplicate_slots_tracker, &mut heaviest_subtree_fork_choice, + &mut epoch_slots_frozen_slots, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, ); - process_gossip_duplicate_confirmed_slots_time.stop(); - + } + compute_slot_stats_time.stop(); - // Ingest any new verified votes from gossip. Important for fork choice - // and switching proofs because these may be votes that haven't yet been - // included in a block, so we may not have yet observed these votes just - // by replaying blocks. - let mut process_unfrozen_gossip_verified_vote_hashes_time = Measure::start("process_gossip_verified_vote_hashes"); - Self::process_gossip_verified_vote_hashes( - &gossip_verified_vote_hash_receiver, - &mut unfrozen_gossip_verified_vote_hashes, - &heaviest_subtree_fork_choice, - &mut latest_validator_votes_for_frozen_banks, + let mut select_forks_time = Measure::start("select_forks_time"); + let (heaviest_bank, heaviest_bank_on_same_voted_fork) = + heaviest_subtree_fork_choice.select_forks( + &frozen_banks, + &tower, + &progress, + &ancestors, + &bank_forks, ); - for _ in gossip_verified_vote_hash_receiver.try_iter() {} - process_unfrozen_gossip_verified_vote_hashes_time.stop(); + select_forks_time.stop(); - // Check to remove any duplicated slots from fork choice - let mut process_duplicate_slots_time = Measure::start("process_duplicate_slots"); - if !tpu_has_bank { - Self::process_duplicate_slots( - &blockstore, - &duplicate_slots_receiver, - &mut duplicate_slots_tracker, - &gossip_duplicate_confirmed_slots, - &mut epoch_slots_frozen_slots, - &bank_forks, - &mut progress, - &mut heaviest_subtree_fork_choice, - &mut duplicate_slots_to_repair, - &ancestor_hashes_replay_update_sender, + Self::check_for_vote_only_mode( + heaviest_bank.slot(), + forks_root, + &in_vote_only_mode, + &bank_forks, + ); + + if let Some(heaviest_bank_on_same_voted_fork) = + heaviest_bank_on_same_voted_fork.as_ref() + { + if let Some(my_latest_landed_vote) = + progress.my_latest_landed_vote(heaviest_bank_on_same_voted_fork.slot()) + { + Self::refresh_last_vote( + &mut tower, + heaviest_bank_on_same_voted_fork, + my_latest_landed_vote, + &vote_account, + &identity_keypair, + &authorized_voter_keypairs.read().unwrap(), + &mut voted_signatures, + has_new_vote_been_rooted, + &mut last_vote_refresh_time, + &voting_sender, + wait_to_vote_slot, ); } - process_duplicate_slots_time.stop(); - - let mut collect_frozen_banks_time = Measure::start("frozen_banks"); - let mut frozen_banks: Vec<_> = bank_forks - .read() - .unwrap() - .frozen_banks() - .into_iter() - .filter(|(slot, _)| *slot >= forks_root) - .map(|(_, bank)| bank) - .collect(); - collect_frozen_banks_time.stop(); - - let mut compute_bank_stats_time = Measure::start("compute_bank_stats"); - let newly_computed_slot_stats = Self::compute_bank_stats( - &vote_account, - &ancestors, - &mut frozen_banks, - &mut tower, - &mut progress, - &vote_tracker, - &cluster_slots, - &bank_forks, - &mut heaviest_subtree_fork_choice, - &mut latest_validator_votes_for_frozen_banks, + } + + let mut select_vote_and_reset_forks_time = + Measure::start("select_vote_and_reset_forks"); + let SelectVoteAndResetForkResult { + vote_bank, + reset_bank, + heaviest_fork_failures, + } = Self::select_vote_and_reset_forks( + &heaviest_bank, + heaviest_bank_on_same_voted_fork.as_ref(), + &ancestors, + &descendants, + &progress, + &mut tower, + &latest_validator_votes_for_frozen_banks, + &heaviest_subtree_fork_choice, + ); + select_vote_and_reset_forks_time.stop(); + + let mut heaviest_fork_failures_time = Measure::start("heaviest_fork_failures_time"); + if tower.is_recent(heaviest_bank.slot()) && !heaviest_fork_failures.is_empty() { + info!( + "Couldn't vote on heaviest fork: {:?}, heaviest_fork_failures: {:?}", + heaviest_bank.slot(), + heaviest_fork_failures ); - compute_bank_stats_time.stop(); - - let mut compute_slot_stats_time = Measure::start("compute_slot_stats_time"); - for slot in newly_computed_slot_stats { - let fork_stats = progress.get_fork_stats(slot).unwrap(); - let confirmed_forks = Self::confirm_forks( - &tower, - &fork_stats.voted_stakes, - fork_stats.total_stake, - &progress, - &bank_forks, - ); - Self::mark_slots_confirmed(&confirmed_forks, &blockstore, &bank_forks, &mut progress, &mut duplicate_slots_tracker, &mut heaviest_subtree_fork_choice, &mut epoch_slots_frozen_slots, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender); - } - compute_slot_stats_time.stop(); - - let mut select_forks_time = Measure::start("select_forks_time"); - let (heaviest_bank, heaviest_bank_on_same_voted_fork) = heaviest_subtree_fork_choice - .select_forks(&frozen_banks, &tower, &progress, &ancestors, &bank_forks); - select_forks_time.stop(); - - Self::check_for_vote_only_mode(heaviest_bank.slot(), forks_root, &in_vote_only_mode, &bank_forks); - - if let Some(heaviest_bank_on_same_voted_fork) = heaviest_bank_on_same_voted_fork.as_ref() { - if let Some(my_latest_landed_vote) = progress.my_latest_landed_vote(heaviest_bank_on_same_voted_fork.slot()) { - Self::refresh_last_vote(&mut tower, - heaviest_bank_on_same_voted_fork, - my_latest_landed_vote, - &vote_account, - &identity_keypair, - &authorized_voter_keypairs.read().unwrap(), - &mut voted_signatures, - has_new_vote_been_rooted, &mut - last_vote_refresh_time, - &voting_sender, - wait_to_vote_slot, - ); + for r in heaviest_fork_failures { + if let HeaviestForkFailures::NoPropagatedConfirmation(slot) = r { + if let Some(latest_leader_slot) = + progress.get_latest_leader_slot_must_exist(slot) + { + progress.log_propagated_stats(latest_leader_slot, &bank_forks); + } } } + } + heaviest_fork_failures_time.stop(); - let mut select_vote_and_reset_forks_time = - Measure::start("select_vote_and_reset_forks"); - let SelectVoteAndResetForkResult { + let mut voting_time = Measure::start("voting_time"); + // Vote on a fork + if let Some((ref vote_bank, ref switch_fork_decision)) = vote_bank { + if let Some(votable_leader) = + leader_schedule_cache.slot_leader_at(vote_bank.slot(), Some(vote_bank)) + { + Self::log_leader_change( + &my_pubkey, + vote_bank.slot(), + &mut current_leader, + &votable_leader, + ); + } + + Self::handle_votable_bank( vote_bank, - reset_bank, - heaviest_fork_failures, - } = Self::select_vote_and_reset_forks( - &heaviest_bank, - heaviest_bank_on_same_voted_fork.as_ref(), - &ancestors, - &descendants, - &progress, + switch_fork_decision, + &bank_forks, &mut tower, - &latest_validator_votes_for_frozen_banks, - &heaviest_subtree_fork_choice, + &mut progress, + &vote_account, + &identity_keypair, + &authorized_voter_keypairs.read().unwrap(), + &blockstore, + &leader_schedule_cache, + &lockouts_sender, + &accounts_background_request_sender, + &latest_root_senders, + &rpc_subscriptions, + &block_commitment_cache, + &mut heaviest_subtree_fork_choice, + &bank_notification_sender, + &mut duplicate_slots_tracker, + &mut gossip_duplicate_confirmed_slots, + &mut unfrozen_gossip_verified_vote_hashes, + &mut voted_signatures, + &mut has_new_vote_been_rooted, + &mut replay_timing, + &voting_sender, + &mut epoch_slots_frozen_slots, + &drop_bank_sender, + wait_to_vote_slot, ); - select_vote_and_reset_forks_time.stop(); + }; + voting_time.stop(); - let mut heaviest_fork_failures_time = Measure::start("heaviest_fork_failures_time"); - if tower.is_recent(heaviest_bank.slot()) && !heaviest_fork_failures.is_empty() { + let mut reset_bank_time = Measure::start("reset_bank"); + // Reset onto a fork + if let Some(reset_bank) = reset_bank { + if last_reset != reset_bank.last_blockhash() { info!( - "Couldn't vote on heaviest fork: {:?}, heaviest_fork_failures: {:?}", - heaviest_bank.slot(), - heaviest_fork_failures + "vote bank: {:?} reset bank: {:?}", + vote_bank + .as_ref() + .map(|(b, switch_fork_decision)| (b.slot(), switch_fork_decision)), + reset_bank.slot(), + ); + let fork_progress = progress + .get(&reset_bank.slot()) + .expect("bank to reset to must exist in progress map"); + datapoint_info!( + "blocks_produced", + ("num_blocks_on_fork", fork_progress.num_blocks_on_fork, i64), + ( + "num_dropped_blocks_on_fork", + fork_progress.num_dropped_blocks_on_fork, + i64 + ), ); - for r in heaviest_fork_failures { - if let HeaviestForkFailures::NoPropagatedConfirmation(slot) = r { - if let Some(latest_leader_slot) = - progress.get_latest_leader_slot_must_exist(slot) - { - progress.log_propagated_stats(latest_leader_slot, &bank_forks); - } - } - } - } - heaviest_fork_failures_time.stop(); + if my_pubkey != cluster_info.id() { + identity_keypair = cluster_info.keypair().clone(); + let my_old_pubkey = my_pubkey; + my_pubkey = identity_keypair.pubkey(); + + // Load the new identity's tower + tower = Tower::restore(tower_storage.as_ref(), &my_pubkey) + .and_then(|restored_tower| { + let root_bank = bank_forks.read().unwrap().root_bank(); + let slot_history = root_bank.get_slot_history(); + restored_tower.adjust_lockouts_after_replay( + root_bank.slot(), + &slot_history, + ) + }) + .unwrap_or_else(|err| { + if err.is_file_missing() { + Tower::new_from_bankforks( + &bank_forks.read().unwrap(), + &my_pubkey, + &vote_account, + ) + } else { + error!("Failed to load tower for {}: {}", my_pubkey, err); + std::process::exit(1); + } + }); - let mut voting_time = Measure::start("voting_time"); - // Vote on a fork - if let Some((ref vote_bank, ref switch_fork_decision)) = vote_bank { - if let Some(votable_leader) = - leader_schedule_cache.slot_leader_at(vote_bank.slot(), Some(vote_bank)) - { - Self::log_leader_change( - &my_pubkey, - vote_bank.slot(), - &mut current_leader, - &votable_leader, - ); + // Ensure the validator can land votes with the new identity before + // becoming leader + has_new_vote_been_rooted = !wait_for_vote_to_start_leader; + warn!("Identity changed from {} to {}", my_old_pubkey, my_pubkey); } - Self::handle_votable_bank( - vote_bank, - switch_fork_decision, - &bank_forks, - &mut tower, - &mut progress, - &vote_account, - &identity_keypair, - &authorized_voter_keypairs.read().unwrap(), + Self::reset_poh_recorder( + &my_pubkey, &blockstore, + &reset_bank, + &poh_recorder, &leader_schedule_cache, - &lockouts_sender, - &accounts_background_request_sender, - &latest_root_senders, - &rpc_subscriptions, - &block_commitment_cache, - &mut heaviest_subtree_fork_choice, - &bank_notification_sender, - &mut duplicate_slots_tracker, - &mut gossip_duplicate_confirmed_slots, - &mut unfrozen_gossip_verified_vote_hashes, - &mut voted_signatures, - &mut has_new_vote_been_rooted, - &mut replay_timing, - &voting_sender, - &mut epoch_slots_frozen_slots, - &drop_bank_sender, - wait_to_vote_slot, ); - }; - voting_time.stop(); - - let mut reset_bank_time = Measure::start("reset_bank"); - // Reset onto a fork - if let Some(reset_bank) = reset_bank { - if last_reset != reset_bank.last_blockhash() { - info!( - "vote bank: {:?} reset bank: {:?}", - vote_bank.as_ref().map(|(b, switch_fork_decision)| ( - b.slot(), - switch_fork_decision - )), - reset_bank.slot(), - ); - let fork_progress = progress - .get(&reset_bank.slot()) - .expect("bank to reset to must exist in progress map"); - datapoint_info!( - "blocks_produced", - ("num_blocks_on_fork", fork_progress.num_blocks_on_fork, i64), - ( - "num_dropped_blocks_on_fork", - fork_progress.num_dropped_blocks_on_fork, - i64 - ), - ); - - if my_pubkey != cluster_info.id() { - identity_keypair = cluster_info.keypair().clone(); - let my_old_pubkey = my_pubkey; - my_pubkey = identity_keypair.pubkey(); - - // Load the new identity's tower - tower = Tower::restore(tower_storage.as_ref(), &my_pubkey) - .and_then(|restored_tower| { - let root_bank = bank_forks.read().unwrap().root_bank(); - let slot_history = root_bank.get_slot_history(); - restored_tower.adjust_lockouts_after_replay(root_bank.slot(), &slot_history) - }). - unwrap_or_else(|err| { - if err.is_file_missing() { - Tower::new_from_bankforks( - &bank_forks.read().unwrap(), - &my_pubkey, - &vote_account, - ) - } else { - error!("Failed to load tower for {}: {}", my_pubkey, err); - std::process::exit(1); - } - }); - - // Ensure the validator can land votes with the new identity before - // becoming leader - has_new_vote_been_rooted = !wait_for_vote_to_start_leader; - warn!("Identity changed from {} to {}", my_old_pubkey, my_pubkey); - } - - Self::reset_poh_recorder( - &my_pubkey, - &blockstore, - &reset_bank, - &poh_recorder, - &leader_schedule_cache, + last_reset = reset_bank.last_blockhash(); + tpu_has_bank = false; + + if let Some(last_voted_slot) = tower.last_voted_slot() { + // If the current heaviest bank is not a descendant of the last voted slot, + // there must be a partition + let partition_detected = Self::is_partition_detected( + &ancestors, + last_voted_slot, + heaviest_bank.slot(), ); - last_reset = reset_bank.last_blockhash(); - tpu_has_bank = false; - - if let Some(last_voted_slot) = tower.last_voted_slot() { - // If the current heaviest bank is not a descendant of the last voted slot, - // there must be a partition - let partition_detected = Self::is_partition_detected(&ancestors, last_voted_slot, heaviest_bank.slot()); - if !partition_exists && partition_detected - { - warn!( + if !partition_exists && partition_detected { + warn!( "PARTITION DETECTED waiting to join heaviest fork: {} last vote: {:?}, reset slot: {}", heaviest_bank.slot(), last_voted_slot, reset_bank.slot(), ); - inc_new_counter_info!("replay_stage-partition_detected", 1); - datapoint_info!( - "replay_stage-partition", - ("slot", reset_bank.slot() as i64, i64) - ); - partition_exists = true; - } else if partition_exists - && !partition_detected - { - warn!( + inc_new_counter_info!("replay_stage-partition_detected", 1); + datapoint_info!( + "replay_stage-partition", + ("slot", reset_bank.slot() as i64, i64) + ); + partition_exists = true; + } else if partition_exists && !partition_detected { + warn!( "PARTITION resolved heaviest fork: {} last vote: {:?}, reset slot: {}", heaviest_bank.slot(), last_voted_slot, reset_bank.slot() ); - partition_exists = false; - inc_new_counter_info!("replay_stage-partition_resolved", 1); - } + partition_exists = false; + inc_new_counter_info!("replay_stage-partition_resolved", 1); } } } - reset_bank_time.stop(); - - let mut start_leader_time = Measure::start("start_leader_time"); - let mut dump_then_repair_correct_slots_time = Measure::start("dump_then_repair_correct_slots_time"); - // Used for correctness check - let poh_bank = poh_recorder.read().unwrap().bank(); - // Dump any duplicate slots that have been confirmed by the network in - // anticipation of repairing the confirmed version of the slot. - // - // Has to be before `maybe_start_leader()`. Otherwise, `ancestors` and `descendants` - // will be outdated, and we cannot assume `poh_bank` will be in either of these maps. - Self::dump_then_repair_correct_slots(&mut duplicate_slots_to_repair, &mut ancestors, &mut descendants, &mut progress, &bank_forks, &blockstore, poh_bank.map(|bank| bank.slot())); - dump_then_repair_correct_slots_time.stop(); + } + reset_bank_time.stop(); + + let mut start_leader_time = Measure::start("start_leader_time"); + let mut dump_then_repair_correct_slots_time = + Measure::start("dump_then_repair_correct_slots_time"); + // Used for correctness check + let poh_bank = poh_recorder.read().unwrap().bank(); + // Dump any duplicate slots that have been confirmed by the network in + // anticipation of repairing the confirmed version of the slot. + // + // Has to be before `maybe_start_leader()`. Otherwise, `ancestors` and `descendants` + // will be outdated, and we cannot assume `poh_bank` will be in either of these maps. + Self::dump_then_repair_correct_slots( + &mut duplicate_slots_to_repair, + &mut ancestors, + &mut descendants, + &mut progress, + &bank_forks, + &blockstore, + poh_bank.map(|bank| bank.slot()), + &mut purge_repair_slot_counter, + ); + dump_then_repair_correct_slots_time.stop(); - let mut retransmit_not_propagated_time = Measure::start("retransmit_not_propagated_time"); - Self::retransmit_latest_unpropagated_leader_slot( + let mut retransmit_not_propagated_time = + Measure::start("retransmit_not_propagated_time"); + Self::retransmit_latest_unpropagated_leader_slot( + &poh_recorder, + &retransmit_slots_sender, + &mut progress, + ); + retransmit_not_propagated_time.stop(); + + // From this point on, its not safe to use ancestors/descendants since maybe_start_leader + // may add a bank that will not included in either of these maps. + drop(ancestors); + drop(descendants); + if !tpu_has_bank { + Self::maybe_start_leader( + &my_pubkey, + &bank_forks, &poh_recorder, - &retransmit_slots_sender, + &leader_schedule_cache, + &rpc_subscriptions, &mut progress, + &retransmit_slots_sender, + &mut skipped_slots_info, + has_new_vote_been_rooted, + transaction_status_sender.is_some(), ); - retransmit_not_propagated_time.stop(); - - // From this point on, its not safe to use ancestors/descendants since maybe_start_leader - // may add a bank that will not included in either of these maps. - drop(ancestors); - drop(descendants); - if !tpu_has_bank { - Self::maybe_start_leader( + + let poh_bank = poh_recorder.read().unwrap().bank(); + if let Some(bank) = poh_bank { + Self::log_leader_change( + &my_pubkey, + bank.slot(), + &mut current_leader, &my_pubkey, - &bank_forks, - &poh_recorder, - &leader_schedule_cache, - &rpc_subscriptions, - &mut progress, - &retransmit_slots_sender, - &mut skipped_slots_info, - has_new_vote_been_rooted, - transaction_status_sender.is_some(), ); - - let poh_bank = poh_recorder.read().unwrap().bank(); - if let Some(bank) = poh_bank { - Self::log_leader_change( - &my_pubkey, - bank.slot(), - &mut current_leader, - &my_pubkey, - ); - } - } - start_leader_time.stop(); - - let mut wait_receive_time = Measure::start("wait_receive_time"); - if !did_complete_bank { - // only wait for the signal if we did not just process a bank; maybe there are more slots available - - let timer = Duration::from_millis(100); - let result = ledger_signal_receiver.recv_timeout(timer); - match result { - Err(RecvTimeoutError::Timeout) => (), - Err(_) => break, - Ok(_) => trace!("blockstore signal"), - }; } - wait_receive_time.stop(); - - replay_timing.update( - collect_frozen_banks_time.as_us(), - compute_bank_stats_time.as_us(), - select_vote_and_reset_forks_time.as_us(), - start_leader_time.as_us(), - reset_bank_time.as_us(), - voting_time.as_us(), - select_forks_time.as_us(), - compute_slot_stats_time.as_us(), - generate_new_bank_forks_time.as_us(), - replay_active_banks_time.as_us(), - wait_receive_time.as_us(), - heaviest_fork_failures_time.as_us(), - if did_complete_bank {1} else {0}, - process_gossip_duplicate_confirmed_slots_time.as_us(), - process_unfrozen_gossip_verified_vote_hashes_time.as_us(), - process_duplicate_slots_time.as_us(), - dump_then_repair_correct_slots_time.as_us(), - retransmit_not_propagated_time.as_us(), - ); } - }) + start_leader_time.stop(); + + let mut wait_receive_time = Measure::start("wait_receive_time"); + if !did_complete_bank { + // only wait for the signal if we did not just process a bank; maybe there are more slots available + + let timer = Duration::from_millis(100); + let result = ledger_signal_receiver.recv_timeout(timer); + match result { + Err(RecvTimeoutError::Timeout) => (), + Err(_) => break, + Ok(_) => trace!("blockstore signal"), + }; + } + wait_receive_time.stop(); + + replay_timing.update( + collect_frozen_banks_time.as_us(), + compute_bank_stats_time.as_us(), + select_vote_and_reset_forks_time.as_us(), + start_leader_time.as_us(), + reset_bank_time.as_us(), + voting_time.as_us(), + select_forks_time.as_us(), + compute_slot_stats_time.as_us(), + generate_new_bank_forks_time.as_us(), + replay_active_banks_time.as_us(), + wait_receive_time.as_us(), + heaviest_fork_failures_time.as_us(), + u64::from(did_complete_bank), + process_gossip_duplicate_confirmed_slots_time.as_us(), + process_unfrozen_gossip_verified_vote_hashes_time.as_us(), + process_duplicate_slots_time.as_us(), + dump_then_repair_correct_slots_time.as_us(), + retransmit_not_propagated_time.as_us(), + ); + } + }; + let t_replay = Builder::new() + .name("solReplayStage".to_string()) + .spawn(run_replay) .unwrap(); Self { @@ -1107,6 +1163,7 @@ impl ReplayStage { bank_forks: &RwLock, blockstore: &Blockstore, poh_bank_slot: Option, + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, ) { if duplicate_slots_to_repair.is_empty() { return; @@ -1175,9 +1232,16 @@ impl ReplayStage { bank_forks, blockstore, ); + let attempt_no = purge_repair_slot_counter + .entry(*duplicate_slot) + .and_modify(|x| *x += 1) + .or_insert(1); + if *attempt_no > MAX_REPAIR_RETRY_LOOP_ATTEMPTS { + panic!("We have tried to repair duplicate slot: {} more than {} times and are unable to freeze a block with bankhash {}, instead we have a block with bankhash {:?}. This is most likely a bug in the runtime. At this point manual intervention is needed to make progress. Exiting", *duplicate_slot, MAX_REPAIR_RETRY_LOOP_ATTEMPTS, *correct_hash, frozen_hash); + } warn!( - "Notifying repair service to repair duplicate slot: {}", - *duplicate_slot, + "Notifying repair service to repair duplicate slot: {}, attempt {}", + *duplicate_slot, *attempt_no, ); true // TODO: Send signal to repair to repair the correct version of @@ -1210,6 +1274,7 @@ impl ReplayStage { bank_forks: &RwLock, duplicate_slots_to_repair: &mut DuplicateSlotsToRepair, ancestor_hashes_replay_update_sender: &AncestorHashesReplayUpdateSender, + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, ) { let root = bank_forks.read().unwrap().root(); for maybe_purgeable_duplicate_slots in epoch_slots_frozen_receiver.try_iter() { @@ -1243,6 +1308,7 @@ impl ReplayStage { fork_choice, duplicate_slots_to_repair, ancestor_hashes_replay_update_sender, + purge_repair_slot_counter, SlotStateUpdate::EpochSlotsFrozen(epoch_slots_frozen_state), ); } @@ -1386,6 +1452,7 @@ impl ReplayStage { fork_choice: &mut HeaviestSubtreeForkChoice, duplicate_slots_to_repair: &mut DuplicateSlotsToRepair, ancestor_hashes_replay_update_sender: &AncestorHashesReplayUpdateSender, + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, ) { let root = bank_forks.read().unwrap().root(); for new_confirmed_slots in gossip_duplicate_confirmed_slots_receiver.try_iter() { @@ -1414,6 +1481,7 @@ impl ReplayStage { fork_choice, duplicate_slots_to_repair, ancestor_hashes_replay_update_sender, + purge_repair_slot_counter, SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), ); } @@ -1452,6 +1520,7 @@ impl ReplayStage { fork_choice: &mut HeaviestSubtreeForkChoice, duplicate_slots_to_repair: &mut DuplicateSlotsToRepair, ancestor_hashes_replay_update_sender: &AncestorHashesReplayUpdateSender, + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, ) { let new_duplicate_slots: Vec = duplicate_slots_receiver.try_iter().collect(); let (root_slot, bank_hashes) = { @@ -1483,6 +1552,7 @@ impl ReplayStage { fork_choice, duplicate_slots_to_repair, ancestor_hashes_replay_update_sender, + purge_repair_slot_counter, SlotStateUpdate::Duplicate(duplicate_state), ); } @@ -1713,6 +1783,7 @@ impl ReplayStage { replay_vote_sender: &ReplayVoteSender, verify_recyclers: &VerifyRecyclers, log_messages_bytes_limit: Option, + prioritization_fee_cache: &PrioritizationFeeCache, ) -> result::Result { let mut w_replay_stats = replay_stats.write().unwrap(); let mut w_replay_progress = replay_progress.write().unwrap(); @@ -1732,6 +1803,7 @@ impl ReplayStage { verify_recyclers, false, log_messages_bytes_limit, + prioritization_fee_cache, )?; let tx_count_after = w_replay_progress.num_txs; let tx_count = tx_count_after - tx_count_before; @@ -1752,6 +1824,7 @@ impl ReplayStage { heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice, duplicate_slots_to_repair: &mut DuplicateSlotsToRepair, ancestor_hashes_replay_update_sender: &AncestorHashesReplayUpdateSender, + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, ) { // Do not remove from progress map when marking dead! Needed by // `process_gossip_duplicate_confirmed_slots()` @@ -1805,6 +1878,7 @@ impl ReplayStage { heaviest_subtree_fork_choice, duplicate_slots_to_repair, ancestor_hashes_replay_update_sender, + purge_repair_slot_counter, SlotStateUpdate::Dead(dead_state), ); } @@ -1827,7 +1901,7 @@ impl ReplayStage { rpc_subscriptions: &Arc, block_commitment_cache: &Arc>, heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice, - bank_notification_sender: &Option, + bank_notification_sender: &Option, duplicate_slots_tracker: &mut DuplicateSlotsTracker, gossip_duplicate_confirmed_slots: &mut GossipDuplicateConfirmedSlots, unfrozen_gossip_verified_vote_hashes: &mut UnfrozenGossipVerifiedVoteHashes, @@ -1853,8 +1927,19 @@ impl ReplayStage { .get(new_root) .expect("Root bank doesn't exist"); let mut rooted_banks = root_bank.parents(); + let oldest_parent = rooted_banks.last().map(|last| last.parent_slot()); rooted_banks.push(root_bank.clone()); let rooted_slots: Vec<_> = rooted_banks.iter().map(|bank| bank.slot()).collect(); + // The following differs from rooted_slots by including the parent slot of the oldest parent bank. + let rooted_slots_with_parents = bank_notification_sender + .as_ref() + .map_or(false, |sender| sender.should_send_parents) + .then(|| { + let mut new_chain = rooted_slots.clone(); + new_chain.push(oldest_parent.unwrap_or_else(|| bank.parent_slot())); + new_chain + }); + // Call leader schedule_cache.set_root() before blockstore.set_root() because // bank_forks.root is consumed by repair_service to update gossip, so we don't want to // get shreds for repair on gossip before we update leader schedule, otherwise they may @@ -1890,8 +1975,16 @@ impl ReplayStage { rpc_subscriptions.notify_roots(rooted_slots); if let Some(sender) = bank_notification_sender { sender - .send(BankNotification::Root(root_bank)) + .sender + .send(BankNotification::NewRootBank(root_bank)) .unwrap_or_else(|err| warn!("bank_notification_sender failed: {:?}", err)); + + if let Some(new_chain) = rooted_slots_with_parents { + sender + .sender + .send(BankNotification::NewRootedChain(new_chain)) + .unwrap_or_else(|err| warn!("bank_notification_sender failed: {:?}", err)); + } } latest_root_senders.iter().for_each(|s| { if let Err(e) = s.send(new_root) { @@ -2012,7 +2105,7 @@ impl ReplayStage { .is_active(&feature_set::compact_vote_state_updates::id()); let vote = match (should_compact, vote) { (true, VoteTransaction::VoteStateUpdate(vote_state_update)) => { - VoteTransaction::from(CompactVoteStateUpdate::from(vote_state_update)) + VoteTransaction::CompactVoteStateUpdate(vote_state_update) } (_, vote) => vote, }; @@ -2075,18 +2168,35 @@ impl ReplayStage { last_voted_slot ); } + + // If we are a non voting validator or have an incorrect setup preventing us from + // generating vote txs, no need to refresh + let last_vote_tx_blockhash = + if let Some(last_vote_tx_blockhash) = tower.last_vote_tx_blockhash() { + last_vote_tx_blockhash + } else { + return; + }; + if my_latest_landed_vote >= last_voted_slot || heaviest_bank_on_same_fork - .is_hash_valid_for_age(&tower.last_vote_tx_blockhash(), MAX_PROCESSING_AGE) - // In order to avoid voting on multiple forks all past MAX_PROCESSING_AGE that don't - // include the last voted blockhash - || last_vote_refresh_time.last_refresh_time.elapsed().as_millis() < MAX_VOTE_REFRESH_INTERVAL_MILLIS as u128 + .is_hash_valid_for_age(&last_vote_tx_blockhash, MAX_PROCESSING_AGE) + || { + // In order to avoid voting on multiple forks all past MAX_PROCESSING_AGE that don't + // include the last voted blockhash + last_vote_refresh_time + .last_refresh_time + .elapsed() + .as_millis() + < MAX_VOTE_REFRESH_INTERVAL_MILLIS as u128 + } { return; } - // TODO: check the timestamp in this vote is correct, i.e. it shouldn't - // have changed from the original timestamp of the vote. + // Update timestamp for refreshed vote + tower.refresh_last_vote_timestamp(heaviest_bank_on_same_fork.slot()); + let vote_tx = Self::generate_vote_tx( identity_keypair, heaviest_bank_on_same_fork, @@ -2228,6 +2338,7 @@ impl ReplayStage { replay_timing: &mut ReplayTiming, log_messages_bytes_limit: Option, active_bank_slots: &[Slot], + prioritization_fee_cache: &PrioritizationFeeCache, ) -> Vec { // Make mutable shared structures thread safe. let progress = RwLock::new(progress); @@ -2303,6 +2414,7 @@ impl ReplayStage { &replay_vote_sender.clone(), &verify_recyclers.clone(), log_messages_bytes_limit, + prioritization_fee_cache, ); replay_blockstore_time.stop(); replay_result.replay_result = Some(blockstore_result); @@ -2333,6 +2445,7 @@ impl ReplayStage { replay_timing: &mut ReplayTiming, log_messages_bytes_limit: Option, bank_slot: Slot, + prioritization_fee_cache: &PrioritizationFeeCache, ) -> ReplaySlotFromBlockstore { let mut replay_result = ReplaySlotFromBlockstore { is_slot_dead: false, @@ -2382,6 +2495,7 @@ impl ReplayStage { &replay_vote_sender.clone(), &verify_recyclers.clone(), log_messages_bytes_limit, + prioritization_fee_cache, ); replay_blockstore_time.stop(); replay_result.replay_result = Some(blockstore_result); @@ -2399,7 +2513,7 @@ impl ReplayStage { transaction_status_sender: Option<&TransactionStatusSender>, cache_block_meta_sender: Option<&CacheBlockMetaSender>, heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice, - bank_notification_sender: &Option, + bank_notification_sender: &Option, rewards_recorder_sender: &Option, rpc_subscriptions: &Arc, duplicate_slots_tracker: &mut DuplicateSlotsTracker, @@ -2413,6 +2527,7 @@ impl ReplayStage { ancestor_hashes_replay_update_sender: &AncestorHashesReplayUpdateSender, block_metadata_notifier: Option, replay_result_vec: &[ReplaySlotFromBlockstore], + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, ) -> bool { // TODO: See if processing of blockstore replay results and bank completion can be made thread safe. let mut did_complete_bank = false; @@ -2443,6 +2558,7 @@ impl ReplayStage { heaviest_subtree_fork_choice, duplicate_slots_to_repair, ancestor_hashes_replay_update_sender, + purge_repair_slot_counter, ); // If the bank was corrupted, don't try to run the below logic to check if the // bank is completed @@ -2473,6 +2589,11 @@ impl ReplayStage { transaction_status_sender.send_transaction_status_freeze_message(bank); } bank.freeze(); + datapoint_info!( + "bank_frozen", + ("slot", bank_slot, i64), + ("hash", bank.hash().to_string(), String), + ); // report cost tracker stats cost_update_sender .send(CostUpdate::FrozenBank { bank: bank.clone() }) @@ -2506,10 +2627,12 @@ impl ReplayStage { heaviest_subtree_fork_choice, duplicate_slots_to_repair, ancestor_hashes_replay_update_sender, + purge_repair_slot_counter, SlotStateUpdate::BankFrozen(bank_frozen_state), ); if let Some(sender) = bank_notification_sender { sender + .sender .send(BankNotification::Frozen(bank.clone())) .unwrap_or_else(|err| warn!("bank_notification_sender failed: {:?}", err)); } @@ -2582,7 +2705,7 @@ impl ReplayStage { verify_recyclers: &VerifyRecyclers, heaviest_subtree_fork_choice: &mut HeaviestSubtreeForkChoice, replay_vote_sender: &ReplayVoteSender, - bank_notification_sender: &Option, + bank_notification_sender: &Option, rewards_recorder_sender: &Option, rpc_subscriptions: &Arc, duplicate_slots_tracker: &mut DuplicateSlotsTracker, @@ -2597,6 +2720,8 @@ impl ReplayStage { block_metadata_notifier: Option, replay_timing: &mut ReplayTiming, log_messages_bytes_limit: Option, + prioritization_fee_cache: &PrioritizationFeeCache, + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, ) -> bool { let active_bank_slots = bank_forks.read().unwrap().active_bank_slots(); let num_active_banks = active_bank_slots.len(); @@ -2626,6 +2751,7 @@ impl ReplayStage { replay_timing, log_messages_bytes_limit, &active_bank_slots, + prioritization_fee_cache, ) } else { active_bank_slots @@ -2643,6 +2769,7 @@ impl ReplayStage { replay_timing, log_messages_bytes_limit, *bank_slot, + prioritization_fee_cache, ) }) .collect() @@ -2660,6 +2787,7 @@ impl ReplayStage { replay_timing, log_messages_bytes_limit, active_bank_slots[0], + prioritization_fee_cache, )] }; @@ -2684,6 +2812,7 @@ impl ReplayStage { ancestor_hashes_replay_update_sender, block_metadata_notifier, &replay_result_vec, + purge_repair_slot_counter, ) } else { false @@ -2902,9 +3031,18 @@ impl ReplayStage { ); } - // Given a heaviest bank, `heaviest_bank` and the next votable bank - // `heaviest_bank_on_same_voted_fork` as the validator's last vote, return - // a bank to vote on, a bank to reset to, + /// Given a `heaviest_bank` and a `heaviest_bank_on_same_voted_fork`, return + /// a bank to vote on, a bank to reset to, and a list of switch failure + /// reasons. + /// + /// If `heaviest_bank_on_same_voted_fork` is `None` due to that fork no + /// longer being valid to vote on, it's possible that a validator will not + /// be able to reset away from the invalid fork that they last voted on. To + /// resolve this scenario, validators need to wait until they can create a + /// switch proof for another fork or until the invalid fork is be marked + /// valid again if it was confirmed by the cluster. + /// Until this is resolved, leaders will build each of their + /// blocks from the last reset bank on the invalid fork. pub fn select_vote_and_reset_forks( heaviest_bank: &Arc, // Should only be None if there was no previous vote @@ -2945,7 +3083,7 @@ impl ReplayStage { ); match switch_fork_decision { - SwitchForkDecision::FailedSwitchThreshold(_, _) => { + SwitchForkDecision::FailedSwitchThreshold(switch_proof_stake, total_stake) => { let reset_bank = heaviest_bank_on_same_voted_fork; // If we can't switch and our last vote was on a non-duplicate/confirmed slot, then // reset to the the next votable bank on the same fork as our last vote, @@ -2969,9 +3107,16 @@ impl ReplayStage { // then there will be no blocks to include the votes for slot 4, and the network halts // because 90% of validators can't vote info!( - "Waiting to switch vote to {}, resetting to slot {:?} for now", + "Waiting to switch vote to {}, + resetting to slot {:?} for now, + switch proof stake: {}, + threshold stake: {}, + total stake: {}", heaviest_bank.slot(), reset_bank.as_ref().map(|b| b.slot()), + switch_proof_stake, + total_stake as f64 * SWITCH_FORK_THRESHOLD, + total_stake ); failure_reasons.push(HeaviestForkFailures::FailedSwitchThreshold( heaviest_bank.slot(), @@ -3109,13 +3254,14 @@ impl ReplayStage { // all its ancestor banks have also reached propagation // threshold as well (Validators can't have voted for a // descendant without also getting the ancestor block) - if leader_propagated_stats.is_propagated || + if leader_propagated_stats.is_propagated || { // If there's no new validators to record, and there's no // newly achieved threshold, then there's no further // information to propagate backwards to past leader blocks - (newly_voted_pubkeys.is_empty() && cluster_slot_pubkeys.is_empty() && - !did_newly_reach_threshold) - { + newly_voted_pubkeys.is_empty() + && cluster_slot_pubkeys.is_empty() + && !did_newly_reach_threshold + } { break; } @@ -3207,6 +3353,7 @@ impl ReplayStage { did_newly_reach_threshold } + #[allow(clippy::too_many_arguments)] fn mark_slots_confirmed( confirmed_forks: &[(Slot, Hash)], blockstore: &Blockstore, @@ -3217,6 +3364,7 @@ impl ReplayStage { epoch_slots_frozen_slots: &mut EpochSlotsFrozenSlots, duplicate_slots_to_repair: &mut DuplicateSlotsToRepair, ancestor_hashes_replay_update_sender: &AncestorHashesReplayUpdateSender, + purge_repair_slot_counter: &mut PurgeRepairSlotCounter, ) { let root_slot = bank_forks.read().unwrap().root(); for (slot, frozen_hash) in confirmed_forks.iter() { @@ -3244,6 +3392,7 @@ impl ReplayStage { fork_choice, duplicate_slots_to_repair, ancestor_hashes_replay_update_sender, + purge_repair_slot_counter, SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), ); } @@ -3462,9 +3611,12 @@ impl ReplayStage { let rewards = bank.rewards.read().unwrap(); if !rewards.is_empty() { rewards_recorder_sender - .send((bank.slot(), rewards.clone())) + .send(RewardsMessage::Batch((bank.slot(), rewards.clone()))) .unwrap_or_else(|err| warn!("rewards_recorder_sender failed: {:?}", err)); } + rewards_recorder_sender + .send(RewardsMessage::Complete(bank.slot())) + .unwrap_or_else(|err| warn!("rewards_recorder_sender failed: {:?}", err)); } } @@ -3642,9 +3794,11 @@ pub(crate) mod tests { OptimisticallyConfirmedBank::locked_from_bank_forks_root(bank_forks); let exit = Arc::new(AtomicBool::new(false)); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::default())), optimistically_confirmed_bank, @@ -3706,7 +3860,12 @@ pub(crate) mod tests { // Insert shreds for slot NUM_CONSECUTIVE_LEADER_SLOTS, // chaining to slot 1 - let (shreds, _) = make_slot_entries(NUM_CONSECUTIVE_LEADER_SLOTS, 1, 8); + let (shreds, _) = make_slot_entries( + NUM_CONSECUTIVE_LEADER_SLOTS, // slot + 1, // parent_slot + 8, // num_entries + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); assert!(bank_forks .read() @@ -3730,7 +3889,12 @@ pub(crate) mod tests { // Insert shreds for slot 2 * NUM_CONSECUTIVE_LEADER_SLOTS, // chaining to slot 1 - let (shreds, _) = make_slot_entries(2 * NUM_CONSECUTIVE_LEADER_SLOTS, 1, 8); + let (shreds, _) = make_slot_entries( + 2 * NUM_CONSECUTIVE_LEADER_SLOTS, + 1, + 8, + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); assert!(bank_forks .read() @@ -3856,10 +4020,7 @@ pub(crate) mod tests { vec![root, root + 1] ); assert_eq!( - epoch_slots_frozen_slots - .into_iter() - .map(|(slot, _hash)| slot) - .collect::>(), + epoch_slots_frozen_slots.into_keys().collect::>(), vec![root, root + 1] ); } @@ -3946,7 +4107,14 @@ pub(crate) mod tests { ), // should cause AccountNotFound error ], ); - entries_to_test_shreds(&[entry], slot, slot.saturating_sub(1), false, 0) + entries_to_test_shreds( + &[entry], + slot, + slot.saturating_sub(1), // parent_slot + false, // is_full_slot + 0, // version + true, // merkle_variant + ) }); assert_matches!( @@ -3976,7 +4144,14 @@ pub(crate) mod tests { blockhash, )], ); - entries_to_test_shreds(&[entry], slot, slot.saturating_sub(1), false, 0) + entries_to_test_shreds( + &[entry], + slot, + slot.saturating_sub(1), // parent_slot + false, // is_full_slot + 0, // version + true, // merkle_variant + ) }); if let Err(BlockstoreProcessorError::InvalidBlock(block_error)) = res { @@ -4001,6 +4176,7 @@ pub(crate) mod tests { slot.saturating_sub(1), false, 0, + true, // merkle_variant ) }); @@ -4025,6 +4201,7 @@ pub(crate) mod tests { slot.saturating_sub(1), false, 0, + true, // merkle_variant ) }); @@ -4045,6 +4222,7 @@ pub(crate) mod tests { slot.saturating_sub(1), true, 0, + true, // merkle_variant ) }); @@ -4067,6 +4245,7 @@ pub(crate) mod tests { slot.saturating_sub(1), false, 0, + true, // merkle_variant ) }); @@ -4090,7 +4269,14 @@ pub(crate) mod tests { let tx = system_transaction::transfer(funded_keypair, &keypair.pubkey(), 2, blockhash); let trailing_entry = entry::next_entry(&last_entry_hash, 1, vec![tx]); entries.push(trailing_entry); - entries_to_test_shreds(&entries, slot, slot.saturating_sub(1), true, 0) + entries_to_test_shreds( + &entries, + slot, + slot.saturating_sub(1), // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ) }); if let Err(BlockstoreProcessorError::InvalidBlock(block_error)) = res { @@ -4174,11 +4360,14 @@ pub(crate) mod tests { &replay_vote_sender, &VerifyRecyclers::default(), None, + &PrioritizationFeeCache::new(0u64), ); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), block_commitment_cache, OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -4199,6 +4388,7 @@ pub(crate) mod tests { &mut heaviest_subtree_fork_choice, &mut DuplicateSlotsToRepair::default(), &ancestor_hashes_replay_update_sender, + &mut PurgeRepairSlotCounter::default(), ); } @@ -4246,9 +4436,11 @@ pub(crate) mod tests { let exit = Arc::new(AtomicBool::new(false)); let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default())); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), block_commitment_cache.clone(), OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -5573,6 +5765,142 @@ pub(crate) mod tests { ); } + #[test] + fn test_unconfirmed_duplicate_slots_and_lockouts_for_non_heaviest_fork() { + /* + Build fork structure: + + slot 0 + | + slot 1 + / \ + slot 2 | + | | + slot 3 | + | | + slot 4 | + slot 5 + */ + let forks = tr(0) / (tr(1) / (tr(2) / (tr(3) / (tr(4)))) / tr(5)); + + let mut vote_simulator = VoteSimulator::new(1); + vote_simulator.fill_bank_forks(forks, &HashMap::>::new(), true); + let (bank_forks, mut progress) = (vote_simulator.bank_forks, vote_simulator.progress); + let ledger_path = get_tmp_ledger_path!(); + let blockstore = Arc::new( + Blockstore::open(&ledger_path).expect("Expected to be able to open database ledger"), + ); + let mut tower = Tower::new_for_tests(8, 2.0 / 3.0); + + // All forks have same weight so heaviest bank to vote/reset on should be the tip of + // the fork with the lower slot + let (vote_fork, reset_fork) = run_compute_and_select_forks( + &bank_forks, + &mut progress, + &mut tower, + &mut vote_simulator.heaviest_subtree_fork_choice, + &mut vote_simulator.latest_validator_votes_for_frozen_banks, + ); + assert_eq!(vote_fork.unwrap(), 4); + assert_eq!(reset_fork.unwrap(), 4); + + // Record the vote for 5 which is not on the heaviest fork. + tower.record_bank_vote( + &bank_forks.read().unwrap().get(5).unwrap(), + &Pubkey::default(), + ); + + // 4 should be the heaviest slot, but should not be votable + // because of lockout. 5 is the heaviest slot on the same fork as the last vote. + let (vote_fork, reset_fork) = run_compute_and_select_forks( + &bank_forks, + &mut progress, + &mut tower, + &mut vote_simulator.heaviest_subtree_fork_choice, + &mut vote_simulator.latest_validator_votes_for_frozen_banks, + ); + assert!(vote_fork.is_none()); + assert_eq!(reset_fork, Some(5)); + + // Mark 5 as duplicate + blockstore.store_duplicate_slot(5, vec![], vec![]).unwrap(); + let mut duplicate_slots_tracker = DuplicateSlotsTracker::default(); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); + let mut gossip_duplicate_confirmed_slots = GossipDuplicateConfirmedSlots::default(); + let mut epoch_slots_frozen_slots = EpochSlotsFrozenSlots::default(); + let bank5_hash = bank_forks.read().unwrap().bank_hash(5).unwrap(); + assert_ne!(bank5_hash, Hash::default()); + let duplicate_state = DuplicateState::new_from_state( + 5, + &gossip_duplicate_confirmed_slots, + &mut vote_simulator.heaviest_subtree_fork_choice, + || progress.is_dead(5).unwrap_or(false), + || Some(bank5_hash), + ); + let (ancestor_hashes_replay_update_sender, _ancestor_hashes_replay_update_receiver) = + unbounded(); + check_slot_agrees_with_cluster( + 5, + bank_forks.read().unwrap().root(), + &blockstore, + &mut duplicate_slots_tracker, + &mut epoch_slots_frozen_slots, + &mut vote_simulator.heaviest_subtree_fork_choice, + &mut DuplicateSlotsToRepair::default(), + &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, + SlotStateUpdate::Duplicate(duplicate_state), + ); + + // 4 should be the heaviest slot, but should not be votable + // because of lockout. 5 is no longer valid due to it being a duplicate. + let (vote_fork, reset_fork) = run_compute_and_select_forks( + &bank_forks, + &mut progress, + &mut tower, + &mut vote_simulator.heaviest_subtree_fork_choice, + &mut vote_simulator.latest_validator_votes_for_frozen_banks, + ); + assert!(vote_fork.is_none()); + assert!(reset_fork.is_none()); + + // If slot 5 is marked as confirmed, it becomes the heaviest bank on same slot again + let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); + gossip_duplicate_confirmed_slots.insert(5, bank5_hash); + let duplicate_confirmed_state = DuplicateConfirmedState::new_from_state( + bank5_hash, + || progress.is_dead(5).unwrap_or(false), + || Some(bank5_hash), + ); + check_slot_agrees_with_cluster( + 5, + bank_forks.read().unwrap().root(), + &blockstore, + &mut duplicate_slots_tracker, + &mut epoch_slots_frozen_slots, + &mut vote_simulator.heaviest_subtree_fork_choice, + &mut duplicate_slots_to_repair, + &ancestor_hashes_replay_update_sender, + &mut purge_repair_slot_counter, + SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), + ); + // The confirmed hash is detected in `progress`, which means + // it's confirmation on the replayed block. This means we have + // the right version of the block, so `duplicate_slots_to_repair` + // should be empty + assert!(duplicate_slots_to_repair.is_empty()); + let (vote_fork, reset_fork) = run_compute_and_select_forks( + &bank_forks, + &mut progress, + &mut tower, + &mut vote_simulator.heaviest_subtree_fork_choice, + &mut vote_simulator.latest_validator_votes_for_frozen_banks, + ); + // Should now pick 5 as the heaviest fork from last vote again. + assert!(vote_fork.is_none()); + assert_eq!(reset_fork.unwrap(), 5); + } + #[test] fn test_unconfirmed_duplicate_slots_and_lockouts() { /* @@ -5654,6 +5982,7 @@ pub(crate) mod tests { &mut vote_simulator.heaviest_subtree_fork_choice, &mut DuplicateSlotsToRepair::default(), &ancestor_hashes_replay_update_sender, + &mut PurgeRepairSlotCounter::default(), SlotStateUpdate::Duplicate(duplicate_state), ); @@ -5665,7 +5994,7 @@ pub(crate) mod tests { &mut vote_simulator.latest_validator_votes_for_frozen_banks, ); assert!(vote_fork.is_none()); - assert_eq!(reset_fork.unwrap(), 3); + assert_eq!(reset_fork, Some(3)); // Now mark 2, an ancestor of 4, as duplicate blockstore.store_duplicate_slot(2, vec![], vec![]).unwrap(); @@ -5687,6 +6016,7 @@ pub(crate) mod tests { &mut vote_simulator.heaviest_subtree_fork_choice, &mut DuplicateSlotsToRepair::default(), &ancestor_hashes_replay_update_sender, + &mut PurgeRepairSlotCounter::default(), SlotStateUpdate::Duplicate(duplicate_state), ); @@ -5721,6 +6051,7 @@ pub(crate) mod tests { &mut vote_simulator.heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut PurgeRepairSlotCounter::default(), SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), ); // The confirmed hash is detected in `progress`, which means @@ -5768,6 +6099,7 @@ pub(crate) mod tests { let mut duplicate_slots_to_repair = DuplicateSlotsToRepair::default(); duplicate_slots_to_repair.insert(1, Hash::new_unique()); duplicate_slots_to_repair.insert(2, Hash::new_unique()); + let mut purge_repair_slot_counter = PurgeRepairSlotCounter::default(); ReplayStage::dump_then_repair_correct_slots( &mut duplicate_slots_to_repair, @@ -5777,6 +6109,7 @@ pub(crate) mod tests { bank_forks, blockstore, None, + &mut purge_repair_slot_counter, ); let r_bank_forks = bank_forks.read().unwrap(); @@ -5794,6 +6127,9 @@ pub(crate) mod tests { assert!(descendants_result.is_none()); } } + assert_eq!(2, purge_repair_slot_counter.len()); + assert_eq!(1, *purge_repair_slot_counter.get(&1).unwrap()); + assert_eq!(1, *purge_repair_slot_counter.get(&2).unwrap()); } fn setup_vote_then_rollback( @@ -5870,6 +6206,7 @@ pub(crate) mod tests { heaviest_subtree_fork_choice, &mut duplicate_slots_to_repair, &ancestor_hashes_replay_update_sender, + &mut PurgeRepairSlotCounter::default(), SlotStateUpdate::DuplicateConfirmed(duplicate_confirmed_state), ); assert_eq!( @@ -5888,6 +6225,7 @@ pub(crate) mod tests { bank_forks, blockstore, None, + &mut PurgeRepairSlotCounter::default(), ); // Check everything was purged properly @@ -6261,7 +6599,10 @@ pub(crate) mod tests { assert_eq!(votes.len(), 1); let vote_tx = &votes[0]; assert_eq!(vote_tx.message.recent_blockhash, bank0.last_blockhash()); - assert_eq!(tower.last_vote_tx_blockhash(), bank0.last_blockhash()); + assert_eq!( + tower.last_vote_tx_blockhash().unwrap(), + bank0.last_blockhash() + ); assert_eq!(tower.last_voted_slot().unwrap(), 0); bank1.process_transaction(vote_tx).unwrap(); bank1.freeze(); @@ -6290,7 +6631,10 @@ pub(crate) mod tests { let votes = cluster_info.get_votes(&mut cursor); assert!(votes.is_empty()); // Tower's latest vote tx blockhash hasn't changed either - assert_eq!(tower.last_vote_tx_blockhash(), bank0.last_blockhash()); + assert_eq!( + tower.last_vote_tx_blockhash().unwrap(), + bank0.last_blockhash() + ); assert_eq!(tower.last_voted_slot().unwrap(), 0); } @@ -6324,7 +6668,10 @@ pub(crate) mod tests { assert_eq!(votes.len(), 1); let vote_tx = &votes[0]; assert_eq!(vote_tx.message.recent_blockhash, bank1.last_blockhash()); - assert_eq!(tower.last_vote_tx_blockhash(), bank1.last_blockhash()); + assert_eq!( + tower.last_vote_tx_blockhash().unwrap(), + bank1.last_blockhash() + ); assert_eq!(tower.last_voted_slot().unwrap(), 1); // Trying to refresh the vote for bank 1 in bank 2 won't succeed because @@ -6346,7 +6693,10 @@ pub(crate) mod tests { // No new votes have been submitted to gossip let votes = cluster_info.get_votes(&mut cursor); assert!(votes.is_empty()); - assert_eq!(tower.last_vote_tx_blockhash(), bank1.last_blockhash()); + assert_eq!( + tower.last_vote_tx_blockhash().unwrap(), + bank1.last_blockhash() + ); assert_eq!(tower.last_voted_slot().unwrap(), 1); // Create a bank where the last vote transaction will have expired @@ -6406,7 +6756,7 @@ pub(crate) mod tests { expired_bank.last_blockhash() ); assert_eq!( - tower.last_vote_tx_blockhash(), + tower.last_vote_tx_blockhash().unwrap(), expired_bank.last_blockhash() ); assert_eq!(tower.last_voted_slot().unwrap(), 1); @@ -6462,7 +6812,7 @@ pub(crate) mod tests { expired_bank.last_blockhash() ); assert_eq!( - tower.last_vote_tx_blockhash(), + tower.last_vote_tx_blockhash().unwrap(), expired_bank.last_blockhash() ); assert_eq!(tower.last_voted_slot().unwrap(), 1); @@ -6509,13 +6859,14 @@ pub(crate) mod tests { bank1.freeze(); bank_forks.write().unwrap().insert(bank1); + progress.get_retransmit_info_mut(0).unwrap().retry_time = Instant::now(); ReplayStage::retransmit_latest_unpropagated_leader_slot( &poh_recorder, &retransmit_slots_sender, &mut progress, ); let res = retransmit_slots_receiver.recv_timeout(Duration::from_millis(10)); - assert!(res.is_ok(), "retry_iteration=0, retry_time=None"); + assert_matches!(res, Err(_)); assert_eq!( progress.get_retransmit_info(0).unwrap().retry_iteration, 0, @@ -6533,8 +6884,9 @@ pub(crate) mod tests { "retry_iteration=0, elapsed < 2^0 * RETRANSMIT_BASE_DELAY_MS" ); - progress.get_retransmit_info_mut(0).unwrap().retry_time = - Some(Instant::now() - Duration::from_millis(RETRANSMIT_BASE_DELAY_MS + 1)); + progress.get_retransmit_info_mut(0).unwrap().retry_time = Instant::now() + .checked_sub(Duration::from_millis(RETRANSMIT_BASE_DELAY_MS + 1)) + .unwrap(); ReplayStage::retransmit_latest_unpropagated_leader_slot( &poh_recorder, &retransmit_slots_sender, @@ -6562,8 +6914,9 @@ pub(crate) mod tests { "retry_iteration=1, elapsed < 2^1 * RETRY_BASE_DELAY_MS" ); - progress.get_retransmit_info_mut(0).unwrap().retry_time = - Some(Instant::now() - Duration::from_millis(RETRANSMIT_BASE_DELAY_MS + 1)); + progress.get_retransmit_info_mut(0).unwrap().retry_time = Instant::now() + .checked_sub(Duration::from_millis(RETRANSMIT_BASE_DELAY_MS + 1)) + .unwrap(); ReplayStage::retransmit_latest_unpropagated_leader_slot( &poh_recorder, &retransmit_slots_sender, @@ -6575,8 +6928,9 @@ pub(crate) mod tests { "retry_iteration=1, elapsed < 2^1 * RETRANSMIT_BASE_DELAY_MS" ); - progress.get_retransmit_info_mut(0).unwrap().retry_time = - Some(Instant::now() - Duration::from_millis(2 * RETRANSMIT_BASE_DELAY_MS + 1)); + progress.get_retransmit_info_mut(0).unwrap().retry_time = Instant::now() + .checked_sub(Duration::from_millis(2 * RETRANSMIT_BASE_DELAY_MS + 1)) + .unwrap(); ReplayStage::retransmit_latest_unpropagated_leader_slot( &poh_recorder, &retransmit_slots_sender, @@ -6599,8 +6953,9 @@ pub(crate) mod tests { .unwrap() .increment_retry_iteration(); - progress.get_retransmit_info_mut(0).unwrap().retry_time = - Some(Instant::now() - Duration::from_millis(2 * RETRANSMIT_BASE_DELAY_MS + 1)); + progress.get_retransmit_info_mut(0).unwrap().retry_time = Instant::now() + .checked_sub(Duration::from_millis(2 * RETRANSMIT_BASE_DELAY_MS + 1)) + .unwrap(); ReplayStage::retransmit_latest_unpropagated_leader_slot( &poh_recorder, &retransmit_slots_sender, @@ -6612,8 +6967,9 @@ pub(crate) mod tests { "retry_iteration=3, elapsed < 2^3 * RETRANSMIT_BASE_DELAY_MS" ); - progress.get_retransmit_info_mut(0).unwrap().retry_time = - Some(Instant::now() - Duration::from_millis(8 * RETRANSMIT_BASE_DELAY_MS + 1)); + progress.get_retransmit_info_mut(0).unwrap().retry_time = Instant::now() + .checked_sub(Duration::from_millis(8 * RETRANSMIT_BASE_DELAY_MS + 1)) + .unwrap(); ReplayStage::retransmit_latest_unpropagated_leader_slot( &poh_recorder, &retransmit_slots_sender, @@ -6655,6 +7011,10 @@ pub(crate) mod tests { } = vote_simulator; let (retransmit_slots_sender, retransmit_slots_receiver) = unbounded(); + let retry_time = Instant::now() + .checked_sub(Duration::from_millis(RETRANSMIT_BASE_DELAY_MS + 1)) + .unwrap(); + progress.get_retransmit_info_mut(0).unwrap().retry_time = retry_time; let mut prev_index = 0; for i in (1..10).chain(11..15) { @@ -6680,6 +7040,7 @@ pub(crate) mod tests { bank.freeze(); bank_forks.write().unwrap().insert(bank); prev_index = i; + progress.get_retransmit_info_mut(i).unwrap().retry_time = retry_time; } // expect single slot when latest_leader_slot is the start of a consecutive range @@ -6746,7 +7107,6 @@ pub(crate) mod tests { ); let (heaviest_bank, heaviest_bank_on_same_fork) = heaviest_subtree_fork_choice .select_forks(&frozen_banks, tower, progress, ancestors, bank_forks); - assert!(heaviest_bank_on_same_fork.is_none()); let SelectVoteAndResetForkResult { vote_bank, reset_bank, diff --git a/core/src/result.rs b/core/src/result.rs index 6c9b66b6d459c0..2aa8f8718f5141 100644 --- a/core/src/result.rs +++ b/core/src/result.rs @@ -3,53 +3,42 @@ use { solana_gossip::{cluster_info, gossip_error::GossipError}, solana_ledger::blockstore, + thiserror::Error, }; -#[derive(Debug)] +#[derive(Debug, Error)] pub enum Error { - Io(std::io::Error), - Recv(crossbeam_channel::RecvError), + #[error(transparent)] + Blockstore(#[from] blockstore::BlockstoreError), + #[error(transparent)] + ClusterInfo(#[from] cluster_info::ClusterInfoError), + #[error(transparent)] + Gossip(#[from] GossipError), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("ReadyTimeout")] ReadyTimeout, - RecvTimeout(crossbeam_channel::RecvTimeoutError), - TrySend, - Serialize(std::boxed::Box), - ClusterInfo(cluster_info::ClusterInfoError), + #[error(transparent)] + Recv(#[from] crossbeam_channel::RecvError), + #[error(transparent)] + RecvTimeout(#[from] crossbeam_channel::RecvTimeoutError), + #[error("Send")] Send, - Blockstore(blockstore::BlockstoreError), - WeightedIndex(rand::distributions::weighted::WeightedError), - Gossip(GossipError), + #[error("TrySend")] + TrySend, + #[error(transparent)] + Serialize(#[from] std::boxed::Box), + #[error(transparent)] + WeightedIndex(#[from] rand::distributions::weighted::WeightedError), } pub type Result = std::result::Result; -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "solana error") - } -} - -impl std::error::Error for Error {} - -impl std::convert::From for Error { - fn from(e: crossbeam_channel::RecvError) -> Error { - Error::Recv(e) - } -} impl std::convert::From for Error { fn from(_e: crossbeam_channel::ReadyTimeoutError) -> Error { Error::ReadyTimeout } } -impl std::convert::From for Error { - fn from(e: crossbeam_channel::RecvTimeoutError) -> Error { - Error::RecvTimeout(e) - } -} -impl std::convert::From for Error { - fn from(e: cluster_info::ClusterInfoError) -> Error { - Error::ClusterInfo(e) - } -} impl std::convert::From> for Error { fn from(_e: crossbeam_channel::TrySendError) -> Error { Error::TrySend @@ -60,31 +49,6 @@ impl std::convert::From> for Error { Error::Send } } -impl std::convert::From for Error { - fn from(e: std::io::Error) -> Error { - Error::Io(e) - } -} -impl std::convert::From> for Error { - fn from(e: std::boxed::Box) -> Error { - Error::Serialize(e) - } -} -impl std::convert::From for Error { - fn from(e: blockstore::BlockstoreError) -> Error { - Error::Blockstore(e) - } -} -impl std::convert::From for Error { - fn from(e: rand::distributions::weighted::WeightedError) -> Error { - Error::WeightedIndex(e) - } -} -impl std::convert::From for Error { - fn from(e: GossipError) -> Error { - Error::Gossip(e) - } -} #[cfg(test)] mod tests { diff --git a/core/src/retransmit_stage.rs b/core/src/retransmit_stage.rs index c6d3855f72b640..3f048a2433acea 100644 --- a/core/src/retransmit_stage.rs +++ b/core/src/retransmit_stage.rs @@ -2,24 +2,22 @@ #![allow(clippy::rc_buffer)] use { - crate::{ - cluster_nodes::{ClusterNodes, ClusterNodesCache}, - packet_hasher::PacketHasher, - }, + crate::cluster_nodes::{self, ClusterNodes, ClusterNodesCache, Error, MAX_NUM_TURBINE_HOPS}, crossbeam_channel::{Receiver, RecvTimeoutError}, itertools::{izip, Itertools}, lru::LruCache, + rand::Rng, rayon::{prelude::*, ThreadPool, ThreadPoolBuilder}, solana_client::rpc_response::SlotUpdate, solana_gossip::{ - cluster_info::{ClusterInfo, DATA_PLANE_FANOUT}, - contact_info::ContactInfo, + cluster_info::ClusterInfo, legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_ledger::{ leader_schedule_cache::LeaderScheduleCache, shred::{self, ShredId}, }, solana_measure::measure::Measure, + solana_perf::sigverify::Deduper, solana_rayon_threadlimit::get_thread_count, solana_rpc::{max_slots::MaxSlots, rpc_subscriptions::RpcSubscriptions}, solana_runtime::{bank::Bank, bank_forks::BankForks}, @@ -43,7 +41,9 @@ use { }; const MAX_DUPLICATE_COUNT: usize = 2; -const DEFAULT_LRU_SIZE: usize = 10_000; +const DEDUPER_FALSE_POSITIVE_RATE: f64 = 0.001; +const DEDUPER_NUM_BITS: u64 = 637_534_199; // 76MB +const DEDUPER_RESET_CYCLE: Duration = Duration::from_secs(5 * 60); // Minimum number of shreds to use rayon parallel iterators. const PAR_ITER_MIN_NUM_SHREDS: usize = 2; @@ -56,14 +56,15 @@ struct RetransmitSlotStats { outset: u64, // 1st shred retransmit timestamp. // Number of shreds sent and received at different // distances from the turbine broadcast root. - num_shreds_received: [usize; 3], - num_shreds_sent: [usize; 3], + num_shreds_received: [usize; MAX_NUM_TURBINE_HOPS], + num_shreds_sent: [usize; MAX_NUM_TURBINE_HOPS], } struct RetransmitStats { since: Instant, num_nodes: AtomicUsize, num_addrs_failed: AtomicUsize, + num_loopback_errs: AtomicUsize, num_shreds: usize, num_shreds_skipped: usize, num_small_batches: usize, @@ -89,10 +90,9 @@ impl RetransmitStats { if self.since.elapsed() < SUBMIT_CADENCE { return; } - let num_peers = cluster_nodes_cache + cluster_nodes_cache .get(root_bank.slot(), root_bank, working_bank, cluster_info) - .num_peers(); - datapoint_info!("retransmit-num_nodes", ("count", num_peers, i64)); + .submit_metrics("cluster_nodes_retransmit", timestamp()); datapoint_info!( "retransmit-stage", ("total_time", self.total_time, i64), @@ -102,6 +102,7 @@ impl RetransmitStats { ("num_small_batches", self.num_small_batches, i64), ("num_nodes", *self.num_nodes.get_mut(), i64), ("num_addrs_failed", *self.num_addrs_failed.get_mut(), i64), + ("num_loopback_errs", *self.num_loopback_errs.get_mut(), i64), ("num_shreds", self.num_shreds, i64), ("num_shreds_skipped", self.num_shreds_skipped, i64), ("retransmit_total", *self.retransmit_total.get_mut(), i64), @@ -120,47 +121,47 @@ impl RetransmitStats { let old = std::mem::replace(self, Self::new(Instant::now())); self.slot_stats = old.slot_stats; } + + fn record_error(&self, err: &Error) { + match err { + Error::Loopback { .. } => { + error!("retransmit_shred: {err}"); + self.num_loopback_errs.fetch_add(1, Ordering::Relaxed) + } + }; + } } -// Map of shred (slot, index, type) => list of hash values seen for that key. -type ShredFilter = LruCache>; +struct ShredDeduper { + deduper: Deduper, + shred_id_filter: Deduper, +} -// Returns true if shred is already received and should skip retransmit. -fn should_skip_retransmit( - key: ShredId, - shred: &[u8], - shreds_received: &mut ShredFilter, - packet_hasher: &PacketHasher, -) -> bool { - match shreds_received.get_mut(&key) { - Some(sent) if sent.len() >= MAX_DUPLICATE_COUNT => true, - Some(sent) => { - let hash = packet_hasher.hash_shred(shred); - if sent.contains(&hash) { - true - } else { - sent.push(hash); - false - } - } - None => { - let hash = packet_hasher.hash_shred(shred); - shreds_received.put(key, vec![hash]); - false +impl ShredDeduper { + fn new(rng: &mut R, num_bits: u64) -> Self { + Self { + deduper: Deduper::new(rng, num_bits), + shred_id_filter: Deduper::new(rng, num_bits), } } -} -fn maybe_reset_shreds_received_cache( - shreds_received: &mut ShredFilter, - packet_hasher: &mut PacketHasher, - hasher_reset_ts: &mut Instant, -) { - const UPDATE_INTERVAL: Duration = Duration::from_secs(1); - if hasher_reset_ts.elapsed() >= UPDATE_INTERVAL { - *hasher_reset_ts = Instant::now(); - shreds_received.clear(); - packet_hasher.reset(); + fn maybe_reset( + &mut self, + rng: &mut R, + false_positive_rate: f64, + reset_cycle: Duration, + ) { + self.deduper + .maybe_reset(rng, false_positive_rate, reset_cycle); + self.shred_id_filter + .maybe_reset(rng, false_positive_rate, reset_cycle); + } + + fn dedup(&self, key: ShredId, shred: &[u8], max_duplicate_count: usize) -> bool { + // In order to detect duplicate blocks across cluster, we retransmit + // max_duplicate_count different shreds for each ShredId. + self.deduper.dedup(shred) + || (0..max_duplicate_count).all(|i| self.shred_id_filter.dedup(&(key, i))) } } @@ -174,9 +175,7 @@ fn retransmit( sockets: &[UdpSocket], stats: &mut RetransmitStats, cluster_nodes_cache: &ClusterNodesCache, - hasher_reset_ts: &mut Instant, - shreds_received: &mut ShredFilter, - packet_hasher: &mut PacketHasher, + shred_deduper: &mut ShredDeduper<2>, max_slots: &MaxSlots, rpc_subscriptions: Option<&RpcSubscriptions>, ) -> Result<(), RecvTimeoutError> { @@ -196,7 +195,11 @@ fn retransmit( stats.epoch_fetch += epoch_fetch.as_us(); let mut epoch_cache_update = Measure::start("retransmit_epoch_cache_update"); - maybe_reset_shreds_received_cache(shreds_received, packet_hasher, hasher_reset_ts); + shred_deduper.maybe_reset( + &mut rand::thread_rng(), + DEDUPER_FALSE_POSITIVE_RATE, + DEDUPER_RESET_CYCLE, + ); epoch_cache_update.stop(); stats.epoch_cache_update += epoch_cache_update.as_us(); // Lookup slot leader and cluster nodes for each slot. @@ -204,7 +207,7 @@ fn retransmit( .into_iter() .filter_map(|shred| { let key = shred::layout::get_shred_id(&shred)?; - if should_skip_retransmit(key, &shred, shreds_received, packet_hasher) { + if shred_deduper.dedup(key, &shred, MAX_DUPLICATE_COUNT) { stats.num_shreds_skipped += 1; None } else { @@ -247,7 +250,7 @@ fn retransmit( shreds .into_iter() .enumerate() - .map(|(index, ((key, shred), slot_leader, cluster_nodes))| { + .filter_map(|(index, ((key, shred), slot_leader, cluster_nodes))| { let (root_distance, num_nodes) = retransmit_shred( &key, &shred, @@ -257,15 +260,20 @@ fn retransmit( socket_addr_space, &sockets[index % sockets.len()], stats, - ); - (key.slot(), root_distance, num_nodes) + ) + .map_err(|err| { + stats.record_error(&err); + err + }) + .ok()?; + Some((key.slot(), root_distance, num_nodes)) }) .fold(HashMap::new(), record) } else { thread_pool.install(|| { shreds .into_par_iter() - .map(|((key, shred), slot_leader, cluster_nodes)| { + .filter_map(|((key, shred), slot_leader, cluster_nodes)| { let index = thread_pool.current_thread_index().unwrap(); let (root_distance, num_nodes) = retransmit_shred( &key, @@ -276,8 +284,13 @@ fn retransmit( socket_addr_space, &sockets[index % sockets.len()], stats, - ); - (key.slot(), root_distance, num_nodes) + ) + .map_err(|err| { + stats.record_error(&err); + err + }) + .ok()?; + Some((key.slot(), root_distance, num_nodes)) }) .fold(HashMap::new, record) .reduce(HashMap::new, RetransmitSlotStats::merge) @@ -299,10 +312,11 @@ fn retransmit_shred( socket_addr_space: &SocketAddrSpace, socket: &UdpSocket, stats: &RetransmitStats, -) -> (/*root_distance:*/ usize, /*num_nodes:*/ usize) { +) -> Result<(/*root_distance:*/ usize, /*num_nodes:*/ usize), Error> { let mut compute_turbine_peers = Measure::start("turbine_start"); + let data_plane_fanout = cluster_nodes::get_data_plane_fanout(key.slot(), root_bank); let (root_distance, addrs) = - cluster_nodes.get_retransmit_addrs(slot_leader, key, root_bank, DATA_PLANE_FANOUT); + cluster_nodes.get_retransmit_addrs(slot_leader, key, root_bank, data_plane_fanout)?; let addrs: Vec<_> = addrs .into_iter() .filter(|addr| ContactInfo::is_valid_address(addr, socket_addr_space)) @@ -333,7 +347,7 @@ fn retransmit_shred( stats .retransmit_total .fetch_add(retransmit_time.as_us(), Ordering::Relaxed); - (root_distance, num_nodes) + Ok((root_distance, num_nodes)) } /// Service to retransmit messages from the leader or layer 1 to relevant peer nodes. @@ -357,18 +371,17 @@ pub fn retransmitter( CLUSTER_NODES_CACHE_NUM_EPOCH_CAP, CLUSTER_NODES_CACHE_TTL, ); - let mut hasher_reset_ts = Instant::now(); + let mut rng = rand::thread_rng(); + let mut shred_deduper = ShredDeduper::<2>::new(&mut rng, DEDUPER_NUM_BITS); let mut stats = RetransmitStats::new(Instant::now()); - let mut shreds_received = LruCache::::new(DEFAULT_LRU_SIZE); - let mut packet_hasher = PacketHasher::default(); let num_threads = get_thread_count().min(8).max(sockets.len()); let thread_pool = ThreadPoolBuilder::new() .num_threads(num_threads) - .thread_name(|i| format!("retransmit-{}", i)) + .thread_name(|i| format!("solRetransmit{:02}", i)) .build() .unwrap(); Builder::new() - .name("solana-retransmitter".to_string()) + .name("solRetransmittr".to_string()) .spawn(move || loop { match retransmit( &thread_pool, @@ -379,9 +392,7 @@ pub fn retransmitter( &sockets, &mut stats, &cluster_nodes_cache, - &mut hasher_reset_ts, - &mut shreds_received, - &mut packet_hasher, + &mut shred_deduper, &max_slots, rpc_subscriptions.as_deref(), ) { @@ -441,7 +452,7 @@ impl AddAssign for RetransmitSlotStats { } else { self.outset.min(outset) }; - for k in 0..3 { + for k in 0..MAX_NUM_TURBINE_HOPS { self.num_shreds_received[k] += num_shreds_received[k]; self.num_shreds_sent[k] += num_shreds_sent[k]; } @@ -456,6 +467,7 @@ impl RetransmitStats { since: now, num_nodes: AtomicUsize::default(), num_addrs_failed: AtomicUsize::default(), + num_loopback_errs: AtomicUsize::default(), num_shreds: 0usize, num_shreds_skipped: 0usize, total_batches: 0usize, @@ -555,9 +567,15 @@ impl RetransmitSlotStats { self.num_shreds_received[2], i64 ), + ( + "num_shreds_received_3rd_layer", + self.num_shreds_received[3], + i64 + ), ("num_shreds_sent_root", self.num_shreds_sent[0], i64), ("num_shreds_sent_1st_layer", self.num_shreds_sent[1], i64), ("num_shreds_sent_2nd_layer", self.num_shreds_sent[2], i64), + ("num_shreds_sent_3rd_layer", self.num_shreds_sent[3], i64), ); } } @@ -566,6 +584,8 @@ impl RetransmitSlotStats { mod tests { use { super::*, + rand::SeedableRng, + rand_chacha::ChaChaRng, solana_ledger::shred::{Shred, ShredFlags}, }; @@ -584,22 +604,12 @@ mod tests { version, 0, ); - let mut shreds_received = LruCache::new(100); - let packet_hasher = PacketHasher::default(); + let mut rng = ChaChaRng::from_seed([0xa5; 32]); + let shred_deduper = ShredDeduper::<2>::new(&mut rng, /*num_bits:*/ 640_007); // unique shred for (1, 5) should pass - assert!(!should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(!shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); // duplicate shred for (1, 5) blocked - assert!(should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); let shred = Shred::new_from_data( slot, @@ -612,19 +622,9 @@ mod tests { 0, ); // first duplicate shred for (1, 5) passed - assert!(!should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(!shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); // then blocked - assert!(should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); let shred = Shred::new_from_data( slot, @@ -637,64 +637,24 @@ mod tests { 0, ); // 2nd duplicate shred for (1, 5) blocked - assert!(should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); - assert!(should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); + assert!(shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); let shred = Shred::new_from_parity_shard(slot, index, &[], 0, 1, 1, 0, version); // Coding at (1, 5) passes - assert!(!should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(!shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); // then blocked - assert!(should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); let shred = Shred::new_from_parity_shard(slot, index, &[], 2, 1, 1, 0, version); // 2nd unique coding at (1, 5) passes - assert!(!should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(!shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); // same again is blocked - assert!(should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); let shred = Shred::new_from_parity_shard(slot, index, &[], 3, 1, 1, 0, version); // Another unique coding at (1, 5) always blocked - assert!(should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); - assert!(should_skip_retransmit( - shred.id(), - shred.payload(), - &mut shreds_received, - &packet_hasher - )); + assert!(shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); + assert!(shred_deduper.dedup(shred.id(), shred.payload(), MAX_DUPLICATE_COUNT)); } } diff --git a/core/src/rewards_recorder_service.rs b/core/src/rewards_recorder_service.rs index 8988441d22222f..d83b0a285f9dd2 100644 --- a/core/src/rewards_recorder_service.rs +++ b/core/src/rewards_recorder_service.rs @@ -6,7 +6,7 @@ use { solana_transaction_status::Reward, std::{ sync::{ - atomic::{AtomicBool, Ordering}, + atomic::{AtomicBool, AtomicU64, Ordering}, Arc, }, thread::{self, Builder, JoinHandle}, @@ -14,8 +14,14 @@ use { }, }; -pub type RewardsRecorderReceiver = Receiver<(Slot, Vec<(Pubkey, RewardInfo)>)>; -pub type RewardsRecorderSender = Sender<(Slot, Vec<(Pubkey, RewardInfo)>)>; +pub type RewardsBatch = (Slot, Vec<(Pubkey, RewardInfo)>); +pub type RewardsRecorderReceiver = Receiver; +pub type RewardsRecorderSender = Sender; + +pub enum RewardsMessage { + Batch(RewardsBatch), + Complete(Slot), +} pub struct RewardsRecorderService { thread_hdl: JoinHandle<()>, @@ -25,18 +31,19 @@ impl RewardsRecorderService { #[allow(clippy::new_ret_no_self)] pub fn new( rewards_receiver: RewardsRecorderReceiver, + max_complete_rewards_slot: Arc, blockstore: Arc, exit: &Arc, ) -> Self { let exit = exit.clone(); let thread_hdl = Builder::new() - .name("solana-rewards-writer".to_string()) + .name("solRewardsWritr".to_string()) .spawn(move || loop { if exit.load(Ordering::Relaxed) { break; } if let Err(RecvTimeoutError::Disconnected) = - Self::write_rewards(&rewards_receiver, &blockstore) + Self::write_rewards(&rewards_receiver, &max_complete_rewards_slot, &blockstore) { break; } @@ -47,23 +54,30 @@ impl RewardsRecorderService { fn write_rewards( rewards_receiver: &RewardsRecorderReceiver, + max_complete_rewards_slot: &Arc, blockstore: &Arc, ) -> Result<(), RecvTimeoutError> { - let (slot, rewards) = rewards_receiver.recv_timeout(Duration::from_secs(1))?; - let rpc_rewards = rewards - .into_iter() - .map(|(pubkey, reward_info)| Reward { - pubkey: pubkey.to_string(), - lamports: reward_info.lamports, - post_balance: reward_info.post_balance, - reward_type: Some(reward_info.reward_type), - commission: reward_info.commission, - }) - .collect(); + match rewards_receiver.recv_timeout(Duration::from_secs(1))? { + RewardsMessage::Batch((slot, rewards)) => { + let rpc_rewards = rewards + .into_iter() + .map(|(pubkey, reward_info)| Reward { + pubkey: pubkey.to_string(), + lamports: reward_info.lamports, + post_balance: reward_info.post_balance, + reward_type: Some(reward_info.reward_type), + commission: reward_info.commission, + }) + .collect(); - blockstore - .write_rewards(slot, rpc_rewards) - .expect("Expect database write to succeed"); + blockstore + .write_rewards(slot, rpc_rewards) + .expect("Expect database write to succeed"); + } + RewardsMessage::Complete(slot) => { + max_complete_rewards_slot.fetch_max(slot, Ordering::SeqCst); + } + } Ok(()) } diff --git a/core/src/sample_performance_service.rs b/core/src/sample_performance_service.rs index fe6820c91dde8d..acc6d08e1e55df 100644 --- a/core/src/sample_performance_service.rs +++ b/core/src/sample_performance_service.rs @@ -50,14 +50,18 @@ impl SamplePerformanceService { blockstore: &Arc, exit: Arc, ) { - let forks = bank_forks.read().unwrap(); - let bank = forks.root_bank(); - let highest_slot = forks.highest_slot(); - drop(forks); - - let mut sample_snapshot = SamplePerformanceSnapshot { - num_transactions: bank.transaction_count(), - num_slots: highest_slot, + let mut sample_snapshot = { + let forks = bank_forks.read().unwrap(); + let bank = forks.root_bank(); + let highest_slot = forks.highest_slot(); + + // Store the absolute transaction count to that we can compute the + // difference between these values at points in time to figure out + // how many transactions occurred in that timespan. + SamplePerformanceSnapshot { + num_transactions: bank.transaction_count(), + num_slots: highest_slot, + } }; let mut now = Instant::now(); diff --git a/core/src/serve_repair.rs b/core/src/serve_repair.rs index a9cc6cd0cb15f8..07016aeace6a9e 100644 --- a/core/src/serve_repair.rs +++ b/core/src/serve_repair.rs @@ -3,7 +3,7 @@ use { cluster_slots::ClusterSlots, duplicate_repair_status::ANCESTOR_HASH_REPAIR_SAMPLE_SIZE, repair_response, - repair_service::{OutstandingShredRepairs, RepairStats}, + repair_service::{OutstandingShredRepairs, RepairStats, REPAIR_MS}, request_response::RequestResponse, result::{Error, Result}, }, @@ -15,7 +15,7 @@ use { }, solana_gossip::{ cluster_info::{ClusterInfo, ClusterInfoError}, - contact_info::ContactInfo, + legacy_contact_info::{LegacyContactInfo as ContactInfo, LegacyContactInfo}, ping_pong::{self, PingCache, Pong}, weighted_shuffle::WeightedShuffle, }, @@ -29,24 +29,23 @@ use { data_budget::DataBudget, packet::{Packet, PacketBatch, PacketBatchRecycler}, }, - solana_runtime::{bank::Bank, bank_forks::BankForks}, + solana_runtime::bank_forks::BankForks, solana_sdk::{ clock::Slot, - feature_set::sign_repair_requests, + genesis_config::ClusterType, hash::{Hash, HASH_BYTES}, packet::PACKET_DATA_SIZE, pubkey::{Pubkey, PUBKEY_BYTES}, signature::{Signable, Signature, Signer, SIGNATURE_BYTES}, signer::keypair::Keypair, - stake_history::Epoch, timing::{duration_as_ms, timestamp}, }, solana_streamer::{ sendmmsg::{batch_send, SendPktsError}, - socket::SocketAddrSpace, streamer::{PacketBatchReceiver, PacketBatchSender}, }, std::{ + cmp::Reverse, collections::HashSet, net::{SocketAddr, UdpSocket}, sync::{ @@ -77,6 +76,7 @@ pub const MAX_ANCESTOR_RESPONSES: usize = pub(crate) const REPAIR_PING_TOKEN_SIZE: usize = HASH_BYTES; pub const REPAIR_PING_CACHE_CAPACITY: usize = 65536; pub const REPAIR_PING_CACHE_TTL: Duration = Duration::from_secs(1280); +const REPAIR_PING_CACHE_RATE_LIMIT_DELAY: Duration = Duration::from_secs(2); pub(crate) const REPAIR_RESPONSE_SERIALIZED_PING_BYTES: usize = 4 /*enum discriminator*/ + PUBKEY_BYTES + REPAIR_PING_TOKEN_SIZE + SIGNATURE_BYTES; const SIGNED_REPAIR_TIME_WINDOW: Duration = Duration::from_secs(60 * 10); // 10 min @@ -131,46 +131,36 @@ impl AncestorHashesRepairType { } #[derive(Debug, Serialize, Deserialize)] -pub enum AncestorHashesResponseVersion { - Current(Vec), -} -impl AncestorHashesResponseVersion { - pub fn into_slot_hashes(self) -> Vec { - match self { - AncestorHashesResponseVersion::Current(slot_hashes) => slot_hashes, - } - } - - pub fn slot_hashes(&self) -> &[SlotHash] { - match self { - AncestorHashesResponseVersion::Current(slot_hashes) => slot_hashes, - } - } - - fn max_ancestors_in_response(&self) -> usize { - match self { - AncestorHashesResponseVersion::Current(_) => MAX_ANCESTOR_RESPONSES, - } - } +pub enum AncestorHashesResponse { + Hashes(Vec), + Ping(Ping), } impl RequestResponse for AncestorHashesRepairType { - type Response = AncestorHashesResponseVersion; + type Response = AncestorHashesResponse; fn num_expected_responses(&self) -> u32 { 1 } - fn verify_response(&self, response: &AncestorHashesResponseVersion) -> bool { - response.slot_hashes().len() <= response.max_ancestors_in_response() + fn verify_response(&self, response: &AncestorHashesResponse) -> bool { + match response { + AncestorHashesResponse::Hashes(hashes) => hashes.len() <= MAX_ANCESTOR_RESPONSES, + AncestorHashesResponse::Ping(ping) => ping.verify(), + } } } #[derive(Default)] struct ServeRepairStats { total_requests: usize, - dropped_requests: usize, + dropped_requests_outbound_bandwidth: usize, + dropped_requests_load_shed: usize, + dropped_requests_low_stake: usize, total_dropped_response_packets: usize, total_response_packets: usize, - total_response_bytes: usize, + total_response_bytes_staked: usize, + total_response_bytes_unstaked: usize, + handle_requests_staked: usize, + handle_requests_unstaked: usize, processed: usize, self_repair: usize, window_index: usize, @@ -178,7 +168,10 @@ struct ServeRepairStats { orphan: usize, pong: usize, ancestor_hashes: usize, - pings_required: usize, + ping_cache_check_failed: usize, + pings_sent: usize, + decode_time_us: u64, + handle_requests_time_us: u64, err_time_skew: usize, err_malformed: usize, err_sig_verify: usize, @@ -212,13 +205,13 @@ pub(crate) type Ping = ping_pong::Ping<[u8; REPAIR_PING_TOKEN_SIZE]>; /// Window protocol messages #[derive(Serialize, Deserialize, Debug)] pub enum RepairProtocol { - LegacyWindowIndex(ContactInfo, Slot, u64), - LegacyHighestWindowIndex(ContactInfo, Slot, u64), - LegacyOrphan(ContactInfo, Slot), - LegacyWindowIndexWithNonce(ContactInfo, Slot, u64, Nonce), - LegacyHighestWindowIndexWithNonce(ContactInfo, Slot, u64, Nonce), - LegacyOrphanWithNonce(ContactInfo, Slot, Nonce), - LegacyAncestorHashes(ContactInfo, Slot, Nonce), + LegacyWindowIndex(LegacyContactInfo, Slot, u64), + LegacyHighestWindowIndex(LegacyContactInfo, Slot, u64), + LegacyOrphan(LegacyContactInfo, Slot), + LegacyWindowIndexWithNonce(LegacyContactInfo, Slot, u64, Nonce), + LegacyHighestWindowIndexWithNonce(LegacyContactInfo, Slot, u64, Nonce), + LegacyOrphanWithNonce(LegacyContactInfo, Slot, Nonce), + LegacyAncestorHashes(LegacyContactInfo, Slot, Nonce), Pong(ping_pong::Pong), WindowIndex { header: RepairRequestHeader, @@ -241,7 +234,7 @@ pub enum RepairProtocol { } #[derive(Serialize, Deserialize, Debug)] -enum RepairResponse { +pub(crate) enum RepairResponse { Ping(Ping), } @@ -279,23 +272,6 @@ impl RepairProtocol { | Self::AncestorHashes { .. } => true, } } - - fn requires_ping_check(&self) -> bool { - match self { - Self::LegacyWindowIndex(_, _, _) - | Self::LegacyHighestWindowIndex(_, _, _) - | Self::LegacyOrphan(_, _) - | Self::LegacyWindowIndexWithNonce(_, _, _, _) - | Self::LegacyHighestWindowIndexWithNonce(_, _, _, _) - | Self::LegacyOrphanWithNonce(_, _, _) - | Self::LegacyAncestorHashes(_, _, _) - | Self::Pong(_) - | Self::AncestorHashes { .. } => false, - Self::WindowIndex { .. } | Self::HighestWindowIndex { .. } | Self::Orphan { .. } => { - true - } - } - } } #[derive(Clone)] @@ -345,10 +321,6 @@ impl ServeRepair { } } - fn my_info(&self) -> ContactInfo { - self.cluster_info.my_contact_info() - } - pub(crate) fn my_id(&self) -> Pubkey { self.cluster_info.id() } @@ -457,24 +429,6 @@ impl ServeRepair { } } - pub(crate) fn sign_repair_requests_activated_epoch(root_bank: &Bank) -> Option { - root_bank - .feature_set - .activated_slot(&sign_repair_requests::id()) - .map(|slot| root_bank.epoch_schedule().get_epoch(slot)) - } - - pub(crate) fn should_sign_repair_request( - slot: Slot, - root_bank: &Bank, - sign_repairs_epoch: Option, - ) -> bool { - match sign_repairs_epoch { - None => false, - Some(feature_epoch) => feature_epoch < root_bank.epoch_schedule().get_epoch(slot), - } - } - /// Process messages from the network fn run_listen( &self, @@ -492,32 +446,98 @@ impl ServeRepair { const MAX_REQUESTS_PER_ITERATION: usize = 1024; let mut total_requests = reqs_v[0].len(); + let socket_addr_space = *self.cluster_info.socket_addr_space(); + let root_bank = self.bank_forks.read().unwrap().root_bank(); + let epoch_staked_nodes = root_bank.epoch_staked_nodes(root_bank.epoch()); + let identity_keypair = self.cluster_info.keypair().clone(); + let my_id = identity_keypair.pubkey(); + let cluster_type = root_bank.cluster_type(); + + let max_buffered_packets = if cluster_type != ClusterType::MainnetBeta { + 2 * MAX_REQUESTS_PER_ITERATION + } else { + MAX_REQUESTS_PER_ITERATION + }; + let mut dropped_requests = 0; while let Ok(more) = requests_receiver.try_recv() { total_requests += more.len(); - if total_requests > MAX_REQUESTS_PER_ITERATION { + if total_requests > max_buffered_packets { dropped_requests += more.len(); } else { reqs_v.push(more); } } - stats.dropped_requests += dropped_requests; + stats.dropped_requests_load_shed += dropped_requests; stats.total_requests += total_requests; - let root_bank = self.bank_forks.read().unwrap().root_bank(); - for reqs in reqs_v { - self.handle_packets( - ping_cache, - recycler, - blockstore, - reqs, - response_sender, - &root_bank, - stats, - data_budget, - ); + let decode_start = Instant::now(); + let mut decoded_reqs = Vec::default(); + for packet in reqs_v.iter().flatten() { + let request: RepairProtocol = match packet.deserialize_slice(..) { + Ok(request) => request, + Err(_) => { + stats.err_malformed += 1; + continue; + } + }; + + let from_addr = packet.meta.socket_addr(); + if !ContactInfo::is_valid_address(&from_addr, &socket_addr_space) { + stats.err_malformed += 1; + continue; + } + + match cluster_type { + ClusterType::Testnet | ClusterType::Development => { + if !Self::verify_signed_packet(&my_id, packet, &request, stats) { + continue; + } + } + ClusterType::MainnetBeta | ClusterType::Devnet => { + // collect stats for signature verification + let _ = Self::verify_signed_packet(&my_id, packet, &request, stats); + } + } + + if request.sender() == &my_id { + stats.self_repair += 1; + continue; + } + + let stake = epoch_staked_nodes + .as_ref() + .and_then(|stakes| stakes.get(request.sender())) + .unwrap_or(&0); + if *stake == 0 { + stats.handle_requests_unstaked += 1; + } else { + stats.handle_requests_staked += 1; + } + decoded_reqs.push((request, from_addr, *stake)); + } + stats.decode_time_us += decode_start.elapsed().as_micros() as u64; + + if decoded_reqs.len() > MAX_REQUESTS_PER_ITERATION { + stats.dropped_requests_low_stake += decoded_reqs.len() - MAX_REQUESTS_PER_ITERATION; + decoded_reqs.sort_unstable_by_key(|(_, _, stake)| Reverse(*stake)); + decoded_reqs.truncate(MAX_REQUESTS_PER_ITERATION); } + + let handle_requests_start = Instant::now(); + self.handle_requests( + ping_cache, + recycler, + blockstore, + decoded_reqs, + response_sender, + stats, + data_budget, + cluster_type, + ); + stats.handle_requests_time_us += handle_requests_start.elapsed().as_micros() as u64; + Ok(()) } @@ -534,14 +554,44 @@ impl ServeRepair { datapoint_info!( "serve_repair-requests_received", ("total_requests", stats.total_requests, i64), - ("dropped_requests", stats.dropped_requests, i64), + ( + "dropped_requests_outbound_bandwidth", + stats.dropped_requests_outbound_bandwidth, + i64 + ), + ( + "dropped_requests_load_shed", + stats.dropped_requests_load_shed, + i64 + ), + ( + "dropped_requests_low_stake", + stats.dropped_requests_low_stake, + i64 + ), ( "total_dropped_response_packets", stats.total_dropped_response_packets, i64 ), + ("handle_requests_staked", stats.handle_requests_staked, i64), + ( + "handle_requests_unstaked", + stats.handle_requests_unstaked, + i64 + ), + ("processed", stats.processed, i64), ("total_response_packets", stats.total_response_packets, i64), - ("total_response_bytes", stats.total_response_bytes, i64), + ( + "total_response_bytes_staked", + stats.total_response_bytes_staked, + i64 + ), + ( + "total_response_bytes_unstaked", + stats.total_response_bytes_unstaked, + i64 + ), ("self_repair", stats.self_repair, i64), ("window_index", stats.window_index, i64), ( @@ -556,7 +606,18 @@ impl ServeRepair { i64 ), ("pong", stats.pong, i64), - ("pings_required", stats.pings_required, i64), + ( + "ping_cache_check_failed", + stats.ping_cache_check_failed, + i64 + ), + ("pings_sent", stats.pings_sent, i64), + ("decode_time_us", stats.decode_time_us, i64), + ( + "handle_requests_time_us", + stats.handle_requests_time_us, + i64 + ), ("err_time_skew", stats.err_time_skew, i64), ("err_malformed", stats.err_malformed, i64), ("err_sig_verify", stats.err_sig_verify, i64), @@ -578,11 +639,18 @@ impl ServeRepair { const MAX_BYTES_PER_SECOND: usize = 12_000_000; const MAX_BYTES_PER_INTERVAL: usize = MAX_BYTES_PER_SECOND * INTERVAL_MS as usize / 1000; - let mut ping_cache = PingCache::new(REPAIR_PING_CACHE_TTL, REPAIR_PING_CACHE_CAPACITY); + // rate limit delay should be greater than the repair request iteration delay + assert!(REPAIR_PING_CACHE_RATE_LIMIT_DELAY > Duration::from_millis(REPAIR_MS)); + + let mut ping_cache = PingCache::new( + REPAIR_PING_CACHE_TTL, + REPAIR_PING_CACHE_RATE_LIMIT_DELAY, + REPAIR_PING_CACHE_CAPACITY, + ); let recycler = PacketBatchRecycler::default(); Builder::new() - .name("solana-repair-listen".to_string()) + .name("solRepairListen".to_string()) .spawn(move || { let mut last_print = Instant::now(); let mut stats = ServeRepairStats::default(); @@ -614,6 +682,7 @@ impl ServeRepair { .unwrap() } + #[must_use] fn verify_signed_packet( my_id: &Pubkey, packet: &Packet, @@ -628,7 +697,6 @@ impl ServeRepair { | RepairProtocol::LegacyHighestWindowIndexWithNonce(_, _, _, _) | RepairProtocol::LegacyOrphanWithNonce(_, _, _) | RepairProtocol::LegacyAncestorHashes(_, _, _) => { - debug_assert!(false); // expecting only signed request types stats.err_unsigned += 1; return false; } @@ -679,113 +747,72 @@ impl ServeRepair { } fn check_ping_cache( + ping_cache: &mut PingCache, request: &RepairProtocol, from_addr: &SocketAddr, identity_keypair: &Keypair, - socket_addr_space: &SocketAddrSpace, - ping_cache: &mut PingCache, - pending_pings: &mut Vec<(SocketAddr, Ping)>, - stats: &mut ServeRepairStats, - ) -> bool { - if !ContactInfo::is_valid_address(from_addr, socket_addr_space) { - stats.err_malformed += 1; - return false; - } + ) -> (bool, Option) { let mut rng = rand::thread_rng(); let mut pingf = move || Ping::new_rand(&mut rng, identity_keypair).ok(); let (check, ping) = ping_cache.check(Instant::now(), (*request.sender(), *from_addr), &mut pingf); - if let Some(ping) = ping { - pending_pings.push((*from_addr, ping)); - } - if !check { - stats.pings_required += 1; - } - check - } - - fn requires_signature_check( - request: &RepairProtocol, - root_bank: &Bank, - sign_repairs_epoch: Option, - ) -> bool { - match request { - RepairProtocol::LegacyWindowIndex(_, slot, _) - | RepairProtocol::LegacyHighestWindowIndex(_, slot, _) - | RepairProtocol::LegacyOrphan(_, slot) - | RepairProtocol::LegacyWindowIndexWithNonce(_, slot, _, _) - | RepairProtocol::LegacyHighestWindowIndexWithNonce(_, slot, _, _) - | RepairProtocol::LegacyOrphanWithNonce(_, slot, _) - | RepairProtocol::LegacyAncestorHashes(_, slot, _) - | RepairProtocol::WindowIndex { slot, .. } - | RepairProtocol::HighestWindowIndex { slot, .. } - | RepairProtocol::Orphan { slot, .. } - | RepairProtocol::AncestorHashes { slot, .. } => { - Self::should_sign_repair_request(*slot, root_bank, sign_repairs_epoch) + let ping_pkt = if let Some(ping) = ping { + match request { + RepairProtocol::LegacyWindowIndex(_, _, _) + | RepairProtocol::LegacyHighestWindowIndex(_, _, _) + | RepairProtocol::LegacyOrphan(_, _) + | RepairProtocol::LegacyWindowIndexWithNonce(_, _, _, _) + | RepairProtocol::LegacyHighestWindowIndexWithNonce(_, _, _, _) + | RepairProtocol::LegacyOrphanWithNonce(_, _, _) + | RepairProtocol::WindowIndex { .. } + | RepairProtocol::HighestWindowIndex { .. } + | RepairProtocol::Orphan { .. } => { + let ping = RepairResponse::Ping(ping); + Packet::from_data(Some(from_addr), ping).ok() + } + RepairProtocol::LegacyAncestorHashes(_, _, _) + | RepairProtocol::AncestorHashes { .. } => { + let ping = AncestorHashesResponse::Ping(ping); + Packet::from_data(Some(from_addr), ping).ok() + } + RepairProtocol::Pong(_) => None, } - RepairProtocol::Pong(_) => true, - } + } else { + None + }; + (check, ping_pkt) } - fn handle_packets( + fn handle_requests( &self, ping_cache: &mut PingCache, recycler: &PacketBatchRecycler, blockstore: &Blockstore, - packet_batch: PacketBatch, + requests: Vec<(RepairProtocol, SocketAddr, /*stake*/ u64)>, response_sender: &PacketBatchSender, - root_bank: &Bank, stats: &mut ServeRepairStats, data_budget: &DataBudget, + cluster_type: ClusterType, ) { - let sign_repairs_epoch = Self::sign_repair_requests_activated_epoch(root_bank); let identity_keypair = self.cluster_info.keypair().clone(); - let socket_addr_space = *self.cluster_info.socket_addr_space(); - let my_id = identity_keypair.pubkey(); let mut pending_pings = Vec::default(); - // iter over the packets - for (i, packet) in packet_batch.iter().enumerate() { - let request: RepairProtocol = match packet.deserialize_slice(..) { - Ok(request) => request, - Err(_) => { - stats.err_malformed += 1; - continue; + let requests_len = requests.len(); + for (i, (request, from_addr, stake)) in requests.into_iter().enumerate() { + if !matches!(&request, RepairProtocol::Pong(_)) { + let (check, ping_pkt) = + Self::check_ping_cache(ping_cache, &request, &from_addr, &identity_keypair); + if let Some(ping_pkt) = ping_pkt { + pending_pings.push(ping_pkt); + } + if !check { + stats.ping_cache_check_failed += 1; + match cluster_type { + ClusterType::Testnet | ClusterType::Development => continue, + ClusterType::MainnetBeta | ClusterType::Devnet => (), + } } - }; - - if request.sender() == &my_id { - stats.self_repair += 1; - continue; - } - - let require_signature_check = - Self::requires_signature_check(&request, root_bank, sign_repairs_epoch); - if require_signature_check && !request.supports_signature() { - stats.err_unsigned += 1; - continue; - } - if request.supports_signature() - && !Self::verify_signed_packet(&my_id, packet, &request, stats) - { - continue; - } - - let from_addr = packet.meta.socket_addr(); - if request.requires_ping_check() - && !Self::check_ping_cache( - &request, - &from_addr, - &identity_keypair, - &socket_addr_space, - ping_cache, - &mut pending_pings, - stats, - ) - { - continue; } - stats.processed += 1; let rsp = match Self::handle_repair( recycler, &from_addr, blockstore, request, stats, ping_cache, @@ -796,63 +823,44 @@ impl ServeRepair { let num_response_packets = rsp.len(); let num_response_bytes = rsp.iter().map(|p| p.meta.size).sum(); if data_budget.take(num_response_bytes) && response_sender.send(rsp).is_ok() { - stats.total_response_bytes += num_response_bytes; stats.total_response_packets += num_response_packets; + match stake > 0 { + true => stats.total_response_bytes_staked += num_response_bytes, + false => stats.total_response_bytes_unstaked += num_response_bytes, + } } else { - stats.dropped_requests += packet_batch.len() - i; + stats.dropped_requests_outbound_bandwidth += requests_len - i; stats.total_dropped_response_packets += num_response_packets; break; } } if !pending_pings.is_empty() { - let packets: Vec<_> = pending_pings - .into_iter() - .filter_map(|(sockaddr, ping)| { - let ping = RepairResponse::Ping(ping); - Packet::from_data(Some(&sockaddr), ping).ok() - }) - .collect(); - let batch = PacketBatch::new(packets); - let _ = response_sender.send(batch); + stats.pings_sent += pending_pings.len(); + let batch = PacketBatch::new(pending_pings); + let _ignore = response_sender.send(batch); } } pub fn ancestor_repair_request_bytes( &self, keypair: &Keypair, - root_bank: &Bank, repair_peer_id: &Pubkey, request_slot: Slot, nonce: Nonce, ) -> Result> { - let sign_repairs_epoch = Self::sign_repair_requests_activated_epoch(root_bank); - let require_sig = - Self::should_sign_repair_request(request_slot, root_bank, sign_repairs_epoch); - - let (request_proto, maybe_keypair) = if require_sig { - let header = RepairRequestHeader { - signature: Signature::default(), - sender: self.my_id(), - recipient: *repair_peer_id, - timestamp: timestamp(), - nonce, - }; - ( - RepairProtocol::AncestorHashes { - header, - slot: request_slot, - }, - Some(keypair), - ) - } else { - ( - RepairProtocol::LegacyAncestorHashes(self.my_info(), request_slot, nonce), - None, - ) + let header = RepairRequestHeader { + signature: Signature::default(), + sender: self.my_id(), + recipient: *repair_peer_id, + timestamp: timestamp(), + nonce, }; - - Self::repair_proto_to_bytes(&request_proto, maybe_keypair) + let request = RepairProtocol::AncestorHashes { + header, + slot: request_slot, + }; + Self::repair_proto_to_bytes(&request, keypair) } pub(crate) fn repair_request( @@ -863,7 +871,7 @@ impl ServeRepair { repair_stats: &mut RepairStats, repair_validators: &Option>, outstanding_requests: &mut OutstandingShredRepairs, - identity_keypair: Option<&Keypair>, + identity_keypair: &Keypair, ) -> Result<(SocketAddr, Vec)> { // find a peer that appears to be accepting replication and has the desired slot, as indicated // by a valid tvu port location @@ -939,67 +947,41 @@ impl ServeRepair { repair_peer_id: &Pubkey, repair_stats: &mut RepairStats, nonce: Nonce, - identity_keypair: Option<&Keypair>, + identity_keypair: &Keypair, ) -> Result> { - let header = if identity_keypair.is_some() { - Some(RepairRequestHeader { - signature: Signature::default(), - sender: self.my_id(), - recipient: *repair_peer_id, - timestamp: timestamp(), - nonce, - }) - } else { - None + let header = RepairRequestHeader { + signature: Signature::default(), + sender: self.my_id(), + recipient: *repair_peer_id, + timestamp: timestamp(), + nonce, }; let request_proto = match repair_request { ShredRepairType::Shred(slot, shred_index) => { repair_stats .shred .update(repair_peer_id, *slot, *shred_index); - if let Some(header) = header { - RepairProtocol::WindowIndex { - header, - slot: *slot, - shred_index: *shred_index, - } - } else { - RepairProtocol::LegacyWindowIndexWithNonce( - self.my_info(), - *slot, - *shred_index, - nonce, - ) + RepairProtocol::WindowIndex { + header, + slot: *slot, + shred_index: *shred_index, } } ShredRepairType::HighestShred(slot, shred_index) => { repair_stats .highest_shred .update(repair_peer_id, *slot, *shred_index); - if let Some(header) = header { - RepairProtocol::HighestWindowIndex { - header, - slot: *slot, - shred_index: *shred_index, - } - } else { - RepairProtocol::LegacyHighestWindowIndexWithNonce( - self.my_info(), - *slot, - *shred_index, - nonce, - ) + RepairProtocol::HighestWindowIndex { + header, + slot: *slot, + shred_index: *shred_index, } } ShredRepairType::Orphan(slot) => { repair_stats.orphan.update(repair_peer_id, *slot, 0); - if let Some(header) = header { - RepairProtocol::Orphan { - header, - slot: *slot, - } - } else { - RepairProtocol::LegacyOrphanWithNonce(self.my_info(), *slot, nonce) + RepairProtocol::Orphan { + header, + slot: *slot, } } }; @@ -1055,17 +1037,12 @@ impl ServeRepair { } } - pub fn repair_proto_to_bytes( - request: &RepairProtocol, - keypair: Option<&Keypair>, - ) -> Result> { + pub fn repair_proto_to_bytes(request: &RepairProtocol, keypair: &Keypair) -> Result> { + debug_assert!(request.supports_signature()); let mut payload = serialize(&request)?; - if let Some(keypair) = keypair { - debug_assert!(request.supports_signature()); - let signable_data = [&payload[..4], &payload[4 + SIGNATURE_BYTES..]].concat(); - let signature = keypair.sign_message(&signable_data[..]); - payload[4..4 + SIGNATURE_BYTES].copy_from_slice(signature.as_ref()); - } + let signable_data = [&payload[..4], &payload[4 + SIGNATURE_BYTES..]].concat(); + let signature = keypair.sign_message(&signable_data[..]); + payload[4..4 + SIGNATURE_BYTES].copy_from_slice(signature.as_ref()); Ok(payload) } @@ -1198,7 +1175,7 @@ impl ServeRepair { // If this slot is not duplicate confirmed, return nothing vec![] }; - let response = AncestorHashesResponseVersion::Current(ancestor_slot_hashes); + let response = AncestorHashesResponse::Hashes(ancestor_slot_hashes); let serialized_response = serialize(&response).ok()?; // Could probably directly write response into packet via `serialize_into()` @@ -1294,7 +1271,7 @@ mod tests { &repair_peer_id, &mut RepairStats::default(), 456, - Some(&keypair), + &keypair, ) .unwrap(); @@ -1318,7 +1295,7 @@ mod tests { #[test] fn test_serialize_deserialize_ancestor_hashes_request() { - let slot = 50; + let slot: Slot = 50; let nonce = 70; let me = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp()); let cluster_info = Arc::new(new_test_cluster_info(me)); @@ -1329,11 +1306,10 @@ mod tests { let mut bank = Bank::new_for_tests(&genesis_config); bank.feature_set = Arc::new(FeatureSet::all_enabled()); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); - let serve_repair = ServeRepair::new(cluster_info.clone(), bank_forks.clone()); + let serve_repair = ServeRepair::new(cluster_info, bank_forks); - let root_bank = bank_forks.read().unwrap().root_bank(); let request_bytes = serve_repair - .ancestor_repair_request_bytes(&keypair, &root_bank, &repair_peer_id, slot, nonce) + .ancestor_repair_request_bytes(&keypair, &repair_peer_id, slot, nonce) .unwrap(); let mut cursor = Cursor::new(&request_bytes[..]); let deserialized_request: RepairProtocol = @@ -1355,35 +1331,10 @@ mod tests { } else { panic!("unexpected request type {:?}", &deserialized_request); } - - let mut bank = Bank::new_for_tests(&genesis_config); - let mut feature_set = FeatureSet::all_enabled(); - feature_set.deactivate(&sign_repair_requests::id()); - bank.feature_set = Arc::new(feature_set); - let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); - let serve_repair = ServeRepair::new(cluster_info, bank_forks.clone()); - - let root_bank = bank_forks.read().unwrap().root_bank(); - let request_bytes = serve_repair - .ancestor_repair_request_bytes(&keypair, &root_bank, &repair_peer_id, slot, nonce) - .unwrap(); - let mut cursor = Cursor::new(&request_bytes[..]); - let deserialized_request: RepairProtocol = - deserialize_from_with_limit(&mut cursor).unwrap(); - assert_eq!(cursor.position(), request_bytes.len() as u64); - if let RepairProtocol::LegacyAncestorHashes(ci, deserialized_slot, deserialized_nonce) = - deserialized_request - { - assert_eq!(slot, deserialized_slot); - assert_eq!(nonce, deserialized_nonce); - assert_eq!(&serve_repair.my_id(), &ci.id); - } else { - panic!("unexpected request type {:?}", &deserialized_request); - } } #[test] - fn test_map_requests_signed_unsigned() { + fn test_map_requests_signed() { let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000); let bank = Bank::new_for_tests(&genesis_config); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); @@ -1398,20 +1349,20 @@ mod tests { let nonce = 70; let request = ShredRepairType::Shred(slot, shred_index); - let rsp = serve_repair + let request_bytes = serve_repair .map_repair_request( &request, &repair_peer_id, &mut RepairStats::default(), nonce, - Some(&keypair), + &keypair, ) .unwrap(); - let mut cursor = Cursor::new(&rsp[..]); + let mut cursor = Cursor::new(&request_bytes[..]); let deserialized_request: RepairProtocol = deserialize_from_with_limit(&mut cursor).unwrap(); - assert_eq!(cursor.position(), rsp.len() as u64); + assert_eq!(cursor.position(), request_bytes.len() as u64); if let RepairProtocol::WindowIndex { header, slot: deserialized_slot, @@ -1423,7 +1374,7 @@ mod tests { assert_eq!(header.nonce, nonce); assert_eq!(&header.sender, &serve_repair.my_id()); assert_eq!(&header.recipient, &repair_peer_id); - let signed_data = [&rsp[..4], &rsp[4 + SIGNATURE_BYTES..]].concat(); + let signed_data = [&request_bytes[..4], &request_bytes[4 + SIGNATURE_BYTES..]].concat(); assert!(header .signature .verify(keypair.pubkey().as_ref(), &signed_data)); @@ -1431,50 +1382,21 @@ mod tests { panic!("unexpected request type {:?}", &deserialized_request); } - let rsp = serve_repair - .map_repair_request( - &request, - &repair_peer_id, - &mut RepairStats::default(), - nonce, - None, - ) - .unwrap(); - - let mut cursor = Cursor::new(&rsp[..]); - let deserialized_request: RepairProtocol = - deserialize_from_with_limit(&mut cursor).unwrap(); - assert_eq!(cursor.position(), rsp.len() as u64); - if let RepairProtocol::LegacyWindowIndexWithNonce( - ci, - deserialized_slot, - deserialized_shred_index, - deserialized_nonce, - ) = deserialized_request - { - assert_eq!(slot, deserialized_slot); - assert_eq!(shred_index, deserialized_shred_index); - assert_eq!(nonce, deserialized_nonce); - assert_eq!(&serve_repair.my_id(), &ci.id); - } else { - panic!("unexpected request type {:?}", &deserialized_request); - } - let request = ShredRepairType::HighestShred(slot, shred_index); - let rsp = serve_repair + let request_bytes = serve_repair .map_repair_request( &request, &repair_peer_id, &mut RepairStats::default(), nonce, - Some(&keypair), + &keypair, ) .unwrap(); - let mut cursor = Cursor::new(&rsp[..]); + let mut cursor = Cursor::new(&request_bytes[..]); let deserialized_request: RepairProtocol = deserialize_from_with_limit(&mut cursor).unwrap(); - assert_eq!(cursor.position(), rsp.len() as u64); + assert_eq!(cursor.position(), request_bytes.len() as u64); if let RepairProtocol::HighestWindowIndex { header, slot: deserialized_slot, @@ -1486,50 +1408,19 @@ mod tests { assert_eq!(header.nonce, nonce); assert_eq!(&header.sender, &serve_repair.my_id()); assert_eq!(&header.recipient, &repair_peer_id); - let signed_data = [&rsp[..4], &rsp[4 + SIGNATURE_BYTES..]].concat(); + let signed_data = [&request_bytes[..4], &request_bytes[4 + SIGNATURE_BYTES..]].concat(); assert!(header .signature .verify(keypair.pubkey().as_ref(), &signed_data)); } else { panic!("unexpected request type {:?}", &deserialized_request); } - - let rsp = serve_repair - .map_repair_request( - &request, - &repair_peer_id, - &mut RepairStats::default(), - nonce, - None, - ) - .unwrap(); - - let mut cursor = Cursor::new(&rsp[..]); - let deserialized_request: RepairProtocol = - deserialize_from_with_limit(&mut cursor).unwrap(); - assert_eq!(cursor.position(), rsp.len() as u64); - if let RepairProtocol::LegacyHighestWindowIndexWithNonce( - ci, - deserialized_slot, - deserialized_shred_index, - deserialized_nonce, - ) = deserialized_request - { - assert_eq!(slot, deserialized_slot); - assert_eq!(shred_index, deserialized_shred_index); - assert_eq!(nonce, deserialized_nonce); - assert_eq!(&serve_repair.my_id(), &ci.id); - } else { - panic!("unexpected request type {:?}", &deserialized_request); - } } #[test] fn test_verify_signed_packet() { - let keypair = Keypair::new(); + let my_keypair = Keypair::new(); let other_keypair = Keypair::new(); - let my_id = Pubkey::new_unique(); - let other_id = Pubkey::new_unique(); fn sign_packet(packet: &mut Packet, keypair: &Keypair) { let signable_data = [ @@ -1543,16 +1434,21 @@ mod tests { // well formed packet let packet = { - let header = RepairRequestHeader::new(keypair.pubkey(), my_id, timestamp(), 678); + let header = RepairRequestHeader::new( + my_keypair.pubkey(), + other_keypair.pubkey(), + timestamp(), + 678, + ); let slot = 239847; let request = RepairProtocol::Orphan { header, slot }; let mut packet = Packet::from_data(None, &request).unwrap(); - sign_packet(&mut packet, &keypair); + sign_packet(&mut packet, &my_keypair); packet }; let request: RepairProtocol = packet.deserialize_slice(..).unwrap(); assert!(ServeRepair::verify_signed_packet( - &my_id, + &other_keypair.pubkey(), &packet, &request, &mut ServeRepairStats::default(), @@ -1560,17 +1456,25 @@ mod tests { // recipient mismatch let packet = { - let header = RepairRequestHeader::new(keypair.pubkey(), other_id, timestamp(), 678); + let header = RepairRequestHeader::new( + my_keypair.pubkey(), + other_keypair.pubkey(), + timestamp(), + 678, + ); let slot = 239847; let request = RepairProtocol::Orphan { header, slot }; let mut packet = Packet::from_data(None, &request).unwrap(); - sign_packet(&mut packet, &keypair); + sign_packet(&mut packet, &my_keypair); packet }; let request: RepairProtocol = packet.deserialize_slice(..).unwrap(); let mut stats = ServeRepairStats::default(); assert!(!ServeRepair::verify_signed_packet( - &my_id, &packet, &request, &mut stats, + &my_keypair.pubkey(), + &packet, + &request, + &mut stats, )); assert_eq!(stats.err_id_mismatch, 1); @@ -1578,23 +1482,36 @@ mod tests { let packet = { let time_diff_ms = u64::try_from(SIGNED_REPAIR_TIME_WINDOW.as_millis() * 2).unwrap(); let old_timestamp = timestamp().saturating_sub(time_diff_ms); - let header = RepairRequestHeader::new(keypair.pubkey(), my_id, old_timestamp, 678); + let header = RepairRequestHeader::new( + my_keypair.pubkey(), + other_keypair.pubkey(), + old_timestamp, + 678, + ); let slot = 239847; let request = RepairProtocol::Orphan { header, slot }; let mut packet = Packet::from_data(None, &request).unwrap(); - sign_packet(&mut packet, &keypair); + sign_packet(&mut packet, &my_keypair); packet }; let request: RepairProtocol = packet.deserialize_slice(..).unwrap(); let mut stats = ServeRepairStats::default(); assert!(!ServeRepair::verify_signed_packet( - &my_id, &packet, &request, &mut stats, + &other_keypair.pubkey(), + &packet, + &request, + &mut stats, )); assert_eq!(stats.err_time_skew, 1); // bad signature let packet = { - let header = RepairRequestHeader::new(keypair.pubkey(), my_id, timestamp(), 678); + let header = RepairRequestHeader::new( + my_keypair.pubkey(), + other_keypair.pubkey(), + timestamp(), + 678, + ); let slot = 239847; let request = RepairProtocol::Orphan { header, slot }; let mut packet = Packet::from_data(None, &request).unwrap(); @@ -1604,7 +1521,10 @@ mod tests { let request: RepairProtocol = packet.deserialize_slice(..).unwrap(); let mut stats = ServeRepairStats::default(); assert!(!ServeRepair::verify_signed_packet( - &my_id, &packet, &request, &mut stats, + &other_keypair.pubkey(), + &packet, + &request, + &mut stats, )); assert_eq!(stats.err_sig_verify, 1); } @@ -1748,6 +1668,7 @@ mod tests { let me = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp()); let cluster_info = Arc::new(new_test_cluster_info(me)); let serve_repair = ServeRepair::new(cluster_info.clone(), bank_forks); + let identity_keypair = cluster_info.keypair().clone(); let mut outstanding_requests = OutstandingShredRepairs::default(); let rv = serve_repair.repair_request( &cluster_slots, @@ -1756,7 +1677,7 @@ mod tests { &mut RepairStats::default(), &None, &mut outstanding_requests, - None, + &identity_keypair, ); assert_matches!(rv, Err(Error::ClusterInfo(ClusterInfoError::NoPeers))); @@ -1785,7 +1706,7 @@ mod tests { &mut RepairStats::default(), &None, &mut outstanding_requests, - None, + &identity_keypair, ) .unwrap(); assert_eq!(nxt.serve_repair, serve_repair_addr); @@ -1820,7 +1741,7 @@ mod tests { &mut RepairStats::default(), &None, &mut outstanding_requests, - None, + &identity_keypair, ) .unwrap(); if rv.0 == serve_repair_addr { @@ -1961,7 +1882,7 @@ mod tests { #[test] fn test_run_ancestor_hashes() { - fn deserialize_ancestor_hashes_response(packet: &Packet) -> AncestorHashesResponseVersion { + fn deserialize_ancestor_hashes_response(packet: &Packet) -> AncestorHashesResponse { packet .deserialize_slice(..packet.meta.size - SIZE_OF_NONCE) .unwrap() @@ -1996,7 +1917,14 @@ mod tests { assert_eq!(rv.len(), 1); let packet = &rv[0]; let ancestor_hashes_response = deserialize_ancestor_hashes_response(packet); - assert!(ancestor_hashes_response.into_slot_hashes().is_empty()); + match ancestor_hashes_response { + AncestorHashesResponse::Hashes(hashes) => { + assert!(hashes.is_empty()); + } + _ => { + panic!("unexpected response: {:?}", &ancestor_hashes_response); + } + } // `slot + num_slots - 1` is not marked duplicate confirmed so nothing should return // empty @@ -2011,7 +1939,14 @@ mod tests { assert_eq!(rv.len(), 1); let packet = &rv[0]; let ancestor_hashes_response = deserialize_ancestor_hashes_response(packet); - assert!(ancestor_hashes_response.into_slot_hashes().is_empty()); + match ancestor_hashes_response { + AncestorHashesResponse::Hashes(hashes) => { + assert!(hashes.is_empty()); + } + _ => { + panic!("unexpected response: {:?}", &ancestor_hashes_response); + } + } // Set duplicate confirmed let mut expected_ancestors = Vec::with_capacity(num_slots as usize); @@ -2033,10 +1968,14 @@ mod tests { assert_eq!(rv.len(), 1); let packet = &rv[0]; let ancestor_hashes_response = deserialize_ancestor_hashes_response(packet); - assert_eq!( - ancestor_hashes_response.into_slot_hashes(), - expected_ancestors - ); + match ancestor_hashes_response { + AncestorHashesResponse::Hashes(hashes) => { + assert_eq!(hashes, expected_ancestors); + } + _ => { + panic!("unexpected response: {:?}", &ancestor_hashes_response); + } + } } Blockstore::destroy(&ledger_path).expect("Expected successful database destruction"); @@ -2058,6 +1997,7 @@ mod tests { ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp()); cluster_info.insert_info(contact_info2.clone()); cluster_info.insert_info(contact_info3.clone()); + let identity_keypair = cluster_info.keypair().clone(); let serve_repair = ServeRepair::new(cluster_info, bank_forks); // If: @@ -2075,7 +2015,7 @@ mod tests { &mut RepairStats::default(), &known_validators, &mut OutstandingShredRepairs::default(), - None, + &identity_keypair, ) .is_err()); } @@ -2093,7 +2033,7 @@ mod tests { &mut RepairStats::default(), &known_validators, &mut OutstandingShredRepairs::default(), - None, + &identity_keypair, ) .is_ok()); @@ -2115,7 +2055,7 @@ mod tests { &mut RepairStats::default(), &None, &mut OutstandingShredRepairs::default(), - None, + &identity_keypair, ) .is_ok()); } @@ -2184,10 +2124,10 @@ mod tests { .into_iter() .map(|slot| (slot, Hash::new_unique())) .collect(); - assert!(repair.verify_response(&AncestorHashesResponseVersion::Current(response.clone()))); + assert!(repair.verify_response(&AncestorHashesResponse::Hashes(response.clone()))); // over the allowed limit, should fail response.push((request_slot, Hash::new_unique())); - assert!(!repair.verify_response(&AncestorHashesResponseVersion::Current(response))); + assert!(!repair.verify_response(&AncestorHashesResponse::Hashes(response))); } } diff --git a/core/src/serve_repair_service.rs b/core/src/serve_repair_service.rs index 72dc7a49e66553..144de5c2a98d48 100644 --- a/core/src/serve_repair_service.rs +++ b/core/src/serve_repair_service.rs @@ -46,7 +46,7 @@ impl ServeRepairService { ); let (response_sender, response_receiver) = unbounded(); let t_responder = streamer::responder( - "serve-repairs", + "Repair", serve_repair_socket, response_receiver, socket_addr_space, diff --git a/core/src/shred_fetch_stage.rs b/core/src/shred_fetch_stage.rs index ae604e766f1957..770a8115c043fe 100644 --- a/core/src/shred_fetch_stage.rs +++ b/core/src/shred_fetch_stage.rs @@ -1,26 +1,28 @@ //! The `shred_fetch_stage` pulls shreds from UDP sockets and sends it to a channel. use { - crate::{packet_hasher::PacketHasher, serve_repair::ServeRepair}, + crate::{cluster_nodes::check_feature_activation, serve_repair::ServeRepair}, crossbeam_channel::{unbounded, Sender}, - lru::LruCache, solana_gossip::cluster_info::ClusterInfo, solana_ledger::shred::{should_discard_shred, ShredFetchStats}, - solana_perf::packet::{Packet, PacketBatch, PacketBatchRecycler, PacketFlags}, - solana_runtime::bank_forks::BankForks, - solana_sdk::clock::{Slot, DEFAULT_MS_PER_SLOT}, + solana_perf::packet::{PacketBatch, PacketBatchRecycler, PacketFlags}, + solana_runtime::{bank::Bank, bank_forks::BankForks}, + solana_sdk::{ + clock::{Slot, DEFAULT_MS_PER_SLOT}, + feature_set, + }, solana_streamer::streamer::{self, PacketBatchReceiver, StreamerReceiveStats}, std::{ net::UdpSocket, - sync::{atomic::AtomicBool, Arc, RwLock}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, RwLock, + }, thread::{self, Builder, JoinHandle}, time::{Duration, Instant}, }, }; -const DEFAULT_LRU_SIZE: usize = 10_000; -type ShredsReceived = LruCache; - pub(crate) struct ShredFetchStage { thread_hdls: Vec>, } @@ -35,33 +37,31 @@ impl ShredFetchStage { name: &'static str, flags: PacketFlags, repair_context: Option<(&UdpSocket, &ClusterInfo)>, + turbine_disabled: Arc, ) { const STATS_SUBMIT_CADENCE: Duration = Duration::from_secs(1); - let mut shreds_received = LruCache::new(DEFAULT_LRU_SIZE); let mut last_updated = Instant::now(); let mut keypair = repair_context .as_ref() .map(|(_, cluster_info)| cluster_info.keypair().clone()); // In the case of bank_forks=None, setup to accept any slot range + let mut root_bank = bank_forks.read().unwrap().root_bank(); let mut last_root = 0; let mut last_slot = std::u64::MAX; let mut slots_per_epoch = 0; let mut stats = ShredFetchStats::default(); - let mut packet_hasher = PacketHasher::default(); for mut packet_batch in recvr { if last_updated.elapsed().as_millis() as u64 > DEFAULT_MS_PER_SLOT { last_updated = Instant::now(); - packet_hasher.reset(); - shreds_received.clear(); { let bank_forks_r = bank_forks.read().unwrap(); last_root = bank_forks_r.root(); let working_bank = bank_forks_r.working_bank(); last_slot = working_bank.slot(); - let root_bank = bank_forks_r.root_bank(); + root_bank = bank_forks_r.root_bank(); slots_per_epoch = root_bank.get_slots_in_epoch(root_bank.epoch()); } keypair = repair_context @@ -85,16 +85,20 @@ impl ShredFetchStage { // Limit shreds to 2 epochs away. let max_slot = last_slot + 2 * slots_per_epoch; + let should_drop_merkle_shreds = + |shred_slot| should_drop_merkle_shreds(shred_slot, &root_bank); + let turbine_disabled = turbine_disabled.load(Ordering::Relaxed); for packet in packet_batch.iter_mut() { - if should_discard_packet( - packet, - last_root, - max_slot, - shred_version, - &packet_hasher, - &mut shreds_received, - &mut stats, - ) { + if turbine_disabled + || should_discard_shred( + packet, + last_root, + max_slot, + shred_version, + should_drop_merkle_shreds, + &mut stats, + ) + { packet.meta.set_discard(true); } else { packet.meta.flags.insert(flags); @@ -107,6 +111,7 @@ impl ShredFetchStage { } } + #[allow(clippy::too_many_arguments)] fn packet_modifier( sockets: Vec>, exit: &Arc, @@ -117,6 +122,7 @@ impl ShredFetchStage { name: &'static str, flags: PacketFlags, repair_context: Option<(Arc, Arc)>, + turbine_disabled: Arc, ) -> (Vec>, JoinHandle<()>) { let (packet_sender, packet_receiver) = unbounded(); let streamers = sockets @@ -135,7 +141,7 @@ impl ShredFetchStage { }) .collect(); let modifier_hdl = Builder::new() - .name("solana-tvu-fetch-stage-packet-modifier".to_string()) + .name("solTvuFetchPMod".to_string()) .spawn(move || { let repair_context = repair_context .as_ref() @@ -148,6 +154,7 @@ impl ShredFetchStage { name, flags, repair_context, + turbine_disabled, ) }) .unwrap(); @@ -162,6 +169,7 @@ impl ShredFetchStage { shred_version: u16, bank_forks: Arc>, cluster_info: Arc, + turbine_disabled: Arc, exit: &Arc, ) -> Self { let recycler = PacketBatchRecycler::warmed(100, 1024); @@ -176,6 +184,7 @@ impl ShredFetchStage { "shred_fetch", PacketFlags::empty(), None, // repair_context + turbine_disabled.clone(), ); let (tvu_forwards_threads, fwd_thread_hdl) = Self::packet_modifier( @@ -188,6 +197,7 @@ impl ShredFetchStage { "shred_fetch_tvu_forwards", PacketFlags::FORWARDED, None, // repair_context + turbine_disabled.clone(), ); let (repair_receiver, repair_handler) = Self::packet_modifier( @@ -200,6 +210,7 @@ impl ShredFetchStage { "shred_fetch_repair", PacketFlags::REPAIR, Some((repair_socket, cluster_info)), + turbine_disabled, ); tvu_threads.extend(tvu_forwards_threads.into_iter()); @@ -221,28 +232,17 @@ impl ShredFetchStage { } } -// Returns true if the packet should be marked as discard. #[must_use] -fn should_discard_packet( - packet: &Packet, - root: Slot, - max_slot: Slot, // Max slot to ingest shreds for. - shred_version: u16, - packet_hasher: &PacketHasher, - shreds_received: &mut ShredsReceived, - stats: &mut ShredFetchStats, -) -> bool { - if should_discard_shred(packet, root, max_slot, shred_version, stats) { - return true; - } - let hash = packet_hasher.hash_packet(packet); - match shreds_received.put(hash, ()) { - None => false, - Some(()) => { - stats.duplicate_shred += 1; - true - } - } +fn should_drop_merkle_shreds(shred_slot: Slot, root_bank: &Bank) -> bool { + check_feature_activation( + &feature_set::keep_merkle_shreds::id(), + shred_slot, + root_bank, + ) && !check_feature_activation( + &feature_set::drop_merkle_shreds::id(), + shred_slot, + root_bank, + ) } #[cfg(test)] @@ -251,14 +251,14 @@ mod tests { super::*, solana_ledger::{ blockstore::MAX_DATA_SHREDS_PER_SLOT, - shred::{Shred, ShredFlags}, + shred::{ReedSolomonCache, Shred, ShredFlags}, }, + solana_sdk::packet::Packet, }; #[test] fn test_data_code_same_index() { solana_logger::setup(); - let mut shreds_received = LruCache::new(DEFAULT_LRU_SIZE); let mut packet = Packet::default(); let mut stats = ShredFetchStats::default(); @@ -276,34 +276,30 @@ mod tests { ); shred.copy_to_packet(&mut packet); - let hasher = PacketHasher::default(); - let last_root = 0; let last_slot = 100; let slots_per_epoch = 10; let max_slot = last_slot + 2 * slots_per_epoch; - assert!(!should_discard_packet( + assert!(!should_discard_shred( &packet, last_root, max_slot, shred_version, - &hasher, - &mut shreds_received, + |_| false, // should_drop_merkle_shreds &mut stats, )); let coding = solana_ledger::shred::Shredder::generate_coding_shreds( &[shred], - false, // is_last_in_slot - 3, // next_code_index + 3, // next_code_index + &ReedSolomonCache::default(), ); coding[0].copy_to_packet(&mut packet); - assert!(!should_discard_packet( + assert!(!should_discard_shred( &packet, last_root, max_slot, shred_version, - &hasher, - &mut shreds_received, + |_| false, // should_drop_merkle_shreds &mut stats, )); } @@ -311,7 +307,6 @@ mod tests { #[test] fn test_shred_filter() { solana_logger::setup(); - let mut shreds_received = LruCache::new(DEFAULT_LRU_SIZE); let mut packet = Packet::default(); let mut stats = ShredFetchStats::default(); let last_root = 0; @@ -320,16 +315,13 @@ mod tests { let shred_version = 59445; let max_slot = last_slot + 2 * slots_per_epoch; - let hasher = PacketHasher::default(); - // packet size is 0, so cannot get index - assert!(should_discard_packet( + assert!(should_discard_shred( &packet, last_root, max_slot, shred_version, - &hasher, - &mut shreds_received, + |_| false, // should_drop_merkle_shreds &mut stats, )); assert_eq!(stats.index_overrun, 1); @@ -346,50 +338,35 @@ mod tests { shred.copy_to_packet(&mut packet); // rejected slot is 2, root is 3 - assert!(should_discard_packet( + assert!(should_discard_shred( &packet, 3, max_slot, shred_version, - &hasher, - &mut shreds_received, + |_| false, // should_drop_merkle_shreds &mut stats, )); assert_eq!(stats.slot_out_of_range, 1); - assert!(should_discard_packet( + assert!(should_discard_shred( &packet, last_root, max_slot, - 345, // shred_version - &hasher, - &mut shreds_received, + 345, // shred_version + |_| false, // should_drop_merkle_shreds &mut stats, )); assert_eq!(stats.shred_version_mismatch, 1); // Accepted for 1,3 - assert!(!should_discard_packet( - &packet, - last_root, - max_slot, - shred_version, - &hasher, - &mut shreds_received, - &mut stats, - )); - - // shreds_received should filter duplicate - assert!(should_discard_packet( + assert!(!should_discard_shred( &packet, last_root, max_slot, shred_version, - &hasher, - &mut shreds_received, + |_| false, // should_drop_merkle_shreds &mut stats, )); - assert_eq!(stats.duplicate_shred, 1); let shred = Shred::new_from_data( 1_000_000, @@ -404,26 +381,24 @@ mod tests { shred.copy_to_packet(&mut packet); // Slot 1 million is too high - assert!(should_discard_packet( + assert!(should_discard_shred( &packet, last_root, max_slot, shred_version, - &hasher, - &mut shreds_received, + |_| false, // should_drop_merkle_shreds &mut stats, )); let index = MAX_DATA_SHREDS_PER_SLOT as u32; let shred = Shred::new_from_data(5, index, 0, &[], ShredFlags::LAST_SHRED_IN_SLOT, 0, 0, 0); shred.copy_to_packet(&mut packet); - assert!(should_discard_packet( + assert!(should_discard_shred( &packet, last_root, max_slot, shred_version, - &hasher, - &mut shreds_received, + |_| false, // should_drop_merkle_shreds &mut stats, )); } diff --git a/core/src/sigverify_shreds.rs b/core/src/sigverify_shreds.rs index f9a50ab8b2a954..b2571987409fe3 100644 --- a/core/src/sigverify_shreds.rs +++ b/core/src/sigverify_shreds.rs @@ -1,22 +1,26 @@ use { crossbeam_channel::{Receiver, RecvTimeoutError, SendError, Sender}, + rayon::{prelude::*, ThreadPool, ThreadPoolBuilder}, + solana_gossip::cluster_info::ClusterInfo, solana_ledger::{ leader_schedule_cache::LeaderScheduleCache, shred, sigverify_shreds::verify_shreds_gpu, }, - solana_perf::{self, packet::PacketBatch, recycler_cache::RecyclerCache}, + solana_perf::{self, packet::PacketBatch, recycler_cache::RecyclerCache, sigverify::Deduper}, + solana_rayon_threadlimit::get_thread_count, solana_runtime::{bank::Bank, bank_forks::BankForks}, - solana_sdk::{clock::Slot, pubkey::Pubkey}, + solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signer}, std::{ collections::HashMap, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, RwLock, - }, + sync::{Arc, RwLock}, thread::{Builder, JoinHandle}, time::{Duration, Instant}, }, }; +const DEDUPER_FALSE_POSITIVE_RATE: f64 = 0.001; +const DEDUPER_NUM_BITS: u64 = 637_534_199; // 76MB +const DEDUPER_RESET_CYCLE: Duration = Duration::from_secs(5 * 60); + #[allow(clippy::enum_variant_names)] enum Error { RecvDisconnected, @@ -25,29 +29,40 @@ enum Error { } pub(crate) fn spawn_shred_sigverify( - // TODO: Hot swap will change pubkey. - self_pubkey: Pubkey, + cluster_info: Arc, bank_forks: Arc>, leader_schedule_cache: Arc, shred_fetch_receiver: Receiver, retransmit_sender: Sender>>, verified_sender: Sender>, - turbine_disabled: Arc, ) -> JoinHandle<()> { let recycler_cache = RecyclerCache::warmed(); let mut stats = ShredSigVerifyStats::new(Instant::now()); - Builder::new() - .name("shred-verifier".to_string()) - .spawn(move || loop { + let thread_pool = ThreadPoolBuilder::new() + .num_threads(get_thread_count()) + .thread_name(|i| format!("solSvrfyShred{i:02}")) + .build() + .unwrap(); + let run_shred_sigverify = move || { + let mut rng = rand::thread_rng(); + let mut deduper = Deduper::<2, [u8]>::new(&mut rng, DEDUPER_NUM_BITS); + loop { + if deduper.maybe_reset(&mut rng, DEDUPER_FALSE_POSITIVE_RATE, DEDUPER_RESET_CYCLE) { + stats.num_deduper_saturations += 1; + } + // We can't store the pubkey outside the loop + // because the identity might be hot swapped. + let self_pubkey = cluster_info.keypair().pubkey(); match run_shred_sigverify( + &thread_pool, &self_pubkey, &bank_forks, &leader_schedule_cache, &recycler_cache, + &deduper, &shred_fetch_receiver, &retransmit_sender, &verified_sender, - &turbine_disabled, &mut stats, ) { Ok(()) => (), @@ -56,19 +71,25 @@ pub(crate) fn spawn_shred_sigverify( Err(Error::SendError) => break, } stats.maybe_submit(); - }) + } + }; + Builder::new() + .name("solShredVerifr".to_string()) + .spawn(run_shred_sigverify) .unwrap() } -fn run_shred_sigverify( +#[allow(clippy::too_many_arguments)] +fn run_shred_sigverify( + thread_pool: &ThreadPool, self_pubkey: &Pubkey, bank_forks: &RwLock, leader_schedule_cache: &LeaderScheduleCache, recycler_cache: &RecyclerCache, + deduper: &Deduper, shred_fetch_receiver: &Receiver, retransmit_sender: &Sender>>, verified_sender: &Sender>, - turbine_disabled: &AtomicBool, stats: &mut ShredSigVerifyStats, ) -> Result<(), Error> { const RECV_TIMEOUT: Duration = Duration::from_secs(1); @@ -80,7 +101,22 @@ fn run_shred_sigverify( stats.num_iters += 1; stats.num_packets += packets.iter().map(PacketBatch::len).sum::(); stats.num_discards_pre += count_discards(&packets); + stats.num_duplicates += thread_pool.install(|| { + packets + .par_iter_mut() + .flatten() + .filter(|packet| { + !packet.meta.discard() + && packet + .data(..) + .map(|data| deduper.dedup(data)) + .unwrap_or(true) + }) + .map(|packet| packet.meta.set_discard(true)) + .count() + }); verify_packets( + thread_pool, self_pubkey, bank_forks, leader_schedule_cache, @@ -97,15 +133,14 @@ fn run_shred_sigverify( .map(<[u8]>::to_vec) .collect(); stats.num_retransmit_shreds += shreds.len(); - if !turbine_disabled.load(Ordering::Relaxed) { - retransmit_sender.send(shreds)?; - verified_sender.send(packets)?; - } + retransmit_sender.send(shreds)?; + verified_sender.send(packets)?; stats.elapsed_micros += now.elapsed().as_micros() as u64; Ok(()) } fn verify_packets( + thread_pool: &ThreadPool, self_pubkey: &Pubkey, bank_forks: &RwLock, leader_schedule_cache: &LeaderScheduleCache, @@ -119,7 +154,7 @@ fn verify_packets( .filter_map(|(slot, pubkey)| Some((slot, pubkey?.to_bytes()))) .chain(std::iter::once((Slot::MAX, [0u8; 32]))) .collect(); - let out = verify_shreds_gpu(packets, &leader_slots, recycler_cache); + let out = verify_shreds_gpu(thread_pool, packets, &leader_slots, recycler_cache); solana_perf::sigverify::mark_disabled(packets, &out); } @@ -188,8 +223,10 @@ struct ShredSigVerifyStats { since: Instant, num_iters: usize, num_packets: usize, - num_discards_pre: usize, + num_deduper_saturations: usize, num_discards_post: usize, + num_discards_pre: usize, + num_duplicates: usize, num_retransmit_shreds: usize, elapsed_micros: u64, } @@ -203,7 +240,9 @@ impl ShredSigVerifyStats { num_iters: 0usize, num_packets: 0usize, num_discards_pre: 0usize, + num_deduper_saturations: 0usize, num_discards_post: 0usize, + num_duplicates: 0usize, num_retransmit_shreds: 0usize, elapsed_micros: 0u64, } @@ -218,7 +257,9 @@ impl ShredSigVerifyStats { ("num_iters", self.num_iters, i64), ("num_packets", self.num_packets, i64), ("num_discards_pre", self.num_discards_pre, i64), + ("num_deduper_saturations", self.num_deduper_saturations, i64), ("num_discards_post", self.num_discards_post, i64), + ("num_duplicates", self.num_duplicates, i64), ("num_retransmit_shreds", self.num_retransmit_shreds, i64), ("elapsed_micros", self.elapsed_micros, i64), ); @@ -282,7 +323,9 @@ mod tests { batches[0][1].buffer_mut()[..shred.payload().len()].copy_from_slice(shred.payload()); batches[0][1].meta.size = shred.payload().len(); + let thread_pool = ThreadPoolBuilder::new().num_threads(3).build().unwrap(); verify_packets( + &thread_pool, &Pubkey::new_unique(), // self_pubkey &bank_forks, &leader_schedule_cache, diff --git a/core/src/sigverify_stage.rs b/core/src/sigverify_stage.rs index 75c863e9f16c9a..a5ac50083cb51a 100644 --- a/core/src/sigverify_stage.rs +++ b/core/src/sigverify_stage.rs @@ -14,8 +14,8 @@ use { solana_perf::{ packet::{Packet, PacketBatch}, sigverify::{ - count_discarded_packets, count_packets_in_batches, count_valid_packets, shrink_batches, - Deduper, + count_discarded_packets, count_packets_in_batches, count_valid_packets, + dedup_packets_and_count_discards, shrink_batches, Deduper, }, }, solana_sdk::timing, @@ -80,6 +80,7 @@ struct SigVerifierStats { dedup_packets_pp_us_hist: histogram::Histogram, // per-packet time to call verify_batch batches_hist: histogram::Histogram, // number of packet batches per verify call packets_hist: histogram::Histogram, // number of packets per verify call + num_deduper_saturations: usize, total_batches: usize, total_packets: usize, total_dedup: usize, @@ -196,6 +197,7 @@ impl SigVerifierStats { ("packets_min", self.packets_hist.minimum().unwrap_or(0), i64), ("packets_max", self.packets_hist.maximum().unwrap_or(0), i64), ("packets_mean", self.packets_hist.mean().unwrap_or(0), i64), + ("num_deduper_saturations", self.num_deduper_saturations, i64), ("total_batches", self.total_batches, i64), ("total_packets", self.total_packets, i64), ("total_dedup", self.total_dedup, i64), @@ -289,8 +291,8 @@ impl SigVerifyStage { (shrink_time.as_us(), shrink_total) } - fn verifier( - deduper: &Deduper, + fn verifier( + deduper: &Deduper, recvr: &find_packet_sender_stake_stage::FindPacketSenderStakeReceiver, verifier: &mut T, stats: &mut SigVerifierStats, @@ -314,7 +316,8 @@ impl SigVerifyStage { discard_random_time.stop(); let mut dedup_time = Measure::start("sigverify_dedup_time"); - let discard_or_dedup_fail = deduper.dedup_packets_and_count_discards( + let discard_or_dedup_fail = dedup_packets_and_count_discards( + deduper, &mut batches, #[inline(always)] |received_packet, removed_before_sigverify_stage, is_dup| { @@ -410,13 +413,17 @@ impl SigVerifyStage { let mut stats = SigVerifierStats::default(); let mut last_print = Instant::now(); const MAX_DEDUPER_AGE: Duration = Duration::from_secs(2); - const MAX_DEDUPER_ITEMS: u32 = 1_000_000; + const DEDUPER_FALSE_POSITIVE_RATE: f64 = 0.001; + const DEDUPER_NUM_BITS: u64 = 63_999_979; Builder::new() - .name("solana-verifier".to_string()) + .name("solSigVerifier".to_string()) .spawn(move || { - let mut deduper = Deduper::new(MAX_DEDUPER_ITEMS, MAX_DEDUPER_AGE); + let mut rng = rand::thread_rng(); + let mut deduper = Deduper::<2, [u8]>::new(&mut rng, DEDUPER_NUM_BITS); loop { - deduper.reset(); + if deduper.maybe_reset(&mut rng, DEDUPER_FALSE_POSITIVE_RATE, MAX_DEDUPER_AGE) { + stats.num_deduper_saturations += 1; + } if let Err(e) = Self::verifier(&deduper, &packet_receiver, &mut verifier, &mut stats) { @@ -472,13 +479,9 @@ mod tests { fn count_non_discard(packet_batches: &[PacketBatch]) -> usize { packet_batches .iter() - .map(|batch| { - batch - .iter() - .map(|p| if p.meta.discard() { 0 } else { 1 }) - .sum::() - }) - .sum::() + .flatten() + .filter(|p| !p.meta.discard()) + .count() } #[test] diff --git a/core/src/snapshot_packager_service.rs b/core/src/snapshot_packager_service.rs index 7077362e4b8725..02b53abb724498 100644 --- a/core/src/snapshot_packager_service.rs +++ b/core/src/snapshot_packager_service.rs @@ -49,7 +49,7 @@ impl SnapshotPackagerService { ); let t_snapshot_packager = Builder::new() - .name("snapshot-packager".to_string()) + .name("solSnapshotPkgr".to_string()) .spawn(move || { renice_this_thread(snapshot_config.packager_thread_niceness_adj).unwrap(); let mut snapshot_gossip_manager = if enable_gossip_push { @@ -298,7 +298,7 @@ mod tests { .collect(); // Create directory of hard links for snapshots - let link_snapshots_dir = tempfile::tempdir_in(&temp_dir).unwrap(); + let link_snapshots_dir = tempfile::tempdir_in(temp_dir).unwrap(); for snapshots_path in snapshots_paths { let snapshot_file_name = snapshots_path.file_name().unwrap(); let link_snapshots_dir = link_snapshots_dir.path().join(snapshot_file_name); diff --git a/core/src/staked_nodes_updater_service.rs b/core/src/staked_nodes_updater_service.rs index a5bd3518590fa6..ebe9fd37a1bcf8 100644 --- a/core/src/staked_nodes_updater_service.rs +++ b/core/src/staked_nodes_updater_service.rs @@ -29,7 +29,7 @@ impl StakedNodesUpdaterService { shared_staked_nodes: Arc>, ) -> Self { let thread_hdl = Builder::new() - .name("sol-sn-updater".to_string()) + .name("solStakedNodeUd".to_string()) .spawn(move || { let mut last_stakes = Instant::now(); while !exit.load(Ordering::Relaxed) { diff --git a/core/src/stats_reporter_service.rs b/core/src/stats_reporter_service.rs index b6f23e4162e08e..90e72aaadb3e46 100644 --- a/core/src/stats_reporter_service.rs +++ b/core/src/stats_reporter_service.rs @@ -22,7 +22,7 @@ impl StatsReporterService { ) -> Self { let exit = exit.clone(); let thread_hdl = Builder::new() - .name("solana-stats-reporter".to_owned()) + .name("solStatsReport".to_owned()) .spawn(move || loop { if exit.load(Ordering::Relaxed) { return; diff --git a/core/src/system_monitor_service.rs b/core/src/system_monitor_service.rs index e1e75e521aed79..f0ea1afb33522e 100644 --- a/core/src/system_monitor_service.rs +++ b/core/src/system_monitor_service.rs @@ -1,3 +1,9 @@ +#[cfg(target_arch = "x86")] +use core::arch::x86::{CpuidResult, __cpuid, __cpuid_count, __get_cpuid_max}; +#[cfg(target_arch = "x86_64")] +use core::arch::x86_64::{CpuidResult, __cpuid, __cpuid_count, __get_cpuid_max}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(target_os = "linux")] use std::{fs::File, io::BufReader}; use { @@ -20,9 +26,10 @@ const MS_PER_M: u64 = MS_PER_S * 60; const MS_PER_H: u64 = MS_PER_M * 60; const SAMPLE_INTERVAL_UDP_MS: u64 = 2 * MS_PER_S; const SAMPLE_INTERVAL_OS_NETWORK_LIMITS_MS: u64 = MS_PER_H; -const SAMPLE_INTERVAL_MEM_MS: u64 = MS_PER_S; -const SAMPLE_INTERVAL_CPU_MS: u64 = MS_PER_S; -const SAMPLE_INTERVAL_DISK_MS: u64 = MS_PER_S; +const SAMPLE_INTERVAL_MEM_MS: u64 = 5 * MS_PER_S; +const SAMPLE_INTERVAL_CPU_MS: u64 = 10 * MS_PER_S; +const SAMPLE_INTERVAL_CPU_ID_MS: u64 = MS_PER_H; +const SAMPLE_INTERVAL_DISK_MS: u64 = 5 * MS_PER_S; const SLEEP_INTERVAL: Duration = Duration::from_millis(500); #[cfg(target_os = "linux")] @@ -30,7 +37,7 @@ const PROC_NET_SNMP_PATH: &str = "/proc/net/snmp"; #[cfg(target_os = "linux")] const PROC_NET_DEV_PATH: &str = "/proc/net/dev"; #[cfg(target_os = "linux")] -const PROC_DISKSTATS_PATH: &str = "/proc/diskstats"; +const SYS_BLOCK_PATH: &str = "/sys/block"; pub struct SystemMonitorService { thread_hdl: JoinHandle<()>, @@ -99,6 +106,32 @@ struct CpuInfo { num_threads: u64, } +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[derive(IntoPrimitive)] +#[repr(i64)] +enum CpuManufacturer { + Other, + Intel, + Amd, +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#[derive(IntoPrimitive, TryFromPrimitive, PartialEq, PartialOrd)] +#[repr(u32)] +// The value passed into cpuid via eax, to control what the result means +enum CpuidParamValue { + Manufacturer = 0, + Processor = 1, + Cache = 2, + SerialNumber = 3, + Topology = 4, + Unsupported = 5, + ThermalAndPower = 6, + Extended = 7, +} +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +const CPUID_PARAM_MAX_SUPPORTED_VALUE: u32 = 7; + #[derive(Default)] #[cfg_attr(not(target_os = "linux"), allow(dead_code))] // These stats are aggregated across all storage devices excluding internal loopbacks. @@ -140,6 +173,29 @@ impl UdpStats { } } +impl DiskStats { + #[cfg_attr(not(target_os = "linux"), allow(dead_code))] + fn accumulate(&mut self, other: &DiskStats) { + self.reads_completed += other.reads_completed; + self.reads_merged += other.reads_merged; + self.sectors_read += other.sectors_read; + self.time_reading_ms += other.time_reading_ms; + self.writes_completed += other.writes_completed; + self.writes_merged += other.writes_merged; + self.sectors_written += other.sectors_written; + self.time_writing_ms += other.time_writing_ms; + self.io_in_progress += other.io_in_progress; + self.time_io_ms += other.time_io_ms; + self.time_io_weighted_ms += other.time_io_weighted_ms; + self.discards_completed += other.discards_completed; + self.discards_merged += other.discards_merged; + self.sectors_discarded += other.sectors_discarded; + self.time_discarding += other.time_discarding; + self.flushes_completed += other.flushes_completed; + self.time_flushing += other.time_flushing; + } +} + fn platform_id() -> String { format!( "{}/{}/{}", @@ -254,49 +310,79 @@ pub fn verify_net_stats_access() -> Result<(), String> { #[cfg(target_os = "linux")] fn read_disk_stats() -> Result { - let file_path_diskstats = PROC_DISKSTATS_PATH; - let file_diskstats = File::open(file_path_diskstats).map_err(|e| e.to_string())?; - let mut reader_diskstats = BufReader::new(file_diskstats); - parse_disk_stats(&mut reader_diskstats) + let mut stats = DiskStats::default(); + let mut num_disks = 0; + let blk_device_dir_iter = std::fs::read_dir(SYS_BLOCK_PATH).map_err(|e| e.to_string())?; + blk_device_dir_iter + .filter_map(|blk_device_dir| { + match blk_device_dir { + Ok(blk_device_dir) => { + let blk_device_dir_name = &blk_device_dir.file_name(); + let blk_device_dir_name = blk_device_dir_name.to_string_lossy(); + if blk_device_dir_name.starts_with("loop") + || blk_device_dir_name.starts_with("dm") + || blk_device_dir_name.starts_with("md") + { + // Filter out loopback devices, dmcrypt volumes, and mdraid volumes + return None; + } + let mut path = blk_device_dir.path(); + path.push("stat"); + match File::open(path) { + Ok(file_diskstats) => Some(file_diskstats), + Err(_) => None, + } + } + Err(_) => None, + } + }) + .for_each(|file_diskstats| { + let mut reader_diskstats = BufReader::new(file_diskstats); + stats.accumulate(&parse_disk_stats(&mut reader_diskstats).unwrap_or_default()); + num_disks += 1; + }); + stats.num_disks = num_disks; + Ok(stats) } #[cfg_attr(not(target_os = "linux"), allow(dead_code))] fn parse_disk_stats(reader_diskstats: &mut impl BufRead) -> Result { let mut stats = DiskStats::default(); - let mut num_disks = 0; - for line in reader_diskstats.lines() { - let line = line.map_err(|e| e.to_string())?; - let values: Vec<_> = line.split_ascii_whitespace().collect(); + let mut line = String::new(); + reader_diskstats + .read_line(&mut line) + .map_err(|e| e.to_string())?; + let values: Vec<_> = line.split_ascii_whitespace().collect(); + let num_elements = values.len(); - if values.len() != 20 { - return Err("parse error, expected exactly 20 disk stat elements".to_string()); - } - if values[2].starts_with("loop") || values[1].ne("0") { - // Filter out the loopback io devices. - // Only look at raw device (filter partitions) - continue; - } + if num_elements != 11 && num_elements != 15 && num_elements != 17 { + return Err("parse error, unknown number of disk stat elements".to_string()); + } - num_disks += 1; - stats.reads_completed += values[3].parse::().map_err(|e| e.to_string())?; - stats.reads_merged += values[4].parse::().map_err(|e| e.to_string())?; - stats.sectors_read += values[5].parse::().map_err(|e| e.to_string())?; - stats.time_reading_ms += values[6].parse::().map_err(|e| e.to_string())?; - stats.writes_completed += values[7].parse::().map_err(|e| e.to_string())?; - stats.writes_merged += values[8].parse::().map_err(|e| e.to_string())?; - stats.sectors_written += values[9].parse::().map_err(|e| e.to_string())?; - stats.time_writing_ms += values[10].parse::().map_err(|e| e.to_string())?; - stats.io_in_progress += values[11].parse::().map_err(|e| e.to_string())?; - stats.time_io_ms += values[12].parse::().map_err(|e| e.to_string())?; - stats.time_io_weighted_ms += values[13].parse::().map_err(|e| e.to_string())?; - stats.discards_completed += values[14].parse::().map_err(|e| e.to_string())?; - stats.discards_merged += values[15].parse::().map_err(|e| e.to_string())?; - stats.sectors_discarded += values[16].parse::().map_err(|e| e.to_string())?; - stats.time_discarding += values[17].parse::().map_err(|e| e.to_string())?; - stats.flushes_completed += values[18].parse::().map_err(|e| e.to_string())?; - stats.time_flushing += values[19].parse::().map_err(|e| e.to_string())?; + stats.reads_completed = values[0].parse::().map_err(|e| e.to_string())?; + stats.reads_merged = values[1].parse::().map_err(|e| e.to_string())?; + stats.sectors_read = values[2].parse::().map_err(|e| e.to_string())?; + stats.time_reading_ms = values[3].parse::().map_err(|e| e.to_string())?; + stats.writes_completed = values[4].parse::().map_err(|e| e.to_string())?; + stats.writes_merged = values[5].parse::().map_err(|e| e.to_string())?; + stats.sectors_written = values[6].parse::().map_err(|e| e.to_string())?; + stats.time_writing_ms = values[7].parse::().map_err(|e| e.to_string())?; + stats.io_in_progress = values[8].parse::().map_err(|e| e.to_string())?; + stats.time_io_ms = values[9].parse::().map_err(|e| e.to_string())?; + stats.time_io_weighted_ms = values[10].parse::().map_err(|e| e.to_string())?; + if num_elements > 11 { + // Kernel 4.18+ appends four more fields for discard + stats.discards_completed = values[11].parse::().map_err(|e| e.to_string())?; + stats.discards_merged = values[12].parse::().map_err(|e| e.to_string())?; + stats.sectors_discarded = values[13].parse::().map_err(|e| e.to_string())?; + stats.time_discarding = values[14].parse::().map_err(|e| e.to_string())?; } - stats.num_disks = num_disks; + if num_elements > 15 { + // Kernel 5.5+ appends two more fields for flush requests + stats.flushes_completed = values[15].parse::().map_err(|e| e.to_string())?; + stats.time_flushing = values[16].parse::().map_err(|e| e.to_string())?; + } + Ok(stats) } @@ -310,7 +396,7 @@ impl SystemMonitorService { ) -> Self { info!("Starting SystemMonitorService"); let thread_hdl = Builder::new() - .name("system-monitor".to_string()) + .name("solSystemMonitr".to_string()) .spawn(move || { Self::run( exit, @@ -637,6 +723,99 @@ impl SystemMonitorService { }) } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn report_cpuid_values() { + const CPUID_MANUFACTURER_EBX_INTEL: u32 = 0x756e6547; + const CPUID_MANUFACTURER_EDX_INTEL: u32 = 0x49656e69; + const CPUID_MANUFACTURER_ECX_INTEL: u32 = 0x6c65746e; + const CPUID_MANUFACTURER_EBX_AMD: u32 = 0x68747541; + const CPUID_MANUFACTURER_EDX_AMD: u32 = 0x69746e65; + const CPUID_MANUFACTURER_ECX_AMD: u32 = 0x444d4163; + + unsafe { + let cpuid_mfr = __cpuid(0); + let cpuid_empty = CpuidResult { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + }; + + let max_leaf = match CpuidParamValue::try_from(std::cmp::min( + cpuid_mfr.eax, + CPUID_PARAM_MAX_SUPPORTED_VALUE, + )) { + Ok(val) => val, + Err(_err) => CpuidParamValue::Manufacturer, + }; + + let mfr_id = if cpuid_mfr.ebx == CPUID_MANUFACTURER_EBX_INTEL + && cpuid_mfr.edx == CPUID_MANUFACTURER_EDX_INTEL + && cpuid_mfr.ecx == CPUID_MANUFACTURER_ECX_INTEL + { + CpuManufacturer::Intel // GenuineIntel + } else if cpuid_mfr.ebx == CPUID_MANUFACTURER_EBX_AMD + && cpuid_mfr.edx == CPUID_MANUFACTURER_EDX_AMD + && cpuid_mfr.ecx == CPUID_MANUFACTURER_ECX_AMD + { + CpuManufacturer::Amd // AuthenticAMD + } else { + CpuManufacturer::Other // anything else + }; + + let cpuid_processor = if CpuidParamValue::Processor <= max_leaf { + __cpuid(CpuidParamValue::Processor.into()) + } else { + cpuid_empty + }; + let cpuid_cache = if CpuidParamValue::Cache <= max_leaf { + __cpuid(CpuidParamValue::Cache.into()) + } else { + cpuid_empty + }; + let cpuid_topology = if CpuidParamValue::Topology <= max_leaf { + __cpuid(CpuidParamValue::Topology.into()) + } else { + cpuid_empty + }; + let cpuid_extended_0 = if CpuidParamValue::Extended <= max_leaf { + __cpuid_count(CpuidParamValue::Extended.into(), 0) + } else { + cpuid_empty + }; + let cpuid_extended_1 = if CpuidParamValue::Extended <= max_leaf { + if 1 <= __get_cpuid_max(CpuidParamValue::Extended.into()).1 { + __cpuid_count(CpuidParamValue::Extended.into(), 1) + } else { + cpuid_empty + } + } else { + cpuid_empty + }; + + datapoint_info!( + "cpuid-values", + ("manufacturer_id", i64::from(mfr_id), i64), + ("cpuid_processor_eax", i64::from(cpuid_processor.eax), i64), + ("cpuid_processor_ebx", i64::from(cpuid_processor.ebx), i64), + ("cpuid_processor_ecx", i64::from(cpuid_processor.ecx), i64), + ("cpuid_processor_edx", i64::from(cpuid_processor.edx), i64), + ("cpuid_cache_eax", i64::from(cpuid_cache.eax), i64), + ("cpuid_cache_ebx", i64::from(cpuid_cache.ebx), i64), + ("cpuid_cache_ecx", i64::from(cpuid_cache.ecx), i64), + ("cpuid_cache_edx", i64::from(cpuid_cache.edx), i64), + ("cpuid_topology_eax", i64::from(cpuid_topology.eax), i64), + ("cpuid_topology_ebx", i64::from(cpuid_topology.ebx), i64), + ("cpuid_topology_ecx", i64::from(cpuid_topology.ecx), i64), + ("cpuid_topology_edx", i64::from(cpuid_topology.edx), i64), + ("cpuid_extended_0_ebx", i64::from(cpuid_extended_0.ebx), i64), + ("cpuid_extended_0_ecx", i64::from(cpuid_extended_0.ecx), i64), + ("cpuid_extended_0_edx", i64::from(cpuid_extended_0.edx), i64), + ("cpuid_extended_1_eax", i64::from(cpuid_extended_1.eax), i64), + ); + }; + } + fn report_cpu_stats() { if let Ok(info) = Self::cpu_info() { datapoint_info!( @@ -799,6 +978,7 @@ impl SystemMonitorService { let udp_timer = AtomicInterval::default(); let mem_timer = AtomicInterval::default(); let cpu_timer = AtomicInterval::default(); + let cpuid_timer = AtomicInterval::default(); let disk_timer = AtomicInterval::default(); loop { @@ -816,8 +996,14 @@ impl SystemMonitorService { if report_os_memory_stats && mem_timer.should_update(SAMPLE_INTERVAL_MEM_MS) { Self::report_mem_stats(); } - if report_os_cpu_stats && cpu_timer.should_update(SAMPLE_INTERVAL_CPU_MS) { - Self::report_cpu_stats(); + if report_os_cpu_stats { + if cpu_timer.should_update(SAMPLE_INTERVAL_CPU_MS) { + Self::report_cpu_stats(); + } + if cpuid_timer.should_update(SAMPLE_INTERVAL_CPU_ID_MS) { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + Self::report_cpuid_values(); + } } if report_os_disk_stats && disk_timer.should_update(SAMPLE_INTERVAL_DISK_MS) { Self::process_disk_stats(&mut disk_stats); @@ -892,35 +1078,43 @@ data" as &[u8]; #[test] fn test_parse_disk_stats() { - const MOCK_DISK: &[u8] = -b" 7 0 loop0 108 0 2906 27 0 0 0 0 0 40 27 0 0 0 0 0 0 -7 1 loop1 48 0 716 23 0 0 0 0 0 28 23 0 0 0 0 0 0 -7 2 loop2 108 0 2916 21 0 0 0 0 0 36 21 0 0 0 0 0 0 -7 3 loop3 257 0 4394 131 0 0 0 0 0 296 131 0 0 0 0 0 0 -7 4 loop4 111 0 2896 62 0 0 0 0 0 68 62 0 0 0 0 0 0 -7 5 loop5 110 0 2914 138 0 0 0 0 0 112 138 0 0 0 0 0 0 -7 6 loop6 68 0 2200 47 0 0 0 0 0 44 47 0 0 0 0 0 0 -7 7 loop7 1397 0 101686 515 0 0 0 0 0 4628 515 0 0 0 0 0 0 -8 0 sda 40883273 294820 1408426268 30522643 352908152 204249001 37827695922 2073754124 0 86054536 2105005805 496399 4 1886486166 167545 18008621 561492 -8 1 sda1 40882879 291543 1408408989 30522451 352908150 204249001 37827695920 2073754122 0 86054508 2104444115 496393 0 1886085576 167541 0 0 -8 14 sda14 73 0 832 22 0 0 0 0 0 48 22 0 0 0 0 0 0 -8 15 sda15 146 3277 9855 62 2 0 2 1 0 76 68 6 4 400590 3 0 0 -7 9 loop9 55 0 2106 41 0 0 0 0 0 28 41 0 0 0 0 0 0 -7 8 loop8 41 0 688 53 0 0 0 0 0 44 53 0 0 0 0 0 0 -7 10 loop10 60 0 748 1 0 0 0 0 0 20 1 0 0 0 0 0 0 -9 0 sdb 1 1 1 1 352908152 204249001 37827695922 2073754124 0 86054536 2105005805 496399 4 1886486166 167545 18008621 561492" as &[u8]; - const UNEXPECTED_DATA: &[u8] = b"un + const MOCK_DISK_11: &[u8] = +b" 2095701 479815 122620302 1904439 43496218 26953623 3935324729 283313376 0 6101780 285220738" as &[u8]; + // Matches kernel 4.18+ format + const MOCK_DISK_15: &[u8] = +b" 2095701 479815 122620302 1904439 43496218 26953623 3935324729 283313376 0 6101780 285220738 0 0 0 0" as &[u8]; + // Matches kernel 5.5+ format + const MOCK_DISK_17: &[u8] = +b" 2095701 479815 122620302 1904439 43496218 26953623 3935324729 283313376 0 6101780 285220738 0 0 0 0 70715 2922" as &[u8]; + const UNEXPECTED_DATA_1: &[u8] = +b" 2095701 479815 122620302 1904439 43496218 26953623 3935324729 283313376 0 6101780 285220738 0 0 0 0 70715" as &[u8]; + + const UNEXPECTED_DATA_2: &[u8] = b"un ex pec ted data" as &[u8]; - let mut mock_disk = MOCK_DISK; + let mut mock_disk = MOCK_DISK_11; let stats = parse_disk_stats(&mut mock_disk).unwrap(); - assert_eq!(stats.reads_completed, 40883274); - assert_eq!(stats.time_flushing, 1122984); + assert_eq!(stats.reads_completed, 2095701); + assert_eq!(stats.time_io_weighted_ms, 285220738); + + let mut mock_disk = MOCK_DISK_15; + let stats = parse_disk_stats(&mut mock_disk).unwrap(); + assert_eq!(stats.reads_completed, 2095701); + assert_eq!(stats.time_discarding, 0); + + let mut mock_disk = MOCK_DISK_17; + let stats = parse_disk_stats(&mut mock_disk).unwrap(); + assert_eq!(stats.reads_completed, 2095701); + assert_eq!(stats.time_flushing, 2922); + + let mut mock_disk = UNEXPECTED_DATA_1; + let stats = parse_disk_stats(&mut mock_disk); + assert!(stats.is_err()); - let mut mock_disk = UNEXPECTED_DATA; + let mut mock_disk = UNEXPECTED_DATA_2; let stats = parse_disk_stats(&mut mock_disk); assert!(stats.is_err()); } diff --git a/core/src/tower1_7_14.rs b/core/src/tower1_7_14.rs index 63b70cdf80a0c6..13dca69e6f6a92 100644 --- a/core/src/tower1_7_14.rs +++ b/core/src/tower1_7_14.rs @@ -22,7 +22,7 @@ pub struct Tower1_7_14 { // blockhash of the voted block itself, depending if the vote slot was refreshed. // For instance, a vote for slot 5, may be refreshed/resubmitted for inclusion in // block 10, in which case `last_vote_tx_blockhash` equals the blockhash of 10, not 5. - pub(crate) last_vote_tx_blockhash: Hash, + pub(crate) last_vote_tx_blockhash: Option, pub(crate) last_timestamp: BlockTimestamp, #[serde(skip)] // Restored last voted slot which cannot be found in SlotHistory at replayed root diff --git a/core/src/tower_storage.rs b/core/src/tower_storage.rs index 330cf7bdc48351..c02b347d4b2980 100644 --- a/core/src/tower_storage.rs +++ b/core/src/tower_storage.rs @@ -173,7 +173,7 @@ impl TowerStorage for FileTowerStorage { trace!("load {}", filename.display()); // Ensure to create parent dir here, because restore() precedes save() always - fs::create_dir_all(&filename.parent().unwrap())?; + fs::create_dir_all(filename.parent().unwrap())?; if let Ok(file) = File::open(&filename) { // New format @@ -402,7 +402,7 @@ pub mod test { vote_state, last_vote: vote.clone(), last_timestamp: BlockTimestamp::default(), - last_vote_tx_blockhash: Hash::default(), + last_vote_tx_blockhash: None, stray_restored_slot: Some(2), last_switch_threshold_check: Option::default(), }; diff --git a/core/src/tpu.rs b/core/src/tpu.rs index cfd546869d6134..21b633c55b269d 100644 --- a/core/src/tpu.rs +++ b/core/src/tpu.rs @@ -62,8 +62,8 @@ pub struct Tpu { banking_stage: BankingStage, cluster_info_vote_listener: ClusterInfoVoteListener, broadcast_stage: BroadcastStage, - tpu_quic_t: Option>, - tpu_forwards_quic_t: Option>, + tpu_quic_t: thread::JoinHandle<()>, + tpu_forwards_quic_t: thread::JoinHandle<()>, find_packet_sender_stake_stage: FindPacketSenderStakeStage, vote_find_packet_sender_stake_stage: FindPacketSenderStakeStage, staked_nodes_updater_service: StakedNodesUpdaterService, @@ -96,8 +96,8 @@ impl Tpu { connection_cache: &Arc, keypair: &Keypair, log_messages_bytes_limit: Option, - enable_quic_servers: bool, staked_nodes: &Arc>, + tpu_enable_udp: bool, ) -> Self { let TpuSockets { transactions: transactions_sockets, @@ -123,6 +123,7 @@ impl Tpu { poh_recorder, tpu_coalesce_ms, Some(bank_forks.read().unwrap().get_vote_only_mode_signal()), + tpu_enable_udp, ); let staked_nodes_updater_service = StakedNodesUpdaterService::new( @@ -138,7 +139,7 @@ impl Tpu { packet_receiver, find_packet_sender_stake_sender, staked_nodes.clone(), - "tpu-find-packet-sender-stake", + "Tpu", ); let (vote_find_packet_sender_stake_sender, vote_find_packet_sender_stake_receiver) = @@ -148,43 +149,39 @@ impl Tpu { vote_packet_receiver, vote_find_packet_sender_stake_sender, staked_nodes.clone(), - "tpu-vote-find-packet-sender-stake", + "Vote", ); let (verified_sender, verified_receiver) = unbounded(); let stats = Arc::new(StreamStats::default()); - let tpu_quic_t = enable_quic_servers.then(|| { - spawn_server( - transactions_quic_sockets, - keypair, - cluster_info.my_contact_info().tpu.ip(), - packet_sender, - exit.clone(), - MAX_QUIC_CONNECTIONS_PER_PEER, - staked_nodes.clone(), - MAX_STAKED_CONNECTIONS, - MAX_UNSTAKED_CONNECTIONS, - stats.clone(), - ) - .unwrap() - }); + let tpu_quic_t = spawn_server( + transactions_quic_sockets, + keypair, + cluster_info.my_contact_info().tpu.ip(), + packet_sender, + exit.clone(), + MAX_QUIC_CONNECTIONS_PER_PEER, + staked_nodes.clone(), + MAX_STAKED_CONNECTIONS, + MAX_UNSTAKED_CONNECTIONS, + stats.clone(), + ) + .unwrap(); - let tpu_forwards_quic_t = enable_quic_servers.then(|| { - spawn_server( - transactions_forwards_quic_sockets, - keypair, - cluster_info.my_contact_info().tpu_forwards.ip(), - forwarded_packet_sender, - exit.clone(), - MAX_QUIC_CONNECTIONS_PER_PEER, - staked_nodes.clone(), - MAX_STAKED_CONNECTIONS.saturating_add(MAX_UNSTAKED_CONNECTIONS), - 0, // Prevent unstaked nodes from forwarding transactions - stats, - ) - .unwrap() - }); + let tpu_forwards_quic_t = spawn_server( + transactions_forwards_quic_sockets, + keypair, + cluster_info.my_contact_info().tpu_forwards.ip(), + forwarded_packet_sender, + exit.clone(), + MAX_QUIC_CONNECTIONS_PER_PEER, + staked_nodes.clone(), + MAX_STAKED_CONNECTIONS.saturating_add(MAX_UNSTAKED_CONNECTIONS), + 0, // Prevent unstaked nodes from forwarding transactions + stats, + ) + .unwrap(); let sigverify_stage = { let verifier = TransactionSigVerifier::new(verified_sender); @@ -271,13 +268,9 @@ impl Tpu { self.find_packet_sender_stake_stage.join(), self.vote_find_packet_sender_stake_stage.join(), self.staked_nodes_updater_service.join(), + self.tpu_quic_t.join(), + self.tpu_forwards_quic_t.join(), ]; - if let Some(tpu_quic_t) = self.tpu_quic_t { - tpu_quic_t.join()?; - } - if let Some(tpu_forwards_quic_t) = self.tpu_forwards_quic_t { - tpu_forwards_quic_t.join()?; - } let broadcast_result = self.broadcast_stage.join(); for result in results { result?; diff --git a/core/src/tracer_packet_stats.rs b/core/src/tracer_packet_stats.rs index f384634f7712e7..03068e4ee663d6 100644 --- a/core/src/tracer_packet_stats.rs +++ b/core/src/tracer_packet_stats.rs @@ -199,7 +199,8 @@ impl TracerPacketStats { ) ); - *self = Self::default(); + let id = self.id; + *self = Self::new(id); self.last_report = timestamp(); } } diff --git a/core/src/tvu.rs b/core/src/tvu.rs index 990b943f7478f0..c4fa37cf25d757 100644 --- a/core/src/tvu.rs +++ b/core/src/tvu.rs @@ -37,13 +37,13 @@ use { }, solana_poh::poh_recorder::PohRecorder, solana_rpc::{ - max_slots::MaxSlots, optimistically_confirmed_bank_tracker::BankNotificationSender, + max_slots::MaxSlots, optimistically_confirmed_bank_tracker::BankNotificationSenderConfig, rpc_subscriptions::RpcSubscriptions, }, solana_runtime::{ accounts_background_service::AbsRequestSender, bank_forks::BankForks, commitment::BlockCommitmentCache, cost_model::CostModel, - vote_sender_types::ReplayVoteSender, + prioritization_fee_cache::PrioritizationFeeCache, vote_sender_types::ReplayVoteSender, }, solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Keypair}, std::{ @@ -119,7 +119,7 @@ impl Tvu { verified_vote_receiver: VerifiedVoteReceiver, replay_vote_sender: ReplayVoteSender, completed_data_sets_sender: CompletedDataSetsSender, - bank_notification_sender: Option, + bank_notification_sender: Option, gossip_confirmed_slots_receiver: GossipDuplicateConfirmedSlotsReceiver, tvu_config: TvuConfig, max_slots: &Arc, @@ -129,6 +129,7 @@ impl Tvu { accounts_background_request_sender: AbsRequestSender, log_messages_bytes_limit: Option, connection_cache: &Arc, + prioritization_fee_cache: &Arc, ) -> Self { let TvuSockets { repair: repair_socket, @@ -153,19 +154,19 @@ impl Tvu { tvu_config.shred_version, bank_forks.clone(), cluster_info.clone(), + turbine_disabled, exit, ); let (verified_sender, verified_receiver) = unbounded(); let (retransmit_sender, retransmit_receiver) = unbounded(); let shred_sigverify = sigverify_shreds::spawn_shred_sigverify( - cluster_info.id(), + cluster_info.clone(), bank_forks.clone(), leader_schedule_cache.clone(), fetch_receiver, retransmit_sender.clone(), verified_sender, - turbine_disabled, ); let retransmit_stage = RetransmitStage::new( @@ -288,6 +289,7 @@ impl Tvu { drop_bank_sender, block_metadata_notifier, log_messages_bytes_limit, + prioritization_fee_cache.clone(), ); let ledger_cleanup_service = tvu_config.max_ledger_shreds.map(|max_ledger_shreds| { @@ -401,6 +403,8 @@ pub mod tests { let (_, gossip_confirmed_slots_receiver) = unbounded(); let bank_forks = Arc::new(RwLock::new(bank_forks)); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); + let _ignored_prioritization_fee_cache = Arc::new(PrioritizationFeeCache::new(0u64)); let tvu = Tvu::new( &vote_keypair.pubkey(), Arc::new(RwLock::new(vec![Arc::new(vote_keypair)])), @@ -420,6 +424,7 @@ pub mod tests { &Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), block_commitment_cache.clone(), OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -450,6 +455,7 @@ pub mod tests { AbsRequestSender::default(), None, &Arc::new(ConnectionCache::default()), + &_ignored_prioritization_fee_cache, ); exit.store(true, Ordering::Relaxed); tvu.join().unwrap(); diff --git a/core/src/unprocessed_packet_batches.rs b/core/src/unprocessed_packet_batches.rs index 3305e264b77d4f..c732a9e2052380 100644 --- a/core/src/unprocessed_packet_batches.rs +++ b/core/src/unprocessed_packet_batches.rs @@ -1,9 +1,9 @@ use { - crate::transaction_priority_details::{ - GetTransactionPriorityDetails, TransactionPriorityDetails, - }, min_max_heap::MinMaxHeap, solana_perf::packet::{Packet, PacketBatch}, + solana_runtime::transaction_priority_details::{ + GetTransactionPriorityDetails, TransactionPriorityDetails, + }, solana_sdk::{ feature_set, hash::Hash, @@ -108,10 +108,15 @@ impl DeserializedPacket { let is_simple_vote = packet.meta.is_simple_vote_tx(); // drop transaction if prioritization fails. - let priority_details = priority_details + let mut priority_details = priority_details .or_else(|| sanitized_transaction.get_transaction_priority_details()) .ok_or(DeserializedPacketError::PrioritizationFailure)?; + // set priority to zero for vote transactions + if is_simple_vote { + priority_details.priority = 0; + }; + Ok(Self { immutable_section: Rc::new(ImmutableDeserializedPacket { original_packet: packet, @@ -586,7 +591,7 @@ mod tests { let capacity = transactions.len(); let mut packet_vector = Vec::with_capacity(capacity); for tx in transactions.iter() { - packet_vector.push(Packet::from_data(None, &tx).unwrap()); + packet_vector.push(Packet::from_data(None, tx).unwrap()); } for index in vote_indexes.iter() { packet_vector[*index].meta.flags |= PacketFlags::SIMPLE_VOTE_TX; diff --git a/core/src/validator.rs b/core/src/validator.rs index b2df978850c1cf..8a153955e50496 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -4,6 +4,7 @@ pub use solana_perf::report_target_features; use { crate::{ accounts_hash_verifier::AccountsHashVerifier, + admin_rpc_post_init::AdminRpcRequestMetadataPostInit, broadcast_stage::BroadcastStageType, cache_block_meta_service::{CacheBlockMetaSender, CacheBlockMetaService}, cluster_info_vote_listener::VoteTracker, @@ -33,9 +34,9 @@ use { ClusterInfo, Node, DEFAULT_CONTACT_DEBUG_INTERVAL_MILLIS, DEFAULT_CONTACT_SAVE_INTERVAL_MILLIS, }, - contact_info::ContactInfo, crds_gossip_pull::CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS, gossip_service::GossipService, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_ledger::{ bank_forks_utils, @@ -56,7 +57,8 @@ use { solana_rpc::{ max_slots::MaxSlots, optimistically_confirmed_bank_tracker::{ - OptimisticallyConfirmedBank, OptimisticallyConfirmedBankTracker, + BankNotificationSenderConfig, OptimisticallyConfirmedBank, + OptimisticallyConfirmedBankTracker, }, rpc::JsonRpcConfig, rpc_completed_slots_service::RpcCompletedSlotsService, @@ -79,6 +81,7 @@ use { commitment::BlockCommitmentCache, cost_model::CostModel, hardened_unpack::{open_genesis_config, MAX_GENESIS_ARCHIVE_UNPACKED_SIZE}, + prioritization_fee_cache::PrioritizationFeeCache, runtime_config::RuntimeConfig, snapshot_archive_info::SnapshotArchiveInfoGetter, snapshot_config::SnapshotConfig, @@ -161,6 +164,7 @@ pub struct ValidatorConfig { pub no_os_disk_stats_reporting: bool, pub poh_pinned_cpu_core: usize, pub poh_hashes_per_batch: u64, + pub process_ledger_before_services: bool, pub account_indexes: AccountSecondaryIndexes, pub accounts_db_caching_enabled: bool, pub accounts_db_config: Option, @@ -174,7 +178,6 @@ pub struct ValidatorConfig { pub wait_to_vote_slot: Option, pub ledger_column_options: LedgerColumnOptions, pub runtime_config: RuntimeConfig, - pub enable_quic_servers: bool, } impl Default for ValidatorConfig { @@ -224,6 +227,7 @@ impl Default for ValidatorConfig { no_os_disk_stats_reporting: true, poh_pinned_cpu_core: poh_service::DEFAULT_PINNED_CPU_CORE, poh_hashes_per_batch: poh_service::DEFAULT_HASHES_PER_BATCH, + process_ledger_before_services: false, account_indexes: AccountSecondaryIndexes::default(), accounts_db_caching_enabled: false, warp_slot: None, @@ -237,7 +241,6 @@ impl Default for ValidatorConfig { wait_to_vote_slot: None, ledger_column_options: LedgerColumnOptions::default(), runtime_config: RuntimeConfig::default(), - enable_quic_servers: true, } } } @@ -259,14 +262,23 @@ impl ValidatorConfig { pub enum ValidatorStartProgress { Initializing, // Catch all, default state SearchingForRpcService, - DownloadingSnapshot { slot: Slot, rpc_addr: SocketAddr }, + DownloadingSnapshot { + slot: Slot, + rpc_addr: SocketAddr, + }, CleaningBlockStore, CleaningAccounts, LoadingLedger, - ProcessingLedger { slot: Slot, max_slot: Slot }, + ProcessingLedger { + slot: Slot, + max_slot: Slot, + }, StartingServices, Halted, // Validator halted due to `--dev-halt-at-slot` argument - WaitingForSupermajority, + WaitingForSupermajority { + slot: Slot, + gossip_stake_percent: u64, + }, // `Running` is the terminal state once the validator fully starts and all services are // operational @@ -293,7 +305,7 @@ impl BlockstoreRootScan { let exit = exit.clone(); Some( Builder::new() - .name("blockstore-root-scan".to_string()) + .name("solBStoreRtScan".to_string()) .spawn(move || blockstore.scan_and_fix_roots(&exit)) .unwrap(), ) @@ -319,6 +331,7 @@ struct TransactionHistoryServices { max_complete_transaction_status_slot: Arc, rewards_recorder_sender: Option, rewards_recorder_service: Option, + max_complete_rewards_slot: Arc, cache_block_meta_sender: Option, cache_block_meta_service: Option, } @@ -383,16 +396,18 @@ impl Validator { socket_addr_space: SocketAddrSpace, use_quic: bool, tpu_connection_pool_size: usize, + tpu_enable_udp: bool, + admin_rpc_service_post_init: Arc>>, ) -> Self { let id = identity_keypair.pubkey(); assert_eq!(id, node.info.id); - + error!("chk1 enters validator"); warn!("identity: {}", id); warn!("vote account: {}", vote_account); if !config.no_os_network_stats_reporting { verify_net_stats_access().unwrap_or_else(|err| { - error!("Failed to access Network stats: {}. Bypass check with --no-os-network-stats-reporting.", err); + error!("Failed to access Network stats: {}.", err); abort(); }); } @@ -429,6 +444,14 @@ impl Validator { info!("entrypoint: {:?}", cluster_entrypoint); } + if rayon::ThreadPoolBuilder::new() + .thread_name(|ix| format!("solRayonGlob{:02}", ix)) + .build_global() + .is_err() + { + warn!("Rayon global thread pool already initialized"); + } + if solana_perf::perf_libs::api().is_some() { info!("Initializing sigverify, this could take a while..."); } else { @@ -450,6 +473,7 @@ impl Validator { *start_progress.write().unwrap() = ValidatorStartProgress::CleaningBlockStore; backup_and_clear_blockstore( ledger_path, + config, wait_for_supermajority_slot + 1, shred_version, ); @@ -525,6 +549,7 @@ impl Validator { max_complete_transaction_status_slot, rewards_recorder_sender, rewards_recorder_service, + max_complete_rewards_slot, cache_block_meta_sender, cache_block_meta_service, }, @@ -665,6 +690,7 @@ impl Validator { }; let leader_schedule_cache = Arc::new(leader_schedule_cache); + info!("chk1 creates_process_blockstore"); let mut process_blockstore = ProcessBlockStore::new( &id, vote_account, @@ -689,6 +715,10 @@ impl Validator { &leader_schedule_cache, ); + if config.process_ledger_before_services { + info!("chk1 runs_process_blockstore"); + process_blockstore.process(); + } *start_progress.write().unwrap() = ValidatorStartProgress::StartingServices; let sample_performance_service = @@ -717,11 +747,13 @@ impl Validator { let rpc_subscriptions = Arc::new(RpcSubscriptions::new_with_config( &exit, max_complete_transaction_status_slot.clone(), + max_complete_rewards_slot.clone(), blockstore.clone(), bank_forks.clone(), block_commitment_cache.clone(), optimistically_confirmed_bank.clone(), &config.pubsub_config, + None, )); let max_slots = Arc::new(MaxSlots::default()); @@ -763,7 +795,7 @@ impl Validator { true => { let mut connection_cache = ConnectionCache::new(tpu_connection_pool_size); connection_cache - .update_client_certificate(&identity_keypair, node.info.gossip.ip()) + .update_client_certificate(&identity_keypair, node.info.tpu.ip()) .expect("Failed to update QUIC client certificates"); connection_cache.set_staked_nodes(&staked_nodes, &identity_keypair.pubkey()); Arc::new(connection_cache) @@ -771,6 +803,10 @@ impl Validator { false => Arc::new(ConnectionCache::with_udp(tpu_connection_pool_size)), }; + // block min prioritization fee cache should be readable by RPC, and writable by validator + // (for now, by replay stage) + let prioritization_fee_cache = Arc::new(PrioritizationFeeCache::default()); + let rpc_override_health_check = Arc::new(AtomicBool::new(false)); let ( json_rpc_service, @@ -809,6 +845,7 @@ impl Validator { genesis_config.hash(), ledger_path, config.validator_exit.clone(), + exit.clone(), config.known_validators.clone(), rpc_override_health_check.clone(), startup_verification_complete, @@ -818,6 +855,8 @@ impl Validator { leader_schedule_cache.clone(), connection_cache.clone(), max_complete_transaction_status_slot, + max_complete_rewards_slot, + prioritization_fee_cache.clone(), )), if !config.rpc_config.full_api { None @@ -842,8 +881,12 @@ impl Validator { optimistically_confirmed_bank, rpc_subscriptions.clone(), confirmed_bank_subscribers, + prioritization_fee_cache.clone(), )), - Some(bank_notification_sender), + Some(BankNotificationSenderConfig { + sender: bank_notification_sender, + should_send_parents: geyser_plugin_service.is_some(), + }), ) } else { (None, None, None, None) @@ -893,6 +936,12 @@ impl Validator { exit.clone(), ); + *admin_rpc_service_post_init.write().unwrap() = Some(AdminRpcRequestMetadataPostInit { + bank_forks: bank_forks.clone(), + cluster_info: cluster_info.clone(), + vote_account: *vote_account, + }); + let waited_for_supermajority = if let Ok(waited) = wait_for_supermajority( config, Some(&mut process_blockstore), @@ -993,6 +1042,7 @@ impl Validator { accounts_background_request_sender, config.runtime_config.log_messages_bytes_limit, &connection_cache, + &prioritization_fee_cache, ); let tpu = Tpu::new( @@ -1020,15 +1070,15 @@ impl Validator { gossip_verified_vote_hash_sender, replay_vote_receiver, replay_vote_sender, - bank_notification_sender, + bank_notification_sender.map(|sender| sender.sender), config.tpu_coalesce_ms, cluster_confirmed_slot_sender, &cost_model, &connection_cache, &identity_keypair, config.runtime_config.log_messages_bytes_limit, - config.enable_quic_servers, &staked_nodes, + tpu_enable_udp, ); datapoint_info!( @@ -1234,7 +1284,7 @@ fn check_poh_speed(genesis_config: &GenesisConfig, maybe_hash_samples: Option BlockstoreOptions { + BlockstoreOptions { + recovery_mode: config.wal_recovery_mode.clone(), + column_options: config.ledger_column_options.clone(), + enforce_ulimit_nofile: config.enforce_ulimit_nofile, + ..BlockstoreOptions::default() + } +} + #[allow(clippy::type_complexity)] fn load_blockstore( config: &ValidatorConfig, @@ -1395,16 +1454,8 @@ fn load_blockstore( ledger_signal_receiver, completed_slots_receiver, .. - } = Blockstore::open_with_signal( - ledger_path, - BlockstoreOptions { - recovery_mode: config.wal_recovery_mode.clone(), - column_options: config.ledger_column_options.clone(), - enforce_ulimit_nofile: config.enforce_ulimit_nofile, - ..BlockstoreOptions::default() - }, - ) - .expect("Failed to open ledger database"); + } = Blockstore::open_with_signal(ledger_path, blockstore_options_from_config(config)) + .expect("Failed to open ledger database"); blockstore.set_no_compaction(config.no_rocksdb_compaction); blockstore.shred_timing_point_sender = poh_timing_point_sender; // following boot sequence (esp BankForks) could set root. so stash the original value @@ -1413,7 +1464,9 @@ fn load_blockstore( let blockstore = Arc::new(blockstore); let blockstore_root_scan = BlockstoreRootScan::new(config, &blockstore, exit); - let halt_at_slot = config.halt_at_slot.or_else(|| highest_slot(&blockstore)); + let halt_at_slot = config + .halt_at_slot + .or_else(|| blockstore.highest_slot().unwrap_or(None)); let process_options = blockstore_processor::ProcessOptions { poh_verify: config.poh_verify, @@ -1512,29 +1565,6 @@ fn load_blockstore( ) } -fn highest_slot(blockstore: &Blockstore) -> Option { - let mut start = Measure::start("Blockstore search for highest slot"); - let highest_slot = blockstore - .slot_meta_iterator(0) - .map(|metas| { - let slots: Vec<_> = metas.map(|(slot, _)| slot).collect(); - if slots.is_empty() { - info!("Ledger is empty"); - None - } else { - let first = slots.first().unwrap(); - Some(*slots.last().unwrap_or(first)) - } - }) - .unwrap_or_else(|err| { - warn!("Failed to ledger slot meta: {}", err); - None - }); - start.stop(); - info!("{}. Found slot {:?}", start, highest_slot); - highest_slot -} - pub struct ProcessBlockStore<'a> { id: &'a Pubkey, vote_account: &'a Pubkey, @@ -1592,34 +1622,23 @@ impl<'a> ProcessBlockStore<'a> { let previous_start_process = *self.start_progress.read().unwrap(); *self.start_progress.write().unwrap() = ValidatorStartProgress::LoadingLedger; - /* - #[allow(clippy::too_many_arguments)] - fn process_blockstore( - blockstore: &Blockstore, - bank_forks: &Arc>, - leader_schedule_cache: &LeaderScheduleCache, - process_options: &blockstore_processor::ProcessOptions, - transaction_status_sender: Option<&TransactionStatusSender>, - cache_block_meta_sender: Option<&CacheBlockMetaSender>, - blockstore_root_scan: BlockstoreRootScan, - accounts_background_request_sender: &AbsRequestSender, - start_progress: &Arc>, - ) { - */ let exit = Arc::new(AtomicBool::new(false)); - if let Some(max_slot) = highest_slot(self.blockstore) { + if let Ok(Some(max_slot)) = self.blockstore.highest_slot() { let bank_forks = self.bank_forks.clone(); let exit = exit.clone(); let start_progress = self.start_progress.clone(); - let _ = std::thread::spawn(move || { - while !exit.load(Ordering::Relaxed) { - let slot = bank_forks.read().unwrap().working_bank().slot(); - *start_progress.write().unwrap() = - ValidatorStartProgress::ProcessingLedger { slot, max_slot }; - sleep(Duration::from_secs(2)); - } - }); + let _ = Builder::new() + .name("solRptLdgrStat".to_string()) + .spawn(move || { + while !exit.load(Ordering::Relaxed) { + let slot = bank_forks.read().unwrap().working_bank().slot(); + *start_progress.write().unwrap() = + ValidatorStartProgress::ProcessingLedger { slot, max_slot }; + sleep(Duration::from_secs(2)); + } + }) + .unwrap(); } blockstore_processor::process_blockstore_from_root( self.blockstore, @@ -1705,8 +1724,6 @@ fn maybe_warp_slot( abort(); }); - process_blockstore.process(); - let mut bank_forks = bank_forks.write().unwrap(); let working_bank = bank_forks.working_bank(); @@ -1752,6 +1769,11 @@ fn maybe_warp_slot( "created snapshot: {}", full_snapshot_archive_info.path().display() ); + + drop(bank_forks); + // Process blockstore after warping bank forks to make sure tower and + // bank forks are in sync. + process_blockstore.process(); } } @@ -1781,15 +1803,31 @@ fn blockstore_contains_bad_shred_version( false } -fn backup_and_clear_blockstore(ledger_path: &Path, start_slot: Slot, shred_version: u16) { - let blockstore = Blockstore::open(ledger_path).unwrap(); +fn backup_and_clear_blockstore( + ledger_path: &Path, + config: &ValidatorConfig, + start_slot: Slot, + shred_version: u16, +) { + let blockstore = + Blockstore::open_with_options(ledger_path, blockstore_options_from_config(config)).unwrap(); let do_copy_and_clear = blockstore_contains_bad_shred_version(&blockstore, start_slot, shred_version); // If found, then copy shreds to another db and clear from start_slot if do_copy_and_clear { - let folder_name = format!("backup_rocksdb_{}", thread_rng().gen_range(0, 99999)); - let backup_blockstore = Blockstore::open(&ledger_path.join(folder_name)); + let folder_name = format!( + "backup_{}_{}", + config + .ledger_column_options + .shred_storage_type + .blockstore_directory(), + thread_rng().gen_range(0, 99999) + ); + let backup_blockstore = Blockstore::open_with_options( + &ledger_path.join(folder_name), + blockstore_options_from_config(config), + ); let mut last_print = Instant::now(); let mut copied = 0; let mut last_slot = None; @@ -1842,10 +1880,12 @@ fn initialize_rpc_transaction_history_services( exit, )); + let max_complete_rewards_slot = Arc::new(AtomicU64::new(blockstore.max_root())); let (rewards_recorder_sender, rewards_receiver) = unbounded(); let rewards_recorder_sender = Some(rewards_recorder_sender); let rewards_recorder_service = Some(RewardsRecorderService::new( rewards_receiver, + max_complete_rewards_slot.clone(), blockstore.clone(), exit, )); @@ -1863,6 +1903,7 @@ fn initialize_rpc_transaction_history_services( max_complete_transaction_status_slot, rewards_recorder_sender, rewards_recorder_service, + max_complete_rewards_slot, cache_block_meta_sender, cache_block_meta_service, } @@ -1890,20 +1931,20 @@ fn wait_for_supermajority( ) -> Result { match config.wait_for_supermajority { None => Ok(false), - Some(wait_for_supermajority) => { + Some(wait_for_supermajority_slot) => { if let Some(process_blockstore) = process_blockstore { process_blockstore.process(); } let bank = bank_forks.read().unwrap().working_bank(); - match wait_for_supermajority.cmp(&bank.slot()) { + match wait_for_supermajority_slot.cmp(&bank.slot()) { std::cmp::Ordering::Less => return Ok(false), std::cmp::Ordering::Greater => { error!( "Ledger does not have enough data to wait for supermajority, \ please enable snapshot fetch. Has {} needs {}", bank.slot(), - wait_for_supermajority + wait_for_supermajority_slot ); return Err(ValidatorError::NotEnoughLedgerData); } @@ -1921,7 +1962,6 @@ fn wait_for_supermajority( } } - *start_progress.write().unwrap() = ValidatorStartProgress::WaitingForSupermajority; for i in 1.. { if i % 10 == 1 { info!( @@ -1934,6 +1974,12 @@ fn wait_for_supermajority( let gossip_stake_percent = get_stake_percent_in_gossip(&bank, cluster_info, i % 10 == 0); + *start_progress.write().unwrap() = + ValidatorStartProgress::WaitingForSupermajority { + slot: wait_for_supermajority_slot, + gossip_stake_percent, + }; + if gossip_stake_percent >= WAIT_FOR_SUPERMAJORITY_THRESHOLD_PERCENT { info!( "Supermajority reached, {}% active stake detected, starting up now.", @@ -2020,6 +2066,7 @@ fn get_stake_percent_in_gossip(bank: &Bank, cluster_info: &ClusterInfo, log: boo "{:.3}% of active stake has the wrong shred version in gossip", (wrong_shred_stake as f64 / total_activated_stake as f64) * 100., ); + wrong_shred_nodes.sort_by(|b, a| a.0.cmp(&b.0)); // sort by reverse stake weight for (stake, identity) in wrong_shred_nodes { info!( " {:.3}% - {}", @@ -2034,6 +2081,7 @@ fn get_stake_percent_in_gossip(bank: &Bank, cluster_info: &ClusterInfo, log: boo "{:.3}% of active stake is not visible in gossip", (offline_stake as f64 / total_activated_stake as f64) * 100. ); + offline_nodes.sort_by(|b, a| a.0.cmp(&b.0)); // sort by reverse stake weight for (stake, identity) in offline_nodes { info!( " {:.3}% - {}", @@ -2085,7 +2133,9 @@ mod tests { use { super::*, crossbeam_channel::{bounded, RecvTimeoutError}, - solana_client::connection_cache::{DEFAULT_TPU_CONNECTION_POOL_SIZE, DEFAULT_TPU_USE_QUIC}, + solana_client::connection_cache::{ + DEFAULT_TPU_CONNECTION_POOL_SIZE, DEFAULT_TPU_ENABLE_UDP, DEFAULT_TPU_USE_QUIC, + }, solana_ledger::{create_new_tmp_ledger, genesis_utils::create_genesis_config_with_leader}, solana_sdk::{genesis_config::create_genesis_config, poh_config::PohConfig}, std::{fs::remove_dir_all, thread, time::Duration}, @@ -2123,7 +2173,10 @@ mod tests { SocketAddrSpace::Unspecified, DEFAULT_TPU_USE_QUIC, DEFAULT_TPU_CONNECTION_POOL_SIZE, + DEFAULT_TPU_ENABLE_UDP, + Arc::new(RwLock::new(None)), ); + assert_eq!( *start_progress.read().unwrap(), ValidatorStartProgress::Running @@ -2140,6 +2193,8 @@ mod tests { solana_entry::entry, solana_ledger::{blockstore, get_tmp_ledger_path}, }; + + let validator_config = ValidatorConfig::default_for_test(); let blockstore_path = get_tmp_ledger_path!(); { let blockstore = Blockstore::open(&blockstore_path).unwrap(); @@ -2149,7 +2204,14 @@ mod tests { info!("creating shreds"); let mut last_print = Instant::now(); for i in 1..10 { - let shreds = blockstore::entries_to_test_shreds(&entries, i, i - 1, true, 1); + let shreds = blockstore::entries_to_test_shreds( + &entries, + i, // slot + i - 1, // parent_slot + true, // is_full_slot + 1, // version + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, true).unwrap(); if last_print.elapsed().as_millis() > 5000 { info!("inserted {}", i); @@ -2159,7 +2221,7 @@ mod tests { drop(blockstore); // this purges and compacts all slots greater than or equal to 5 - backup_and_clear_blockstore(&blockstore_path, 5, 2); + backup_and_clear_blockstore(&blockstore_path, &validator_config, 5, 2); let blockstore = Blockstore::open(&blockstore_path).unwrap(); // assert that slots less than 5 aren't affected @@ -2177,15 +2239,15 @@ mod tests { fn validator_parallel_exit() { let leader_keypair = Keypair::new(); let leader_node = Node::new_localhost_with_pubkey(&leader_keypair.pubkey()); + let genesis_config = + create_genesis_config_with_leader(10_000, &leader_keypair.pubkey(), 1000) + .genesis_config; let mut ledger_paths = vec![]; let mut validators: Vec = (0..2) .map(|_| { let validator_keypair = Keypair::new(); let validator_node = Node::new_localhost_with_pubkey(&validator_keypair.pubkey()); - let genesis_config = - create_genesis_config_with_leader(10_000, &leader_keypair.pubkey(), 1000) - .genesis_config; let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config); ledger_paths.push(validator_ledger_path.clone()); let vote_account_keypair = Keypair::new(); @@ -2206,6 +2268,8 @@ mod tests { SocketAddrSpace::Unspecified, DEFAULT_TPU_USE_QUIC, DEFAULT_TPU_CONNECTION_POOL_SIZE, + DEFAULT_TPU_ENABLE_UDP, + Arc::new(RwLock::new(None)), ) }) .collect(); @@ -2222,8 +2286,7 @@ mod tests { sender.send(()).unwrap(); }); - // timeout of 30s for shutting down the validators - let timeout = Duration::from_secs(30); + let timeout = Duration::from_secs(120); if let Err(RecvTimeoutError::Timeout) = receiver.recv_timeout(timeout) { panic!("timeout for shutting down validators",); } diff --git a/core/src/verified_vote_packets.rs b/core/src/verified_vote_packets.rs index 709db89d9b1197..47cfabfacece3e 100644 --- a/core/src/verified_vote_packets.rs +++ b/core/src/verified_vote_packets.rs @@ -7,7 +7,7 @@ use { }, solana_sdk::{ account::from_account, - clock::Slot, + clock::{Slot, UnixTimestamp}, feature_set::{allow_votes_to_directly_update_vote_state, FeatureSet}, hash::Hash, pubkey::Pubkey, @@ -133,6 +133,7 @@ impl<'a> Iterator for ValidatorGossipVotesIterator<'a> { hash, packet_batch, signature, + .. }) => self .filter_vote(slot, hash, packet_batch, signature) .map(|packet| vec![packet]) @@ -165,6 +166,7 @@ pub struct GossipVote { hash: Hash, packet_batch: PacketBatch, signature: Signature, + timestamp: Option, } pub enum SingleValidatorVotes { @@ -180,6 +182,13 @@ impl SingleValidatorVotes { } } + fn get_latest_timestamp(&self) -> Option { + match self { + Self::FullTowerVote(vote) => vote.timestamp, + _ => None, + } + } + #[cfg(test)] fn len(&self) -> usize { match self { @@ -224,15 +233,24 @@ impl VerifiedVotePackets { } let slot = vote.last_voted_slot().unwrap(); let hash = vote.hash(); + let timestamp = vote.timestamp(); match (vote, is_full_tower_vote_enabled) { (VoteStateUpdate(_), true) => { - let latest_gossip_slot = match self.0.get(&vote_account_key) { - Some(vote) => vote.get_latest_gossip_slot(), - _ => 0, - }; + let (latest_gossip_slot, latest_timestamp) = + self.0.get(&vote_account_key).map_or((0, None), |vote| { + (vote.get_latest_gossip_slot(), vote.get_latest_timestamp()) + }); // Since votes are not incremental, we keep only the latest vote - if slot > latest_gossip_slot { + // If the vote is for the same slot we will only allow it if + // it has a later timestamp (refreshed vote) + // + // Timestamp can be None if something was wrong with the senders clock. + // We directly compare as Options to ensure that votes with proper + // timestamps have precedence (Some is > None). + if slot > latest_gossip_slot + || ((slot == latest_gossip_slot) && (timestamp > latest_timestamp)) + { self.0.insert( vote_account_key, FullTowerVote(GossipVote { @@ -240,6 +258,7 @@ impl VerifiedVotePackets { hash, packet_batch, signature, + timestamp, }), ); } @@ -259,6 +278,7 @@ impl VerifiedVotePackets { hash, packet_batch, signature, + .. } = std::mem::take(gossip_vote); votes.insert((slot, hash), (packet_batch, signature)); self.0.insert(vote_account_key, IncrementalVotes(votes)); @@ -296,7 +316,7 @@ mod tests { use { super::{SingleValidatorVotes::*, *}, crate::{result::Error, vote_simulator::VoteSimulator}, - crossbeam_channel::unbounded, + crossbeam_channel::{unbounded, Receiver, Sender}, solana_perf::packet::Packet, solana_sdk::slot_hashes::MAX_ENTRIES, solana_vote_program::vote_state::{Lockout, Vote, VoteStateUpdate}, @@ -678,6 +698,110 @@ mod tests { ); } + fn send_vote_state_update_and_process( + s: &Sender>, + r: &Receiver>, + vote: VoteStateUpdate, + vote_account_key: Pubkey, + feature_set: Option>, + verified_vote_packets: &mut VerifiedVotePackets, + ) -> GossipVote { + s.send(vec![VerifiedVoteMetadata { + vote_account_key, + vote: VoteTransaction::from(vote), + packet_batch: PacketBatch::default(), + signature: Signature::new(&[1u8; 64]), + }]) + .unwrap(); + verified_vote_packets + .receive_and_process_vote_packets(r, true, feature_set) + .unwrap(); + match verified_vote_packets.0.get(&vote_account_key).unwrap() { + SingleValidatorVotes::FullTowerVote(gossip_vote) => gossip_vote.clone(), + _ => panic!("Received incremental vote"), + } + } + + #[test] + fn test_latest_vote_tie_break_with_feature() { + let (s, r) = unbounded(); + let vote_account_key = solana_sdk::pubkey::new_rand(); + + // Send identical vote state updates with different timestamps + let mut vote = VoteStateUpdate::from(vec![(2, 4), (4, 3), (6, 2), (7, 1)]); + vote.timestamp = Some(5); + + let mut vote_later_ts = vote.clone(); + vote_later_ts.timestamp = Some(6); + + let mut vote_earlier_ts = vote.clone(); + vote_earlier_ts.timestamp = Some(4); + + let mut vote_no_ts = vote.clone(); + vote_no_ts.timestamp = None; + + let mut verified_vote_packets = VerifiedVotePackets(HashMap::new()); + let mut feature_set = FeatureSet::default(); + feature_set.activate(&allow_votes_to_directly_update_vote_state::id(), 0); + let feature_set = Some(Arc::new(feature_set)); + + // Original vote + let GossipVote { + slot, timestamp, .. + } = send_vote_state_update_and_process( + &s, + &r, + vote.clone(), + vote_account_key, + feature_set.clone(), + &mut verified_vote_packets, + ); + assert_eq!(slot, vote.last_voted_slot().unwrap()); + assert_eq!(timestamp, vote.timestamp); + + // Same vote with later timestamp should override + let GossipVote { + slot, timestamp, .. + } = send_vote_state_update_and_process( + &s, + &r, + vote_later_ts.clone(), + vote_account_key, + feature_set.clone(), + &mut verified_vote_packets, + ); + assert_eq!(slot, vote_later_ts.last_voted_slot().unwrap()); + assert_eq!(timestamp, vote_later_ts.timestamp); + + // Same vote with earlier timestamp should not override + let GossipVote { + slot, timestamp, .. + } = send_vote_state_update_and_process( + &s, + &r, + vote_earlier_ts, + vote_account_key, + feature_set.clone(), + &mut verified_vote_packets, + ); + assert_eq!(slot, vote_later_ts.last_voted_slot().unwrap()); + assert_eq!(timestamp, vote_later_ts.timestamp); + + // Same vote with no timestamp should not override + let GossipVote { + slot, timestamp, .. + } = send_vote_state_update_and_process( + &s, + &r, + vote_no_ts, + vote_account_key, + feature_set, + &mut verified_vote_packets, + ); + assert_eq!(slot, vote_later_ts.last_voted_slot().unwrap()); + assert_eq!(timestamp, vote_later_ts.timestamp); + } + #[test] fn test_latest_vote_feature_upgrade() { let (s, r) = unbounded(); diff --git a/core/src/voting_service.rs b/core/src/voting_service.rs index 29cf4699dd575e..cbd53a1c3bc23b 100644 --- a/core/src/voting_service.rs +++ b/core/src/voting_service.rs @@ -46,7 +46,7 @@ impl VotingService { bank_forks: Arc>, ) -> Self { let thread_hdl = Builder::new() - .name("sol-vote-service".to_string()) + .name("solVoteService".to_string()) .spawn(move || { for vote_op in vote_receiver.iter() { let rooted_bank = bank_forks.read().unwrap().root_bank().clone(); diff --git a/core/src/warm_quic_cache_service.rs b/core/src/warm_quic_cache_service.rs index 2632d031019ed9..d685210dd799e2 100644 --- a/core/src/warm_quic_cache_service.rs +++ b/core/src/warm_quic_cache_service.rs @@ -32,7 +32,7 @@ impl WarmQuicCacheService { exit: Arc, ) -> Self { let thread_hdl = Builder::new() - .name("sol-warm-quic-service".to_string()) + .name("solWarmQuicSvc".to_string()) .spawn(move || { let slot_jitter = thread_rng().gen_range(-CACHE_JITTER_SLOT, CACHE_JITTER_SLOT); let mut maybe_last_leader = None; @@ -50,7 +50,7 @@ impl WarmQuicCacheService { .lookup_contact_info(&leader_pubkey, |leader| leader.tpu) { let conn = connection_cache.get_connection(&addr); - if let Err(err) = conn.send_wire_transaction(&[0u8]) { + if let Err(err) = conn.send_wire_transaction([0u8]) { warn!( "Failed to warmup QUIC connection to the leader {:?}, Error {:?}", leader_pubkey, err diff --git a/core/src/window_service.rs b/core/src/window_service.rs index da4cbcb4452b11..a76fa88f30d30a 100644 --- a/core/src/window_service.rs +++ b/core/src/window_service.rs @@ -16,7 +16,7 @@ use { solana_ledger::{ blockstore::{Blockstore, BlockstoreInsertionMetrics}, leader_schedule_cache::LeaderScheduleCache, - shred::{self, Nonce, Shred}, + shred::{self, Nonce, ReedSolomonCache, Shred}, }, solana_measure::measure::Measure, solana_metrics::inc_new_counter_error, @@ -220,6 +220,7 @@ fn run_insert( completed_data_sets_sender: &CompletedDataSetsSender, retransmit_sender: &Sender>, outstanding_requests: &RwLock, + reed_solomon_cache: &ReedSolomonCache, ) -> Result<()> where F: Fn(Shred), @@ -282,6 +283,7 @@ where false, // is_trusted Some(retransmit_sender), &handle_duplicate, + reed_solomon_cache, metrics, )?; for index in inserted_indices { @@ -375,7 +377,7 @@ impl WindowService { inc_new_counter_error!("solana-check-duplicate-error", 1, 1); }; Builder::new() - .name("solana-check-duplicate".to_string()) + .name("solWinCheckDup".to_string()) .spawn(move || { while !exit.load(Ordering::Relaxed) { if let Err(e) = run_check_duplicate( @@ -408,11 +410,12 @@ impl WindowService { }; let thread_pool = rayon::ThreadPoolBuilder::new() .num_threads(get_thread_count().min(8)) - .thread_name(|i| format!("window-insert-{}", i)) + .thread_name(|i| format!("solWinInsert{:02}", i)) .build() .unwrap(); + let reed_solomon_cache = ReedSolomonCache::default(); Builder::new() - .name("solana-window-insert".to_string()) + .name("solWinInsert".to_string()) .spawn(move || { let handle_duplicate = |shred| { let _ = check_duplicate_sender.send(shred); @@ -426,12 +429,13 @@ impl WindowService { &verified_receiver, &blockstore, &leader_schedule_cache, - &handle_duplicate, + handle_duplicate, &mut metrics, &mut ws_metrics, &completed_data_sets_sender, &retransmit_sender, &outstanding_requests, + &reed_solomon_cache, ) { ws_metrics.record_error(&e); if Self::should_exit_on_error(e, &handle_error) { @@ -479,10 +483,10 @@ mod test { use { super::*, solana_entry::entry::{create_ticks, Entry}, - solana_gossip::contact_info::ContactInfo, + solana_gossip::legacy_contact_info::LegacyContactInfo as ContactInfo, solana_ledger::{ blockstore::{make_many_slot_entries, Blockstore}, - get_tmp_ledger_path, + get_tmp_ledger_path_auto_delete, shred::{ProcessShredsStats, Shredder}, }, solana_sdk::{ @@ -506,6 +510,8 @@ mod test { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); data_shreds @@ -513,8 +519,8 @@ mod test { #[test] fn test_process_shred() { - let blockstore_path = get_tmp_ledger_path!(); - let blockstore = Arc::new(Blockstore::open(&blockstore_path).unwrap()); + let ledger_path = get_tmp_ledger_path_auto_delete!(); + let blockstore = Arc::new(Blockstore::open(ledger_path.path()).unwrap()); let num_entries = 10; let original_entries = create_ticks(num_entries, 0, Hash::default()); let mut shreds = local_entries_to_shred(&original_entries, 0, 0, &Keypair::new()); @@ -524,25 +530,27 @@ mod test { .expect("Expect successful processing of shred"); assert_eq!(blockstore.get_slot_entries(0, 0).unwrap(), original_entries); - - drop(blockstore); - Blockstore::destroy(&blockstore_path).expect("Expected successful database destruction"); } #[test] fn test_run_check_duplicate() { - let blockstore_path = get_tmp_ledger_path!(); - let blockstore = Arc::new(Blockstore::open(&blockstore_path).unwrap()); + let ledger_path = get_tmp_ledger_path_auto_delete!(); + let blockstore = Arc::new(Blockstore::open(ledger_path.path()).unwrap()); let (sender, receiver) = unbounded(); let (duplicate_slot_sender, duplicate_slot_receiver) = unbounded(); let (shreds, _) = make_many_slot_entries(5, 5, 10); blockstore .insert_shreds(shreds.clone(), None, false) .unwrap(); - let mut duplicate_shred = shreds[1].clone(); - duplicate_shred.set_slot(shreds[0].slot()); + let duplicate_index = 0; + let original_shred = shreds[duplicate_index].clone(); + let duplicate_shred = { + let (mut shreds, _) = make_many_slot_entries(5, 1, 10); + shreds.swap_remove(duplicate_index) + }; + assert_eq!(duplicate_shred.slot(), shreds[0].slot()); let duplicate_shred_slot = duplicate_shred.slot(); - sender.send(duplicate_shred).unwrap(); + sender.send(duplicate_shred.clone()).unwrap(); assert!(!blockstore.has_duplicate_shreds_in_slot(duplicate_shred_slot)); let keypair = Keypair::new(); let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), timestamp()); @@ -558,13 +566,87 @@ mod test { &duplicate_slot_sender, ) .unwrap(); - assert!(blockstore.has_duplicate_shreds_in_slot(duplicate_shred_slot)); + + // Make sure the correct duplicate proof was stored + let duplicate_proof = blockstore.get_duplicate_slot(duplicate_shred_slot).unwrap(); + assert_eq!(duplicate_proof.shred1, *original_shred.payload()); + assert_eq!(duplicate_proof.shred2, *duplicate_shred.payload()); + + // Make sure a duplicate signal was sent assert_eq!( duplicate_slot_receiver.try_recv().unwrap(), duplicate_shred_slot ); } + #[test] + fn test_store_duplicate_shreds_same_batch() { + let ledger_path = get_tmp_ledger_path_auto_delete!(); + let blockstore = Arc::new(Blockstore::open(ledger_path.path()).unwrap()); + let (duplicate_shred_sender, duplicate_shred_receiver) = unbounded(); + let (duplicate_slot_sender, duplicate_slot_receiver) = unbounded(); + let exit = Arc::new(AtomicBool::new(false)); + let keypair = Keypair::new(); + let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), timestamp()); + let cluster_info = Arc::new(ClusterInfo::new( + contact_info, + Arc::new(keypair), + SocketAddrSpace::Unspecified, + )); + + // Start duplicate thread receiving and inserting duplicates + let t_check_duplicate = WindowService::start_check_duplicate_thread( + cluster_info, + exit.clone(), + blockstore.clone(), + duplicate_shred_receiver, + duplicate_slot_sender, + ); + + let handle_duplicate = |shred| { + let _ = duplicate_shred_sender.send(shred); + }; + let num_trials = 100; + for slot in 0..num_trials { + let (shreds, _) = make_many_slot_entries(slot, 1, 10); + let duplicate_index = 0; + let original_shred = shreds[duplicate_index].clone(); + let duplicate_shred = { + let (mut shreds, _) = make_many_slot_entries(slot, 1, 10); + shreds.swap_remove(duplicate_index) + }; + assert_eq!(duplicate_shred.slot(), slot); + // Simulate storing both duplicate shreds in the same batch + blockstore + .insert_shreds_handle_duplicate( + vec![original_shred.clone(), duplicate_shred.clone()], + vec![false, false], + None, + false, // is_trusted + None, + &handle_duplicate, + &ReedSolomonCache::default(), + &mut BlockstoreInsertionMetrics::default(), + ) + .unwrap(); + + // Make sure a duplicate signal was sent + assert_eq!( + duplicate_slot_receiver + .recv_timeout(Duration::from_millis(5_000)) + .unwrap(), + slot + ); + + // Make sure the correct duplicate proof was stored + let duplicate_proof = blockstore.get_duplicate_slot(slot).unwrap(); + assert_eq!(duplicate_proof.shred1, *original_shred.payload()); + assert_eq!(duplicate_proof.shred2, *duplicate_shred.payload()); + } + exit.store(true, Ordering::Relaxed); + t_check_duplicate.join().unwrap(); + } + #[test] fn test_prune_shreds() { use { diff --git a/core/tests/ledger_cleanup.rs b/core/tests/ledger_cleanup.rs index 809f35972d8ca6..3e43eb8c897545 100644 --- a/core/tests/ledger_cleanup.rs +++ b/core/tests/ledger_cleanup.rs @@ -479,8 +479,6 @@ mod tests { let mut num_shreds = 0; let mut max_speed = 0f32; let mut min_speed = f32::MAX; - let (mut shreds_with_parent, _) = make_many_slot_shreds( - 1, batch_size_slots, shreds_per_slot); let (first_shreds, _) = make_many_slot_shreds( 0, batch_size_slots, shreds_per_slot); loop { @@ -502,14 +500,10 @@ mod tests { break; } } else { - let mut slot_id = start_slot; + let slot_id = start_slot; if slot_id > 0 { - for shred in shreds_with_parent.iter_mut() { - shred.set_slot(slot_id); - if shred.index() as u64 == shreds_per_slot - 1 { - slot_id += 1; - } - } + let (shreds_with_parent, _) = make_many_slot_shreds( + slot_id, batch_size_slots, shreds_per_slot); total += shreds_with_parent.len(); cloned_blockstore.insert_shreds( shreds_with_parent.clone(), None, false).unwrap() diff --git a/core/tests/snapshots.rs b/core/tests/snapshots.rs index ba3e4415178129..0e57371a37ce69 100644 --- a/core/tests/snapshots.rs +++ b/core/tests/snapshots.rs @@ -10,7 +10,9 @@ use { accounts_hash_verifier::AccountsHashVerifier, snapshot_packager_service::SnapshotPackagerService, }, - solana_gossip::{cluster_info::ClusterInfo, contact_info::ContactInfo}, + solana_gossip::{ + cluster_info::ClusterInfo, legacy_contact_info::LegacyContactInfo as ContactInfo, + }, solana_runtime::{ accounts_background_service::{ AbsRequestHandler, AbsRequestSender, AccountsBackgroundService, SnapshotRequestHandler, @@ -257,6 +259,7 @@ fn run_bank_forks_snapshot_n( accounts_package.snapshot_links.path(), accounts_package.slot, &last_bank.get_accounts_hash(), + None, ); let snapshot_package = SnapshotPackage::new(accounts_package, last_bank.get_accounts_hash()); snapshot_utils::archive_snapshot_package( @@ -449,7 +452,7 @@ fn test_concurrent_snapshot_packaging( // currently sitting in the channel snapshot_utils::purge_old_bank_snapshots(bank_snapshots_dir); - let mut bank_snapshots = snapshot_utils::get_bank_snapshots_pre(&bank_snapshots_dir); + let mut bank_snapshots = snapshot_utils::get_bank_snapshots_pre(bank_snapshots_dir); bank_snapshots.sort_unstable(); assert!(bank_snapshots .into_iter() @@ -492,6 +495,7 @@ fn test_concurrent_snapshot_packaging( accounts_package.snapshot_links.path(), accounts_package.slot, &Hash::default(), + None, ); let snapshot_package = SnapshotPackage::new(accounts_package, Hash::default()); pending_snapshot_package @@ -535,6 +539,7 @@ fn test_concurrent_snapshot_packaging( saved_snapshots_dir.path(), saved_slot, &Hash::default(), + None, ); snapshot_utils::verify_snapshot_archive( diff --git a/docs/.gitignore b/docs/.gitignore index c9a1663eb851ab..3ecae797dfa8b9 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -12,6 +12,9 @@ /static/img/*.png vercel.json +# use npm and package-lock.json +yarn.lock + # Misc .DS_Store .env diff --git a/docs/README.md b/docs/README.md index fb9faa502cc9fb..b28d3e44cddf46 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,40 +1,63 @@ -# Docs Readme +# Solana Docs Readme -Solana's Docs are built using [Docusaurus 2](https://v2.docusaurus.io/) with `npm`. +Solana's Docs are built using [Docusaurus v2](https://v2.docusaurus.io/) with `npm`. Static content delivery is handled using `vercel`. -### Installing Docusaurus +## Local Development -```sh -$ npm install +To set up the Solana Docs site locally: + +- install dependencies using `npm` +- build locally via `./build.sh` +- run the local development server +- make your changes and updates as needed + +> Note: After cloning this repo to your local machine, all the local development commands are run from within this `docs` directory. + +### Install dependencies + +Install the site's dependencies via `npm`: + +```bash +npm install ``` -### Local Development +### Build locally -This command starts a local development server and opens up a browser window. -Most changes are reflected live without having to restart the server. -(You might have to run build.sh first if you run into failures) +The build script generates static content into the `build` directory and can be served using any static content hosting service. -```sh -$ npm run start +```bash +./build.sh ``` -### Build Locally +Running this build script requires **Docker**, and will auto fetch the [solanalabs/rust](https://hub.docker.com/r/solanalabs/rust) image from Docker hub to compile the desired version of the [Solana CLI](https://docs.solana.com/cli) from source. -This command generates static content into the `build` directory and can be -served using any static content hosting service. +This build script will also: -```sh -$ docs/build.sh +- generate the `cli/usage.md` document from the output of each of the Solana CLI commands and sub-commands +- convert each of the `art/*.bob` files into SVG images used throughout the docs +- generate the language [Translations](#translations) + +> Note: Running this build script is **required** before being able to run the site locally via the `npm run start` command since it will generate the `cli/usage.md` document. + +If you run into errors or issues with this step, see [Common Issues](#common-issues) below. See also [CI Build Flow](#ci-build-flow) for more details on production deployments of the docs. + +### Local development server + +This command starts the Docusaurus local development server and opens up a browser window. + +```bash +npm run start ``` -### Translations +> Note: Most changes are reflected live without having to restart the server or refresh the page. However, some changes may require a manual refresh of the page or a restart of the development server (via the command above). -Translations are sourced from [Crowdin](https://docusaurus.io/docs/i18n/crowdin) -and generated when master is built. -For local development use the following two commands in the `docs` directory. +## Translations -To download the newest Documentation translations run: +Translations are sourced from [Crowdin](https://docusaurus.io/docs/i18n/crowdin) and generated when `master` is built. +For local development use the following two commands in this `docs` directory. + +To download the newest documentation translations run: ```sh npm run crowdin:download @@ -46,14 +69,66 @@ To upload changes from `src` & generate [explicit IDs](https://docusaurus.io/doc npm run crowdin:upload ``` -### CI Build Flow +## CI Build Flow -The docs are built and published in Travis CI with the `docs/build.sh` script. -On each PR, the docs are built, but not published. +The docs are built and published in Travis CI with the `./build.sh` script. On each PR, the docs are built, but not published. -In each post-commit build, docs are built and published using `vercel` to their -respective domain depending on the build branch. +In each post-commit build, docs are built and published using `vercel` to their respective domain depending on the build branch. - Master branch docs are published to `edge.docs.solana.com` - Beta branch docs are published to `beta.docs.solana.com` - Latest release tag docs are published to `docs.solana.com` + +## Common Issues + +### Bad sidebars file (or `cli/usage` not found) + +```bash +Error: Bad sidebars file. +These sidebar document ids do not exist: +- cli/usage, +``` + +If you have NOT successfully run the build script, then the `cli/usage.md` file will not exist within your local repo (since it is in `.gitignore`). Not having this doc file, will result in the error message above. + +If the Rust toolchain (specifically `cargo`) is installed on your system, you can specifically build the `cli/usage.md` document via: + +```bash +./build-cli-usage.sh +``` + +Or using Docker and the normal build script, you can perform a full production build of the docs to generate this file: + +```bash +./build.sh +``` + +### Permission denied for the Docker socket + +```bash +Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post +``` + +Running docs build script (`./build.sh`) required the use of Docker.\*\*\*\* + +Ensuring you have Docker installed on your system and it is running. + +You may also try running either of these build scripts (and by association, Docker) with elevation permissions via `sudo`: + +```bash +sudo ./build.sh +# or +sudo ./build-cli-usage.sh +``` + +### Multiple SVG images not found + +```bash +Error: Image static/img/***.svg used in src/***.md not found. +``` + +During the build process of the docs (specifically within the `./convert-ascii-to-svg.sh` script run by `./build.sh`), each of the `art/*.bob` files are converted to SVG images and saved to the `static/img` directory. + +To correct this issue, use the steps above to [build the docs locally](#build-locally). + +> Note: While not generating and saving these SVG images within your local repo will **NOT** prevent you from running the local development server, it will result in numerous output errors in your terminal. diff --git a/docs/art/data-plane-fanout.bob b/docs/art/data-plane-fanout.bob index 9096d3aac955e5..13bc9de7c60934 100644 --- a/docs/art/data-plane-fanout.bob +++ b/docs/art/data-plane-fanout.bob @@ -3,13 +3,13 @@ | +-----------------+ Neighborhood 0 +-----------------+ | | | +--------------------->+ | | | | Validator 1 | | Validator 2 | | -| | +<---------------------+ | | +| | Root | | | | | +--------+-+------+ +------+-+--------+ | | | | | | | | | +-----------------------------+ | | | | | +------------------------+------+ | | | | | | | | -+------------------------------------------------------------------+ ++------------|------|------------------------|--------|------------+ | | | | v v v v +---------+------+---+ +-+--------+---------+ diff --git a/docs/art/data-plane-neighborhood.bob b/docs/art/data-plane-neighborhood.bob index bf2b82695f1fa4..ccdd3b6dd60f9f 100644 --- a/docs/art/data-plane-neighborhood.bob +++ b/docs/art/data-plane-neighborhood.bob @@ -1,25 +1,28 @@ +---------------------------------------------------------------------------------------------------------+ | Neighborhood Above | -| | -| +----------------+ +----------------+ +----------------+ +----------------+ | -| | +------>+ +------>+ +------>+ | | +| +-----------------+-----------------------+-------------------------+ | +| | | | | | +| | v v v | +| +--------------+-+ +----------------+ +----------------+ +----------------+ | +| | | | | | | | | | | | Neighbor 1 | | Neighbor 2 | | Neighbor 3 | | Neighbor 4 | | -| | +<------+ +<------+ +<------+ | | -| +--+-------------+ +--+-------------+ +-----+----------+ +--+-------------+ | -| | | | | | -+---------------------------------------------------------------------------------------------------------+ - | | | | - | | | | - | | | | - | | | | - | | | | -+---------------------------------------------------------------------------------------------------------+ -| | | Neighborhood Below | | | -| v v v v | -| +--+-------------+ +--+-------------+ +-----+----------+ +--+-------------+ | -| | +------>+ +------>+ +------>+ | | +| | Anchor | | | | | | | | +| +--+-------------+ +---+------------+ +------+---------+ +---+------------+ | +| | | | | | ++---------|-------------------------|---------------------------|---------------------|-------------------+ + | | | | + | | | | + | | | | + | | | | ++---------|-------------------------|---------------------------|---------------------|-------------------+ +| | | Neighborhood Below | | | +| v v v v | +| +--+-------------+ +---+------------+ +------+---------+ +---+------------+ | +| | | | | | | | | | | | Neighbor 1 | | Neighbor 2 | | Neighbor 3 | | Neighbor 4 | | -| | +<------+ +<------+ +<------+ | | -| +----------------+ +----------------+ +----------------+ +----------------+ | -| | +| | Anchor | | | | | | | | +| +--------------+-+ +----------------+ +----------------+ +----------------+ | +| | ^ ^ ^ | +| | | | | | +| +-----------------+-----------------------+-------------------------+ | +---------------------------------------------------------------------------------------------------------+ diff --git a/docs/art/data-plane-seeding.bob b/docs/art/data-plane-seeding.bob index 41e0bb75eacd2f..5a0d3b342c8b2c 100644 --- a/docs/art/data-plane-seeding.bob +++ b/docs/art/data-plane-seeding.bob @@ -1,15 +1,15 @@ +--------------+ | | - +------------+ Leader +------------+ - | | | | - | +--------------+ | - v v -+------------+----------------------------------------+------------+ -| | -| +-----------------+ Neighborhood 0 +-----------------+ | + +------------+ Leader | + | | | + | +--------------+ + | ++------------|-----------------------------------------------------+ +| v | +| +--------+--------+ Neighborhood 0 +-----------------+ | | | +--------------------->+ | | | | Validator 1 | | Validator 2 | | -| | +<---------------------+ | | +| | Root | | | | | +-----------------+ +-----------------+ | | | +------------------------------------------------------------------+ diff --git a/docs/build-cli-usage.sh b/docs/build-cli-usage.sh index 17c7d1de0cbbdc..e994c54d83a570 100755 --- a/docs/build-cli-usage.sh +++ b/docs/build-cli-usage.sh @@ -4,6 +4,29 @@ set -e cd "$(dirname "$0")" cargo=../cargo +# shellcheck source=ci/env.sh +source ../ci/env.sh + +# Get current channel +eval "$(../ci/channel-info.sh)" + +# set the output file' location +out=${1:-src/cli/usage.md} + +# load the usage file's header +cat src/cli/.usage.md.header > "$out" + +# Skip generating the usage doc for non deployment commits of the docs +if [[ -n $CI ]]; then + if [[ $CI_BRANCH != $EDGE_CHANNEL* ]] && [[ $CI_BRANCH != $BETA_CHANNEL* ]] && [[ $CI_BRANCH != $STABLE_CHANNEL* ]]; then + echo "**NOTE:** The usage doc is only auto-generated during full production deployments of the docs" + echo "**NOTE:** This usage doc is auto-generated during deployments" >> "$out" + exit + fi +fi + +echo 'Building the solana cli from source...' + # shellcheck source=ci/rust-version.sh source ../ci/rust-version.sh stable @@ -14,10 +37,6 @@ source ../ci/rust-version.sh stable usage=$("$cargo" stable -q run -p solana-cli -- -C ~/.foo --help | sed -e 's|'"$HOME"'|~|g' -e 's/[[:space:]]\+$//') -out=${1:-src/cli/usage.md} - -cat src/cli/.usage.md.header > "$out" - section() { declare mark=${2:-"###"} declare section=$1 diff --git a/docs/components/Card.jsx b/docs/components/Card.jsx new file mode 100644 index 00000000000000..67989e67dc109d --- /dev/null +++ b/docs/components/Card.jsx @@ -0,0 +1,53 @@ +import React from "react"; +import clsx from "clsx"; +import Link from "@docusaurus/Link"; +import styles from "../src/pages/styles.module.css"; +import Translate from "@docusaurus/Translate"; + +function Card({ to, header, body, externalIcon = false }) { + /* + Both the `header` and `body` expect an object with the following type + header = { + label: String, // + translateId: String // + } + */ + + return ( +
+ +
+

+ + {header.label} + + {externalIcon && ( + + )} +

+
+ + {typeof body === "object" && ( +
+

+ {body.label} +

+
+ )} + +
+ ); +} + +export default Card; diff --git a/docs/components/CodeDocBlock.jsx b/docs/components/CodeDocBlock.jsx new file mode 100644 index 00000000000000..bd0099b99e7830 --- /dev/null +++ b/docs/components/CodeDocBlock.jsx @@ -0,0 +1,161 @@ +import React from "react"; +import Link from "@docusaurus/Link"; +// import clsx from "clsx"; +import styles from "../src/pages/CodeDocBlock.module.css"; + +export function DocBlock({ children }) { + return
{children}
; +} + +export function DocSideBySide({ children }) { + return
{children}
; +} + +export function CodeParams({ children }) { + return
{children}
; +} + +export function CodeSnippets({ children }) { + return ( +
+ {/*

Code Sample:

*/} + + {children} +
+ ); +} + +/* + Display a single Parameter +*/ +export function Parameter(props) { + const { + name = null, + type = null, + required = null, + optional = null, + children, + } = computeHeader(props); + + return ( +
+

+ {name && name} {type && type} {required && required}{" "} + {optional && optional} +

+ + {children} +
+ ); +} + +/* + Display a single Parameter's field data +*/ +export function Field(props) { + const { + name = null, + type = null, + values = null, + required = null, + defaultValue = null, + optional = null, + children, + } = computeHeader(props); + + return ( +
+

+ {name && name} {type && type} {required && required}{" "} + {optional && optional} + {defaultValue && defaultValue} +

+ +
+ {values && values} + + {children} +
+
+ ); +} + +/* + Parse an array of string values to display +*/ +export function Values({ values = null }) { + // format the Parameter's values + if (values && Array.isArray(values) && values?.length) { + values = values.map((value) => ( + + {value} + + )); + } + + return ( +

+ Values: {values} +

+ ); +} + +/* + Compute the formatted Parameter and Field component's header meta data +*/ +function computeHeader({ + name = null, + type = null, + href = null, + values = null, + required = null, + defaultValue = null, + optional = null, + children, +}) { + // format the Parameter's name + if (name) { + name = {name}; + + if (href) name = {name}; + } + + // format the Parameter's type + if (type) type = {type}; + + // format the Parameter's values + if (values && Array.isArray(values)) { + values = values.map((value) => ( + {value} + )); + } + + // format the `defaultValue` flag + if (defaultValue) { + defaultValue = ( + + Default: {defaultValue.toString()} + + ); + } + + // format the `required` flag + if (required) { + required = required; + } + // format the `optional` flag + else if (optional) { + optional = optional; + } + + return { + name, + type, + href, + values, + required, + defaultValue, + optional, + children, + }; +} diff --git a/docs/components/HomeCtaLinks.jsx b/docs/components/HomeCtaLinks.jsx new file mode 100644 index 00000000000000..6a7283425ad6a7 --- /dev/null +++ b/docs/components/HomeCtaLinks.jsx @@ -0,0 +1,34 @@ +import React from "react"; +import Card from "./Card"; + +export default function HomeCtaLinks() { + return ( +
+
+ + + + + +
+
+ ); +} diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 0735f62839e60c..95b13418385242 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -45,6 +45,9 @@ module.exports = { }, }, themeConfig: { + prism: { + additionalLanguages: ["rust"], + }, navbar: { logo: { alt: "Solana Logo", @@ -53,49 +56,69 @@ module.exports = { }, items: [ { - href: "https://spl.solana.com", - label: "Program Library »", + to: "introduction", + label: "Learn", position: "left", }, { - to: "developing/programming-model/overview", - label: "Develop", + to: "cluster/overview", + label: "Architecture", position: "left", }, { - to: "running-validator", - label: "Validate", + to: "cli", + label: "CLI", position: "left", }, { - to: "integrations/exchange", - label: "Integrate", + to: "/developers", + label: "Developers", position: "left", }, { - to: "cluster/overview", - label: "Learn", + to: "running-validator", + label: "Validators", position: "left", }, + { + label: "More", + position: "left", + items: [ + { label: "Terminology", to: "terminology" }, + { label: "Staking", to: "staking" }, + { label: "Integrations", to: "integrations/exchange" }, + { label: "Economics", to: "economics_overview" }, + { label: "Proposals", to: "proposals" }, + { + href: "https://spl.solana.com", + label: "Solana Program Library »", + }, + ], + }, { type: "localeDropdown", position: "right", }, { - href: "https://discordapp.com/invite/pquxPsq", - label: "Chat", + href: "https://solana.com/discord", + // label: "Discord", + className: "header-link-icon header-discord-link", + "aria-label": "Solana Discord", position: "right", }, { href: "https://github.com/solana-labs/solana", - label: "GitHub", + // label: "GitHub", + className: "header-link-icon header-github-link", + "aria-label": "GitHub repository", position: "right", }, ], }, algolia: { // This API key is "search-only" and safe to be published - apiKey: "d58e0d68c875346d52645d68b13f3ac0", + apiKey: "011e01358301f5023b02da5db6af7f4d", + appId: "FQ12ISJR4B", indexName: "solana", contextualSearch: true, }, @@ -103,37 +126,69 @@ module.exports = { style: "dark", links: [ { - title: "Docs", + title: "Documentation", items: [ { - label: "Introduction", + label: "Learn", to: "introduction", }, + { + label: "Developers", + to: "/developers", + }, + { + label: "Validators", + to: "running-validator", + }, + { + label: "Command Line", + to: "cli", + }, + { + label: "Architecture", + to: "cluster/overview", + }, ], }, { title: "Community", items: [ { - label: "Discord", - href: "https://discordapp.com/invite/pquxPsq", + label: "Stack Exchange »", + href: "https://solana.stackexchange.com/", + }, + { + label: "GitHub »", + href: "https://github.com/solana-labs/solana", + }, + { + label: "Discord »", + href: "https://solana.com/discord", }, { - label: "Twitter", + label: "Twitter »", href: "https://twitter.com/solana", }, { - label: "Forums", - href: "https://forums.solana.com", + label: "Forum »", + href: "https://forum.solana.com", }, ], }, { - title: "More", + title: "Resources", items: [ { - label: "GitHub", - href: "https://github.com/solana-labs/solana", + label: "Proposals", + to: "proposals", + }, + { + label: "Integrations", + to: "integrations/exchange", + }, + { + href: "https://spl.solana.com", + label: "Solana Program Library »", }, ], }, @@ -147,6 +202,7 @@ module.exports = { { docs: { path: "src", + breadcrumbs: false, routeBasePath: "/", sidebarPath: require.resolve("./sidebars.js"), remarkPlugins: [math], @@ -155,6 +211,12 @@ module.exports = { theme: { customCss: require.resolve("./src/css/custom.css"), }, + // Google Analytics are only active in prod + gtag: { + // this GA code is safe to be published + trackingID: "G-94WS0LRZRS", + anonymizeIP: true, + }, }, ], ], diff --git a/docs/layouts/CardLayout.js b/docs/layouts/CardLayout.js new file mode 100644 index 00000000000000..71083e905a1477 --- /dev/null +++ b/docs/layouts/CardLayout.js @@ -0,0 +1,92 @@ +import React from "react"; +import Layout from "@theme/Layout"; +import DocSidebar from "@theme/DocSidebar"; +import SidebarStyles from "@docusaurus/theme-classic/lib/theme/DocPage/Layout/Sidebar/styles.module.css"; +import DocPageStyles from "@docusaurus/theme-classic/lib/theme/DocPage/Layout/styles.module.css"; +import sidebar from "../sidebars"; + +function CardLayout({ + children, + sidebarKey = false, + title = "", + description = "", + path = "", +}) { + // load the sidebar item from the master `sidebars.js` file + let sidebarItems = (sidebarKey && sidebar?.[sidebarKey]) || []; + + // process each of the loaded sidebar items for formatting + if (sidebarItems?.length) sidebarItems = parseSidebar(sidebarItems); + + // return the page layout, ready to go + return ( + +
+ {sidebarItems?.length > 0 && ( + + )} + +
{children}
+
+
+ ); +} +export default CardLayout; + +/* + Create a simple label based on the string of a doc file path +*/ +const computeLabel = (label) => { + label = label.split("/"); + label = label[label?.length - 1]?.replace("-", " "); + label = label.charAt(0).toUpperCase() + label.slice(1); + return label && label; +}; + +/* + Recursively parse the sidebar +*/ +const parseSidebar = (sidebarItems) => { + Object.keys(sidebarItems).forEach((key) => { + if (sidebarItems[key]?.type?.toLowerCase() === "category") { + sidebarItems[key].items = parseSidebar(sidebarItems[key].items); + } else sidebarItems[key] = formatter(sidebarItems[key]); + }); + return sidebarItems; +}; + +/* + Parser to format a sidebar item to be compatible with the `DocSidebar` component +*/ +const formatter = (item) => { + // handle string only document ids + if (typeof item === "string") { + item = { + type: "link", + href: item, + label: computeLabel(item) || item || "[unknown label]", + }; + } + + // handle object style docs + else if (item?.type?.toLowerCase() === "doc") { + item.type = "link"; + item.href = item.id; + item.label = item.label || computeLabel(item.href) || "[unknown label]"; + delete item.id; + } + + // fix for local routing that does not specify starting at the site root + if ( + !( + item?.href?.startsWith("/") || + item?.href?.startsWith("http:") || + item?.href?.startsWith("https") + ) + ) + item.href = `/${item?.href}`; + + return item; +}; diff --git a/docs/package-lock.json b/docs/package-lock.json index 9afffa7f9216b2..f98935ffa7dad2 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -9,11 +9,12 @@ "version": "0.0.0", "dependencies": { "@crowdin/cli": "^3.6.1", - "@docusaurus/core": "^2.0.0-beta.0", - "@docusaurus/preset-classic": "^2.0.0-beta.0", - "@docusaurus/theme-search-algolia": "^2.0.0-beta.0", + "@docusaurus/core": "^2.2.0", + "@docusaurus/plugin-google-gtag": "^2.4.0", + "@docusaurus/preset-classic": "^2.2.0", + "@docusaurus/theme-search-algolia": "^2.2.0", "babel-eslint": "^10.1.0", - "clsx": "^1.1.1", + "clsx": "^1.2.1", "eslint": "^7.3.1", "eslint-plugin-react": "^7.20.0", "postcss": "^8.2.13", @@ -26,178 +27,201 @@ } }, "node_modules/@algolia/autocomplete-core": { - "version": "1.0.0-alpha.44", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.0.0-alpha.44.tgz", - "integrity": "sha512-2iMXthldMIDXtlbg9omRKLgg1bLo2ZzINAEqwhNjUeyj1ceEyL1ck6FY0VnJpf2LsjmNthHCz2BuFk+nYUeDNA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.7.1.tgz", + "integrity": "sha512-eiZw+fxMzNQn01S8dA/hcCpoWCOCwcIIEUtHHdzN5TGB3IpzLbuhqFeTfh2OUhhgkE8Uo17+wH+QJ/wYyQmmzg==", "dependencies": { - "@algolia/autocomplete-shared": "1.0.0-alpha.44" + "@algolia/autocomplete-shared": "1.7.1" } }, "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.0.0-alpha.44", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.0.0-alpha.44.tgz", - "integrity": "sha512-DCHwo5ovzg9k2ejUolGNTLFnIA7GpsrkbNJTy1sFbMnYfBmeK8egZPZnEl7lBTr27OaZu7IkWpTepLVSztZyng==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.1.tgz", + "integrity": "sha512-pJwmIxeJCymU1M6cGujnaIYcY3QPOVYZOXhFkWVM7IxKzy272BwCvMFMyc5NpG/QmiObBxjo7myd060OeTNJXg==", "dependencies": { - "@algolia/autocomplete-shared": "1.0.0-alpha.44" + "@algolia/autocomplete-shared": "1.7.1" }, "peerDependencies": { - "@algolia/client-search": "^4.5.1", - "algoliasearch": "^4.5.1" + "@algolia/client-search": "^4.9.1", + "algoliasearch": "^4.9.1" } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.0.0-alpha.44", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.0.0-alpha.44.tgz", - "integrity": "sha512-2oQZPERYV+yNx/yoVWYjZZdOqsitJ5dfxXJjL18yczOXH6ujnsq+DTczSrX+RjzjQdVeJ1UAG053EJQF/FOiMg==" + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.1.tgz", + "integrity": "sha512-eTmGVqY3GeyBTT8IWiB2K5EuURAqhnumfktAEoHxfDY2o7vg2rSnO16ZtIG0fMgt3py28Vwgq42/bVEuaQV7pg==" }, "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.9.1.tgz", - "integrity": "sha512-bAUU9vKCy45uTTlzJw0LYu1IjoZsmzL6lgjaVFaW1crhX/4P+JD5ReQv3n/wpiXSFaHq1WEO3WyH2g3ymzeipQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.2.tgz", + "integrity": "sha512-FRweBkK/ywO+GKYfAWbrepewQsPTIEirhi1BdykX9mxvBPtGNKccYAxvGdDCumU1jL4r3cayio4psfzKMejBlA==", "dependencies": { - "@algolia/cache-common": "4.9.1" + "@algolia/cache-common": "4.14.2" } }, "node_modules/@algolia/cache-common": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.9.1.tgz", - "integrity": "sha512-tcvw4mOfFy44V4ZxDEy9wNGr6vFROZKRpXKTEBgdw/WBn6mX51H1ar4RWtceDEcDU4H5fIv5tsY3ip2hU+fTPg==" + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.14.2.tgz", + "integrity": "sha512-SbvAlG9VqNanCErr44q6lEKD2qoK4XtFNx9Qn8FK26ePCI8I9yU7pYB+eM/cZdS9SzQCRJBbHUumVr4bsQ4uxg==" }, "node_modules/@algolia/cache-in-memory": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.9.1.tgz", - "integrity": "sha512-IEJrHonvdymW2CnRfJtsTVWyfAH05xPEFkGXGCw00+6JNCj8Dln3TeaRLiaaY1srlyGedkemekQm1/Xb46CGOQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.14.2.tgz", + "integrity": "sha512-HrOukWoop9XB/VFojPv1R5SVXowgI56T9pmezd/djh2JnVN/vXswhXV51RKy4nCpqxyHt/aGFSq2qkDvj6KiuQ==", "dependencies": { - "@algolia/cache-common": "4.9.1" + "@algolia/cache-common": "4.14.2" } }, "node_modules/@algolia/client-account": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.9.1.tgz", - "integrity": "sha512-Shpjeuwb7i2LR5QuWREb6UbEQLGB+Pl/J5+wPgILJDP/uWp7jpl0ase9mYNQGKj7TjztpSpQCPZ3dSHPnzZPfw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.14.2.tgz", + "integrity": "sha512-WHtriQqGyibbb/Rx71YY43T0cXqyelEU0lB2QMBRXvD2X0iyeGl4qMxocgEIcbHyK7uqE7hKgjT8aBrHqhgc1w==", "dependencies": { - "@algolia/client-common": "4.9.1", - "@algolia/client-search": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/client-common": "4.14.2", + "@algolia/client-search": "4.14.2", + "@algolia/transporter": "4.14.2" } }, "node_modules/@algolia/client-analytics": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.9.1.tgz", - "integrity": "sha512-/g6OkOSIA+A0t/tjvbL6iG/zV4El4LPFgv/tcAYHTH27BmlNtnEXw+iFpGjeUlQoPily9WVB3QNLMJkaNwL3HA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.14.2.tgz", + "integrity": "sha512-yBvBv2mw+HX5a+aeR0dkvUbFZsiC4FKSnfqk9rrfX+QrlNOKEhCG0tJzjiOggRW4EcNqRmaTULIYvIzQVL2KYQ==", "dependencies": { - "@algolia/client-common": "4.9.1", - "@algolia/client-search": "4.9.1", - "@algolia/requester-common": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/client-common": "4.14.2", + "@algolia/client-search": "4.14.2", + "@algolia/requester-common": "4.14.2", + "@algolia/transporter": "4.14.2" } }, "node_modules/@algolia/client-common": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.9.1.tgz", - "integrity": "sha512-UziRTZ8km3qwoVPIyEre8TV6V+MX7UtbfVqPmSafZ0xu41UUZ+sL56YoKjOXkbKuybeIC9prXMGy/ID5bXkTqg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.14.2.tgz", + "integrity": "sha512-43o4fslNLcktgtDMVaT5XwlzsDPzlqvqesRi4MjQz2x4/Sxm7zYg5LRYFol1BIhG6EwxKvSUq8HcC/KxJu3J0Q==", "dependencies": { - "@algolia/requester-common": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/requester-common": "4.14.2", + "@algolia/transporter": "4.14.2" } }, - "node_modules/@algolia/client-recommendation": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-recommendation/-/client-recommendation-4.9.1.tgz", - "integrity": "sha512-Drtvvm1PNIOpYf4HFlkPFstFQ3IsN+TRmxur2F7y6Faplb5ybISa8ithu1tmlTdyTf3A78hQUQjgJet6qD2XZw==", + "node_modules/@algolia/client-personalization": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.14.2.tgz", + "integrity": "sha512-ACCoLi0cL8CBZ1W/2juehSltrw2iqsQBnfiu/Rbl9W2yE6o2ZUb97+sqN/jBqYNQBS+o0ekTMKNkQjHHAcEXNw==", "dependencies": { - "@algolia/client-common": "4.9.1", - "@algolia/requester-common": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/client-common": "4.14.2", + "@algolia/requester-common": "4.14.2", + "@algolia/transporter": "4.14.2" } }, "node_modules/@algolia/client-search": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.9.1.tgz", - "integrity": "sha512-r9Cw2r8kJr45iYncFDht6EshARghU265wuY8Q8oHrpFHjAziEYdsUOdNmQKbsSH5J3gLjDPx1EI5DzVd6ivn3w==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.14.2.tgz", + "integrity": "sha512-L5zScdOmcZ6NGiVbLKTvP02UbxZ0njd5Vq9nJAmPFtjffUSOGEp11BmD2oMJ5QvARgx2XbX4KzTTNS5ECYIMWw==", "dependencies": { - "@algolia/client-common": "4.9.1", - "@algolia/requester-common": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/client-common": "4.14.2", + "@algolia/requester-common": "4.14.2", + "@algolia/transporter": "4.14.2" } }, + "node_modules/@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, "node_modules/@algolia/logger-common": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.9.1.tgz", - "integrity": "sha512-9mPrbFlFyPT7or/7PXTiJjyOewWB9QRkZKVXkt5zHAUiUzGxmmdpJIGpPv3YQnDur8lXrXaRI0MHXUuIDMY1ng==" + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.14.2.tgz", + "integrity": "sha512-/JGlYvdV++IcMHBnVFsqEisTiOeEr6cUJtpjz8zc0A9c31JrtLm318Njc72p14Pnkw3A/5lHHh+QxpJ6WFTmsA==" }, "node_modules/@algolia/logger-console": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.9.1.tgz", - "integrity": "sha512-74VUwjtFjFpjZpi3QoHIPv0kcr3vWUSHX/Vs8PJW3lPsD4CgyhFenQbG9v+ZnyH0JrJwiYTtzfmrVh7IMWZGrQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.14.2.tgz", + "integrity": "sha512-8S2PlpdshbkwlLCSAB5f8c91xyc84VM9Ar9EdfE9UmX+NrKNYnWR1maXXVDQQoto07G1Ol/tYFnFVhUZq0xV/g==", "dependencies": { - "@algolia/logger-common": "4.9.1" + "@algolia/logger-common": "4.14.2" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.9.1.tgz", - "integrity": "sha512-zc46tk5o0ikOAz3uYiRAMxC2iVKAMFKT7nNZnLB5IzT0uqAh7pz/+D/UvIxP4bKmsllpBSnPcpfQF+OI4Ag/BA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.2.tgz", + "integrity": "sha512-CEh//xYz/WfxHFh7pcMjQNWgpl4wFB85lUMRyVwaDPibNzQRVcV33YS+63fShFWc2+42YEipFGH2iPzlpszmDw==", "dependencies": { - "@algolia/requester-common": "4.9.1" + "@algolia/requester-common": "4.14.2" } }, "node_modules/@algolia/requester-common": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.9.1.tgz", - "integrity": "sha512-9hPgXnlCSbqJqF69M5x5WN3h51Dc+mk/iWNeJSVxExHGvCDfBBZd0v6S15i8q2a9cD1I2RnhMpbnX5BmGtabVA==" + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.14.2.tgz", + "integrity": "sha512-73YQsBOKa5fvVV3My7iZHu1sUqmjjfs9TteFWwPwDmnad7T0VTCopttcsM3OjLxZFtBnX61Xxl2T2gmG2O4ehg==" }, "node_modules/@algolia/requester-node-http": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.9.1.tgz", - "integrity": "sha512-vYNVbSCuyrCSCjHBQJk+tLZtWCjvvDf5tSbRJjyJYMqpnXuIuP7gZm24iHil4NPYBhbBj5NU2ZDAhc/gTn75Ag==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.14.2.tgz", + "integrity": "sha512-oDbb02kd1o5GTEld4pETlPZLY0e+gOSWjWMJHWTgDXbv9rm/o2cF7japO6Vj1ENnrqWvLBmW1OzV9g6FUFhFXg==", "dependencies": { - "@algolia/requester-common": "4.9.1" + "@algolia/requester-common": "4.14.2" } }, "node_modules/@algolia/transporter": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.9.1.tgz", - "integrity": "sha512-AbjFfGzX+cAuj7Qyc536OxIQzjFOA5FU2ANGStx8LBH+AKXScwfkx67C05riuaRR5adSCLMSEbVvUscH0nF+6A==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.14.2.tgz", + "integrity": "sha512-t89dfQb2T9MFQHidjHcfhh6iGMNwvuKUvojAj+JsrHAGbuSy7yE4BylhLX6R0Q1xYRoC4Vvv+O5qIw/LdnQfsQ==", + "dependencies": { + "@algolia/cache-common": "4.14.2", + "@algolia/logger-common": "4.14.2", + "@algolia/requester-common": "4.14.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dependencies": { - "@algolia/cache-common": "4.9.1", - "@algolia/logger-common": "4.9.1", - "@algolia/requester-common": "4.9.1" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dependencies": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", - "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==" + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", + "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==", + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/core": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.2.tgz", - "integrity": "sha512-OgC1mON+l4U4B4wiohJlQNUU3H73mpTyYY3j/c8U9dr9UagGGSm+WFpzjy/YLdoyjiG++c1kIDgxCo/mLwQJeQ==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.2", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.2", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", + "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.6", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helpers": "^7.19.4", + "@babel/parser": "^7.19.6", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "json5": "^2.2.1", + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -216,42 +240,67 @@ } }, "node_modules/@babel/generator": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.2.tgz", - "integrity": "sha512-OnADYbKrffDVai5qcpkMxQ7caomHOoEwjkouqnN2QhydAjowFAZcsdecFIRUBdb+ZcruwYE4ythYmF1UBZU5xQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.6.tgz", + "integrity": "sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==", "dependencies": { - "@babel/types": "^7.14.2", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.19.4", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", "dependencies": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", - "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", - "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", + "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", "dependencies": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", + "@babel/compat-data": "^7.19.3", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", "semver": "^6.3.0" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0" } @@ -265,42 +314,47 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.2.tgz", - "integrity": "sha512-6YctwVsmlkchxfGUogvVrrhzyD3grFJyluj5JgDlQrwfMLJSt5tdAzFZfPf4H2Xoi5YLcQ6BxfJlaOBHuctyIw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", + "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz", - "integrity": "sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "regexpu-core": "^4.7.1" + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.0.tgz", - "integrity": "sha512-JT8tHuFjKBo8NnaUbblz7mIu1nnvUDiHVjXXkulZULyidvo/7P6TY7+YqpV37IfF+KUFxmlK04elKtGKXaiVgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dependencies": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2", @@ -318,169 +372,234 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz", - "integrity": "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==", - "dependencies": { - "@babel/types": "^7.13.0" + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", - "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", "dependencies": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.14.2" + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dependencies": { - "@babel/types": "^7.12.13" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz", - "integrity": "sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "dependencies": { - "@babel/traverse": "^7.13.15", - "@babel/types": "^7.13.16" + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", "dependencies": { - "@babel/types": "^7.13.12" + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", - "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "dependencies": { - "@babel/types": "^7.13.12" + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz", - "integrity": "sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", + "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", "dependencies": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.19.4", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", "dependencies": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz", - "integrity": "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-wrap-function": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", - "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", - "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", + "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", "dependencies": { - "@babel/types": "^7.13.12" + "@babel/types": "^7.19.4" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", "dependencies": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "dependencies": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==" + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz", - "integrity": "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", - "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", + "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", "dependencies": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.4", + "@babel/types": "^7.19.4" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", + "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/highlight/node_modules/ansi-styles": { @@ -518,12 +637,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "engines": { "node": ">=0.8.0" } @@ -531,7 +650,7 @@ "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "engines": { "node": ">=4" } @@ -548,9 +667,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.2.tgz", - "integrity": "sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.6.tgz", + "integrity": "sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==", "bin": { "parser": "bin/babel-parser.js" }, @@ -558,201 +677,262 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz", - "integrity": "sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.13.12" + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.2.tgz", - "integrity": "sha512-b1AM4F6fwck4N8ItZ/AtC4FP/cqZqmKRQ4FaTDutwSYyjuhtvsGEMLK4N/ztV/ImP40BjIDyMgBQAeAMsQYVFQ==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz", + "integrity": "sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", - "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.13.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.13.11.tgz", - "integrity": "sha512-fJTdFI4bfnMjvxJyNuaf8i9mVcZ0UhetaGEUHaHV9KEnibLugJkZAtXikR8KcYj+NYmI4DZMS8yQAyg+hvfSqg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-class-static-block": "^7.12.13" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.2.tgz", - "integrity": "sha512-oxVQZIWFh91vuNEMKltqNsKLFWkOIyJc95k2Gv9lWVyDfPUQGSSlbDEgWuJUU1afGE9WwlzpucMZ3yDRHIItkA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.2.tgz", - "integrity": "sha512-sRxW3z3Zp3pFfLAgVEvzTFutTXax837oOatUIvSG9o5gRj9mKwm3br1Se5f4QalTQs9x4AzlA/HrCWbQIHASUQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.2.tgz", - "integrity": "sha512-w2DtsfXBBJddJacXMBhElGEYqCZQqN99Se1qeYn8DVLB33owlrlLftIbMzn5nz1OITfDVknXF433tBrLEAOEjA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.2.tgz", - "integrity": "sha512-1JAZtUrqYyGsS7IDmFeaem+/LJqujfLZ2weLR9ugB0ufUPjzf8cguyVT1g5im7f7RXxuLq1xUxEzvm68uYRtGg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.2.tgz", - "integrity": "sha512-ebR0zU9OvI2N4qiAC38KIAK75KItpIPTpAtd2r4OZmMFeKbKJpUFLYP2EuDut82+BmYi8sz42B+TfTptJ9iG5Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.2.tgz", - "integrity": "sha512-DcTQY9syxu9BpU3Uo94fjCB3LN9/hgPS8oUL7KrSW3bA2ePrKZZPJcc5y0hoJAM9dft3pGfErtEUvxXQcfLxUg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.2.tgz", - "integrity": "sha512-hBIQFxwZi8GIp934+nj5uV31mqclC1aYDhctDu5khTi9PCCUOczyy0b34W0oE9U/eJXiqQaKyVsmjeagOaSlbw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz", + "integrity": "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==", "dependencies": { - "@babel/compat-data": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.14.2" + "@babel/plugin-transform-parameters": "^7.18.8" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.2.tgz", - "integrity": "sha512-XtkJsmJtBaUbOxZsNk0Fvrv8eiqgneug0A6aqLFZ4TSkar2L5dSXWcnUKHgmjJt49pyB/6ZHvkr3dPgl9MOWRQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.2.tgz", - "integrity": "sha512-qQByMRPwMZJainfig10BoaDldx/+VDtNcrA7qdNaEOAj6VXud+gfrkA8j4CRAU5HjnWREXqIpSpH30qZX1xivA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz", - "integrity": "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz", - "integrity": "sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-create-class-features-plugin": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-private-property-in-object": "^7.14.0" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", - "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=4" @@ -784,11 +964,14 @@ } }, "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.12.13.tgz", - "integrity": "sha512-ZmKQ0ZXR0nYpHZIIuj9zE7oIqCx2hw9TKi+lIo73NNrMPAZGHfS92/VRV0ZmPj6H2ffBgyFHXvJ5NYsNeEaP2A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -816,6 +999,20 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", @@ -828,11 +1025,14 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", - "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -905,431 +1105,511 @@ } }, "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz", - "integrity": "sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", - "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz", - "integrity": "sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", - "integrity": "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz", - "integrity": "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", "dependencies": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0" + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", - "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.2.tgz", - "integrity": "sha512-neZZcP19NugZZqNwMTH+KoBjx5WyvESPSIOQb4JHpfd+zPfqcH65RMu5xJju5+6q/Y2VzYrleQTr+b6METyyxg==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz", + "integrity": "sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.2.tgz", - "integrity": "sha512-7oafAVcucHquA/VZCsXv/gmuiHeYd64UJyyTYU+MPfNu0KeNlxw06IeENBO8bJjXVbolu+j1MM5aKQtH1OMCNg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", + "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz", - "integrity": "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.13.17", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", - "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz", + "integrity": "sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", - "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", - "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", - "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz", - "integrity": "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==", + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", - "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", - "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", - "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.2.tgz", - "integrity": "sha512-hPC6XBswt8P3G2D1tSV2HzdKvkqOpmbyoy+g73JG0qlF/qx2y3KaMmXb1fLrpmWGLZYA0ojCvaHdzFWjlmV+Pw==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", "dependencies": { - "@babel/helper-module-transforms": "^7.14.2", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-amd/node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dependencies": { - "object.assign": "^4.1.0" - } - }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz", - "integrity": "sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-simple-access": "^7.13.12", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-commonjs/node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dependencies": { - "object.assign": "^4.1.0" - } - }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz", - "integrity": "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", "dependencies": { - "@babel/helper-hoist-variables": "^7.13.0", - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-identifier": "^7.12.11", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-systemjs/node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dependencies": { - "object.assign": "^4.1.0" - } - }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz", - "integrity": "sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", "dependencies": { - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", - "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", - "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", - "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.2.tgz", - "integrity": "sha512-NxoVmA3APNCC1JdMXkdYXuQS+EMdqy0vIwyDHeKHiJKRxmp1qGSdb0JLEIoPRhkx6H/8Qi3RJ3uqOCYw8giy9A==", + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", - "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.13.13.tgz", - "integrity": "sha512-SNJU53VM/SjQL0bZhyU+f4kJQz7bQQajnrZRSaU21hruG/NWY41AEM9AWXeXX90pYr/C2yAmTgI6yW3LlLrAUQ==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.20.2.tgz", + "integrity": "sha512-KS/G8YI8uwMGKErLFOHS/ekhqdHhpEloxs43NecQHVgo2QuQSyJhGIY1fL8UGl9wy5ItVwwoUL4YxVqsplGq2g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.2.tgz", - "integrity": "sha512-zCubvP+jjahpnFJvPaHPiGVfuVUjXHhFvJKQdNnsmSsiU9kR/rCZ41jHc++tERD2zV+p7Hr6is+t5b6iWTCqSw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz", - "integrity": "sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/types": "^7.13.12" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.17.tgz", - "integrity": "sha512-BPjYV86SVuOaudFhsJR1zjgxxOhJDt6JHNoD48DxWEIxUCAMjV1ys6DYw4SDYZh0b1QsS2vfIA9t/ZsQGsDOUQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.12.17" + "@babel/plugin-transform-react-jsx": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz", - "integrity": "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz", - "integrity": "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", "dependencies": { - "regenerator-transform": "^0.14.2" + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", - "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.2.tgz", - "integrity": "sha512-LyA2AiPkaYzI7G5e2YI4NCasTfFe7mZvlupNprDOB7CdNUHb2DQC4uV6oeZ0396gOcicUzUCh0MShL6wiUgk+Q==", - "dependencies": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-polyfill-corejs2": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^0.2.0", - "babel-plugin-polyfill-regenerator": "^0.2.0", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", "semver": "^6.3.0" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } @@ -1343,127 +1623,153 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", - "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz", - "integrity": "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", - "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz", - "integrity": "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", - "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.13.0.tgz", - "integrity": "sha512-elQEwluzaU8R8dbVuW2Q2Y8Nznf7hnjM7+DSCd14Lo5fF63C9qNLbwZYbmZrtV9/ySpSUpkRpQXvJb6xyu4hCQ==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.3.tgz", + "integrity": "sha512-z6fnuK9ve9u/0X0rRvI9MY0xg+DOUaABDYOe+/SQTxtlptaBB/V9JIUxJn6xp3lMBeb9qe8xSFmHU35oZDXD+w==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-typescript": "^7.12.13" + "@babel/helper-create-class-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-typescript": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", - "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", - "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/preset-env": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.2.tgz", - "integrity": "sha512-7dD7lVT8GMrE73v4lvDEb85cgcQhdES91BSD7jS/xjC6QY8PnRhux35ac+GCpbiRhp8crexBvZZqnaL6VrY8TQ==", - "dependencies": { - "@babel/compat-data": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-async-generator-functions": "^7.14.2", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-class-static-block": "^7.13.11", - "@babel/plugin-proposal-dynamic-import": "^7.14.2", - "@babel/plugin-proposal-export-namespace-from": "^7.14.2", - "@babel/plugin-proposal-json-strings": "^7.14.2", - "@babel/plugin-proposal-logical-assignment-operators": "^7.14.2", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.2", - "@babel/plugin-proposal-numeric-separator": "^7.14.2", - "@babel/plugin-proposal-object-rest-spread": "^7.14.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.14.2", - "@babel/plugin-proposal-optional-chaining": "^7.14.2", - "@babel/plugin-proposal-private-methods": "^7.13.0", - "@babel/plugin-proposal-private-property-in-object": "^7.14.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.19.4.tgz", + "integrity": "sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==", + "dependencies": { + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.19.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.19.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -1471,48 +1777,51 @@ "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.0", - "@babel/plugin-syntax-top-level-await": "^7.12.13", - "@babel/plugin-transform-arrow-functions": "^7.13.0", - "@babel/plugin-transform-async-to-generator": "^7.13.0", - "@babel/plugin-transform-block-scoped-functions": "^7.12.13", - "@babel/plugin-transform-block-scoping": "^7.14.2", - "@babel/plugin-transform-classes": "^7.14.2", - "@babel/plugin-transform-computed-properties": "^7.13.0", - "@babel/plugin-transform-destructuring": "^7.13.17", - "@babel/plugin-transform-dotall-regex": "^7.12.13", - "@babel/plugin-transform-duplicate-keys": "^7.12.13", - "@babel/plugin-transform-exponentiation-operator": "^7.12.13", - "@babel/plugin-transform-for-of": "^7.13.0", - "@babel/plugin-transform-function-name": "^7.12.13", - "@babel/plugin-transform-literals": "^7.12.13", - "@babel/plugin-transform-member-expression-literals": "^7.12.13", - "@babel/plugin-transform-modules-amd": "^7.14.2", - "@babel/plugin-transform-modules-commonjs": "^7.14.0", - "@babel/plugin-transform-modules-systemjs": "^7.13.8", - "@babel/plugin-transform-modules-umd": "^7.14.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", - "@babel/plugin-transform-new-target": "^7.12.13", - "@babel/plugin-transform-object-super": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.14.2", - "@babel/plugin-transform-property-literals": "^7.12.13", - "@babel/plugin-transform-regenerator": "^7.13.15", - "@babel/plugin-transform-reserved-words": "^7.12.13", - "@babel/plugin-transform-shorthand-properties": "^7.12.13", - "@babel/plugin-transform-spread": "^7.13.0", - "@babel/plugin-transform-sticky-regex": "^7.12.13", - "@babel/plugin-transform-template-literals": "^7.13.0", - "@babel/plugin-transform-typeof-symbol": "^7.12.13", - "@babel/plugin-transform-unicode-escapes": "^7.12.13", - "@babel/plugin-transform-unicode-regex": "^7.12.13", - "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.14.2", - "babel-plugin-polyfill-corejs2": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^0.2.0", - "babel-plugin-polyfill-regenerator": "^0.2.0", - "core-js-compat": "^3.9.0", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.19.4", + "@babel/plugin-transform-classes": "^7.19.0", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.19.4", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.0", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.19.4", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" }, + "engines": { + "node": ">=6.9.0" + }, "peerDependencies": { "@babel/core": "^7.0.0-0" } @@ -1526,9 +1835,9 @@ } }, "node_modules/@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", - "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", @@ -1541,221 +1850,342 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.13.13.tgz", - "integrity": "sha512-gx+tDLIE06sRjKJkVtpZ/t3mzCDOnPG+ggHZG9lffUbX8+wC739x20YQc9V35Do6ZAxaUc/HhVHIiOzz5MvDmA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-transform-react-display-name": "^7.12.13", - "@babel/plugin-transform-react-jsx": "^7.13.12", - "@babel/plugin-transform-react-jsx-development": "^7.12.17", - "@babel/plugin-transform-react-pure-annotations": "^7.12.1" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/preset-typescript": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.13.0.tgz", - "integrity": "sha512-LXJwxrHy0N3f6gIJlYbLta1D9BDtHpQeqwzM0LIfjDlr6UE/D5Mc7W4iDiQzaE+ks0sTjT26ArcHWnJVt0QiHw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-transform-typescript": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/runtime": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz", - "integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", + "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", "dependencies": { "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz", - "integrity": "sha512-0R0HTZWHLk6G8jIk0FtoX+AatCtKnswS98VhXwGImFc759PJRp4Tru0PQYZofyijTFUr+gT8Mu7sgXVJLQ0ceg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.6.tgz", + "integrity": "sha512-oWNn1ZlGde7b4i/3tnixpH9qI0bOAACiUs+KEES4UUCnsPjVWFlWdLV/iwJuPC2qp3EowbAqsm+0XqNwnwYhxA==", "dependencies": { - "core-js-pure": "^3.0.0", + "core-js-pure": "^3.25.1", "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", - "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.2", - "@babel/types": "^7.14.2", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.6.tgz", + "integrity": "sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.19.6", + "@babel/types": "^7.19.4", "debug": "^4.1.0", "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", - "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "optional": true, + "engines": { + "node": ">=0.1.90" } }, "node_modules/@crowdin/cli": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@crowdin/cli/-/cli-3.6.1.tgz", - "integrity": "sha512-RUKFrPCX3R1MJPyRpBSqWFwmjbDlVWLHtXAzx2FPeyjnyMXrXLPJ8YEl4t8YU+96q/0t46qTdmMLeQmYyDEvGQ==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@crowdin/cli/-/cli-3.9.0.tgz", + "integrity": "sha512-4wQjqJZmU/mg3VYfRL6IYXw/pPAL9vdfW3QVSBovYA+bYaEt43ZuGsSrqeBGOhLehasWwRqklXWsl96gxQlLdw==", "dependencies": { + "njre": "^0.2.0", "shelljs": "^0.8.4" }, "bin": { "crowdin": "jdeploy-bundle/jdeploy.js" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@docsearch/css": { - "version": "3.0.0-alpha.36", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.0.0-alpha.36.tgz", - "integrity": "sha512-zSN2SXuZPDqQaSFzYa1kOwToukqzhLHG7c66iO+/PlmWb6/RZ5cjTkG6VCJynlohRWea7AqZKWS/ptm8kM2Dmg==" + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.2.2.tgz", + "integrity": "sha512-VB0Evx4ikS1ZlW1YVUw+vI9b3H/UXMCo4W/ZWy+n56Sho4KOqyCHcINVays91TJt7HTV/CKO3FCbm2VJg5Wipw==" }, "node_modules/@docsearch/react": { - "version": "3.0.0-alpha.36", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.0.0-alpha.36.tgz", - "integrity": "sha512-synYZDHalvMzesFiy7kK+uoz4oTdWSTbe2cU+iiUjwFMyQ+WWjWwGVnvcvk+cjj9pRCVaZo5y5WpqNXq1j8k9Q==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.2.2.tgz", + "integrity": "sha512-1Hn2SNQUFVPrzqvaj+vxXZfsfn3rnW8CoyGAJ1LqXMY9py8GbxK8VfmJ5Z6z4LwG9849tGru/N6dp0cQO6r9Ag==", "dependencies": { - "@algolia/autocomplete-core": "1.0.0-alpha.44", - "@algolia/autocomplete-preset-algolia": "1.0.0-alpha.44", - "@docsearch/css": "3.0.0-alpha.36", + "@algolia/autocomplete-core": "1.7.1", + "@algolia/autocomplete-preset-algolia": "1.7.1", + "@docsearch/css": "3.2.2", "algoliasearch": "^4.0.0" }, "peerDependencies": { - "@types/react": ">= 16.8.0 < 18.0.0", - "react": ">= 16.8.0 < 18.0.0", - "react-dom": ">= 16.8.0 < 18.0.0" + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, "node_modules/@docusaurus/core": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.0.0-beta.0.tgz", - "integrity": "sha512-xWwpuEwFRKJmZvNGOpr/dyRDnx/psckLPsozQTg2hu3u81Wqu9gigWgYK/C2fPlEjxMcVw0/2WH+zwpbyWmF2Q==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.2.0.tgz", + "integrity": "sha512-Vd6XOluKQqzG12fEs9prJgDtyn6DPok9vmUWDR2E6/nV5Fl9SVkhEQOBxwObjk3kQh7OY7vguFaLh0jqdApWsA==", "dependencies": { - "@babel/core": "^7.12.16", - "@babel/generator": "^7.12.15", + "@babel/core": "^7.18.6", + "@babel/generator": "^7.18.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.12.15", - "@babel/preset-env": "^7.12.16", - "@babel/preset-react": "^7.12.13", - "@babel/preset-typescript": "^7.12.16", - "@babel/runtime": "^7.12.5", - "@babel/runtime-corejs3": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@docusaurus/cssnano-preset": "2.0.0-beta.0", - "@docusaurus/react-loadable": "5.5.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "@endiliey/static-site-generator-webpack-plugin": "^4.0.0", - "@svgr/webpack": "^5.5.0", - "autoprefixer": "^10.2.5", - "babel-loader": "^8.2.2", - "babel-plugin-dynamic-import-node": "2.3.0", - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "chokidar": "^3.5.1", - "clean-css": "^5.1.1", + "@babel/plugin-transform-runtime": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@babel/runtime": "^7.18.6", + "@babel/runtime-corejs3": "^7.18.6", + "@babel/traverse": "^7.18.8", + "@docusaurus/cssnano-preset": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-common": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "@slorber/static-site-generator-webpack-plugin": "^4.0.7", + "@svgr/webpack": "^6.2.1", + "autoprefixer": "^10.4.7", + "babel-loader": "^8.2.5", + "babel-plugin-dynamic-import-node": "^2.3.3", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "clean-css": "^5.3.0", + "cli-table3": "^0.6.2", + "combine-promises": "^1.1.0", "commander": "^5.1.0", - "copy-webpack-plugin": "^8.1.0", - "core-js": "^3.9.1", - "css-loader": "^5.1.1", - "css-minimizer-webpack-plugin": "^2.0.0", - "cssnano": "^5.0.1", - "del": "^6.0.0", + "copy-webpack-plugin": "^11.0.0", + "core-js": "^3.23.3", + "css-loader": "^6.7.1", + "css-minimizer-webpack-plugin": "^4.0.0", + "cssnano": "^5.1.12", + "del": "^6.1.1", "detect-port": "^1.3.0", - "eta": "^1.12.1", - "express": "^4.17.1", + "escape-html": "^1.0.3", + "eta": "^1.12.3", "file-loader": "^6.2.0", - "fs-extra": "^9.1.0", - "github-slugger": "^1.3.0", - "globby": "^11.0.2", - "html-minifier-terser": "^5.1.1", - "html-tags": "^3.1.0", - "html-webpack-plugin": "^5.2.0", + "fs-extra": "^10.1.0", + "html-minifier-terser": "^6.1.0", + "html-tags": "^3.2.0", + "html-webpack-plugin": "^5.5.0", "import-fresh": "^3.3.0", - "is-root": "^2.1.0", "leven": "^3.1.0", - "lodash": "^4.17.20", - "mini-css-extract-plugin": "^1.4.0", - "module-alias": "^2.2.2", - "nprogress": "^0.2.0", - "postcss": "^8.2.10", - "postcss-loader": "^5.2.0", - "prompts": "^2.4.0", - "react-dev-utils": "^11.0.1", - "react-error-overlay": "^6.0.9", - "react-helmet": "^6.1.0", - "react-loadable": "^5.5.0", + "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.4.14", + "postcss-loader": "^7.0.0", + "prompts": "^2.4.2", + "react-dev-utils": "^12.0.1", + "react-helmet-async": "^1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", "react-loadable-ssr-addon-v5-slorber": "^1.0.1", - "react-router": "^5.2.0", + "react-router": "^5.3.3", "react-router-config": "^5.1.1", - "react-router-dom": "^5.2.0", - "resolve-pathname": "^3.0.0", - "rtl-detect": "^1.0.2", - "semver": "^7.3.4", + "react-router-dom": "^5.3.3", + "rtl-detect": "^1.0.4", + "semver": "^7.3.7", "serve-handler": "^6.1.3", - "shelljs": "^0.8.4", - "std-env": "^2.2.1", - "strip-ansi": "^6.0.0", - "terser-webpack-plugin": "^5.1.1", - "tslib": "^2.1.0", + "shelljs": "^0.8.5", + "terser-webpack-plugin": "^5.3.3", + "tslib": "^2.4.0", "update-notifier": "^5.1.0", "url-loader": "^4.1.1", - "wait-on": "^5.2.1", - "webpack": "^5.28.0", - "webpack-bundle-analyzer": "^4.4.0", - "webpack-dev-server": "^3.11.2", - "webpack-merge": "^5.7.3", - "webpackbar": "^5.0.0-3" + "wait-on": "^6.0.1", + "webpack": "^5.73.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-dev-server": "^4.9.3", + "webpack-merge": "^5.8.0", + "webpackbar": "^5.0.2" }, "bin": { - "docusaurus": "bin/docusaurus.js" + "docusaurus": "bin/docusaurus.mjs" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" } }, + "node_modules/@docusaurus/core/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@docusaurus/core/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@docusaurus/core/node_modules/boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@docusaurus/core/node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@docusaurus/core/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, "node_modules/@docusaurus/core/node_modules/postcss-loader": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-5.3.0.tgz", - "integrity": "sha512-/+Z1RAmssdiSLgIZwnJHwBMnlABPgF7giYzTN2NOfr9D21IJZ4mQC1R2miwp80zno9M4zMD/umGI8cR+2EL5zw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz", + "integrity": "sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==", "dependencies": { "cosmiconfig": "^7.0.0", - "klona": "^2.0.4", - "semver": "^7.3.4" + "klona": "^2.0.5", + "semver": "^7.3.8" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", @@ -1766,3466 +2196,3944 @@ "webpack": "^5.0.0" } }, - "node_modules/@docusaurus/cssnano-preset": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.0.tgz", - "integrity": "sha512-gqQHeQCDHZDd5NaiKZwDiyg75sBCqDyAsvmFukkDAty8xE7u9IhzbOQKvCAtwseuvzu2BNN41gnJ8bz7vZzQiw==", + "node_modules/@docusaurus/core/node_modules/react-loadable": { + "name": "@docusaurus/react-loadable", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", "dependencies": { - "cssnano-preset-advanced": "^5.0.0", - "postcss": "^8.2.10", - "postcss-sort-media-queries": "^3.8.9" + "@types/react": "*", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "*" } }, - "node_modules/@docusaurus/mdx-loader": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.0.tgz", - "integrity": "sha512-oQLS2ZeUnqw79CV37glglZpaYgFfA5Az5lT83m5tJfMUZjoK4ehG1XWBeUzWy8QQNI452yAID8jz8jihEQeCcw==", - "dependencies": { - "@babel/parser": "^7.12.16", - "@babel/traverse": "^7.12.13", - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@mdx-js/mdx": "^1.6.21", - "@mdx-js/react": "^1.6.21", - "escape-html": "^1.0.3", - "file-loader": "^6.2.0", - "fs-extra": "^9.1.0", - "github-slugger": "^1.3.0", - "gray-matter": "^4.0.2", - "mdast-util-to-string": "^2.0.0", - "remark-emoji": "^2.1.0", - "stringify-object": "^3.3.0", - "unist-util-visit": "^2.0.2", - "url-loader": "^4.1.1", - "webpack": "^5.28.0" + "node_modules/@docusaurus/core/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=12.13.0" + "node": ">=12" }, - "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@docusaurus/plugin-content-blog": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.0.tgz", - "integrity": "sha512-lz63i5k/23RJ3Rk/2fIsYAoD8Wua3b5b0AbH2JoOhQu1iAIQiV8m91Z3XALBSzA3nBtAOIweNI7yzWL+JFSTvw==", - "dependencies": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/mdx-loader": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "chalk": "^4.1.0", - "feed": "^4.2.2", - "fs-extra": "^9.1.0", - "globby": "^11.0.2", - "loader-utils": "^2.0.0", - "lodash": "^4.17.20", - "reading-time": "^1.3.0", - "remark-admonitions": "^1.2.1", - "tslib": "^2.1.0", - "webpack": "^5.28.0" + "node_modules/@docusaurus/core/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dependencies": { + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=12.13.0" + "node": ">=12" }, - "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@docusaurus/plugin-content-docs": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.0.tgz", - "integrity": "sha512-WdDQUh2rRCbfJswVc0vY9EaAspxgziqpVEZja8+BmQR/TZh7HuLplT6GJbiFbE4RvwM3+PwG/jHMPglYDK60kw==", - "dependencies": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/mdx-loader": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "chalk": "^4.1.0", - "combine-promises": "^1.1.0", - "execa": "^5.0.0", - "fs-extra": "^9.1.0", - "globby": "^11.0.2", - "import-fresh": "^3.2.2", - "js-yaml": "^4.0.0", - "loader-utils": "^1.2.3", - "lodash": "^4.17.20", - "remark-admonitions": "^1.2.1", - "shelljs": "^0.8.4", - "tslib": "^2.1.0", - "utility-types": "^3.10.0", - "webpack": "^5.28.0" + "node_modules/@docusaurus/core/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@docusaurus/core/node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dependencies": { + "string-width": "^5.0.1" }, "engines": { - "node": ">=12.13.0" + "node": ">=12" }, - "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@docusaurus/plugin-content-docs/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "node_modules/@docusaurus/core/node_modules/wrap-ansi": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", + "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", "dependencies": { - "minimist": "^1.2.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@docusaurus/plugin-content-docs/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "node_modules/@docusaurus/cssnano-preset": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.2.0.tgz", + "integrity": "sha512-mAAwCo4n66TMWBH1kXnHVZsakW9VAXJzTO4yZukuL3ro4F+JtkMwKfh42EG75K/J/YIFQG5I/Bzy0UH/hFxaTg==", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" + "cssnano-preset-advanced": "^5.3.8", + "postcss": "^8.4.14", + "postcss-sort-media-queries": "^4.2.1", + "tslib": "^2.4.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=16.14" } }, - "node_modules/@docusaurus/plugin-content-pages": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.0.tgz", - "integrity": "sha512-mk5LVVSvn+HJPKBaAs/Pceq/hTGxF2LVBvJEquuQz0NMAW3QdBWaYRRpOrL9CO8v+ygn5RuLslXsyZBsDNuhww==", - "dependencies": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/mdx-loader": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "globby": "^11.0.2", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "remark-admonitions": "^1.2.1", - "slash": "^3.0.0", - "tslib": "^2.1.0", - "webpack": "^5.28.0" + "node_modules/@docusaurus/logger": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.2.0.tgz", + "integrity": "sha512-DF3j1cA5y2nNsu/vk8AG7xwpZu6f5MKkPPMaaIbgXLnWGfm6+wkOeW7kNrxnM95YOhKUkJUophX69nGUnLsm0A==", + "dependencies": { + "chalk": "^4.1.2", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/mdx-loader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.2.0.tgz", + "integrity": "sha512-X2bzo3T0jW0VhUU+XdQofcEeozXOTmKQMvc8tUnWRdTnCvj4XEcBVdC3g+/jftceluiwSTNRAX4VBOJdNt18jA==", + "dependencies": { + "@babel/parser": "^7.18.8", + "@babel/traverse": "^7.18.8", + "@docusaurus/logger": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@mdx-js/mdx": "^1.6.22", + "escape-html": "^1.0.3", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "image-size": "^1.0.1", + "mdast-util-to-string": "^2.0.0", + "remark-emoji": "^2.2.0", + "stringify-object": "^3.3.0", + "tslib": "^2.4.0", + "unified": "^9.2.2", + "unist-util-visit": "^2.0.3", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/plugin-debug": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.0.tgz", - "integrity": "sha512-m75sZdF8Yccxfih3qfdQg9DucMTrYBnmeTA8GNmdVaK701Ip8t50d1pDJchtu0FSEh6vzVB9C6D2YD5YgVFp8A==", - "dependencies": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "react-json-view": "^1.21.1", - "tslib": "^2.1.0" + "node_modules/@docusaurus/mdx-loader/node_modules/unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" }, - "engines": { - "node": ">=12.13.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@docusaurus/module-type-aliases": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.2.0.tgz", + "integrity": "sha512-wDGW4IHKoOr9YuJgy7uYuKWrDrSpsUSDHLZnWQYM9fN7D5EpSmYHjFruUpKWVyxLpD/Wh0rW8hYZwdjJIQUQCQ==", + "dependencies": { + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/types": "2.2.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" }, "peerDependencies": { - "react": "^16.8.4 || ^17.0.0", - "react-dom": "^16.8.4 || ^17.0.0" + "react": "*", + "react-dom": "*" } }, - "node_modules/@docusaurus/plugin-google-analytics": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.0.tgz", - "integrity": "sha512-7lHrg1L+adc8VbiaLexa15i4fdq4MRPUTLMxRPAWz+QskhisW89Ryi2/gDmfMNqLblX84Qg2RASa+2gqO4wepw==", + "node_modules/@docusaurus/module-type-aliases/node_modules/react-loadable": { + "name": "@docusaurus/react-loadable", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", "dependencies": { - "@docusaurus/core": "2.0.0-beta.0" + "@types/react": "*", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/@docusaurus/plugin-content-blog": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.2.0.tgz", + "integrity": "sha512-0mWBinEh0a5J2+8ZJXJXbrCk1tSTNf7Nm4tYAl5h2/xx+PvH/Bnu0V+7mMljYm/1QlDYALNIIaT/JcoZQFUN3w==", + "dependencies": { + "@docusaurus/core": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-common": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "cheerio": "^1.0.0-rc.12", + "feed": "^4.2.2", + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "reading-time": "^1.5.0", + "tslib": "^2.4.0", + "unist-util-visit": "^2.0.3", + "utility-types": "^3.10.0", + "webpack": "^5.73.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/plugin-google-gtag": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.0.tgz", - "integrity": "sha512-V7zaYbhAMv0jexm5H/5sAnoM1GHibcn9QQk5UWC++x1kE0KRuLDZHV+9OyvW5wr0wWFajod/b88SpUpSMF5u+g==", - "dependencies": { - "@docusaurus/core": "2.0.0-beta.0" + "node_modules/@docusaurus/plugin-content-docs": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.2.0.tgz", + "integrity": "sha512-BOazBR0XjzsHE+2K1wpNxz5QZmrJgmm3+0Re0EVPYFGW8qndCWGNtXW/0lGKhecVPML8yyFeAmnUCIs7xM2wPw==", + "dependencies": { + "@docusaurus/core": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/module-type-aliases": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "@types/react-router-config": "^5.0.6", + "combine-promises": "^1.1.0", + "fs-extra": "^10.1.0", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/plugin-sitemap": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.0.tgz", - "integrity": "sha512-dvmk8Sr+6pBkiKDb7Rjdp0GeFDWPUlayoJWK3fN3g0Fno6uxFfYhNZyXJ+ObyCA7HoW5rzeBMiO+uAja19JXTg==", - "dependencies": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "fs-extra": "^9.1.0", - "sitemap": "^6.3.6", - "tslib": "^2.1.0" + "node_modules/@docusaurus/plugin-content-pages": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.2.0.tgz", + "integrity": "sha512-+OTK3FQHk5WMvdelz8v19PbEbx+CNT6VSpx7nVOvMNs5yJCKvmqBJBQ2ZSxROxhVDYn+CZOlmyrC56NSXzHf6g==", + "dependencies": { + "@docusaurus/core": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "fs-extra": "^10.1.0", + "tslib": "^2.4.0", + "webpack": "^5.73.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/preset-classic": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.0.tgz", - "integrity": "sha512-cFpR0UaAeUt5qVx1bpidhlar6tiRNITIQlxP4bOVsjbxVTZhZ/cNuIz7C+2zFPCuKIflGXdTIQOrucPmd7z51Q==", + "node_modules/@docusaurus/plugin-debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.2.0.tgz", + "integrity": "sha512-p9vOep8+7OVl6r/NREEYxf4HMAjV8JMYJ7Bos5fCFO0Wyi9AZEo0sCTliRd7R8+dlJXZEgcngSdxAUo/Q+CJow==", "dependencies": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/plugin-content-blog": "2.0.0-beta.0", - "@docusaurus/plugin-content-docs": "2.0.0-beta.0", - "@docusaurus/plugin-content-pages": "2.0.0-beta.0", - "@docusaurus/plugin-debug": "2.0.0-beta.0", - "@docusaurus/plugin-google-analytics": "2.0.0-beta.0", - "@docusaurus/plugin-google-gtag": "2.0.0-beta.0", - "@docusaurus/plugin-sitemap": "2.0.0-beta.0", - "@docusaurus/theme-classic": "2.0.0-beta.0", - "@docusaurus/theme-search-algolia": "2.0.0-beta.0" + "@docusaurus/core": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "fs-extra": "^10.1.0", + "react-json-view": "^1.21.3", + "tslib": "^2.4.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/react-loadable": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.0.tgz", - "integrity": "sha512-Ld/kwUE6yATIOTLq3JCsWiTa/drisajwKqBQ2Rw6IcT+sFsKfYek8F2jSH8f68AT73xX97UehduZeCSlnuCBIg==", + "node_modules/@docusaurus/plugin-google-analytics": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.2.0.tgz", + "integrity": "sha512-+eZVVxVeEnV5nVQJdey9ZsfyEVMls6VyWTIj8SmX0k5EbqGvnIfET+J2pYEuKQnDIHxy+syRMoRM6AHXdHYGIg==", "dependencies": { - "prop-types": "^15.6.2" + "@docusaurus/core": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" }, "peerDependencies": { - "react": "*" + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/theme-classic": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.0.tgz", - "integrity": "sha512-cBNtwAyg3be7Gk41FazMtgyibAcfuYaGHhGHIDRsXfc/qp3RhbiGiei7tyh200QT0NgKZxiVQy/r4d0mtjC++Q==", - "dependencies": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/plugin-content-blog": "2.0.0-beta.0", - "@docusaurus/plugin-content-docs": "2.0.0-beta.0", - "@docusaurus/plugin-content-pages": "2.0.0-beta.0", - "@docusaurus/theme-common": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "@mdx-js/mdx": "^1.6.21", - "@mdx-js/react": "^1.6.21", - "chalk": "^4.1.0", - "clsx": "^1.1.1", - "copy-text-to-clipboard": "^3.0.0", - "fs-extra": "^9.1.0", - "globby": "^11.0.2", - "infima": "0.2.0-alpha.23", - "lodash": "^4.17.20", - "parse-numeric-range": "^1.2.0", - "postcss": "^8.2.10", - "prism-react-renderer": "^1.1.1", - "prismjs": "^1.23.0", - "prop-types": "^15.7.2", - "react-router-dom": "^5.2.0", - "rtlcss": "^3.1.2" + "node_modules/@docusaurus/plugin-google-gtag": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.0.tgz", + "integrity": "sha512-adj/70DANaQs2+TF/nRdMezDXFAV/O/pjAbUgmKBlyOTq5qoMe0Tk4muvQIwWUmiUQxFJe+sKlZGM771ownyOg==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "tslib": "^2.4.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/theme-common": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.0.0-beta.0.tgz", - "integrity": "sha512-2rcVmQpvbdAgnzTWuM7Bfpu+2TQm928bhlvxn226jQy7IYz8ySRlIode63HhCtpx03hpdMCkrK6HxhfEcvHjQg==", - "dependencies": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/plugin-content-blog": "2.0.0-beta.0", - "@docusaurus/plugin-content-docs": "2.0.0-beta.0", - "@docusaurus/plugin-content-pages": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "tslib": "^2.1.0" + "node_modules/@docusaurus/plugin-google-gtag/node_modules/@docusaurus/core": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.4.0.tgz", + "integrity": "sha512-J55/WEoIpRcLf3afO5POHPguVZosKmJEQWKBL+K7TAnfuE7i+Y0NPLlkKtnWCehagGsgTqClfQEexH/UT4kELA==", + "dependencies": { + "@babel/core": "^7.18.6", + "@babel/generator": "^7.18.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@babel/runtime": "^7.18.6", + "@babel/runtime-corejs3": "^7.18.6", + "@babel/traverse": "^7.18.8", + "@docusaurus/cssnano-preset": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "@slorber/static-site-generator-webpack-plugin": "^4.0.7", + "@svgr/webpack": "^6.2.1", + "autoprefixer": "^10.4.7", + "babel-loader": "^8.2.5", + "babel-plugin-dynamic-import-node": "^2.3.3", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "clean-css": "^5.3.0", + "cli-table3": "^0.6.2", + "combine-promises": "^1.1.0", + "commander": "^5.1.0", + "copy-webpack-plugin": "^11.0.0", + "core-js": "^3.23.3", + "css-loader": "^6.7.1", + "css-minimizer-webpack-plugin": "^4.0.0", + "cssnano": "^5.1.12", + "del": "^6.1.1", + "detect-port": "^1.3.0", + "escape-html": "^1.0.3", + "eta": "^2.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "html-minifier-terser": "^6.1.0", + "html-tags": "^3.2.0", + "html-webpack-plugin": "^5.5.0", + "import-fresh": "^3.3.0", + "leven": "^3.1.0", + "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.4.14", + "postcss-loader": "^7.0.0", + "prompts": "^2.4.2", + "react-dev-utils": "^12.0.1", + "react-helmet-async": "^1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", + "react-loadable-ssr-addon-v5-slorber": "^1.0.1", + "react-router": "^5.3.3", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.3", + "rtl-detect": "^1.0.4", + "semver": "^7.3.7", + "serve-handler": "^6.1.3", + "shelljs": "^0.8.5", + "terser-webpack-plugin": "^5.3.3", + "tslib": "^2.4.0", + "update-notifier": "^5.1.0", + "url-loader": "^4.1.1", + "wait-on": "^6.0.1", + "webpack": "^5.73.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-dev-server": "^4.9.3", + "webpack-merge": "^5.8.0", + "webpackbar": "^5.0.2" + }, + "bin": { + "docusaurus": "bin/docusaurus.mjs" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" }, "peerDependencies": { - "prism-react-renderer": "^1.1.1", "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/theme-search-algolia": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.0.tgz", - "integrity": "sha512-/GhgAm4yuwqTXWTsWnqpFYxpjTv+t45Wk8q/LmTVINa+A7b6jkMkch2lygagIt69/ufDm2Uw6eYhgrmF4DJqfQ==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/@docusaurus/cssnano-preset": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.0.tgz", + "integrity": "sha512-RmdiA3IpsLgZGXRzqnmTbGv43W4OD44PCo+6Q/aYjEM2V57vKCVqNzuafE94jv0z/PjHoXUrjr69SaRymBKYYw==", + "dependencies": { + "cssnano-preset-advanced": "^5.3.8", + "postcss": "^8.4.14", + "postcss-sort-media-queries": "^4.2.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/plugin-google-gtag/node_modules/@docusaurus/logger": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.4.0.tgz", + "integrity": "sha512-T8+qR4APN+MjcC9yL2Es+xPJ2923S9hpzDmMtdsOcUGLqpCGBbU1vp3AAqDwXtVgFkq+NsEk7sHdVsfLWR/AXw==", "dependencies": { - "@docsearch/react": "^3.0.0-alpha.33", - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/theme-common": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "algoliasearch": "^4.8.4", - "algoliasearch-helper": "^3.3.4", - "clsx": "^1.1.1", - "eta": "^1.12.1", - "lodash": "^4.17.20" + "chalk": "^4.1.2", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/plugin-google-gtag/node_modules/@docusaurus/mdx-loader": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.4.0.tgz", + "integrity": "sha512-GWoH4izZKOmFoC+gbI2/y8deH/xKLvzz/T5BsEexBye8EHQlwsA7FMrVa48N063bJBH4FUOiRRXxk5rq9cC36g==", + "dependencies": { + "@babel/parser": "^7.18.8", + "@babel/traverse": "^7.18.8", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@mdx-js/mdx": "^1.6.22", + "escape-html": "^1.0.3", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "image-size": "^1.0.1", + "mdast-util-to-string": "^2.0.0", + "remark-emoji": "^2.2.0", + "stringify-object": "^3.3.0", + "tslib": "^2.4.0", + "unified": "^9.2.2", + "unist-util-visit": "^2.0.3", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/types": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.0.0-beta.0.tgz", - "integrity": "sha512-z9PI+GbtYwqTXnkX4/a/A6psDX2p8N2uWlN2f4ifrm8WY4WhR9yiTOh0uo0pIqqaUQQvkEq3o5hOXuXLECEs+w==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/@docusaurus/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.4.0.tgz", + "integrity": "sha512-xaBXr+KIPDkIaef06c+i2HeTqVNixB7yFut5fBXPGI2f1rrmEV2vLMznNGsFwvZ5XmA3Quuefd4OGRkdo97Dhw==", "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", "commander": "^5.1.0", - "joi": "^17.4.0", - "querystring": "0.2.0", - "webpack": "^5.28.0", - "webpack-merge": "^5.7.3" + "joi": "^17.6.0", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0", + "webpack-merge": "^5.8.0" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@docusaurus/utils": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.0.0-beta.0.tgz", - "integrity": "sha512-bvrT1EQu0maavr0Hb/lke9jmpzgVL/9tn5VQtbyahf472eJFY0bQDExllDrHK+l784SUvucqX0iaQeg0q6ySUw==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/@docusaurus/utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.4.0.tgz", + "integrity": "sha512-89hLYkvtRX92j+C+ERYTuSUK6nF9bGM32QThcHPg2EDDHVw6FzYQXmX6/p+pU5SDyyx5nBlE4qXR92RxCAOqfg==", "dependencies": { - "@docusaurus/types": "2.0.0-beta.0", - "@types/github-slugger": "^1.3.0", - "chalk": "^4.1.0", + "@docusaurus/logger": "2.4.0", + "@svgr/webpack": "^6.2.1", "escape-string-regexp": "^4.0.0", - "fs-extra": "^9.1.0", - "gray-matter": "^4.0.2", - "lodash": "^4.17.20", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "github-slugger": "^1.4.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", "resolve-pathname": "^3.0.0", - "tslib": "^2.1.0" + "shelljs": "^0.8.5", + "tslib": "^2.4.0", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } } }, - "node_modules/@docusaurus/utils-validation": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.0.tgz", - "integrity": "sha512-ELl/FVJ6xBz35TisZ1NmJhjbiVXDeU++K531PEFPCPmwnQPh7S6hZXdPnR71/Kc3BmuN9X2ZkwGOqNKVfys2Bg==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/@docusaurus/utils-common": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.4.0.tgz", + "integrity": "sha512-zIMf10xuKxddYfLg5cS19x44zud/E9I7lj3+0bv8UIs0aahpErfNrGhijEfJpAfikhQ8tL3m35nH3hJ3sOG82A==", "dependencies": { - "@docusaurus/utils": "2.0.0-beta.0", - "chalk": "^4.1.0", - "joi": "^17.4.0", - "tslib": "^2.1.0" + "tslib": "^2.4.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.14" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } } }, - "node_modules/@endiliey/static-site-generator-webpack-plugin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@endiliey/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.0.tgz", - "integrity": "sha512-3MBqYCs30qk1OBRC697NqhGouYbs71D1B8hrk/AFJC6GwF2QaJOQZtA1JYAaGSe650sZ8r5ppRTtCRXepDWlng==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/@docusaurus/utils-validation": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.4.0.tgz", + "integrity": "sha512-IrBsBbbAp6y7mZdJx4S4pIA7dUyWSA0GNosPk6ZJ0fX3uYIEQgcQSGIgTeSC+8xPEx3c16o03en1jSDpgQgz/w==", "dependencies": { - "bluebird": "^3.7.1", - "cheerio": "^0.22.0", - "eval": "^0.1.4", - "url": "^0.11.0", - "webpack-sources": "^1.4.3" + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", + "joi": "^17.6.0", + "js-yaml": "^4.1.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=16.14" } }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", - "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, + "node_modules/@docusaurus/plugin-google-gtag/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" + "node_modules/@docusaurus/plugin-google-gtag/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", "dependencies": { - "type-fest": "^0.8.1" + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "node_modules/@docusaurus/plugin-google-gtag/node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "engines": { + "node": ">=10" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/cosmiconfig": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", + "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" } }, - "node_modules/@hapi/hoek": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz", - "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==" - }, - "node_modules/@hapi/topo": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", - "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } + "node_modules/@docusaurus/plugin-google-gtag/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, - "node_modules/@mdx-js/mdx": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", - "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==", - "dependencies": { - "@babel/core": "7.12.9", - "@babel/plugin-syntax-jsx": "7.12.1", - "@babel/plugin-syntax-object-rest-spread": "7.8.3", - "@mdx-js/util": "1.6.22", - "babel-plugin-apply-mdx-type-prop": "1.6.22", - "babel-plugin-extract-import-names": "1.6.22", - "camelcase-css": "2.0.1", - "detab": "2.0.4", - "hast-util-raw": "6.0.1", - "lodash.uniq": "4.5.0", - "mdast-util-to-hast": "10.0.1", - "remark-footnotes": "2.0.0", - "remark-mdx": "1.6.22", - "remark-parse": "8.0.3", - "remark-squeeze-paragraphs": "4.0.0", - "style-to-object": "0.3.0", - "unified": "9.2.0", - "unist-builder": "2.0.3", - "unist-util-visit": "2.0.3" + "node_modules/@docusaurus/plugin-google-gtag/node_modules/eta": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.0.1.tgz", + "integrity": "sha512-46E2qDPDm7QA+usjffUWz9KfXsxVZclPOuKsXs4ZWZdI/X1wpDF7AO424pt7fdYohCzWsIkXAhNGXSlwo5naAg==", + "engines": { + "node": ">=6.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/eta-dev/eta?sponsor=1" } }, - "node_modules/@mdx-js/mdx/node_modules/@babel/core": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", - "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/postcss-loader": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.2.4.tgz", + "integrity": "sha512-F88rpxxNspo5hatIc+orYwZDtHFaVFOSIVAx+fBfJC1GmhWbVmPWtmg2gXKE1OxJbneOSGn8PWdIwsZFcruS+w==", "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.7", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.9", - "@babel/types": "^7.12.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" + "cosmiconfig": "^8.1.3", + "cosmiconfig-typescript-loader": "^4.3.0", + "klona": "^2.0.6", + "semver": "^7.3.8" }, "engines": { - "node": ">=6.9.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@mdx-js/mdx/node_modules/@babel/plugin-syntax-jsx": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", - "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@mdx-js/mdx/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@mdx-js/react": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", - "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "postcss": "^7.0.0 || ^8.0.1", + "ts-node": ">=10", + "typescript": ">=4", + "webpack": "^5.0.0" }, - "peerDependencies": { - "react": "^16.13.1 || ^17.0.0" - } - }, - "node_modules/@mdx-js/util": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz", - "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/react-loadable": { + "name": "@docusaurus/react-loadable", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", "dependencies": { - "@nodelib/fs.stat": "2.0.4", - "run-parallel": "^1.1.9" + "@types/react": "*", + "prop-types": "^15.6.2" }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", - "engines": { - "node": ">= 8" + "peerDependencies": { + "react": "*" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dependencies": { - "@nodelib/fs.scandir": "2.1.4", - "fastq": "^1.6.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/@polka/url": { - "version": "1.0.0-next.12", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.12.tgz", - "integrity": "sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ==" - }, - "node_modules/@sideway/address": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", - "integrity": "sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", - "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", - "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", - "engines": { - "node": ">=10" + "node_modules/@docusaurus/plugin-google-gtag/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dependencies": { + "ansi-regex": "^6.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", - "engines": { - "node": ">=10" + "node_modules/@docusaurus/plugin-google-gtag/node_modules/unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dependencies": { + "string-width": "^5.0.1" + }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", + "node_modules/@docusaurus/plugin-google-gtag/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", + "node_modules/@docusaurus/plugin-sitemap": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.2.0.tgz", + "integrity": "sha512-0jAmyRDN/aI265CbWZNZuQpFqiZuo+5otk2MylU9iVrz/4J7gSc+ZJ9cy4EHrEsW7PV8s1w18hIEsmcA1YgkKg==", + "dependencies": { + "@docusaurus/core": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-common": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "fs-extra": "^10.1.0", + "sitemap": "^7.1.1", + "tslib": "^2.4.0" + }, "engines": { - "node": ">=10" + "node": ">=16.14" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "node_modules/@docusaurus/preset-classic": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.2.0.tgz", + "integrity": "sha512-yKIWPGNx7BT8v2wjFIWvYrS+nvN04W+UameSFf8lEiJk6pss0kL6SG2MRvyULiI3BDxH+tj6qe02ncpSPGwumg==", "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + "@docusaurus/core": "2.2.0", + "@docusaurus/plugin-content-blog": "2.2.0", + "@docusaurus/plugin-content-docs": "2.2.0", + "@docusaurus/plugin-content-pages": "2.2.0", + "@docusaurus/plugin-debug": "2.2.0", + "@docusaurus/plugin-google-analytics": "2.2.0", + "@docusaurus/plugin-google-gtag": "2.2.0", + "@docusaurus/plugin-sitemap": "2.2.0", + "@docusaurus/theme-classic": "2.2.0", + "@docusaurus/theme-common": "2.2.0", + "@docusaurus/theme-search-algolia": "2.2.0", + "@docusaurus/types": "2.2.0" }, "engines": { - "node": ">=10" + "node": ">=16.14" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "node_modules/@docusaurus/preset-classic/node_modules/@docusaurus/plugin-google-gtag": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.2.0.tgz", + "integrity": "sha512-6SOgczP/dYdkqUMGTRqgxAS1eTp6MnJDAQMy8VCF1QKbWZmlkx4agHDexihqmYyCujTYHqDAhm1hV26EET54NQ==", "dependencies": { - "@svgr/plugin-jsx": "^5.5.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" + "@docusaurus/core": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "tslib": "^2.4.0" }, "engines": { - "node": ">=10" + "node": ">=16.14" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "node_modules/@docusaurus/react-loadable": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", "dependencies": { - "@babel/types": "^7.12.6" + "@types/react": "*", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "*" + } + }, + "node_modules/@docusaurus/theme-classic": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.2.0.tgz", + "integrity": "sha512-kjbg/qJPwZ6H1CU/i9d4l/LcFgnuzeiGgMQlt6yPqKo0SOJIBMPuz7Rnu3r/WWbZFPi//o8acclacOzmXdUUEg==", + "dependencies": { + "@docusaurus/core": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/module-type-aliases": "2.2.0", + "@docusaurus/plugin-content-blog": "2.2.0", + "@docusaurus/plugin-content-docs": "2.2.0", + "@docusaurus/plugin-content-pages": "2.2.0", + "@docusaurus/theme-common": "2.2.0", + "@docusaurus/theme-translations": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-common": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "@mdx-js/react": "^1.6.22", + "clsx": "^1.2.1", + "copy-text-to-clipboard": "^3.0.1", + "infima": "0.2.0-alpha.42", + "lodash": "^4.17.21", + "nprogress": "^0.2.0", + "postcss": "^8.4.14", + "prism-react-renderer": "^1.3.5", + "prismjs": "^1.28.0", + "react-router-dom": "^5.3.3", + "rtlcss": "^3.5.0", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" }, "engines": { - "node": ">=10" + "node": ">=16.14" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", - "dependencies": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" + "node_modules/@docusaurus/theme-common": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.2.0.tgz", + "integrity": "sha512-R8BnDjYoN90DCL75gP7qYQfSjyitXuP9TdzgsKDmSFPNyrdE3twtPNa2dIN+h+p/pr+PagfxwWbd6dn722A1Dw==", + "dependencies": { + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/module-type-aliases": "2.2.0", + "@docusaurus/plugin-content-blog": "2.2.0", + "@docusaurus/plugin-content-docs": "2.2.0", + "@docusaurus/plugin-content-pages": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "clsx": "^1.2.1", + "parse-numeric-range": "^1.3.0", + "prism-react-renderer": "^1.3.5", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" + }, + "engines": { + "node": ">=16.14" + }, + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" + } + }, + "node_modules/@docusaurus/theme-search-algolia": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.2.0.tgz", + "integrity": "sha512-2h38B0tqlxgR2FZ9LpAkGrpDWVdXZ7vltfmTdX+4RsDs3A7khiNsmZB+x/x6sA4+G2V2CvrsPMlsYBy5X+cY1w==", + "dependencies": { + "@docsearch/react": "^3.1.1", + "@docusaurus/core": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/plugin-content-docs": "2.2.0", + "@docusaurus/theme-common": "2.2.0", + "@docusaurus/theme-translations": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "algoliasearch": "^4.13.1", + "algoliasearch-helper": "^3.10.0", + "clsx": "^1.2.1", + "eta": "^1.12.3", + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" }, "engines": { - "node": ">=10" + "node": ">=16.14" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "node_modules/@docusaurus/theme-translations": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.2.0.tgz", + "integrity": "sha512-3T140AG11OjJrtKlY4pMZ5BzbGRDjNs2co5hJ6uYJG1bVWlhcaFGqkaZ5lCgKflaNHD7UHBHU9Ec5f69jTdd6w==", "dependencies": { - "cosmiconfig": "^7.0.0", - "deepmerge": "^4.2.2", - "svgo": "^1.2.2" + "fs-extra": "^10.1.0", + "tslib": "^2.4.0" }, "engines": { - "node": ">=10" + "node": ">=16.14" + } + }, + "node_modules/@docusaurus/types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.2.0.tgz", + "integrity": "sha512-b6xxyoexfbRNRI8gjblzVOnLr4peCJhGbYGPpJ3LFqpi5nsFfoK4mmDLvWdeah0B7gmJeXabN7nQkFoqeSdmOw==", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "commander": "^5.1.0", + "joi": "^17.6.0", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0", + "webpack-merge": "^5.8.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "peerDependencies": { + "react": "^16.8.4 || ^17.0.0", + "react-dom": "^16.8.4 || ^17.0.0" } }, - "node_modules/@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "node_modules/@docusaurus/utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.2.0.tgz", + "integrity": "sha512-oNk3cjvx7Tt1Lgh/aeZAmFpGV2pDr5nHKrBVx6hTkzGhrnMuQqLt6UPlQjdYQ3QHXwyF/ZtZMO1D5Pfi0lu7SA==", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" + "@docusaurus/logger": "2.2.0", + "@svgr/webpack": "^6.2.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "github-slugger": "^1.4.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", + "resolve-pathname": "^3.0.0", + "shelljs": "^0.8.5", + "tslib": "^2.4.0", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" }, "engines": { - "node": ">=10" + "node": ">=16.14" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } } }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "node_modules/@docusaurus/utils-common": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.2.0.tgz", + "integrity": "sha512-qebnerHp+cyovdUseDQyYFvMW1n1nv61zGe5JJfoNQUnjKuApch3IVsz+/lZ9a38pId8kqehC1Ao2bW/s0ntDA==", "dependencies": { - "defer-to-connect": "^1.0.1" + "tslib": "^2.4.0" }, "engines": { - "node": ">=6" + "node": ">=16.14" + }, + "peerDependencies": { + "@docusaurus/types": "*" + }, + "peerDependenciesMeta": { + "@docusaurus/types": { + "optional": true + } } }, - "node_modules/@trysound/sax": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", - "integrity": "sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow==", + "node_modules/@docusaurus/utils-validation": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.2.0.tgz", + "integrity": "sha512-I1hcsG3yoCkasOL5qQAYAfnmVoLei7apugT6m4crQjmDGxq+UkiRrq55UqmDDyZlac/6ax/JC0p+usZ6W4nVyg==", + "dependencies": { + "@docusaurus/logger": "2.2.0", + "@docusaurus/utils": "2.2.0", + "joi": "^17.6.0", + "js-yaml": "^4.1.0", + "tslib": "^2.4.0" + }, "engines": { - "node": ">=10.13.0" + "node": ">=16.14" } }, - "node_modules/@types/eslint": { - "version": "7.2.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.10.tgz", - "integrity": "sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ==", + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", - "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "sprintf-js": "~1.0.2" } }, - "node_modules/@types/estree": { - "version": "0.0.47", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.47.tgz", - "integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==" - }, - "node_modules/@types/github-slugger": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz", - "integrity": "sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g==" - }, - "node_modules/@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@types/hast": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.1.tgz", - "integrity": "sha512-viwwrB+6xGzw+G1eWpF9geV3fnsDgXqHG+cqgiHrvQfDUW5hzhCyV7Sy3UJxhfRFBsgky2SSW33qi/YrIkjX5Q==", + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dependencies": { - "@types/unist": "*" + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@types/html-minifier-terser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", - "integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==" - }, - "node_modules/@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" - }, - "node_modules/@types/katex": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.11.0.tgz", - "integrity": "sha512-27BfE8zASRLYfSBNMk5/+KIjr2CBBrH0i5lhsO04fca4TGirIIMay73v3zNkzqmsaeIa/Mi5kejWDcxPLAmkvA==" + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" }, - "node_modules/@types/mdast": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", - "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", "dependencies": { - "@types/unist": "*" + "@hapi/hoek": "^9.0.0" } }, - "node_modules/@types/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==" - }, - "node_modules/@types/node": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.3.tgz", - "integrity": "sha512-/WbxFeBU+0F79z9RdEOXH4CsDga+ibi5M8uEYr91u3CkT/pdWcV8MCook+4wDPnZBexRdwWS+PiVZ2xJviAzcQ==" - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "node_modules/@types/parse5": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", - "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.3", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", - "peer": true - }, - "node_modules/@types/q": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", - "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" - }, - "node_modules/@types/react": { - "version": "17.0.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.5.tgz", - "integrity": "sha512-bj4biDB9ZJmGAYTWSKJly6bMr4BLUiBrx9ujiJEoP9XIDY9CTaPGxE5QWN/1WjpPLzYF7/jRNnV2nNxNe970sw==", - "peer": true, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" } }, - "node_modules/@types/sax": { + "node_modules/@humanwhocodes/object-schema": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-dqYdvN7Sbw8QT/0Ci5rhjE4/iCMJEM0Y9rHpCu+gGXD9Lwbz28t6HI2yegsB6BoV1sShRMU6lAmAcgRjmFy7LA==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", - "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==", - "peer": true + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, - "node_modules/@types/unist": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", - "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz", - "integrity": "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==", + "node_modules/@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0" + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz", - "integrity": "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz", - "integrity": "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz", - "integrity": "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz", - "integrity": "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==", + "node_modules/@jest/types": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", + "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", - "@xtuc/long": "4.2.2" + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz", - "integrity": "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz", - "integrity": "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0" + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz", - "integrity": "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz", - "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==", - "dependencies": { - "@xtuc/long": "4.2.2" + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz", - "integrity": "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz", - "integrity": "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/helper-wasm-section": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-opt": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "@webassemblyjs/wast-printer": "1.11.0" + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz", - "integrity": "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==", + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz", - "integrity": "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz", - "integrity": "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==", - "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" - } + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz", - "integrity": "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==", + "node_modules/@mdx-js/mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", + "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==", "dependencies": { - "@webassemblyjs/ast": "1.11.0", - "@xtuc/long": "4.2.2" + "@babel/core": "7.12.9", + "@babel/plugin-syntax-jsx": "7.12.1", + "@babel/plugin-syntax-object-rest-spread": "7.8.3", + "@mdx-js/util": "1.6.22", + "babel-plugin-apply-mdx-type-prop": "1.6.22", + "babel-plugin-extract-import-names": "1.6.22", + "camelcase-css": "2.0.1", + "detab": "2.0.4", + "hast-util-raw": "6.0.1", + "lodash.uniq": "4.5.0", + "mdast-util-to-hast": "10.0.1", + "remark-footnotes": "2.0.0", + "remark-mdx": "1.6.22", + "remark-parse": "8.0.3", + "remark-squeeze-paragraphs": "4.0.0", + "style-to-object": "0.3.0", + "unified": "9.2.0", + "unist-builder": "2.0.3", + "unist-util-visit": "2.0.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "node_modules/@mdx-js/mdx/node_modules/@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "bin": { - "acorn": "bin/acorn" + "node": ">=6.9.0" }, - "engines": { - "node": ">=0.4.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "node_modules/@mdx-js/mdx/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/acorn-walk": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.0.tgz", - "integrity": "sha512-mjmzmv12YIG/G8JQdQuz2MUDShEJ6teYpT5bmWA4q7iwoGen8xtt3twF3OvzIUl+Q06aWIjvnwQUKvQ6TtMRjg==", - "engines": { - "node": ">=0.4.0" + "node_modules/@mdx-js/mdx/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" } }, - "node_modules/address": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", - "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "node_modules/@mdx-js/mdx/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "engines": { - "node": ">= 0.12.0" + "node": ">=0.10.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "node_modules/@mdx-js/react": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", + "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, + "node_modules/@mdx-js/util": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz", + "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==", "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "peerDependencies": { - "ajv": ">=5.0.0" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/algoliasearch": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.9.1.tgz", - "integrity": "sha512-EeJUYXzBEhZSsL6tXc3hseLBCtlNLa1MZ4mlMK6EeX38yRjY5vgnFcNNml6uUhlOjvheKxgkKRpPWkxgL8Cqkg==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.9.1", - "@algolia/cache-common": "4.9.1", - "@algolia/cache-in-memory": "4.9.1", - "@algolia/client-account": "4.9.1", - "@algolia/client-analytics": "4.9.1", - "@algolia/client-common": "4.9.1", - "@algolia/client-recommendation": "4.9.1", - "@algolia/client-search": "4.9.1", - "@algolia/logger-common": "4.9.1", - "@algolia/logger-console": "4.9.1", - "@algolia/requester-browser-xhr": "4.9.1", - "@algolia/requester-common": "4.9.1", - "@algolia/requester-node-http": "4.9.1", - "@algolia/transporter": "4.9.1" + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" } }, - "node_modules/algoliasearch-helper": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.4.4.tgz", - "integrity": "sha512-OjyVLjykaYKCMxxRMZNiwLp8CS310E0qAeIY2NaublcmLAh8/SL19+zYHp7XCLtMem2ZXwl3ywMiA32O9jszuw==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dependencies": { - "events": "^1.1.1" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, - "peerDependencies": { - "algoliasearch": ">= 3.1 < 5" + "engines": { + "node": ">= 8" } }, - "node_modules/alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" + "node_modules/@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==" }, - "node_modules/ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "node_modules/@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", "dependencies": { - "string-width": "^3.0.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "engines": { - "node": ">=6" + "@hapi/hoek": "^9.0.0" } }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "node_modules/@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" }, - "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { + "node_modules/@sideway/pinpoint": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "engines": { "node": ">=6" } }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/@slorber/static-site-generator-webpack-plugin": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz", + "integrity": "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==", "dependencies": { - "ansi-regex": "^4.1.0" + "eval": "^0.1.8", + "p-map": "^4.0.0", + "webpack-sources": "^3.2.2" }, "engines": { - "node": ">=6" + "node": ">=14" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/@slorber/static-site-generator-webpack-plugin/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "engines": { - "node": ">=6" + "node": ">=10.13.0" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", + "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.5.0.tgz", + "integrity": "sha512-8zYdkym7qNyfXpWvu4yq46k41pyNM9SOstoWhKlm+IfdCE1DdnRKeMUPsWIEO/DEkaWxJ8T9esNdG3QwQ93jBA==", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.5.0.tgz", + "integrity": "sha512-NFdxMq3xA42Kb1UbzCVxplUc0iqSyM9X8kopImvFnB+uSDdzIHOdbs1op8ofAvVRtbg4oZiyRl3fTYeKcOe9Iw==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", + "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", + "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", + "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==", "engines": { - "node": ">= 8" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/arg": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.0.tgz", - "integrity": "sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ==" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", + "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==", "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", + "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "node_modules/@svgr/babel-preset": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", + "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", + "@svgr/babel-plugin-remove-jsx-attribute": "*", + "@svgr/babel-plugin-remove-jsx-empty-expression": "*", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", + "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", + "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", + "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", + "@svgr/babel-plugin-transform-svg-component": "^6.5.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "node_modules/@svgr/core": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", + "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", + "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", + "dependencies": { + "@babel/types": "^7.20.0", + "entities": "^4.4.0" + }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "node_modules/@svgr/plugin-jsx": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", + "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", + "dependencies": { + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/hast-util-to-babel-ast": "^6.5.1", + "svg-parser": "^2.0.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "^6.0.0" } }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "node_modules/@svgr/plugin-svgo": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", + "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", + "dependencies": { + "cosmiconfig": "^7.0.1", + "deepmerge": "^4.2.2", + "svgo": "^2.8.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", - "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "node_modules/@svgr/webpack": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", + "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "function-bind": "^1.1.1" + "@babel/core": "^7.19.6", + "@babel/plugin-transform-react-constant-elements": "^7.18.12", + "@babel/preset-env": "^7.19.4", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@svgr/core": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "@svgr/plugin-svgo": "^6.5.1" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "engines": { - "node": ">=8" + "node": ">=10.13.0" } }, - "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dependencies": { - "lodash": "^4.17.14" - } + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "peer": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "peer": true }, - "node_modules/async-each": { + "node_modules/@tsconfig/node14": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "peer": true }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "peer": true }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dependencies": { + "@types/node": "*" } }, - "node_modules/autoprefixer": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.5.tgz", - "integrity": "sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==", - "dependencies": { - "browserslist": "^4.16.3", - "caniuse-lite": "^1.0.30001196", - "colorette": "^1.2.2", - "fraction.js": "^4.0.13", - "normalize-range": "^0.1.2", - "postcss-value-parser": "^4.1.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" } }, - "node_modules/axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", "dependencies": { - "follow-redirects": "^1.10.0" + "@types/express-serve-static-core": "*", + "@types/node": "*" } }, - "node_modules/babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", - "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", + "node_modules/@types/eslint": { + "version": "8.4.7", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.7.tgz", + "integrity": "sha512-ehM7cCt2RSFs42mb+lcmhFT9ouIlV92PuaeRGn8N8c98oMjG4Z5pJHA9b1QiCcuqnbPSHcyfiD3mlhqMaHsQIw==", "dependencies": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - }, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "eslint": ">= 4.12.1" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/babel-loader": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", - "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + }, + "node_modules/@types/express": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", + "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.31", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "node_modules/babel-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "node_modules/@types/express-serve-static-core": { + "version": "4.17.32", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz", + "integrity": "sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==", "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" } }, - "node_modules/babel-plugin-apply-mdx-type-prop": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz", - "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==", + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", "dependencies": { - "@babel/helper-plugin-utils": "7.10.4", - "@mdx-js/util": "1.6.22" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@babel/core": "^7.11.6" + "@types/unist": "*" } }, - "node_modules/babel-plugin-apply-mdx-type-prop/node_modules/@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "dependencies": { - "object.assign": "^4.1.0" - } + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" }, - "node_modules/babel-plugin-extract-import-names": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz", - "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==", + "node_modules/@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", "dependencies": { - "@babel/helper-plugin-utils": "7.10.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "@types/node": "*" } }, - "node_modules/babel-plugin-extract-import-names/node_modules/@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.0.tgz", - "integrity": "sha512-9bNwiR0dS881c5SHnzCmmGlMkJLl0OUZvxrxHo9w/iNoRuqaPjqlvBf4HrovXtQs/au5yKkpcdgfT1cC5PAZwg==", + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dependencies": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.2.0", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dependencies": { + "@types/istanbul-lib-report": "*" } }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.0.tgz", - "integrity": "sha512-zZyi7p3BCUyzNxLx8KV61zTINkkV65zVkDAFNZmrTCRVhjo1jAS+YLvDJ9Jgd/w2tsAviCwFHReYfxO3Iql8Yg==", + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + }, + "node_modules/@types/katex": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.11.1.tgz", + "integrity": "sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg==" + }, + "node_modules/@types/mdast": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", + "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.2.0", - "core-js-compat": "^3.9.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/unist": "*" } }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.0.tgz", - "integrity": "sha512-J7vKbCuD2Xi/eEHxquHN14bXAW9CXtecwuLrOIDJtcZzTaPzV1VdEfoUf9AzcRBMolKUQKM9/GVojeh0hFiqMg==", + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + }, + "node_modules/@types/node": { + "version": "18.11.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz", + "integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==" + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "node_modules/@types/parse5": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", + "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/react": { + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.2.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" } }, - "node_modules/bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "node_modules/@types/react-router-config": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.6.tgz", + "integrity": "sha512-db1mx37a1EJDf1XeX8jJN7R3PZABmJQXR8r28yUjVMFSjkmnQo6X6pOEEmNl+Tp2gYQOGPdYbFIipBtdElZ3Yg==", "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" } }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" } }, - "node_modules/base16": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", - "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + "node_modules/@types/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==", + "dependencies": { + "@types/node": "*" + } }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "engines": { - "node": "*" + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dependencies": { + "@types/express": "*" } }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "engines": { - "node": ">=8" + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dependencies": { + "@types/mime": "*", + "@types/node": "*" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", "dependencies": { - "file-uri-to-path": "1.0.0" + "@types/node": "*" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" }, - "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "node_modules/@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", "dependencies": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "engines": { - "node": ">= 0.8" + "@types/node": "*" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@types/yargs": { + "version": "17.0.19", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.19.tgz", + "integrity": "sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==", "dependencies": { - "ms": "2.0.0" + "@types/yargs-parser": "*" } }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, - "node_modules/bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dependencies": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" } }, - "node_modules/bonjour/node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" }, - "node_modules/boxen": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", - "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.0", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "dependencies": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "@xtuc/long": "4.2.2" } }, - "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" }, - "node_modules/buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } }, - "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "engines": { - "node": ">= 0.8" + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" } }, - "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" } }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "engines": { - "node": ">=8" - } + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.6" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=6" + "node": ">=0.4.0" } }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.4.0" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "node_modules/address": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz", + "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==", "engines": { - "node": ">= 6" + "node": ">= 10.0.0" } }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001341", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz", - "integrity": "sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "node_modules/ccount": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", - "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, "funding": { "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ajv": "^8.0.0" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "ajv": "^8.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, "funding": { "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "node_modules/character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", - "dependencies": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" - }, - "engines": { - "node": ">= 0.6" + "node_modules/algoliasearch": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.14.2.tgz", + "integrity": "sha512-ngbEQonGEmf8dyEh5f+uOIihv4176dgbuOZspiuhmTTBRBuzWu3KCGHre6uHj5YyuC7pNvQGzB6ZNJyZi0z+Sg==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.14.2", + "@algolia/cache-common": "4.14.2", + "@algolia/cache-in-memory": "4.14.2", + "@algolia/client-account": "4.14.2", + "@algolia/client-analytics": "4.14.2", + "@algolia/client-common": "4.14.2", + "@algolia/client-personalization": "4.14.2", + "@algolia/client-search": "4.14.2", + "@algolia/logger-common": "4.14.2", + "@algolia/logger-console": "4.14.2", + "@algolia/requester-browser-xhr": "4.14.2", + "@algolia/requester-common": "4.14.2", + "@algolia/requester-node-http": "4.14.2", + "@algolia/transporter": "4.14.2" } }, - "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "node_modules/algoliasearch-helper": { + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.1.tgz", + "integrity": "sha512-mvsPN3eK4E0bZG0/WlWJjeqe/bUD2KOEVOl0GyL/TGXn6wcpZU8NOuztGHCUKXkyg5gq6YzUakVTmnmSSO5Yiw==", "dependencies": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "@algolia/events": "^4.0.1" }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.1" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "engines": { - "node": ">=6.0" + "peerDependencies": { + "algoliasearch": ">= 3.1 < 6" } }, - "node_modules/ci-info": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.1.1.tgz", - "integrity": "sha512-kdRWLBIJwdsYJWYJFtAFFYxybguqeF91qpZaggjG5Nf8QKdizFG2hjqvaTXbxFIcYbSaD74KpAXv6BSm17DHEQ==" - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" + "string-width": "^4.1.0" } }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" } }, - "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/class-utils/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "kind-of": "^3.0.2" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dependencies": { - "is-buffer": "^1.1.5" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "node_modules/array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/class-utils/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/clean-css": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.2.tgz", - "integrity": "sha512-QcaGg9OuMo+0Ds933yLOY+gHPWbxhxqF0HDexmToPf8pczvmvZGYzd+QqWp9/mkucAOKViI+dSFOqoZIvXbeBw==", + "node_modules/array.prototype.flatmap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", "dependencies": { - "source-map": "~0.6.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" }, "engines": { - "node": ">= 10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "engines": { - "node": ">=6" + "node": ">= 4.0.0" } }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "node_modules/autoprefixer": { + "version": "10.4.12", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz", + "integrity": "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001407", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, "engines": { - "node": ">=6" + "node": "^10 || ^12 || >=14" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/clipboard": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz", - "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==", - "optional": true, + "node_modules/axios": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", "dependencies": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" + "follow-redirects": "^1.14.7" } }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, "engines": { "node": ">=6" + }, + "peerDependencies": { + "eslint": ">= 4.12.1" } }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "dependencies": { - "color-convert": "^1.9.0" + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" }, "engines": { - "node": ">=4" + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" } }, - "node_modules/cliui/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/babel-plugin-apply-mdx-type-prop": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz", + "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==", "dependencies": { - "color-name": "1.1.3" + "@babel/helper-plugin-utils": "7.10.4", + "@mdx-js/util": "1.6.22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@babel/core": "^7.11.6" } }, - "node_modules/cliui/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "node_modules/babel-plugin-apply-mdx-type-prop/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" } }, - "node_modules/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/babel-plugin-extract-import-names": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz", + "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==", "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "@babel/helper-plugin-utils": "7.10.4" }, - "engines": { - "node": ">=6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/babel-plugin-extract-import-names/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dependencies": { - "ansi-regex": "^4.1.0" + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" }, - "engines": { - "node": ">=6" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" }, - "engines": { - "node": ">=6" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" + "@babel/helper-define-polyfill-provider": "^0.3.3" }, - "engines": { - "node": ">=6" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/clone-response": { + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dependencies": { - "mimic-response": "^1.0.0" + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" } }, - "node_modules/clsx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", - "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 4.0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/coa/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { - "node": ">=4" + "node": ">= 0.8" } }, - "node_modules/coa/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" + "ms": "2.0.0" } }, - "node_modules/coa/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", "dependencies": { - "color-name": "1.1.3" + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, - "node_modules/coa/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, - "node_modules/coa/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/coa/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/coa/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dependencies": { - "has-flag": "^3.0.0" + "fill-range": "^7.0.1" }, "engines": { - "node": ">=4" - } - }, - "node_modules/collapse-white-space": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", - "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node": ">=8" } }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" }, "engines": { - "node": ">=0.10.0" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", - "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", - "dependencies": { - "color-convert": "^1.9.1", - "color-string": "^1.5.4" + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "engines": { - "node": ">=7.0.0" + "node": ">= 0.8" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-string": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", - "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" } }, - "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dependencies": { - "color-name": "1.1.3" + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } }, - "node_modules/combine-promises": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz", - "integrity": "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==", + "node_modules/cacheable-request/node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" } }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "engines": { - "node": ">= 0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "engines": { - "node": ">= 0.8" + "node": ">= 6" } }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dependencies": { - "ms": "2.0.0" + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" } }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "node_modules/caniuse-lite": { + "version": "1.0.30001422", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001422.tgz", + "integrity": "sha512-hSesn02u1QacQHhaxl/kNMZwqVG35Sz/8DgvmgedxSH8z9UUpcDYSPYgsj3x5dQNRcNp6BwpSfQfVzYUTm+fog==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "node_modules/ccount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "engines": { - "node": ">=0.8" + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", "dependencies": { - "safe-buffer": "5.1.2" + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" } }, - "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "node_modules/cheerio/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", "dependencies": { - "safe-buffer": "~5.1.1" + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, "engines": { - "node": ">= 0.6" + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "engines": { - "node": ">=0.10.0" + "node": ">=6.0" } }, - "node_modules/copy-text-to-clipboard": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz", - "integrity": "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==", + "node_modules/ci-info": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", + "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/copy-webpack-plugin": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-8.1.1.tgz", - "integrity": "sha512-rYM2uzRxrLRpcyPqGceRBDpxxUV8vcDqIKxAUKfcnFpcrPxT5+XvhTxv7XLjo5AvEJFPdAE3zCogG2JVahqgSQ==", + "node_modules/clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", "dependencies": { - "fast-glob": "^3.2.5", - "glob-parent": "^5.1.1", - "globby": "^11.0.3", - "normalize-path": "^3.0.0", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1" + "source-map": "~0.6.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "engines": { + "node": ">=6" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "string-width": "^4.2.0" }, "engines": { - "node": ">= 10.13.0" + "node": "10.* || >= 12.*" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "optionalDependencies": { + "@colors/colors": "1.5.0" } }, - "node_modules/core-js": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.12.1.tgz", - "integrity": "sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/core-js-compat": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.12.1.tgz", - "integrity": "sha512-i6h5qODpw6EsHAoIdQhKoZdWn+dGBF3dSS8m5tif36RlWvW3A6+yu2S16QHUo3CrkzrnEskMAt9f8FxmY9fhWQ==", + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dependencies": { - "browserslist": "^4.16.6", - "semver": "7.0.0" + "mimic-response": "^1.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "bin": { - "semver": "bin/semver.js" + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" } }, - "node_modules/core-js-pure": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz", - "integrity": "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ==", - "hasInstallScript": true, + "node_modules/collapse-white-space": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", + "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "node_modules/cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=10" + "node": ">=7.0.0" } }, - "node_modules/cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", - "dependencies": { - "node-fetch": "2.6.7" - } + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "node_modules/combine-promises": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/combine-promises/-/combine-promises-1.1.0.tgz", + "integrity": "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==", "engines": { - "node": ">= 8" + "node": ">=10" } }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/css-color-names": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", - "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==", + "node_modules/command-exists-promise": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/command-exists-promise/-/command-exists-promise-2.0.2.tgz", + "integrity": "sha512-T6PB6vdFrwnHXg/I0kivM3DqaCGZLjjYSOe0a5WgFKcz1sOnmOeIjnhQPXVXX3QjVbLyTJ85lJkX6lUpukTzaA==", "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/css-declaration-sorter": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.0.0.tgz", - "integrity": "sha512-S0TE4E0ha5+tBHdLWPc5n+S8E4dFBS5xScPvgHkLNZwWvX4ISoFGhGeerLC9uS1cKA/sC+K2wHq6qEbcagT/fg==", - "dependencies": { - "timsort": "^0.3.0" - }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "engines": { - "node": ">= 10" - }, - "peerDependencies": { - "postcss": "^8.0.9" + "node": ">= 6" } }, - "node_modules/css-loader": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.4.tgz", - "integrity": "sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw==", + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dependencies": { - "camelcase": "^6.2.0", - "icss-utils": "^5.1.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.10", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "schema-utils": "^3.0.0", - "semver": "^7.3.5" + "mime-db": ">= 1.43.0 < 2" }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.27.0 || ^5.0.0" + "node": ">= 0.6" } }, - "node_modules/css-loader/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">= 0.8.0" } }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-2.0.0.tgz", - "integrity": "sha512-cG/uc94727tx5pBNtb1Sd7gvUPzwmcQi1lkpfqTpdkuNq75hJCw7bIVsCNijLm4dhDcr1atvuysl2rZqOG8Txw==", + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "cssnano": "^5.0.0", - "jest-worker": "^26.3.0", - "p-limit": "^3.0.2", - "postcss": "^8.2.9", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - } + "ms": "2.0.0" } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=8" } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dependencies": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" + "node": ">=0.8" } }, - "node_modules/css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" }, - "node_modules/css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=8.0.0" + "node": ">= 0.6" } }, - "node_modules/css-tree/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { - "node": "*" + "node": ">= 0.6" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/copy-text-to-clipboard": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz", + "integrity": "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==", "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cssnano": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.2.tgz", - "integrity": "sha512-8JK3EnPsjQsULme9/e5M2hF564f/480hwsdcHvQ7ZtAIMfQ1O3SCfs+b8Mjf5KJxhYApyRshR2QSovEJi2K72Q==", + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", "dependencies": { - "cosmiconfig": "^7.0.0", - "cssnano-preset-default": "^5.0.1", - "is-resolvable": "^1.1.0" + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/cssnano" + "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "postcss": "^8.2.1" + "webpack": "^5.1.0" } }, - "node_modules/cssnano-preset-advanced": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.0.1.tgz", - "integrity": "sha512-g+LB6GcihLXcBEdDh+mzk1qX9jgtBkVpzAg1OlgrH6C+qKIQYRHwAPyaoXy95Ci83sYYXlwJ0OrqLYTIUEBLZQ==", + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { - "autoprefixer": "^10.0.2", - "cssnano-preset-default": "^5.0.1", - "postcss-discard-unused": "^5.0.0", - "postcss-merge-idents": "^5.0.0", - "postcss-reduce-idents": "^5.0.0", - "postcss-zindex": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, - "peerDependencies": { - "postcss": "^8.2.1" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/cssnano-preset-default": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.0.1.tgz", - "integrity": "sha512-cfmfThYODGqhpQKDq9H0MTAqkMvZ3dGbOUTBKw0xWZiIycMqHid22LsJXJl4r1qX4qzDeKxcSyQ/Xb5Mu3Z//Q==", - "dependencies": { - "css-declaration-sorter": "6.0.0", - "cssnano-utils": "^2.0.0", - "postcss-calc": "^8.0.0", - "postcss-colormin": "^5.0.0", - "postcss-convert-values": "^5.0.0", - "postcss-discard-comments": "^5.0.0", - "postcss-discard-duplicates": "^5.0.0", - "postcss-discard-empty": "^5.0.0", - "postcss-discard-overridden": "^5.0.0", - "postcss-merge-longhand": "^5.0.1", - "postcss-merge-rules": "^5.0.0", - "postcss-minify-font-values": "^5.0.0", - "postcss-minify-gradients": "^5.0.0", - "postcss-minify-params": "^5.0.0", - "postcss-minify-selectors": "^5.0.0", - "postcss-normalize-charset": "^5.0.0", - "postcss-normalize-display-values": "^5.0.0", - "postcss-normalize-positions": "^5.0.0", - "postcss-normalize-repeat-style": "^5.0.0", - "postcss-normalize-string": "^5.0.0", - "postcss-normalize-timing-functions": "^5.0.0", - "postcss-normalize-unicode": "^5.0.0", - "postcss-normalize-url": "^5.0.0", - "postcss-normalize-whitespace": "^5.0.0", - "postcss-ordered-values": "^5.0.0", - "postcss-reduce-initial": "^5.0.0", - "postcss-reduce-transforms": "^5.0.0", - "postcss-svgo": "^5.0.0", - "postcss-unique-selectors": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" }, "peerDependencies": { - "postcss": "^8.2.1" + "ajv": "^8.8.2" } }, - "node_modules/cssnano-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.0.tgz", - "integrity": "sha512-xvxmTszdrvSyTACdPe8VU5J6p4sm3egpgw54dILvNqt5eBUv6TFjACLhSxtRuEsxYrgy8uDy269YjScO5aKbGA==", - "engines": { - "node": "^10 || ^12 || >=14.0" + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" }, - "peerDependencies": { - "postcss": "^8.2.1" + "engines": { + "node": ">=10.13.0" } }, - "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", "dependencies": { - "css-tree": "^1.1.2" + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "engines": { - "node": ">=8.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/csso/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/copy-webpack-plugin/node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/csstype": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", - "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==", - "peer": true + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dependencies": { - "ms": "2.1.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" }, "engines": { - "node": ">=6.0" + "node": ">= 12.13.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "engines": { - "node": ">=0.10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dependencies": { - "mimic-response": "^1.0.0" - }, - "engines": { - "node": ">=4" + "node_modules/core-js": { + "version": "3.25.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.5.tgz", + "integrity": "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "node_modules/core-js-compat": { + "version": "3.25.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.5.tgz", + "integrity": "sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==", "dependencies": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" + "browserslist": "^4.21.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" + "node_modules/core-js-pure": { + "version": "3.25.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.5.tgz", + "integrity": "sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "dependencies": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", + "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", "engines": { - "node": ">=6" + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=7", + "ts-node": ">=10", + "typescript": ">=3" } }, - "node_modules/default-gateway/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "peer": true + }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" + "node-fetch": "2.6.7" } }, - "node_modules/default-gateway/node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=6" + "node": ">= 8" } }, - "node_modules/default-gateway/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" - }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/default-gateway/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "node_modules/css-declaration-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" } }, - "node_modules/default-gateway/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "node_modules/css-loader": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", + "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", "dependencies": { - "path-key": "^2.0.0" + "icss-utils": "^5.1.0", + "postcss": "^8.4.19", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" }, "engines": { - "node": ">=4" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" } }, - "node_modules/default-gateway/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "node_modules/css-minimizer-webpack-plugin": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", + "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "dependencies": { + "cssnano": "^5.1.8", + "jest-worker": "^29.1.2", + "postcss": "^8.4.17", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, "engines": { - "node": ">=4" + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "lightningcss": { + "optional": true + } } }, - "node_modules/default-gateway/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/default-gateway/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dependencies": { - "shebang-regex": "^1.0.0" + "fast-deep-equal": "^3.1.3" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/default-gateway/node_modules/shebang-regex": { + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "engines": { - "node": ">=0.10.0" - } + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "node_modules/default-gateway/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dependencies": { - "isexe": "^2.0.0" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" }, - "bin": { - "which": "bin/which" + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" - }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dependencies": { - "object-keys": "^1.0.12" + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" }, - "engines": { - "node": ">= 0.4" + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "mdn-data": "2.0.14", + "source-map": "^0.6.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0.0" } }, - "node_modules/del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.13.tgz", + "integrity": "sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ==", + "dependencies": { + "cssnano-preset-default": "^5.2.12", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-advanced": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.9.tgz", + "integrity": "sha512-njnh4pp1xCsibJcEHnWZb4EEzni0ePMqPuPNyuWT4Z+YeXmsgqNuTPIljXFEXhxGsWs9183JkXgHxc1TcsahIg==", + "dependencies": { + "autoprefixer": "^10.4.12", + "cssnano-preset-default": "^5.2.13", + "postcss-discard-unused": "^5.1.0", + "postcss-merge-idents": "^5.1.1", + "postcss-reduce-idents": "^5.2.0", + "postcss-zindex": "^5.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz", + "integrity": "sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==", + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.3", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.1", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", "dependencies": { "globby": "^11.0.1", "graceful-fs": "^4.2.4", @@ -5243,24 +6151,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", - "optional": true - }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/detab": { "version": "2.0.4", @@ -5275,24 +6181,21 @@ } }, "node_modules/detect-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.5.tgz", - "integrity": "sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" }, "node_modules/detect-port": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz", - "integrity": "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", "dependencies": { "address": "^1.0.1", - "debug": "^2.6.0" + "debug": "4" }, "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" - }, - "engines": { - "node": ">= 4.2.1" + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" } }, "node_modules/detect-port-alt": { @@ -5322,21 +6225,17 @@ "node_modules/detect-port-alt/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/detect-port/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "peer": true, + "engines": { + "node": ">=0.3.1" } }, - "node_modules/detect-port/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -5351,23 +6250,17 @@ "node_modules/dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" }, "node_modules/dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "dependencies": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", "dependencies": { - "buffer-indexof": "^1.0.0" + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" } }, "node_modules/doctrine": { @@ -5390,34 +6283,54 @@ } }, "node_modules/dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dependencies": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] }, "node_modules/domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dependencies": { - "domelementtype": "1" + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, "node_modules/domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, "node_modules/dot-case": { @@ -5454,24 +6367,29 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.3.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", - "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==" + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, "node_modules/emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/emojis-list": { "version": "3.0.0", @@ -5493,7 +6411,7 @@ "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } @@ -5507,9 +6425,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", - "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -5530,19 +6448,14 @@ } }, "node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "dependencies": { - "prr": "~1.0.1" + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "engines": { + "node": ">=0.12" }, - "bin": { - "errno": "cli.js" + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/error-ex": { @@ -5554,26 +6467,34 @@ } }, "node_modules/es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", "dependencies": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5583,9 +6504,17 @@ } }, "node_modules/es-module-lexer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz", - "integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==" + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dependencies": { + "has": "^1.0.3" + } }, "node_modules/es-to-primitive": { "version": "1.2.1", @@ -5622,7 +6551,7 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -5636,27 +6565,30 @@ } }, "node_modules/eslint": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz", - "integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dependencies": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.1", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", + "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", @@ -5665,7 +6597,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -5674,7 +6606,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -5689,28 +6621,30 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.23.2.tgz", - "integrity": "sha512-AfjgFQB+nYszudkxRkTFu0UR1zEQig0ArVMPloKhxwlwkzaw/fBiH0QWcBBhZONlXqQC51+nfqFrkn4EzHcGBw==", + "version": "7.31.10", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz", + "integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==", "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", "doctrine": "^2.1.0", - "has": "^1.0.3", + "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.3", - "object.fromentries": "^2.0.4", - "object.values": "^1.1.3", - "prop-types": "^15.7.2", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.4" + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -5725,17 +6659,29 @@ } }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -5748,8 +6694,16 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "2.1.0", + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dependencies": { @@ -5795,9 +6749,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "dependencies": { "type-fest": "^0.20.2" }, @@ -5856,14 +6810,6 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -5875,18 +6821,10 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "engines": { - "node": ">=4.0" - } - }, "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "engines": { "node": ">=4.0" } @@ -5900,9 +6838,9 @@ } }, "node_modules/eta": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/eta/-/eta-1.12.1.tgz", - "integrity": "sha512-H8npoci2J/7XiPnVcCVulBSPsTNGvGaINyMjQDU8AFqp9LGsEYS88g2CiU+d01Sg44WtX7o4nb8wUJ9vnI+tiA==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/eta/-/eta-1.12.3.tgz", + "integrity": "sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg==", "engines": { "node": ">=6.0.0" }, @@ -5913,16 +6851,17 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } }, "node_modules/eval": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.6.tgz", - "integrity": "sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", "dependencies": { + "@types/node": "*", "require-like": ">= 0.1.1" }, "engines": { @@ -5935,28 +6874,17 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/eventsource": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", - "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", - "dependencies": { - "original": "^1.0.0" - }, + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "engines": { - "node": ">=0.12.0" + "node": ">=0.8.x" } }, "node_modules/execa": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", - "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -5975,149 +6903,39 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -6126,6 +6944,11 @@ "node": ">= 0.10.0" } }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -6137,7 +6960,7 @@ "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/extend": { "version": "3.0.2", @@ -6147,7 +6970,7 @@ "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "dependencies": { "is-extendable": "^0.1.0" }, @@ -6155,54 +6978,24 @@ "node": ">=0.10.0" } }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, "node_modules/fast-json-stable-stringify": { @@ -6213,28 +7006,28 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fast-url-parser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", "dependencies": { "punycode": "^1.3.2" } }, "node_modules/fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dependencies": { "websocket-driver": ">=0.5.1" }, @@ -6251,17 +7044,17 @@ } }, "node_modules/fbjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz", - "integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", + "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", "dependencies": { - "cross-fetch": "^3.0.4", + "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", "loose-envify": "^1.0.0", "object-assign": "^4.1.0", "promise": "^7.1.1", "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" + "ua-parser-js": "^0.7.30" } }, "node_modules/fbjs-css-vars": { @@ -6269,6 +7062,14 @@ "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -6280,28 +7081,6 @@ "node": ">=0.4.0" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -6333,11 +7112,11 @@ } }, "node_modules/file-loader/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dependencies": { - "@types/json-schema": "^7.0.6", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" }, @@ -6349,16 +7128,10 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, "node_modules/filesize": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", - "integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", "engines": { "node": ">= 0.4.0" } @@ -6375,16 +7148,16 @@ } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { @@ -6402,12 +7175,12 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -6445,26 +7218,26 @@ } }, "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "node_modules/flux": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.1.tgz", - "integrity": "sha512-emk4RCvJ8RzNP2lNpphKnG7r18q8elDYNAPx7xn+bDeOIo9FFfxEfIQ2y6YbQNmnsGD3nH1noxtLE64Puz1bRQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz", + "integrity": "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==", "dependencies": { "fbemitter": "^3.0.0", - "fbjs": "^3.0.0" + "fbjs": "^3.0.1" }, "peerDependencies": { "react": "^15.0.2 || ^16.0.0 || ^17.0.0" } }, "node_modules/follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "funding": [ { "type": "individual", @@ -6480,284 +7253,156 @@ } } }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/fork-ts-checker-webpack-plugin": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz", - "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", "dependencies": { - "@babel/code-frame": "^7.5.5", - "chalk": "^2.4.1", - "micromatch": "^3.1.10", + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", "minimatch": "^3.0.4", - "semver": "^5.6.0", - "tapable": "^1.0.0", - "worker-rpc": "^0.1.0" + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" }, "engines": { - "node": ">=6.11.5", + "node": ">=10", "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "dependencies": { - "color-convert": "^1.9.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" }, "engines": { - "node": ">=4" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/color-name": { + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "engines": { - "node": ">=0.8.0" + "node": ">=6" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", "engines": { - "node": ">=4" + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/micromatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.0.tgz", - "integrity": "sha512-o9lSKpK0TDqDwTL24Hxqi6I99s942l6TYkfl6WvGWgLOIFz/YonSGKfiSeMadoiNvTfqnfOa9mjb5SGVbBK9/w==", - "engines": { - "node": "*" + "node": ">=12" } }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" + "minipass": "^2.6.0" } }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.2", @@ -6777,10 +7422,35 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -6790,22 +7460,14 @@ "node": ">=6.9.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6827,31 +7489,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/github-slugger": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", - "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", - "dependencies": { - "emoji-regex": ">=6.0.0 <=6.1.1" - } + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", + "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==" }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -6944,15 +7610,15 @@ } }, "node_modules/globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { @@ -6963,22 +7629,13 @@ } }, "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "engines": { "node": ">= 4" } }, - "node_modules/good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "optional": true, - "dependencies": { - "delegate": "^3.1.2" - } - }, "node_modules/got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -7012,9 +7669,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "node_modules/gray-matter": { "version": "4.0.3", @@ -7051,15 +7708,17 @@ } }, "node_modules/gzip-size": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", - "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", "dependencies": { - "duplexer": "^0.1.1", - "pify": "^4.0.1" + "duplexer": "^0.1.2" }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/handle-thing": { @@ -7079,9 +7738,9 @@ } }, "node_modules/has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7094,78 +7753,40 @@ "node": ">=8" } }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-value": { + "node_modules/has-property-descriptors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-intrinsic": "^1.1.1" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/has-values/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dependencies": { - "kind-of": "^3.0.2" + "node": ">= 0.4" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dependencies": { - "is-buffer": "^1.1.5" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dependencies": { - "is-buffer": "^1.1.5" + "node": ">= 0.4" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-yarn": { @@ -7304,11 +7925,6 @@ "he": "bin/he" } }, - "node_modules/hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" - }, "node_modules/history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -7333,7 +7949,7 @@ "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -7355,6 +7971,11 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/hpack.js/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -7363,74 +7984,48 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" - }, - "node_modules/hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" - }, "node_modules/html-entities": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", - "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" }, "node_modules/html-minifier-terser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", - "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", "dependencies": { - "camel-case": "^4.1.1", - "clean-css": "^4.2.3", - "commander": "^4.1.1", + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", "he": "^1.2.0", - "param-case": "^3.0.3", + "param-case": "^3.0.4", "relateurl": "^0.2.7", - "terser": "^4.6.3" + "terser": "^5.10.0" }, "bin": { "html-minifier-terser": "cli.js" }, "engines": { - "node": ">=6" - } - }, - "node_modules/html-minifier-terser/node_modules/clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 4.0" + "node": ">=12" } }, "node_modules/html-minifier-terser/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/html-minifier-terser/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "engines": { - "node": ">=0.10.0" + "node": ">= 12" } }, "node_modules/html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/html-void-elements": { @@ -7443,14 +8038,14 @@ } }, "node_modules/html-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-rZsVvPXUYFyME0cuGkyOHfx9hmkFa4pWfxY/mdY38PsBEaVNsRoA+Id+8z6DBDgyv3zaw6XQszdF8HLwfQvcdQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", "dependencies": { - "@types/html-minifier-terser": "^5.0.0", - "html-minifier-terser": "^5.0.1", - "lodash": "^4.17.20", - "pretty-error": "^2.1.1", + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", "tapable": "^2.0.0" }, "engines": { @@ -7465,16 +8060,21 @@ } }, "node_modules/htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dependencies": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" } }, "node_modules/http-cache-semantics": { @@ -7485,32 +8085,27 @@ "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, "node_modules/http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" }, "node_modules/http-proxy": { "version": "1.18.1", @@ -7526,136 +8121,37 @@ } }, "node_modules/http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "dependencies": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "node": ">=12.0.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/http-proxy-middleware/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" + "peerDependencies": { + "@types/express": "^4.17.13" }, - "engines": { - "node": ">=0.10.0" + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } } }, - "node_modules/http-proxy-middleware/node_modules/is-number": { + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/micromatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/http-proxy-middleware/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "node": ">=10" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/human-signals": { @@ -7696,10 +8192,24 @@ "node": ">= 4" } }, + "node_modules/image-size": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz", + "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/immer": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz", - "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==", + "version": "9.0.17", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.17.tgz", + "integrity": "sha512-+hBruaLSQvkPfxRiTLK/mi4vLH+/VQS6z2KJahdoxlleFOI8ARqzOF17uy12eFDlqWmPoygwc5evgwcp+dlHhg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -7723,97 +8233,15 @@ "node_modules/import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dependencies": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", "engines": { "node": ">=4" } }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "engines": { "node": ">=0.8.19" } @@ -7826,15 +8254,10 @@ "node": ">=8" } }, - "node_modules/indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" - }, "node_modules/infima": { - "version": "0.2.0-alpha.23", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.23.tgz", - "integrity": "sha512-V0RTjB1otjpH3E2asbydx3gz7ovdSJsuV7r9JTdBggqRilnelTJUcXxLawBQQKsjQi5qPcRTjxnlaV8xyyKhhw==", + "version": "0.2.0-alpha.42", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.42.tgz", + "integrity": "sha512-ift8OXNbQQwtbIt6z16KnSWP7uJ/SysSMFI4F87MNRTicypfl4Pv3E2OGVv6N3nSZFJvA8imYulCBS64iyHYww==", "engines": { "node": ">=12" } @@ -7842,7 +8265,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -7863,18 +8286,6 @@ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, - "node_modules/internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "dependencies": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -7896,44 +8307,20 @@ "node": ">= 0.10" } }, - "node_modules/ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "node_modules/ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "engines": { - "node": ">=4" + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" } }, "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", "engines": { - "node": ">=0.10.0" + "node": ">= 10" } }, "node_modules/is-alphabetical": { @@ -7958,29 +8345,18 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-arguments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", - "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", - "dependencies": { - "call-bind": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-bigint": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", - "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7997,11 +8373,12 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", - "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -8033,9 +8410,9 @@ } }, "node_modules/is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "engines": { "node": ">= 0.4" }, @@ -8059,31 +8436,10 @@ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" }, - "node_modules/is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dependencies": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - } - }, - "node_modules/is-color-stop/node_modules/css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "engines": { - "node": "*" - } - }, "node_modules/is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dependencies": { "has": "^1.0.3" }, @@ -8091,21 +8447,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dependencies": { - "kind-of": "^6.0.0" + "has-tostringtag": "^1.0.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-date-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", - "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", "engines": { "node": ">= 0.4" }, @@ -8122,19 +8470,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -8152,7 +8487,7 @@ "node_modules/is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", "engines": { "node": ">=0.10.0" } @@ -8160,7 +8495,7 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "engines": { "node": ">=0.10.0" } @@ -8174,9 +8509,9 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dependencies": { "is-extglob": "^2.1.1" }, @@ -8209,9 +8544,9 @@ } }, "node_modules/is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "engines": { "node": ">= 0.4" }, @@ -8239,9 +8574,12 @@ } }, "node_modules/is-number-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", - "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -8252,7 +8590,7 @@ "node_modules/is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", "engines": { "node": ">=0.10.0" } @@ -8265,28 +8603,6 @@ "node": ">=6" } }, - "node_modules/is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dependencies": { - "is-path-inside": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-in-cwd/node_modules/is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dependencies": { - "path-is-inside": "^1.0.2" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -8315,12 +8631,12 @@ } }, "node_modules/is-regex": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", - "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dependencies": { "call-bind": "^1.0.2", - "has-symbols": "^1.0.2" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -8332,16 +8648,11 @@ "node_modules/is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", "engines": { "node": ">=0.10.0" } }, - "node_modules/is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" - }, "node_modules/is-root": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", @@ -8350,18 +8661,35 @@ "node": ">=6" } }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-string": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", - "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -8386,7 +8714,18 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/is-whitespace-character": { "version": "1.0.4", @@ -8397,14 +8736,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-word-character": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", @@ -8433,42 +8764,73 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "engines": { "node": ">=0.10.0" } }, + "node_modules/jest-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", + "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", + "dependencies": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", "dependencies": { "@types/node": "*", + "jest-util": "^29.3.1", "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" + "supports-color": "^8.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/joi": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.0.tgz", - "integrity": "sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg==", + "version": "17.6.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.3.tgz", + "integrity": "sha512-YlQsIaS9MHYekzf1Qe11LjTkNzx9qhYluK3172z38RxYoAUf82XMX1p1DG1H4Wtk2ED/vPdSn9OggqtDu+aTow==", "dependencies": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.0", + "@sideway/address": "^4.1.3", "@sideway/formula": "^3.0.0", "@sideway/pinpoint": "^2.0.0" } @@ -8503,12 +8865,7 @@ "node_modules/json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -8523,20 +8880,12 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" - }, - "node_modules/json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dependencies": { - "minimist": "^1.2.5" - }, + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", "bin": { "json5": "lib/cli.js" }, @@ -8556,12 +8905,12 @@ } }, "node_modules/jsx-ast-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", - "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", "dependencies": { - "array-includes": "^3.1.2", - "object.assign": "^4.1.2" + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" }, "engines": { "node": ">=4.0" @@ -8591,11 +8940,6 @@ "json-buffer": "3.0.0" } }, - "node_modules/killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -8613,9 +8957,9 @@ } }, "node_modules/klona": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", - "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", "engines": { "node": ">= 8" } @@ -8651,23 +8995,31 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "engines": { + "node": ">=10" + } + }, "node_modules/lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "engines": { "node": ">=6.11.5" } }, "node_modules/loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -8693,117 +9045,40 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" - }, - "node_modules/lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, "node_modules/lodash.curry": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", - "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "node_modules/lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "node_modules/lodash.flow": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", - "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" - }, - "node_modules/lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" - }, - "node_modules/lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" - }, - "node_modules/lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" - }, - "node_modules/lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" - }, - "node_modules/lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" - }, - "node_modules/lodash.toarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", - "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" - }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=" + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==" }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "node_modules/loglevel": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", - "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==", - "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" - } + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -8865,24 +9140,11 @@ "semver": "bin/semver.js" } }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "peer": true }, "node_modules/markdown-escapes": { "version": "1.0.4", @@ -8946,58 +9208,38 @@ } }, "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" }, "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { "node": ">= 0.6" } }, - "node_modules/memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "node_modules/memory-fs/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/memory-fs/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/memfs": { + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz", + "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==", "dependencies": { - "safe-buffer": "~5.1.0" + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" } }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -9015,23 +9257,18 @@ "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { "node": ">= 0.6" } }, - "node_modules/microevent.ts": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", - "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==" - }, "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" @@ -9049,19 +9286,19 @@ } }, "node_modules/mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.47.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -9083,50 +9320,67 @@ "node": ">=4" } }, - "node_modules/mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "dependencies": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - }, - "peerDependencies": { - "prop-types": "^15.0.0", - "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, "node_modules/mini-css-extract-plugin": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.0.tgz", - "integrity": "sha512-nPFKI7NSy6uONUo9yn2hIfb9vyYvkFu95qki0e21DQ9uaqNKDP15DGpK0KnV6wDroWxPHtExrdEwx/yDQ8nVRw==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", + "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "webpack-sources": "^1.1.0" + "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.4.0 || ^5.0.0" + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" } }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", @@ -9139,9 +9393,9 @@ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -9150,81 +9404,75 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" } }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/minipass/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" + "minipass": "^2.9.0" } }, "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, "bin": { "mkdirp": "bin/cmd.js" - }, + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", "engines": { "node": ">=10" } }, - "node_modules/module-alias": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", - "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dependencies": { - "dns-packet": "^1.3.1", + "dns-packet": "^5.2.2", "thunky": "^1.0.2" }, "bin": { "multicast-dns": "cli.js" } }, - "node_modules/multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" - }, - "node_modules/nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "optional": true - }, "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -9232,59 +9480,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } @@ -9294,10 +9498,19 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "node_modules/njre": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/njre/-/njre-0.2.0.tgz", + "integrity": "sha512-+Wq8R6VmjK+jI8a9NdzfU6Vh50r3tjsdvl5KJE1OyHeH8I/nx5Ptm12qpO3qNUbstXuZfBDgDL0qQZw9JyjhMw==", + "dependencies": { + "command-exists-promise": "^2.0.2", + "node-fetch": "^2.5.0", + "tar": "^4.4.8", + "yauzl": "^2.10.0" + }, + "engines": { + "node": ">=8" + } }, "node_modules/no-case": { "version": "3.0.4", @@ -9309,11 +9522,11 @@ } }, "node_modules/node-emoji": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", "dependencies": { - "lodash.toarray": "^4.4.0" + "lodash": "^4.17.21" } }, "node_modules/node-fetch": { @@ -9336,17 +9549,17 @@ } }, "node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "engines": { - "node": ">= 6.0.0" + "node": ">= 6.13.0" } }, "node_modules/node-releases": { - "version": "1.1.72", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", - "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -9359,17 +9572,20 @@ "node_modules/normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "engines": { "node": ">=0.10.0" } }, "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm-run-path": { @@ -9386,128 +9602,33 @@ "node_modules/nprogress": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" }, "node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dependencies": { - "boolbase": "~1.0.0" + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "engines": { "node": ">=0.10.0" } }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", - "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-keys": { @@ -9518,25 +9639,14 @@ "node": ">= 0.4" } }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -9547,28 +9657,26 @@ } }, "node_modules/object.entries": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", - "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" + "es-abstract": "^1.19.1" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", - "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" + "es-abstract": "^1.19.1" }, "engines": { "node": ">= 0.4" @@ -9577,42 +9685,26 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", - "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", + "node_modules/object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2" - }, - "engines": { - "node": ">= 0.8" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object.values": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", - "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" + "es-abstract": "^1.19.1" }, "engines": { "node": ">= 0.4" @@ -9627,9 +9719,9 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, @@ -9648,7 +9740,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dependencies": { "wrappy": "1" } @@ -9668,15 +9760,16 @@ } }, "node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9690,25 +9783,6 @@ "opener": "bin/opener-bin.js" } }, - "node_modules/opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "dependencies": { - "is-wsl": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/opn/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "engines": { - "node": ">=4" - } - }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -9725,14 +9799,6 @@ "node": ">= 0.8.0" } }, - "node_modules/original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dependencies": { - "url-parse": "^1.4.3" - } - }, "node_modules/p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", @@ -9741,23 +9807,15 @@ "node": ">=6" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "engines": { - "node": ">=4" - } - }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dependencies": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9774,20 +9832,6 @@ "node": ">=8" } }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -9803,14 +9847,15 @@ } }, "node_modules/p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "dependencies": { - "retry": "^0.12.0" + "@types/retry": "0.12.0", + "retry": "^0.13.1" }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/p-try": { @@ -9898,15 +9943,38 @@ } }, "node_modules/parse-numeric-range": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.2.0.tgz", - "integrity": "sha512-1q2tXpAOplPxcl8vrIGPWz1dJxxfmdRkCFcpxxMBerDnGuuHalOWF/xj9L8Nn5XoTUoB/6F0CeQBp2fMgkOYFg==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -9924,19 +9992,6 @@ "tslib": "^2.0.3" } }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -9948,7 +10003,7 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "engines": { "node": ">=0.10.0" } @@ -9956,7 +10011,7 @@ "node_modules/path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" }, "node_modules/path-key": { "version": "3.1.1", @@ -9974,7 +10029,7 @@ "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "node_modules/path-type": { "version": "4.0.0", @@ -9984,10 +10039,20 @@ "node": ">=8" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, "node_modules/picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "engines": { "node": ">=8.6" }, @@ -9995,33 +10060,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -10064,2241 +10102,803 @@ "path-exists": "^3.0.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "engines": { - "node": ">=4" - } - }, - "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/portfinder/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "8.2.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz", - "integrity": "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==", - "dependencies": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", - "source-map": "^0.6.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/postcss-calc": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.0.0.tgz", - "integrity": "sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==", - "dependencies": { - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-colormin": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.0.0.tgz", - "integrity": "sha512-Yt84+5V6CgS/AhK7d7MA58vG8dSZ7+ytlRtWLaQhag3HXOncTfmYpuUOX4cDoXjvLfw1sHRCHMiBjYhc35CymQ==", - "dependencies": { - "browserslist": "^4.16.0", - "color": "^3.1.1", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-convert-values": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.0.tgz", - "integrity": "sha512-V5kmYm4xoBAjNs+eHY/6XzXJkkGeg4kwNf2ocfqhLb1WBPEa4oaSmoi1fnVO7Dkblqvus9h+AenDvhCKUCK7uQ==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-discard-comments": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.0.tgz", - "integrity": "sha512-Umig6Gxs8m20RihiXY6QkePd6mp4FxkA1Dg+f/Kd6uw0gEMfKRjDeQOyFkLibexbJJGHpE3lrN/Q0R9SMrUMbQ==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.0.tgz", - "integrity": "sha512-vEJJ+Y3pFUnO1FyCBA6PSisGjHtnphL3V6GsNvkASq/VkP3OX5/No5RYXXLxHa2QegStNzg6HYrYdo71uR4caQ==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-discard-empty": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.0.tgz", - "integrity": "sha512-+wigy099Y1xZxG36WG5L1f2zeH1oicntkJEW4TDIqKKDO2g9XVB3OhoiHTu08rDEjLnbcab4rw0BAccwi2VjiQ==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.0.tgz", - "integrity": "sha512-hybnScTaZM2iEA6kzVQ6Spozy7kVdLw+lGw8hftLlBEzt93uzXoltkYp9u0tI8xbfhxDLTOOzHsHQCkYdmzRUg==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-discard-unused": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.0.0.tgz", - "integrity": "sha512-C+bchjnGRoGlSQjACMts/FlpY3LMDEUS5+9rHKxvl/NFUY/5OYWjkA1AEUo9HDWnFB44CFgcm6khLMSIbrjVEQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz", - "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==", - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.4", - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "semver": "^7.3.4" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/postcss-loader/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/postcss-merge-idents": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.0.0.tgz", - "integrity": "sha512-s8wwhAB/SJDPkcVxj31s2SGzgrO66ktUYjWh6j4qwY67Mzxx3/TkK+m/+v6tU/xyW4TmGd4yuyTXsHaaLC0jLg==", - "dependencies": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.1.tgz", - "integrity": "sha512-H1RO8le5deFGumQzuhJjuL0bIXPRysa+w7xtk5KrHe38oiaSS9ksPXDo24+IOS3SETPhip0J5+1uCOW+ALs3Yw==", - "dependencies": { - "css-color-names": "^1.0.1", - "postcss-value-parser": "^4.1.0", - "stylehacks": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-merge-rules": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.0.tgz", - "integrity": "sha512-TfsXbKjNYCGfUPEXGIGPySnMiJbdS+3gcVeV8gwmJP4RajyKZHW8E0FYDL1WmggTj3hi+m+WUCAvqRpX2ut4Kg==", - "dependencies": { - "browserslist": "^4.16.0", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^2.0.0", - "postcss-selector-parser": "^6.0.4", - "vendors": "^1.0.3" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.0.tgz", - "integrity": "sha512-zi2JhFaMOcIaNxhndX5uhsqSY1rexKDp23wV8EOmC9XERqzLbHsoRye3aYF716Zm+hkcR4loqKDt8LZlmihwAg==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.0.tgz", - "integrity": "sha512-/jPtNgs6JySMwgsE5dPOq8a2xEopWTW3RyqoB9fLqxgR+mDUNLSi7joKd+N1z7FXWgVkc4l/dEBMXHgNAaUbvg==", - "dependencies": { - "cssnano-utils": "^2.0.0", - "is-color-stop": "^1.1.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-minify-params": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.0.tgz", - "integrity": "sha512-KvZYIxTPBVKjdd+XgObq9A+Sfv8lMkXTpbZTsjhr42XbfWIeLaTItMlygsDWfjArEc3muUfDaUFgNSeDiJ5jug==", - "dependencies": { - "alphanum-sort": "^1.0.2", - "browserslist": "^4.16.0", - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0", - "uniqs": "^2.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.0.0.tgz", - "integrity": "sha512-cEM0O0eWwFIvmo6nfB0lH0vO/XFwgqIvymODbfPXZ1gTA3i76FKnb7TGUrEpiTxaXH6tgYQ6DcTHwRiRS+YQLQ==", - "dependencies": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^3.1.2" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dependencies": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.0.tgz", - "integrity": "sha512-pqsCkgo9KmQP0ew6DqSA+uP9YN6EfsW20pQ3JU5JoQge09Z6Too4qU0TNDsTNWuEaP8SWsMp+19l15210MsDZQ==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.0.tgz", - "integrity": "sha512-t4f2d//gH1f7Ns0Jq3eNdnWuPT7TeLuISZ6RQx4j8gpl5XrhkdshdNcOnlrEK48YU6Tcb6jqK7dorME3N4oOGA==", - "dependencies": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.0.tgz", - "integrity": "sha512-0o6/qU5ky74X/eWYj/tv4iiKCm3YqJnrhmVADpIMNXxzFZywsSQxl8F7cKs8jQEtF3VrJBgcDHTexZy1zgDoYg==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.0.tgz", - "integrity": "sha512-KRT14JbrXKcFMYuc4q7lh8lvv8u22wLyMrq+UpHKLtbx2H/LOjvWXYdoDxmNrrrJzomAWL+ViEXr48/IhSUJnQ==", - "dependencies": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-normalize-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.0.tgz", - "integrity": "sha512-wSO4pf7GNcDZpmelREWYADF1+XZWrAcbFLQCOqoE92ZwYgaP/RLumkUTaamEzdT2YKRZAH8eLLKGWotU/7FNPw==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.0.tgz", - "integrity": "sha512-TwPaDX+wl9wO3MUm23lzGmOzGCGKnpk+rSDgzB2INpakD5dgWR3L6bJq1P1LQYzBAvz8fRIj2NWdnZdV4EV98Q==", - "dependencies": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.0.tgz", - "integrity": "sha512-2CpVoz/67rXU5s9tsPZDxG1YGS9OFHwoY9gsLAzrURrCxTAb0H7Vp87/62LvVPgRWTa5ZmvgmqTp2rL8tlm72A==", - "dependencies": { - "browserslist": "^4.16.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-normalize-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.0.tgz", - "integrity": "sha512-ICDaGFBqLgA3dlrCIRuhblLl80D13YtgEV9NJPTYJtgR72vu61KgxAHv+z/lKMs1EbwfSQa3ALjOFLSmXiE34A==", - "dependencies": { - "is-absolute-url": "^3.0.3", - "normalize-url": "^4.5.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.0.tgz", - "integrity": "sha512-KRnxQvQAVkJfaeXSz7JlnD9nBN9sFZF9lrk9452Q2uRoqrRSkinqifF8Iex7wZGei2DZVG/qpmDFDmRvbNAOGA==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-ordered-values": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.0.tgz", - "integrity": "sha512-dPr+SRObiHueCIc4IUaG0aOGQmYkuNu50wQvdXTGKy+rzi2mjmPsbeDsheLk5WPb9Zyf2tp8E+I+h40cnivm6g==", - "dependencies": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-reduce-idents": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.0.0.tgz", - "integrity": "sha512-wDth7wkXAZ91i7GNe+/PJKyC9NOR2n04U0t5nnqlvlkKhMhnRn/8NJLYQRa7ZZHPGOZcOfvugrhblioTTg2X8A==", - "dependencies": { - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.0.tgz", - "integrity": "sha512-wR6pXUaFbSMG1oCKx8pKVA+rnSXCHlca5jMrlmkmif+uig0HNUTV9oGN5kjKsM3mATQAldv2PF9Tbl2vqLFjnA==", - "dependencies": { - "browserslist": "^4.16.0", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.0.tgz", - "integrity": "sha512-iHdGODW4YzM3WjVecBhPQt6fpJC4lGQZxJKjkBNHpp2b8dzmvj0ogKThqya+IRodQEFzjfXgYeESkf172FH5Lw==", - "dependencies": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-sort-media-queries": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-3.9.10.tgz", - "integrity": "sha512-pyCWbMrpQq4WjcYFrcVAvxS/+iHnXK5pxa1SAm1s9U4HZjGYU4gkCHwbHbzJ2ZFiiRYpRNRp85QuFvg6ZyKHxw==", - "dependencies": { - "sort-css-media-queries": "1.5.4" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.2.13" - } - }, - "node_modules/postcss-svgo": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.0.tgz", - "integrity": "sha512-M3/VS4sFI1Yp9g0bPL+xzzCNz5iLdRUztoFaugMit5a8sMfkVzzhwqbsOlD8IFFymCdJDmXmh31waYHWw1K4BA==", - "dependencies": { - "postcss-value-parser": "^4.1.0", - "svgo": "^2.3.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/postcss-svgo/node_modules/css-select": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", - "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^4.0.0", - "domhandler": "^4.0.0", - "domutils": "^2.4.3", - "nth-check": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/postcss-svgo/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/postcss-svgo/node_modules/css-what": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", - "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/postcss-svgo/node_modules/dom-serializer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", - "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/postcss-svgo/node_modules/domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/postcss-svgo/node_modules/domhandler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", - "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/postcss-svgo/node_modules/domutils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", - "integrity": "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/postcss-svgo/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/postcss-svgo/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/postcss-svgo/node_modules/nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/postcss-svgo/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-svgo/node_modules/svgo": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.3.0.tgz", - "integrity": "sha512-fz4IKjNO6HDPgIQxu4IxwtubtbSfGEAJUq/IXyTPIkGhWck/faiiwfkvsB8LnBkKLvSoyNNIY6d13lZprJMc9Q==", - "dependencies": { - "@trysound/sax": "0.1.1", - "chalk": "^4.1.0", - "commander": "^7.1.0", - "css-select": "^3.1.2", - "css-tree": "^1.1.2", - "csso": "^4.2.0", - "stable": "^0.1.8" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.0.tgz", - "integrity": "sha512-o9l4pF8SRn7aCMTmzb/kNv/kjV7wPZpZ8Nlb1Gq8v/Qvw969K1wanz1RVA0ehHzWe9+wHXaC2DvZlak/gdMJ5w==", - "dependencies": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.2", - "uniqs": "^2.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" - }, - "node_modules/postcss-zindex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.0.0.tgz", - "integrity": "sha512-thJp90qNZedxzfljsAnu7V35L/Zue/nVvWzPDLKZuqHmwDuy1vd3xkFVYfEa8WZZQaetvHtsi3uwjVD3UJAVeg==", - "dependencies": { - "has": "^1.0.3", - "uniqs": "^2.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" - } - }, - "node_modules/postcss/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "engines": { - "node": ">=4" - } - }, - "node_modules/prettier": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz", - "integrity": "sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/pretty-error": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", - "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^2.0.4" - } - }, - "node_modules/pretty-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", - "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/prism-react-renderer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.2.0.tgz", - "integrity": "sha512-GHqzxLYImx1iKN1jJURcuRoA/0ygCcNhfGw1IT8nPIMzarmKQ3Nc+JcG0gi8JXQzuh0C5ShE4npMIoqNin40hg==", - "peerDependencies": { - "react": ">=0.14.9" - } - }, - "node_modules/prismjs": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", - "integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", - "optionalDependencies": { - "clipboard": "^2.0.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/prompts": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", - "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", - "dependencies": { - "xtend": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dependencies": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dependencies": { - "escape-goat": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pure-color": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", - "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", - "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-base16-styling": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", - "integrity": "sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=", - "dependencies": { - "base16": "^1.0.0", - "lodash.curry": "^4.0.1", - "lodash.flow": "^3.3.0", - "pure-color": "^1.2.0" - } - }, - "node_modules/react-dev-utils": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", - "integrity": "sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A==", - "dependencies": { - "@babel/code-frame": "7.10.4", - "address": "1.1.2", - "browserslist": "4.14.2", - "chalk": "2.4.2", - "cross-spawn": "7.0.3", - "detect-port-alt": "1.1.6", - "escape-string-regexp": "2.0.0", - "filesize": "6.1.0", - "find-up": "4.1.0", - "fork-ts-checker-webpack-plugin": "4.1.6", - "global-modules": "2.0.0", - "globby": "11.0.1", - "gzip-size": "5.1.1", - "immer": "8.0.1", - "is-root": "2.1.0", - "loader-utils": "2.0.0", - "open": "^7.0.2", - "pkg-up": "3.1.0", - "prompts": "2.4.0", - "react-error-overlay": "^6.0.9", - "recursive-readdir": "2.2.2", - "shell-quote": "1.7.2", - "strip-ansi": "6.0.0", - "text-table": "0.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-dev-utils/node_modules/@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/react-dev-utils/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/react-dev-utils/node_modules/browserslist": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz", - "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==", - "dependencies": { - "caniuse-lite": "^1.0.30001125", - "electron-to-chromium": "^1.3.564", - "escalade": "^3.0.2", - "node-releases": "^1.1.61" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - }, - "node_modules/react-dev-utils/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/react-dev-utils/node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/react-dev-utils/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/react-dev-utils/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/react-dev-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dev-utils/node_modules/globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/react-dev-utils/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/react-dev-utils/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/react-dev-utils/node_modules/prompts": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", - "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/react-dev-utils/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/react-dom": { - "version": "16.14.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", - "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" - }, - "peerDependencies": { - "react": "^16.14.0" - } - }, - "node_modules/react-error-overlay": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", - "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" - }, - "node_modules/react-fast-compare": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", - "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" - }, - "node_modules/react-helmet": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", - "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", - "dependencies": { - "object-assign": "^4.1.1", - "prop-types": "^15.7.2", - "react-fast-compare": "^3.1.1", - "react-side-effect": "^2.1.0" - }, - "peerDependencies": { - "react": ">=16.3.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-json-view": { - "version": "1.21.3", - "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", - "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", - "dependencies": { - "flux": "^4.0.1", - "react-base16-styling": "^0.6.0", - "react-lifecycles-compat": "^3.0.4", - "react-textarea-autosize": "^8.3.2" - }, - "peerDependencies": { - "react": "^17.0.0 || ^16.3.0 || ^15.5.4", - "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" - } - }, - "node_modules/react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "node_modules/react-loadable": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/react-loadable/-/react-loadable-5.5.0.tgz", - "integrity": "sha512-C8Aui0ZpMd4KokxRdVAm2bQtI03k2RMRNzOB+IipV3yxFTSVICv7WoUr5L9ALB5BmKO1iHgZtWM8EvYG83otdg==", - "dependencies": { - "prop-types": "^15.5.0" - }, - "peerDependencies": { - "react": "*" - } - }, - "node_modules/react-loadable-ssr-addon-v5-slorber": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", - "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", - "dependencies": { - "@babel/runtime": "^7.10.3" - }, - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "react-loadable": "*", - "webpack": ">=4.41.1 || 5.x" - } - }, - "node_modules/react-router": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", - "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router-config": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", - "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", - "dependencies": { - "@babel/runtime": "^7.1.2" - }, - "peerDependencies": { - "react": ">=15", - "react-router": ">=5" - } - }, - "node_modules/react-router-dom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", - "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.2.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "node_modules/react-router/node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/react-side-effect": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz", - "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==", - "peerDependencies": { - "react": "^16.3.0 || ^17.0.0" - } - }, - "node_modules/react-textarea-autosize": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.2.tgz", - "integrity": "sha512-JrMWVgQSaExQByP3ggI1eA8zF4mF0+ddVuX7acUeK2V7bmrpjVOY72vmLz2IXFJSAXoY3D80nEzrn0GWajWK3Q==", - "dependencies": { - "@babel/runtime": "^7.10.2", - "use-composed-ref": "^1.0.0", - "use-latest": "^1.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "node": ">=6" } }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "p-limit": "^2.0.0" }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], "dependencies": { - "picomatch": "^2.2.1" + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" }, "engines": { - "node": ">=8.10.0" + "node": "^10 || ^12 || >=14" } }, - "node_modules/reading-time": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.3.0.tgz", - "integrity": "sha512-RJ8J5O6UvrclfZpcPSPuKusrdRfoY7uXXoYOOdeswZNtSkQaewT3919yz6RyloDBR+iwcUyz5zGOUjhgvfuv3g==" - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", "dependencies": { - "resolve": "^1.1.6" + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" }, - "engines": { - "node": ">= 0.10" + "peerDependencies": { + "postcss": "^8.2.2" } }, - "node_modules/recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "node_modules/postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", "dependencies": { - "minimatch": "3.0.4" + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "node_modules/regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", "dependencies": { - "regenerate": "^1.4.0" + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=4" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } }, - "node_modules/regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dependencies": { - "@babel/runtime": "^7.8.4" + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "engines": { + "node": "^10 || ^12 || >=14.0" }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/regex-not/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "node_modules/postcss-discard-unused": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", + "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "postcss-selector-parser": "^6.0.5" }, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/regex-not/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/postcss-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz", + "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==", "dependencies": { - "is-plain-object": "^2.0.4" + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "node_modules/postcss-merge-idents": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", + "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || >=14.0" }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", "dependencies": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" }, "engines": { - "node": ">=4" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "node_modules/postcss-merge-rules": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz", + "integrity": "sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==", "dependencies": { - "rc": "^1.2.8" + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" }, "engines": { - "node": ">=6.0.0" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/registry-url": { + "node_modules/postcss-minify-font-values": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", "dependencies": { - "rc": "^1.2.8" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" - }, - "node_modules/regjsparser": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", - "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", "dependencies": { - "jsesc": "~0.5.0" + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" }, - "bin": { - "regjsparser": "bin/parser" + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "bin": { - "jsesc": "bin/jsesc" + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/rehype-katex": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-4.0.0.tgz", - "integrity": "sha512-0mgBqYugQyIW0eUl6RDOZ28Cat2YzrnWGaYgKCMQnJw6ClmKgLqXBnkDAPGh2mwxvkkKwQOUMUpSLpA5rt7rzA==", + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", "dependencies": { - "@types/katex": "^0.11.0", - "hast-util-to-text": "^2.0.0", - "katex": "^0.12.0", - "rehype-parse": "^7.0.0", - "unified": "^9.0.0", - "unist-util-visit": "^2.0.0" + "postcss-selector-parser": "^6.0.5" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/rehype-parse": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz", - "integrity": "sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==", - "dependencies": { - "hast-util-from-parse5": "^6.0.0", - "parse5": "^6.0.0" + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "engines": { + "node": "^10 || ^12 || >= 14" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, "engines": { - "node": ">= 0.10" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/remark-admonitions": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/remark-admonitions/-/remark-admonitions-1.2.1.tgz", - "integrity": "sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow==", + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", "dependencies": { - "rehype-parse": "^6.0.2", - "unified": "^8.4.2", - "unist-util-visit": "^2.0.1" + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/remark-admonitions/node_modules/hast-util-from-parse5": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz", - "integrity": "sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA==", + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dependencies": { - "ccount": "^1.0.3", - "hastscript": "^5.0.0", - "property-information": "^5.0.0", - "web-namespaces": "^1.1.2", - "xtend": "^4.0.1" + "icss-utils": "^5.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/remark-admonitions/node_modules/hastscript": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz", - "integrity": "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==", - "dependencies": { - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "engines": { + "node": "^10 || ^12 || >=14.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/remark-admonitions/node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" - }, - "node_modules/remark-admonitions/node_modules/rehype-parse": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.2.tgz", - "integrity": "sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug==", + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", "dependencies": { - "hast-util-from-parse5": "^5.0.0", - "parse5": "^5.0.0", - "xtend": "^4.0.0" + "postcss-value-parser": "^4.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/remark-admonitions/node_modules/unified": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-8.4.2.tgz", - "integrity": "sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA==", + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", "dependencies": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" + "postcss-value-parser": "^4.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/remark-emoji": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz", - "integrity": "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==", + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", "dependencies": { - "emoticon": "^3.2.0", - "node-emoji": "^1.10.0", - "unist-util-visit": "^2.0.3" - } - }, - "node_modules/remark-footnotes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", - "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-math": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-3.0.1.tgz", - "integrity": "sha512-epT77R/HK0x7NqrWHdSV75uNLwn8g9qTyMqCRCDujL0vj/6T6+yhdrR7mjELWtkse+Fw02kijAaBuVcHBor1+Q==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/remark-mdx": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz", - "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==", + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", "dependencies": { - "@babel/core": "7.12.9", - "@babel/helper-plugin-utils": "7.10.4", - "@babel/plugin-proposal-object-rest-spread": "7.12.1", - "@babel/plugin-syntax-jsx": "7.12.1", - "@mdx-js/util": "1.6.22", - "is-alphabetical": "1.0.4", - "remark-parse": "8.0.3", - "unified": "9.2.0" + "postcss-value-parser": "^4.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/remark-mdx/node_modules/@babel/core": { - "version": "7.12.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", - "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.5", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.7", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.9", - "@babel/types": "^7.12.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": ">=6.9.0" + "node": "^10 || ^12 || >=14.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/remark-mdx/node_modules/@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, - "node_modules/remark-mdx/node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "postcss": "^8.2.15" } }, - "node_modules/remark-mdx/node_modules/@babel/plugin-syntax-jsx": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", - "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/remark-mdx/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" + "postcss": "^8.2.15" } }, - "node_modules/remark-parse": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", - "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", "dependencies": { - "ccount": "^1.0.0", - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^2.0.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^2.0.0", - "vfile-location": "^3.0.0", - "xtend": "^4.0.1" + "postcss-value-parser": "^4.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/remark-squeeze-paragraphs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz", - "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==", + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", "dependencies": { - "mdast-squeeze-paragraphs": "^4.0.0" + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "node_modules/renderkid": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz", - "integrity": "sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==", + "node_modules/postcss-reduce-idents": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", + "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", "dependencies": { - "css-select": "^2.0.2", - "dom-converter": "^0.2", - "htmlparser2": "^3.10.1", - "lodash": "^4.17.20", - "strip-ansi": "^3.0.0" - } - }, - "node_modules/renderkid/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/renderkid/node_modules/css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "node_modules/postcss-reduce-initial": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz", + "integrity": "sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==", "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/renderkid/node_modules/css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">= 6" + "node": "^10 || ^12 || >=14.0" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/renderkid/node_modules/domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" } }, - "node_modules/renderkid/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "node_modules/postcss-sort-media-queries": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.3.0.tgz", + "integrity": "sha512-jAl8gJM2DvuIJiI9sL1CuiHtKM4s5aEIomkU8G3LFvbP+p8i7Sz8VV63uieTgoewGqKbi+hxBTiOKJlB35upCg==", "dependencies": { - "ansi-regex": "^2.0.0" + "sort-css-media-queries": "2.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.4.16" } }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, "engines": { - "node": ">=0.10" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "engines": { - "node": ">=0.10.0" - } + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/postcss-zindex": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", + "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/require-like": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "engines": { - "node": "*" + "node": ">= 0.8.0" } }, - "node_modules/require-main-filename": { + "node_modules/prepend-http": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", + "engines": { + "node": ">=4" + } }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", "dependencies": { - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" + "lodash": "^4.17.20", + "renderkid": "^3.0.0" } }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "node_modules/pretty-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz", + "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==", "engines": { "node": ">=4" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" + "node_modules/prism-react-renderer": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", + "integrity": "sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==", + "peerDependencies": { + "react": ">=0.14.9" } }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "deprecated": "https://github.com/lydell/resolve-url#deprecated" - }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dependencies": { - "lowercase-keys": "^1.0.0" + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" } }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "engines": { - "node": ">=0.12" + "node": ">=0.4.0" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "engines": { - "node": ">= 4" + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dependencies": { + "asap": "~2.0.3" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" - }, - "node_modules/rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "xtend": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/rtl-detect": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.3.tgz", - "integrity": "sha512-2sMcZO60tL9YDEFe24gqddg3hJ+xSmJFN8IExcQUxeHxQzydQrN6GHPL+yAWgzItXSI7es53hcZC9pJneuZDKA==" - }, - "node_modules/rtlcss": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.1.2.tgz", - "integrity": "sha512-b04YSX37siupPOWUEguEBReWX2w4QT89C0PI9g2JzZycbq7zrgPmTr1DA1pizSWpKRFdCjjnrx/SSvU4fOHmGg==", + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { - "chalk": "^4.1.0", - "find-up": "^5.0.0", - "mkdirp": "^1.0.4", - "postcss": "^8.2.4", - "strip-json-comments": "^3.1.1" - }, - "bin": { - "rtlcss": "bin/rtlcss.js" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, - "peerDependencies": { - "postcss": "^8.2.4" + "engines": { + "node": ">= 0.10" } }, - "node_modules/rtlcss/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.10" } }, - "node_modules/rtlcss/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dependencies": { - "p-locate": "^5.0.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dependencies": { + "escape-goat": "^2.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/rtlcss/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dependencies": { - "p-limit": "^3.0.2" + "side-channel": "^1.0.4" }, "engines": { - "node": ">=10" + "node": ">=0.6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "funding": [ { "type": "github", @@ -12312,964 +10912,1025 @@ "type": "consulting", "url": "https://feross.org/support" } - ], + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dependencies": { - "queue-microtask": "^1.2.2" + "safe-buffer": "^5.1.0" } }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dependencies": { - "tslib": "^1.9.0" - }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "engines": { - "npm": ">=2.0.0" + "node": ">= 0.6" } }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { - "ret": "~0.1.10" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "node_modules/scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" } }, - "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=0.10.0" } }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", - "optional": true - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" - }, - "node_modules/selfsigned": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", - "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "node_modules/react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", "dependencies": { - "node-forge": "^0.10.0" + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" } }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, - "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "node_modules/react-dev-utils/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dependencies": { - "semver": "^6.3.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "engines": { + "node": ">= 12.13.0" } }, - "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "node_modules/react-dev-utils/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/react-dev-utils/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dependencies": { - "ms": "2.0.0" + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "node_modules/react-dev-utils/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dependencies": { - "randombytes": "^2.1.0" + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/serve-handler": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", - "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", + "node_modules/react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "dependencies": { - "bytes": "3.0.0", - "content-disposition": "0.5.2", - "fast-url-parser": "1.1.3", - "mime-types": "2.1.18", - "minimatch": "3.0.4", - "path-is-inside": "1.0.2", - "path-to-regexp": "2.2.1", - "range-parser": "1.2.0" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + }, + "peerDependencies": { + "react": "^16.14.0" } }, - "node_modules/serve-handler/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "engines": { - "node": ">= 0.8" - } + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, - "node_modules/serve-handler/node_modules/content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "engines": { - "node": ">= 0.6" - } + "node_modules/react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, - "node_modules/serve-handler/node_modules/mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "engines": { - "node": ">= 0.6" + "node_modules/react-helmet-async": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/serve-handler/node_modules/mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", "dependencies": { - "mime-db": "~1.33.0" + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" }, - "engines": { - "node": ">= 0.6" + "peerDependencies": { + "react": "^17.0.0 || ^16.3.0 || ^15.5.4", + "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" } }, - "node_modules/serve-handler/node_modules/path-to-regexp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", - "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, - "node_modules/serve-handler/node_modules/range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", - "engines": { - "node": ">= 0.6" + "node_modules/react-loadable": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-loadable/-/react-loadable-5.5.0.tgz", + "integrity": "sha512-C8Aui0ZpMd4KokxRdVAm2bQtI03k2RMRNzOB+IipV3yxFTSVICv7WoUr5L9ALB5BmKO1iHgZtWM8EvYG83otdg==", + "peer": true, + "dependencies": { + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": "*" } }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "node_modules/react-loadable-ssr-addon-v5-slorber": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz", + "integrity": "sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A==", "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "@babel/runtime": "^7.10.3" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10.13.0" + }, + "peerDependencies": { + "react-loadable": "*", + "webpack": ">=4.41.1 || 5.x" } }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/react-router": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", "dependencies": { - "ms": "2.0.0" + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" } }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "node_modules/react-router-config": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz", + "integrity": "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "@babel/runtime": "^7.1.2" }, - "engines": { - "node": ">= 0.6" + "peerDependencies": { + "react": ">=15", + "react-router": ">=5" } }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "node_modules/react-router-dom": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", + "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.3.4", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "node_modules/react-router/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "node_modules/react-router/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dependencies": { + "isarray": "0.0.1" + } }, - "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "node_modules/react-textarea-autosize": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.0.tgz", + "integrity": "sha512-YrTFaEHLgJsi8sJVYHBzYn+mkP3prGkmP2DKb/tm0t7CLJY5t1Rxix8070LAKb0wby7bl/lf2EeHkuMihMZMwQ==", "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" + "@babel/runtime": "^7.10.2", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dependencies": { - "kind-of": "^6.0.2" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=8" + "node": ">=8.10.0" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/reading-time": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dependencies": { - "shebang-regex": "^3.0.0" + "resolve": "^1.1.6" }, "engines": { - "node": ">=8" + "node": ">= 0.10" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dependencies": { + "minimatch": "^3.0.5" + }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/shell-quote": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, - "node_modules/shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" + "regenerate": "^1.4.2" }, "engines": { "node": ">=4" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/regenerator-runtime": { + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "node_modules/regexpu-core": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", + "integrity": "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==", "dependencies": { - "is-arrayish": "^0.3.1" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/sirv": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.11.tgz", - "integrity": "sha512-SR36i3/LSWja7AJNRBz4fF/Xjpn7lQFI30tZ434dIy+bitLYSP+ZEenHg36i23V2SGEz+kqjksg0uOGZ5LPiqg==", + }, + "node_modules/registry-auth-token": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", "dependencies": { - "@polka/url": "^1.0.0-next.9", - "mime": "^2.3.1", - "totalist": "^1.0.0" + "rc": "1.2.8" }, "engines": { - "node": ">= 10" + "node": ">=6.0.0" } }, - "node_modules/sirv/node_modules/mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", - "bin": { - "mime": "cli.js" + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dependencies": { + "rc": "^1.2.8" }, "engines": { - "node": ">=4.0.0" + "node": ">=8" } }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + "node_modules/regjsgen": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==" }, - "node_modules/sitemap": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-6.4.0.tgz", - "integrity": "sha512-DoPKNc2/apQZTUnfiOONWctwq7s6dZVspxAZe2VPMNtoqNq7HgXRvlRnbIpKjf+8+piQdWncwcy+YhhTGY5USQ==", + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dependencies": { - "@types/node": "^14.14.28", - "@types/sax": "^1.2.1", - "arg": "^5.0.0", - "sax": "^1.2.4" + "jsesc": "~0.5.0" }, "bin": { - "sitemap": "dist/cli.js" - }, - "engines": { - "node": ">=10.3.0", - "npm": ">=5.6.0" + "regjsparser": "bin/parser" } }, - "node_modules/sitemap/node_modules/@types/node": { - "version": "14.14.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.45.tgz", - "integrity": "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" } }, - "node_modules/slice-ansi": { + "node_modules/rehype-katex": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-4.0.0.tgz", + "integrity": "sha512-0mgBqYugQyIW0eUl6RDOZ28Cat2YzrnWGaYgKCMQnJw6ClmKgLqXBnkDAPGh2mwxvkkKwQOUMUpSLpA5rt7rzA==", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" + "@types/katex": "^0.11.0", + "hast-util-to-text": "^2.0.0", + "katex": "^0.12.0", + "rehype-parse": "^7.0.0", + "unified": "^9.0.0", + "unist-util-visit": "^2.0.0" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "node_modules/rehype-parse": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz", + "integrity": "sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==", "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "hast-util-from-parse5": "^6.0.0", + "parse5": "^6.0.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "node_modules/remark-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz", + "integrity": "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==", "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "emoticon": "^3.2.0", + "node-emoji": "^1.10.0", + "unist-util-visit": "^2.0.3" + } + }, + "node_modules/remark-footnotes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", + "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/snapdragon-util": { + "node_modules/remark-math": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-3.0.1.tgz", + "integrity": "sha512-epT77R/HK0x7NqrWHdSV75uNLwn8g9qTyMqCRCDujL0vj/6T6+yhdrR7mjELWtkse+Fw02kijAaBuVcHBor1+Q==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz", + "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==", "dependencies": { - "kind-of": "^3.2.0" + "@babel/core": "7.12.9", + "@babel/helper-plugin-utils": "7.10.4", + "@babel/plugin-proposal-object-rest-spread": "7.12.1", + "@babel/plugin-syntax-jsx": "7.12.1", + "@mdx-js/util": "1.6.22", + "is-alphabetical": "1.0.4", + "remark-parse": "8.0.3", + "unified": "9.2.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/snapdragon-util/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/remark-mdx/node_modules/@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", "dependencies": { - "is-buffer": "^1.1.5" + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/remark-mdx/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "node_modules/remark-mdx/node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", "dependencies": { - "ms": "2.0.0" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "node_modules/remark-mdx/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", "dependencies": { - "is-descriptor": "^0.1.0" + "@babel/helper-plugin-utils": "^7.10.4" }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/remark-mdx/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/remark-mdx/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "node_modules/remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", "dependencies": { - "kind-of": "^3.0.2" + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/remark-squeeze-paragraphs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==", "dependencies": { - "is-buffer": "^1.1.5" + "mdast-squeeze-paragraphs": "^4.0.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/snapdragon/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } }, - "node_modules/snapdragon/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "node_modules/renderkid/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dependencies": { - "kind-of": "^3.0.2" + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dependencies": { - "is-buffer": "^1.1.5" + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/renderkid/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "domelementtype": "^2.2.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/sockjs": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", - "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^3.4.0", - "websocket-driver": "^0.7.4" + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/sockjs-client": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.1.tgz", - "integrity": "sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ==", - "dependencies": { - "debug": "^3.2.6", - "eventsource": "^1.0.7", - "faye-websocket": "^0.11.3", - "inherits": "^2.0.4", - "json3": "^3.3.3", - "url-parse": "^1.5.1" + "node_modules/renderkid/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/sockjs-client/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/renderkid/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], "dependencies": { - "ms": "^2.1.1" + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" } }, - "node_modules/sort-css-media-queries": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-1.5.4.tgz", - "integrity": "sha512-YP5W/h4Sid/YP7Lp87ejJ5jP13/Mtqt2vx33XyhO+IAugKlufRPbOrPlIiEUuxmpNBSBd3EeeQpFhdu3RfI2Ag==", + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "engines": { - "node": ">= 6.3.0" + "node": ">=0.10" } }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "engines": { "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/require-like": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", + "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==", "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, - "node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "engines": { - "node": ">=6.0.0" + "node": ">=4" } }, - "node_modules/spdy-transport": { + "node_modules/resolve-pathname": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" + "lowercase-keys": "^1.0.0" } }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dependencies": { - "extend-shallow": "^3.0.0" - }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/split-string/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "engines": { + "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/split-string/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dependencies": { - "is-plain-object": "^2.0.4" + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, - "node_modules/state-toggle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", - "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } + "node_modules/rtl-detect": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz", + "integrity": "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==" }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "node_modules/rtlcss": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz", + "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==", "dependencies": { - "is-descriptor": "^0.1.0" + "find-up": "^5.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.3.11", + "strip-json-comments": "^3.1.1" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "rtlcss": "bin/rtlcss.js" } }, - "node_modules/static-extend/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "node_modules/rtlcss/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dependencies": { - "kind-of": "^3.0.2" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" + "node": ">=10" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/static-extend/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/static-extend/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "node_modules/rtlcss/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dependencies": { - "kind-of": "^3.0.2" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/rtlcss/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dependencies": { - "is-buffer": "^1.1.5" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/rtlcss/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/std-env": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.3.0.tgz", - "integrity": "sha512-4qT5B45+Kjef2Z6pE0BkskzsH0GO7GrND0wGlTM1ioUe3v0dGYx9ZJH0Aro/YyA8fqQ5EyIKDRjZojJYMFTflw==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "ci-info": "^3.0.0" + "queue-microtask": "^1.2.2" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "dependencies": { - "safe-buffer": "~5.2.0" + "tslib": "^2.1.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", @@ -13288,1947 +11949,2046 @@ } ] }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "node_modules/string.prototype.matchall": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz", - "integrity": "sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ==", + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has-symbols": "^1.0.1", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", - "side-channel": "^1.0.4" + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=4" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "node-forge": "^1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "node_modules/semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", "dependencies": { - "ansi-regex": "^5.0.0" + "semver": "^6.3.0" }, "engines": { "node": ">=8" } }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", - "engines": { - "node": ">=0.10.0" + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/strip-final-newline": { + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dependencies": { + "randombytes": "^2.1.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/serve-handler": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.3.tgz", + "integrity": "sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "fast-url-parser": "1.1.3", + "mime-types": "2.1.18", + "minimatch": "3.0.4", + "path-is-inside": "1.0.2", + "path-to-regexp": "2.2.1", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/style-to-object": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", - "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", - "dependencies": { - "inline-style-parser": "0.1.1" + "node_modules/serve-handler/node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "engines": { + "node": ">= 0.6" } }, - "node_modules/stylehacks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.0.tgz", - "integrity": "sha512-QOWm6XivDLb+fqffTZP8jrmPmPITVChl2KCY2R05nsCWwLi3VGhCdVc3IVGNwd1zzTt1jPd67zIKjpQfxzQZeA==", + "node_modules/serve-handler/node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dependencies": { - "browserslist": "^4.16.0", - "postcss-selector-parser": "^6.0.4" + "mime-db": "~1.33.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.1" + "node": ">= 0.6" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/serve-handler/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dependencies": { - "has-flag": "^4.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + "node_modules/serve-handler/node_modules/path-to-regexp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", + "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" }, - "node_modules/svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "dependencies": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "bin": { - "svgo": "bin/svgo" + "node_modules/serve-handler/node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" }, "engines": { - "node": ">=4.0.0" + "node": ">= 0.8.0" } }, - "node_modules/svgo/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" + "ms": "2.0.0" } }, - "node_modules/svgo/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" } }, - "node_modules/svgo/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" }, "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/svgo/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" }, - "node_modules/svgo/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/svgo/node_modules/css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, - "node_modules/svgo/node_modules/css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "node": ">= 0.6" } }, - "node_modules/svgo/node_modules/domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/svgo/node_modules/escape-string-regexp": { + "node_modules/setimmediate": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, - "node_modules/svgo/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "node_modules/svgo/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "kind-of": "^6.0.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=8" } }, - "node_modules/svgo/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" }, - "node_modules/svgo/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dependencies": { - "has-flag": "^3.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dependencies": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "engines": { - "node": ">=10.0.0" + "node": ">=8" } }, - "node_modules/table/node_modules/ajv": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.3.0.tgz", - "integrity": "sha512-RYE7B5An83d7eWnDR8kbdaIFqmKCNsP16ay1hDbJEU+sa0e3H9SebskCt0Uufem6cfAVu7Col6ubcn/W+Sm8/Q==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, + "node_modules/shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/tapable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", - "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", - "engines": { - "node": ">=6" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" }, "bin": { - "terser": "bin/terser" + "shjs": "bin/shjs" }, "engines": { - "node": ">=6.0.0" + "node": ">=4" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz", - "integrity": "sha512-6QhDaAiVHIQr5Ab3XUWZyDmrIPCHMiqJVljMF91YKyqwKkL5QHnYMkrMBy96v9Z7ev1hGhSEw1HQZc2p/s5Z8Q==", + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dependencies": { - "jest-worker": "^26.6.2", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "source-map": "^0.6.1", - "terser": "^5.7.0" - }, - "engines": { - "node": ">= 10.13.0" + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/terser-webpack-plugin/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "node_modules/sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">= 10" } }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, - "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz", - "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==", + "node_modules/sitemap": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", + "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.2.4" }, "bin": { - "terser": "bin/terser" + "sitemap": "dist/cli.js" }, "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin/node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/terser/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0", + "npm": ">=5.6.0" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "node_modules/timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" - }, - "node_modules/tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", - "optional": true - }, - "node_modules/tiny-invariant": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", - "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + "node_modules/sitemap/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dependencies": { - "kind-of": "^3.0.2" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/to-object-path/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" } }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "node_modules/sort-css-media-queries": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", + "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==", "engines": { - "node": ">=6" + "node": ">= 6.3.0" } }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "engines": { "node": ">=0.10.0" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "engines": { - "node": ">=8.0" + "node": ">=0.10.0" } }, - "node_modules/to-regex/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/to-regex/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, "engines": { - "node": ">=0.6" + "node": ">=6.0.0" } }, - "node_modules/totalist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", - "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", - "engines": { - "node": ">=6" + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, - "node_modules/trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" }, - "node_modules/trim-trailing-lines": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", - "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==", + "node_modules/state-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", + "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" } }, - "node_modules/ts-essentials": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-2.0.12.tgz", - "integrity": "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==" - }, - "node_modules/tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { - "prelude-ls": "^1.2.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" + "node_modules/string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "node_modules/string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" }, - "engines": { - "node": ">= 0.6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "dependencies": { - "is-typedarray": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ua-parser-js": { - "version": "0.7.28", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", - "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, "engines": { - "node": "*" + "node": ">=4" } }, - "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/unherit": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", - "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", - "dependencies": { - "inherits": "^2.0.0", - "xtend": "^4.0.0" + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "engines": { - "node": ">=4" + "node_modules/style-to-object": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", + "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", + "dependencies": { + "inline-style-parser": "0.1.1" } }, - "node_modules/unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", "dependencies": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" }, "engines": { - "node": ">=4" + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" } }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "engines": { - "node": ">=4" - } - }, - "node_modules/unified": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", - "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", - "dependencies": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, - "node_modules/uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" - }, - "node_modules/uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, + "node_modules/svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "engines": { - "node": ">=8" + "node": ">= 10" } }, - "node_modules/unist-builder": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", - "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", + "node_modules/svgo/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/unist-util-find-after": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz", - "integrity": "sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==", + "node_modules/svgo/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dependencies": { - "unist-util-is": "^4.0.0" + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/unist-util-generated": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", - "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", + "node_modules/svgo/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "node_modules/svgo/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/unist-util-position": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", - "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", + "node_modules/svgo/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/unist-util-remove": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz", - "integrity": "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==", + "node_modules/table": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", "dependencies": { - "unist-util-is": "^4.0.0" + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=10.0.0" } }, - "node_modules/unist-util-remove-position": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", - "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "node_modules/table/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dependencies": { - "unist-util-visit": "^2.0.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "dependencies": { - "@types/unist": "^2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" } }, - "node_modules/unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": ">=4.5" } }, - "node_modules/unist-util-visit-parents": { + "node_modules/tar/node_modules/yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", - "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/terser": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "engines": { - "node": ">= 10.0.0" + "node": ">=10" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "node_modules/terser-webpack-plugin": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.14", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" + }, "engines": { - "node": ">= 0.8" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "node_modules/unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" } }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dependencies": { - "isarray": "1.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "node_modules/terser/node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "engines": { - "node": ">=4", - "yarn": "*" + "node": ">=4" } }, - "node_modules/update-notifier": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", - "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dependencies": { - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.4.0", - "is-npm": "^5.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.1.0", - "pupa": "^2.1.1", - "semver": "^7.3.4", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" + "is-number": "^7.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/yeoman/update-notifier?sponsor=1" + "node": ">=8.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" } }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "node_modules/totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", "engines": { "node": ">=6" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "deprecated": "Please see https://github.com/lydell/urix#deprecated" + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" + "node_modules/trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==" + }, + "node_modules/trim-trailing-lines": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", + "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/url-loader": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", - "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "node_modules/trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "peer": true, "dependencies": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.27", - "schema-utils": "^3.0.0" + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" }, "peerDependencies": { - "file-loader": "*", - "webpack": "^4.0.0 || ^5.0.0" + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" }, "peerDependenciesMeta": { - "file-loader": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { "optional": true } } }, - "node_modules/url-loader/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dependencies": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "node_modules/ts-node/node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "peer": true, + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=0.4.0" } }, - "node_modules/url-parse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", - "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "peer": true }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dependencies": { - "prepend-http": "^2.0.0" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=4" + "node": ">= 0.8.0" } }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/use-composed-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.1.0.tgz", - "integrity": "sha512-my1lNHGWsSDAhhVAT4MKs6IjBUtG6ZG11uUqexPH9PptiIZDQOzaF4f5tEbJ2+7qvNbtXNBbU3SfmN+fXlWDhg==", + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dependencies": { - "ts-essentials": "^2.0.3" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/use-isomorphic-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz", - "integrity": "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" } }, - "node_modules/use-latest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz", - "integrity": "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==", - "dependencies": { - "use-isomorphic-layout-effect": "^1.0.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "node_modules/typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=4.2.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "node_modules/ua-parser-js": { + "version": "0.7.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", + "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } }, - "node_modules/util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + "node_modules/unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "dependencies": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "engines": { - "node": ">= 4" + "node": ">=4" } }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, "engines": { - "node": ">= 0.4.0" + "node": ">=4" } }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "bin": { - "uuid": "bin/uuid" + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "engines": { + "node": ">=4" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + "node_modules/unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/vendors": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", - "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "node_modules/unist-builder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", + "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/vfile": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", - "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "node_modules/unist-util-find-after": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-3.0.0.tgz", + "integrity": "sha512-ojlBqfsBftYXExNu3+hHLfJQ/X1jYY/9vdm4yZWjIbf0VuWF6CRufci1ZyoD/wV2TYMKxXUoNuoqwy+CkgzAiQ==", "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" + "unist-util-is": "^4.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/vfile-location": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", - "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", + "node_modules/unist-util-generated": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", + "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/wait-on": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-5.3.0.tgz", - "integrity": "sha512-DwrHrnTK+/0QFaB9a8Ol5Lna3k7WvUR4jzSKmz0YaPBpuN2sACyiPVKVfj6ejnjcajAcvn3wlbTyMIn9AZouOg==", - "dependencies": { - "axios": "^0.21.1", - "joi": "^17.3.0", - "lodash": "^4.17.21", - "minimist": "^1.2.5", - "rxjs": "^6.6.3" - }, - "bin": { - "wait-on": "bin/wait-on" - }, - "engines": { - "node": ">=8.9.0" + "node_modules/unist-util-position": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", + "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/watchpack": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", - "integrity": "sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==", + "node_modules/unist-util-remove": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz", + "integrity": "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==", "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" + "unist-util-is": "^4.0.0" }, - "engines": { - "node": ">=10.13.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "node_modules/unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", "dependencies": { - "minimalistic-assert": "^1.0.0" + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/web-namespaces": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", - "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dependencies": { + "@types/unist": "^2.0.2" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "node_modules/webpack": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.37.0.tgz", - "integrity": "sha512-yvdhgcI6QkQkDe1hINBAJ1UNevqNGTVaCkD2SSJcB8rcrNNl922RI8i2DXUAuNfANoxwsiXXEA4ZPZI9q2oGLA==", - "dependencies": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.47", - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/wasm-edit": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "acorn": "^8.2.1", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.0", - "es-module-lexer": "^0.4.0", - "eslint-scope": "^5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.0.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.1", - "watchpack": "^2.0.0", - "webpack-sources": "^2.1.1" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" + "node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } + "url": "https://opencollective.com/unified" } }, - "node_modules/webpack-bundle-analyzer": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.1.tgz", - "integrity": "sha512-j5m7WgytCkiVBoOGavzNokBOqxe6Mma13X1asfVYtKWM3wxBiRRu1u1iG0Iol5+qp9WgyhkMmBAcvjEfJ2bdDw==", + "node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", "dependencies": { - "acorn": "^8.0.4", - "acorn-walk": "^8.0.0", - "chalk": "^4.1.0", - "commander": "^6.2.0", - "gzip-size": "^6.0.0", - "lodash": "^4.17.20", - "opener": "^1.5.2", - "sirv": "^1.0.7", - "ws": "^7.3.1" - }, - "bin": { - "webpack-bundle-analyzer": "lib/bin/analyzer.js" + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" }, - "engines": { - "node": ">= 10.13.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/webpack-bundle-analyzer/node_modules/acorn": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", - "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==", - "bin": { - "acorn": "bin/acorn" - }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "engines": { - "node": ">=0.4.0" + "node": ">= 10.0.0" } }, - "node_modules/webpack-bundle-analyzer/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "engines": { - "node": ">= 6" + "node": ">= 0.8" } }, - "node_modules/webpack-bundle-analyzer/node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], "dependencies": { - "duplexer": "^0.1.2" + "escalade": "^3.1.1", + "picocolors": "^1.0.0" }, - "engines": { - "node": ">=10" + "bin": { + "browserslist-lint": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/webpack-dev-middleware": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", - "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "node_modules/update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", "dependencies": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", - "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=10" }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" } }, - "node_modules/webpack-dev-middleware/node_modules/mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/webpack-dev-middleware/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "node_modules/uri-js/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" } }, - "node_modules/webpack-dev-server": { - "version": "3.11.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz", - "integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==", + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", "dependencies": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.3.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.8", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.26", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.8", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "^0.3.21", - "sockjs-client": "^1.5.0", - "spdy": "^4.0.2", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "^13.3.2" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" }, "engines": { - "node": ">= 6.11.5" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" }, "peerDependencies": { + "file-loader": "*", "webpack": "^4.0.0 || ^5.0.0" }, "peerDependenciesMeta": { - "webpack-cli": { + "file-loader": { "optional": true } } }, - "node_modules/webpack-dev-server/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-dev-server/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/webpack-dev-server/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "node_modules/url-loader/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dependencies": { - "remove-trailing-separator": "^1.0.1" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack-dev-server/node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", "dependencies": { - "array-uniq": "^1.0.1" + "prepend-http": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/webpack-dev-server/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "engines": { - "node": ">=0.10.0" + "node_modules/use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/webpack-dev-server/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, - "engines": { - "node": ">=0.10.0" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.", + "node_modules/use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "use-isomorphic-layout-effect": "^1.1.1" }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/webpack-dev-server/node_modules/del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dependencies": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, - "engines": { - "node": ">=6" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/webpack-dev-server/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/webpack-dev-server/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "engines": { - "node": ">= 4.0" + "node": ">= 0.4.0" } }, - "node_modules/webpack-dev-server/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/webpack-dev-server/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dependencies": { - "is-extglob": "^2.1.0" - }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "peer": true + }, + "node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/webpack-dev-server/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "node_modules/vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/webpack-dev-server/node_modules/globby/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "engines": { - "node": ">=0.10.0" + "node_modules/vfile-location": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", + "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/webpack-dev-server/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/webpack-dev-server/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "node_modules/wait-on": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz", + "integrity": "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==", "dependencies": { - "binary-extensions": "^1.0.0" + "axios": "^0.25.0", + "joi": "^17.6.0", + "lodash": "^4.17.21", + "minimist": "^1.2.5", + "rxjs": "^7.5.4" + }, + "bin": { + "wait-on": "bin/wait-on" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.0.0" } }, - "node_modules/webpack-dev-server/node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/webpack-dev-server/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dependencies": { - "is-plain-object": "^2.0.4" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, - "node_modules/webpack-dev-server/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" + "minimalistic-assert": "^1.0.0" } }, - "node_modules/webpack-dev-server/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" + "node_modules/web-namespaces": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", + "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, - "node_modules/webpack-dev-server/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/webpack-bundle-analyzer": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.1.tgz", + "integrity": "sha512-oKz9Oz9j3rUciLNfpGFjOb49/jEpXNmWdVH8Ls//zNcnLlQdTGXQQMsBbb/gR7Zl8WNLxVCq+0Hqbx3zv6twBw==", "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" } }, - "node_modules/webpack-dev-server/node_modules/micromatch/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "node_modules/webpack-bundle-analyzer/node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/webpack-dev-server/node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "engines": { - "node": ">=6" + "node": ">= 10" } }, - "node_modules/webpack-dev-server/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/webpack-dev-server/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=0.10" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/webpack-dev-server/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dependencies": { - "glob": "^7.1.3" + "fast-deep-equal": "^3.1.3" }, - "bin": { - "rimraf": "bin.js" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" }, "engines": { - "node": ">= 4" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack-dev-server/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/webpack-dev-server": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/webpack-dev-server/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/webpack-dev-server/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dependencies": { - "ansi-regex": "^2.0.0" + "webpack-dev-server": "bin/webpack-dev-server.js" }, "engines": { - "node": ">=0.10.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, - "node_modules/webpack-dev-server/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { - "has-flag": "^3.0.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=6" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/webpack-dev-server/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "fast-deep-equal": "^3.1.3" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dependencies": { - "async-limiter": "~1.0.0" - } + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "node_modules/webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dependencies": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" }, "engines": { - "node": ">= 6" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack-log/node_modules/ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "engines": { - "node": ">=6" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/webpack-merge": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", - "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dependencies": { "clone-deep": "^4.0.1", "wildcard": "^2.0.0" @@ -15237,27 +13997,10 @@ "node": ">=10.0.0" } }, - "node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/webpack-sources/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/webpack/node_modules/acorn": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", - "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", "bin": { "acorn": "bin/acorn" }, @@ -15265,72 +14008,61 @@ "node": ">=0.4.0" } }, - "node_modules/webpack/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" + "node_modules/webpack/node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "peerDependencies": { + "acorn": "^8" } }, "node_modules/webpack/node_modules/schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dependencies": { - "@types/json-schema": "^7.0.6", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" }, "engines": { "node": ">= 10.13.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/webpack/node_modules/webpack-sources": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", - "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", - "dependencies": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "engines": { "node": ">=10.13.0" } }, "node_modules/webpackbar": { - "version": "5.0.0-3", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz", - "integrity": "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", + "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", "dependencies": { - "ansi-escapes": "^4.3.1", "chalk": "^4.1.0", - "consola": "^2.15.0", - "figures": "^3.2.0", + "consola": "^2.15.3", "pretty-time": "^1.1.0", - "std-env": "^2.2.1", - "text-table": "^0.2.0", - "wrap-ansi": "^7.0.0" + "std-env": "^3.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "peerDependencies": { "webpack": "3 || 4 || 5" } }, + "node_modules/webpackbar/node_modules/std-env": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.0.tgz", + "integrity": "sha512-cNNS+VYsXIs5gI6gJipO4qZ8YYT274JHvNnQ1/R/x8Q8mdP0qj0zoMchRXmBNPqp/0eOEhX+3g7g6Fgb7meLIQ==" + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -15355,7 +14087,7 @@ "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -15390,11 +14122,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, "node_modules/widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -15419,14 +14146,6 @@ "node": ">=0.10.0" } }, - "node_modules/worker-rpc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", - "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", - "dependencies": { - "microevent.ts": "~0.1.1" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -15446,7 +14165,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -15460,9 +14179,9 @@ } }, "node_modules/ws": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", - "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "engines": { "node": ">=8.3.0" }, @@ -15506,11 +14225,6 @@ "node": ">=0.4" } }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -15524,137 +14238,20 @@ "node": ">= 6" } }, - "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/yargs-parser/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "node_modules/yargs/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "engines": { - "node": ">=4" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "peer": true, "engines": { "node": ">=6" } @@ -15682,174 +14279,188 @@ }, "dependencies": { "@algolia/autocomplete-core": { - "version": "1.0.0-alpha.44", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.0.0-alpha.44.tgz", - "integrity": "sha512-2iMXthldMIDXtlbg9omRKLgg1bLo2ZzINAEqwhNjUeyj1ceEyL1ck6FY0VnJpf2LsjmNthHCz2BuFk+nYUeDNA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.7.1.tgz", + "integrity": "sha512-eiZw+fxMzNQn01S8dA/hcCpoWCOCwcIIEUtHHdzN5TGB3IpzLbuhqFeTfh2OUhhgkE8Uo17+wH+QJ/wYyQmmzg==", "requires": { - "@algolia/autocomplete-shared": "1.0.0-alpha.44" + "@algolia/autocomplete-shared": "1.7.1" } }, "@algolia/autocomplete-preset-algolia": { - "version": "1.0.0-alpha.44", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.0.0-alpha.44.tgz", - "integrity": "sha512-DCHwo5ovzg9k2ejUolGNTLFnIA7GpsrkbNJTy1sFbMnYfBmeK8egZPZnEl7lBTr27OaZu7IkWpTepLVSztZyng==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.1.tgz", + "integrity": "sha512-pJwmIxeJCymU1M6cGujnaIYcY3QPOVYZOXhFkWVM7IxKzy272BwCvMFMyc5NpG/QmiObBxjo7myd060OeTNJXg==", "requires": { - "@algolia/autocomplete-shared": "1.0.0-alpha.44" + "@algolia/autocomplete-shared": "1.7.1" } }, "@algolia/autocomplete-shared": { - "version": "1.0.0-alpha.44", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.0.0-alpha.44.tgz", - "integrity": "sha512-2oQZPERYV+yNx/yoVWYjZZdOqsitJ5dfxXJjL18yczOXH6ujnsq+DTczSrX+RjzjQdVeJ1UAG053EJQF/FOiMg==" + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.1.tgz", + "integrity": "sha512-eTmGVqY3GeyBTT8IWiB2K5EuURAqhnumfktAEoHxfDY2o7vg2rSnO16ZtIG0fMgt3py28Vwgq42/bVEuaQV7pg==" }, "@algolia/cache-browser-local-storage": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.9.1.tgz", - "integrity": "sha512-bAUU9vKCy45uTTlzJw0LYu1IjoZsmzL6lgjaVFaW1crhX/4P+JD5ReQv3n/wpiXSFaHq1WEO3WyH2g3ymzeipQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.2.tgz", + "integrity": "sha512-FRweBkK/ywO+GKYfAWbrepewQsPTIEirhi1BdykX9mxvBPtGNKccYAxvGdDCumU1jL4r3cayio4psfzKMejBlA==", "requires": { - "@algolia/cache-common": "4.9.1" + "@algolia/cache-common": "4.14.2" } }, "@algolia/cache-common": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.9.1.tgz", - "integrity": "sha512-tcvw4mOfFy44V4ZxDEy9wNGr6vFROZKRpXKTEBgdw/WBn6mX51H1ar4RWtceDEcDU4H5fIv5tsY3ip2hU+fTPg==" + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.14.2.tgz", + "integrity": "sha512-SbvAlG9VqNanCErr44q6lEKD2qoK4XtFNx9Qn8FK26ePCI8I9yU7pYB+eM/cZdS9SzQCRJBbHUumVr4bsQ4uxg==" }, "@algolia/cache-in-memory": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.9.1.tgz", - "integrity": "sha512-IEJrHonvdymW2CnRfJtsTVWyfAH05xPEFkGXGCw00+6JNCj8Dln3TeaRLiaaY1srlyGedkemekQm1/Xb46CGOQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.14.2.tgz", + "integrity": "sha512-HrOukWoop9XB/VFojPv1R5SVXowgI56T9pmezd/djh2JnVN/vXswhXV51RKy4nCpqxyHt/aGFSq2qkDvj6KiuQ==", "requires": { - "@algolia/cache-common": "4.9.1" + "@algolia/cache-common": "4.14.2" } }, "@algolia/client-account": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.9.1.tgz", - "integrity": "sha512-Shpjeuwb7i2LR5QuWREb6UbEQLGB+Pl/J5+wPgILJDP/uWp7jpl0ase9mYNQGKj7TjztpSpQCPZ3dSHPnzZPfw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.14.2.tgz", + "integrity": "sha512-WHtriQqGyibbb/Rx71YY43T0cXqyelEU0lB2QMBRXvD2X0iyeGl4qMxocgEIcbHyK7uqE7hKgjT8aBrHqhgc1w==", "requires": { - "@algolia/client-common": "4.9.1", - "@algolia/client-search": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/client-common": "4.14.2", + "@algolia/client-search": "4.14.2", + "@algolia/transporter": "4.14.2" } }, "@algolia/client-analytics": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.9.1.tgz", - "integrity": "sha512-/g6OkOSIA+A0t/tjvbL6iG/zV4El4LPFgv/tcAYHTH27BmlNtnEXw+iFpGjeUlQoPily9WVB3QNLMJkaNwL3HA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.14.2.tgz", + "integrity": "sha512-yBvBv2mw+HX5a+aeR0dkvUbFZsiC4FKSnfqk9rrfX+QrlNOKEhCG0tJzjiOggRW4EcNqRmaTULIYvIzQVL2KYQ==", "requires": { - "@algolia/client-common": "4.9.1", - "@algolia/client-search": "4.9.1", - "@algolia/requester-common": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/client-common": "4.14.2", + "@algolia/client-search": "4.14.2", + "@algolia/requester-common": "4.14.2", + "@algolia/transporter": "4.14.2" } }, "@algolia/client-common": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.9.1.tgz", - "integrity": "sha512-UziRTZ8km3qwoVPIyEre8TV6V+MX7UtbfVqPmSafZ0xu41UUZ+sL56YoKjOXkbKuybeIC9prXMGy/ID5bXkTqg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.14.2.tgz", + "integrity": "sha512-43o4fslNLcktgtDMVaT5XwlzsDPzlqvqesRi4MjQz2x4/Sxm7zYg5LRYFol1BIhG6EwxKvSUq8HcC/KxJu3J0Q==", "requires": { - "@algolia/requester-common": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/requester-common": "4.14.2", + "@algolia/transporter": "4.14.2" } }, - "@algolia/client-recommendation": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-recommendation/-/client-recommendation-4.9.1.tgz", - "integrity": "sha512-Drtvvm1PNIOpYf4HFlkPFstFQ3IsN+TRmxur2F7y6Faplb5ybISa8ithu1tmlTdyTf3A78hQUQjgJet6qD2XZw==", + "@algolia/client-personalization": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.14.2.tgz", + "integrity": "sha512-ACCoLi0cL8CBZ1W/2juehSltrw2iqsQBnfiu/Rbl9W2yE6o2ZUb97+sqN/jBqYNQBS+o0ekTMKNkQjHHAcEXNw==", "requires": { - "@algolia/client-common": "4.9.1", - "@algolia/requester-common": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/client-common": "4.14.2", + "@algolia/requester-common": "4.14.2", + "@algolia/transporter": "4.14.2" } }, "@algolia/client-search": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.9.1.tgz", - "integrity": "sha512-r9Cw2r8kJr45iYncFDht6EshARghU265wuY8Q8oHrpFHjAziEYdsUOdNmQKbsSH5J3gLjDPx1EI5DzVd6ivn3w==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.14.2.tgz", + "integrity": "sha512-L5zScdOmcZ6NGiVbLKTvP02UbxZ0njd5Vq9nJAmPFtjffUSOGEp11BmD2oMJ5QvARgx2XbX4KzTTNS5ECYIMWw==", "requires": { - "@algolia/client-common": "4.9.1", - "@algolia/requester-common": "4.9.1", - "@algolia/transporter": "4.9.1" + "@algolia/client-common": "4.14.2", + "@algolia/requester-common": "4.14.2", + "@algolia/transporter": "4.14.2" } }, + "@algolia/events": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + }, "@algolia/logger-common": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.9.1.tgz", - "integrity": "sha512-9mPrbFlFyPT7or/7PXTiJjyOewWB9QRkZKVXkt5zHAUiUzGxmmdpJIGpPv3YQnDur8lXrXaRI0MHXUuIDMY1ng==" + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.14.2.tgz", + "integrity": "sha512-/JGlYvdV++IcMHBnVFsqEisTiOeEr6cUJtpjz8zc0A9c31JrtLm318Njc72p14Pnkw3A/5lHHh+QxpJ6WFTmsA==" }, "@algolia/logger-console": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.9.1.tgz", - "integrity": "sha512-74VUwjtFjFpjZpi3QoHIPv0kcr3vWUSHX/Vs8PJW3lPsD4CgyhFenQbG9v+ZnyH0JrJwiYTtzfmrVh7IMWZGrQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.14.2.tgz", + "integrity": "sha512-8S2PlpdshbkwlLCSAB5f8c91xyc84VM9Ar9EdfE9UmX+NrKNYnWR1maXXVDQQoto07G1Ol/tYFnFVhUZq0xV/g==", "requires": { - "@algolia/logger-common": "4.9.1" + "@algolia/logger-common": "4.14.2" } }, "@algolia/requester-browser-xhr": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.9.1.tgz", - "integrity": "sha512-zc46tk5o0ikOAz3uYiRAMxC2iVKAMFKT7nNZnLB5IzT0uqAh7pz/+D/UvIxP4bKmsllpBSnPcpfQF+OI4Ag/BA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.2.tgz", + "integrity": "sha512-CEh//xYz/WfxHFh7pcMjQNWgpl4wFB85lUMRyVwaDPibNzQRVcV33YS+63fShFWc2+42YEipFGH2iPzlpszmDw==", "requires": { - "@algolia/requester-common": "4.9.1" + "@algolia/requester-common": "4.14.2" } }, "@algolia/requester-common": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.9.1.tgz", - "integrity": "sha512-9hPgXnlCSbqJqF69M5x5WN3h51Dc+mk/iWNeJSVxExHGvCDfBBZd0v6S15i8q2a9cD1I2RnhMpbnX5BmGtabVA==" + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.14.2.tgz", + "integrity": "sha512-73YQsBOKa5fvVV3My7iZHu1sUqmjjfs9TteFWwPwDmnad7T0VTCopttcsM3OjLxZFtBnX61Xxl2T2gmG2O4ehg==" }, "@algolia/requester-node-http": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.9.1.tgz", - "integrity": "sha512-vYNVbSCuyrCSCjHBQJk+tLZtWCjvvDf5tSbRJjyJYMqpnXuIuP7gZm24iHil4NPYBhbBj5NU2ZDAhc/gTn75Ag==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.14.2.tgz", + "integrity": "sha512-oDbb02kd1o5GTEld4pETlPZLY0e+gOSWjWMJHWTgDXbv9rm/o2cF7japO6Vj1ENnrqWvLBmW1OzV9g6FUFhFXg==", "requires": { - "@algolia/requester-common": "4.9.1" + "@algolia/requester-common": "4.14.2" } }, "@algolia/transporter": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.9.1.tgz", - "integrity": "sha512-AbjFfGzX+cAuj7Qyc536OxIQzjFOA5FU2ANGStx8LBH+AKXScwfkx67C05riuaRR5adSCLMSEbVvUscH0nF+6A==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.14.2.tgz", + "integrity": "sha512-t89dfQb2T9MFQHidjHcfhh6iGMNwvuKUvojAj+JsrHAGbuSy7yE4BylhLX6R0Q1xYRoC4Vvv+O5qIw/LdnQfsQ==", + "requires": { + "@algolia/cache-common": "4.14.2", + "@algolia/logger-common": "4.14.2", + "@algolia/requester-common": "4.14.2" + } + }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "requires": { - "@algolia/cache-common": "4.9.1", - "@algolia/logger-common": "4.9.1", - "@algolia/requester-common": "4.9.1" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "requires": { - "@babel/highlight": "^7.12.13" + "@babel/highlight": "^7.18.6" } }, "@babel/compat-data": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", - "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==" + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz", + "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==" }, "@babel/core": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.2.tgz", - "integrity": "sha512-OgC1mON+l4U4B4wiohJlQNUU3H73mpTyYY3j/c8U9dr9UagGGSm+WFpzjy/YLdoyjiG++c1kIDgxCo/mLwQJeQ==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-module-transforms": "^7.14.2", - "@babel/helpers": "^7.14.0", - "@babel/parser": "^7.14.2", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", + "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.6", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helpers": "^7.19.4", + "@babel/parser": "^7.19.6", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" + "json5": "^2.2.1", + "semver": "^6.3.0" }, "dependencies": { "semver": { @@ -15860,40 +14471,52 @@ } }, "@babel/generator": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.2.tgz", - "integrity": "sha512-OnADYbKrffDVai5qcpkMxQ7caomHOoEwjkouqnN2QhydAjowFAZcsdecFIRUBdb+ZcruwYE4ythYmF1UBZU5xQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.6.tgz", + "integrity": "sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==", "requires": { - "@babel/types": "^7.14.2", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.19.4", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } } }, "@babel/helper-annotate-as-pure": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz", - "integrity": "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.18.6" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz", - "integrity": "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", "requires": { - "@babel/helper-explode-assignable-expression": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" } }, "@babel/helper-compilation-targets": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", - "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz", + "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==", "requires": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-validator-option": "^7.12.17", - "browserslist": "^4.14.5", + "@babel/compat-data": "^7.19.3", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", "semver": "^6.3.0" }, "dependencies": { @@ -15905,36 +14528,35 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.2.tgz", - "integrity": "sha512-6YctwVsmlkchxfGUogvVrrhzyD3grFJyluj5JgDlQrwfMLJSt5tdAzFZfPf4H2Xoi5YLcQ6BxfJlaOBHuctyIw==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", + "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz", - "integrity": "sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "regexpu-core": "^4.7.1" + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" } }, "@babel/helper-define-polyfill-provider": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.0.tgz", - "integrity": "sha512-JT8tHuFjKBo8NnaUbblz7mIu1nnvUDiHVjXXkulZULyidvo/7P6TY7+YqpV37IfF+KUFxmlK04elKtGKXaiVgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "requires": { - "@babel/helper-compilation-targets": "^7.13.0", - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/traverse": "^7.13.0", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2", @@ -15948,167 +14570,169 @@ } } }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + }, "@babel/helper-explode-assignable-expression": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz", - "integrity": "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", "requires": { - "@babel/types": "^7.13.0" + "@babel/types": "^7.18.6" } }, "@babel/helper-function-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", - "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.14.2" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "requires": { - "@babel/types": "^7.12.13" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { - "version": "7.13.16", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz", - "integrity": "sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", "requires": { - "@babel/traverse": "^7.13.15", - "@babel/types": "^7.13.16" + "@babel/types": "^7.18.6" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz", - "integrity": "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", "requires": { - "@babel/types": "^7.13.12" + "@babel/types": "^7.18.9" } }, "@babel/helper-module-imports": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz", - "integrity": "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", "requires": { - "@babel/types": "^7.13.12" + "@babel/types": "^7.18.6" } }, "@babel/helper-module-transforms": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz", - "integrity": "sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", + "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", "requires": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-simple-access": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.14.0", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.2", - "@babel/types": "^7.14.2" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.19.4", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.6", + "@babel/types": "^7.19.4" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz", - "integrity": "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.18.6" } }, "@babel/helper-plugin-utils": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", - "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==" }, "@babel/helper-remap-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz", - "integrity": "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-wrap-function": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" } }, "@babel/helper-replace-supers": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", - "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", "requires": { - "@babel/helper-member-expression-to-functions": "^7.13.12", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" } }, "@babel/helper-simple-access": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz", - "integrity": "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", + "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", "requires": { - "@babel/types": "^7.13.12" + "@babel/types": "^7.19.4" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", "requires": { - "@babel/types": "^7.12.1" + "@babel/types": "^7.18.9" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", "requires": { - "@babel/types": "^7.12.13" + "@babel/types": "^7.18.6" } }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" + }, "@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" }, "@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==" + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" }, "@babel/helper-wrap-function": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz", - "integrity": "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", "requires": { - "@babel/helper-function-name": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/helpers": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", - "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz", + "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==", "requires": { - "@babel/template": "^7.12.13", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.14.0" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.4", + "@babel/types": "^7.19.4" } }, "@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "requires": { - "@babel/helper-validator-identifier": "^7.14.0", + "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -16142,17 +14766,17 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, "supports-color": { "version": "5.5.0", @@ -16165,160 +14789,170 @@ } }, "@babel/parser": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.2.tgz", - "integrity": "sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ==" + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.6.tgz", + "integrity": "sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz", - "integrity": "sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.13.12" + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.2.tgz", - "integrity": "sha512-b1AM4F6fwck4N8ItZ/AtC4FP/cqZqmKRQ4FaTDutwSYyjuhtvsGEMLK4N/ztV/ImP40BjIDyMgBQAeAMsQYVFQ==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz", + "integrity": "sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz", - "integrity": "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-proposal-class-static-block": { - "version": "7.13.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.13.11.tgz", - "integrity": "sha512-fJTdFI4bfnMjvxJyNuaf8i9mVcZ0UhetaGEUHaHV9KEnibLugJkZAtXikR8KcYj+NYmI4DZMS8yQAyg+hvfSqg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-class-static-block": "^7.12.13" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.2.tgz", - "integrity": "sha512-oxVQZIWFh91vuNEMKltqNsKLFWkOIyJc95k2Gv9lWVyDfPUQGSSlbDEgWuJUU1afGE9WwlzpucMZ3yDRHIItkA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.2.tgz", - "integrity": "sha512-sRxW3z3Zp3pFfLAgVEvzTFutTXax837oOatUIvSG9o5gRj9mKwm3br1Se5f4QalTQs9x4AzlA/HrCWbQIHASUQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.2.tgz", - "integrity": "sha512-w2DtsfXBBJddJacXMBhElGEYqCZQqN99Se1qeYn8DVLB33owlrlLftIbMzn5nz1OITfDVknXF433tBrLEAOEjA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3" } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.2.tgz", - "integrity": "sha512-1JAZtUrqYyGsS7IDmFeaem+/LJqujfLZ2weLR9ugB0ufUPjzf8cguyVT1g5im7f7RXxuLq1xUxEzvm68uYRtGg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.2.tgz", - "integrity": "sha512-ebR0zU9OvI2N4qiAC38KIAK75KItpIPTpAtd2r4OZmMFeKbKJpUFLYP2EuDut82+BmYi8sz42B+TfTptJ9iG5Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.2.tgz", - "integrity": "sha512-DcTQY9syxu9BpU3Uo94fjCB3LN9/hgPS8oUL7KrSW3bA2ePrKZZPJcc5y0hoJAM9dft3pGfErtEUvxXQcfLxUg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.2.tgz", - "integrity": "sha512-hBIQFxwZi8GIp934+nj5uV31mqclC1aYDhctDu5khTi9PCCUOczyy0b34W0oE9U/eJXiqQaKyVsmjeagOaSlbw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz", + "integrity": "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==", "requires": { - "@babel/compat-data": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.14.2" + "@babel/plugin-transform-parameters": "^7.18.8" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.2.tgz", - "integrity": "sha512-XtkJsmJtBaUbOxZsNk0Fvrv8eiqgneug0A6aqLFZ4TSkar2L5dSXWcnUKHgmjJt49pyB/6ZHvkr3dPgl9MOWRQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.2.tgz", - "integrity": "sha512-qQByMRPwMZJainfig10BoaDldx/+VDtNcrA7qdNaEOAj6VXud+gfrkA8j4CRAU5HjnWREXqIpSpH30qZX1xivA==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, "@babel/plugin-proposal-private-methods": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz", - "integrity": "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-proposal-private-property-in-object": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz", - "integrity": "sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-create-class-features-plugin": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-private-property-in-object": "^7.14.0" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", - "integrity": "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-syntax-async-generators": { @@ -16338,11 +14972,11 @@ } }, "@babel/plugin-syntax-class-static-block": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.12.13.tgz", - "integrity": "sha512-ZmKQ0ZXR0nYpHZIIuj9zE7oIqCx2hw9TKi+lIo73NNrMPAZGHfS92/VRV0ZmPj6H2ffBgyFHXvJ5NYsNeEaP2A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-syntax-dynamic-import": { @@ -16361,6 +14995,14 @@ "@babel/helper-plugin-utils": "^7.8.3" } }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", @@ -16370,11 +15012,11 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz", - "integrity": "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -16426,336 +15068,308 @@ } }, "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz", - "integrity": "sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-syntax-top-level-await": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", - "integrity": "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-syntax-typescript": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz", - "integrity": "sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", - "integrity": "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz", - "integrity": "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", "requires": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-remap-async-to-generator": "^7.13.0" + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz", - "integrity": "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.2.tgz", - "integrity": "sha512-neZZcP19NugZZqNwMTH+KoBjx5WyvESPSIOQb4JHpfd+zPfqcH65RMu5xJju5+6q/Y2VzYrleQTr+b6METyyxg==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz", + "integrity": "sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-classes": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.2.tgz", - "integrity": "sha512-7oafAVcucHquA/VZCsXv/gmuiHeYd64UJyyTYU+MPfNu0KeNlxw06IeENBO8bJjXVbolu+j1MM5aKQtH1OMCNg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-replace-supers": "^7.13.12", - "@babel/helper-split-export-declaration": "^7.12.13", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", + "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz", - "integrity": "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-destructuring": { - "version": "7.13.17", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", - "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz", + "integrity": "sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz", - "integrity": "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz", - "integrity": "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz", - "integrity": "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-for-of": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz", - "integrity": "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==", + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz", - "integrity": "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", "requires": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz", - "integrity": "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz", - "integrity": "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.2.tgz", - "integrity": "sha512-hPC6XBswt8P3G2D1tSV2HzdKvkqOpmbyoy+g73JG0qlF/qx2y3KaMmXb1fLrpmWGLZYA0ojCvaHdzFWjlmV+Pw==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", "requires": { - "@babel/helper-module-transforms": "^7.14.2", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "dependencies": { - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "requires": { - "object.assign": "^4.1.0" - } - } + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz", - "integrity": "sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", "requires": { - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-simple-access": "^7.13.12", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "dependencies": { - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "requires": { - "object.assign": "^4.1.0" - } - } + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz", - "integrity": "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", "requires": { - "@babel/helper-hoist-variables": "^7.13.0", - "@babel/helper-module-transforms": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-identifier": "^7.12.11", - "babel-plugin-dynamic-import-node": "^2.3.3" - }, - "dependencies": { - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "requires": { - "object.assign": "^4.1.0" - } - } + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz", - "integrity": "sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", "requires": { - "@babel/helper-module-transforms": "^7.14.0", - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz", - "integrity": "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", + "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-transform-new-target": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz", - "integrity": "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-object-super": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz", - "integrity": "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13", - "@babel/helper-replace-supers": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" } }, "@babel/plugin-transform-parameters": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.2.tgz", - "integrity": "sha512-NxoVmA3APNCC1JdMXkdYXuQS+EMdqy0vIwyDHeKHiJKRxmp1qGSdb0JLEIoPRhkx6H/8Qi3RJ3uqOCYw8giy9A==", + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-property-literals": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz", - "integrity": "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-react-constant-elements": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.13.13.tgz", - "integrity": "sha512-SNJU53VM/SjQL0bZhyU+f4kJQz7bQQajnrZRSaU21hruG/NWY41AEM9AWXeXX90pYr/C2yAmTgI6yW3LlLrAUQ==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.20.2.tgz", + "integrity": "sha512-KS/G8YI8uwMGKErLFOHS/ekhqdHhpEloxs43NecQHVgo2QuQSyJhGIY1fL8UGl9wy5ItVwwoUL4YxVqsplGq2g==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-react-display-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.2.tgz", - "integrity": "sha512-zCubvP+jjahpnFJvPaHPiGVfuVUjXHhFvJKQdNnsmSsiU9kR/rCZ41jHc++tERD2zV+p7Hr6is+t5b6iWTCqSw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", + "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz", - "integrity": "sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", "requires": { - "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/types": "^7.13.12" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.19.0" } }, "@babel/plugin-transform-react-jsx-development": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.17.tgz", - "integrity": "sha512-BPjYV86SVuOaudFhsJR1zjgxxOhJDt6JHNoD48DxWEIxUCAMjV1ys6DYw4SDYZh0b1QsS2vfIA9t/ZsQGsDOUQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", + "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", "requires": { - "@babel/plugin-transform-react-jsx": "^7.12.17" + "@babel/plugin-transform-react-jsx": "^7.18.6" } }, "@babel/plugin-transform-react-pure-annotations": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz", - "integrity": "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", + "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-regenerator": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz", - "integrity": "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", "requires": { - "regenerator-transform": "^0.14.2" + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz", - "integrity": "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-runtime": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.2.tgz", - "integrity": "sha512-LyA2AiPkaYzI7G5e2YI4NCasTfFe7mZvlupNprDOB7CdNUHb2DQC4uV6oeZ0396gOcicUzUCh0MShL6wiUgk+Q==", - "requires": { - "@babel/helper-module-imports": "^7.13.12", - "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-polyfill-corejs2": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^0.2.0", - "babel-plugin-polyfill-regenerator": "^0.2.0", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", "semver": "^6.3.0" }, "dependencies": { @@ -16767,103 +15381,105 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz", - "integrity": "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-spread": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz", - "integrity": "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz", - "integrity": "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-transform-template-literals": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz", - "integrity": "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz", - "integrity": "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==", + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-typescript": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.13.0.tgz", - "integrity": "sha512-elQEwluzaU8R8dbVuW2Q2Y8Nznf7hnjM7+DSCd14Lo5fF63C9qNLbwZYbmZrtV9/ySpSUpkRpQXvJb6xyu4hCQ==", + "version": "7.19.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.3.tgz", + "integrity": "sha512-z6fnuK9ve9u/0X0rRvI9MY0xg+DOUaABDYOe+/SQTxtlptaBB/V9JIUxJn6xp3lMBeb9qe8xSFmHU35oZDXD+w==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.13.0", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/plugin-syntax-typescript": "^7.12.13" + "@babel/helper-create-class-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-typescript": "^7.18.6" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", - "integrity": "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.18.9" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz", - "integrity": "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.13", - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/preset-env": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.2.tgz", - "integrity": "sha512-7dD7lVT8GMrE73v4lvDEb85cgcQhdES91BSD7jS/xjC6QY8PnRhux35ac+GCpbiRhp8crexBvZZqnaL6VrY8TQ==", - "requires": { - "@babel/compat-data": "^7.14.0", - "@babel/helper-compilation-targets": "^7.13.16", - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-async-generator-functions": "^7.14.2", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-class-static-block": "^7.13.11", - "@babel/plugin-proposal-dynamic-import": "^7.14.2", - "@babel/plugin-proposal-export-namespace-from": "^7.14.2", - "@babel/plugin-proposal-json-strings": "^7.14.2", - "@babel/plugin-proposal-logical-assignment-operators": "^7.14.2", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.2", - "@babel/plugin-proposal-numeric-separator": "^7.14.2", - "@babel/plugin-proposal-object-rest-spread": "^7.14.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.14.2", - "@babel/plugin-proposal-optional-chaining": "^7.14.2", - "@babel/plugin-proposal-private-methods": "^7.13.0", - "@babel/plugin-proposal-private-property-in-object": "^7.14.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.19.4.tgz", + "integrity": "sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==", + "requires": { + "@babel/compat-data": "^7.19.4", + "@babel/helper-compilation-targets": "^7.19.3", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.19.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.19.4", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -16871,46 +15487,46 @@ "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.0", - "@babel/plugin-syntax-top-level-await": "^7.12.13", - "@babel/plugin-transform-arrow-functions": "^7.13.0", - "@babel/plugin-transform-async-to-generator": "^7.13.0", - "@babel/plugin-transform-block-scoped-functions": "^7.12.13", - "@babel/plugin-transform-block-scoping": "^7.14.2", - "@babel/plugin-transform-classes": "^7.14.2", - "@babel/plugin-transform-computed-properties": "^7.13.0", - "@babel/plugin-transform-destructuring": "^7.13.17", - "@babel/plugin-transform-dotall-regex": "^7.12.13", - "@babel/plugin-transform-duplicate-keys": "^7.12.13", - "@babel/plugin-transform-exponentiation-operator": "^7.12.13", - "@babel/plugin-transform-for-of": "^7.13.0", - "@babel/plugin-transform-function-name": "^7.12.13", - "@babel/plugin-transform-literals": "^7.12.13", - "@babel/plugin-transform-member-expression-literals": "^7.12.13", - "@babel/plugin-transform-modules-amd": "^7.14.2", - "@babel/plugin-transform-modules-commonjs": "^7.14.0", - "@babel/plugin-transform-modules-systemjs": "^7.13.8", - "@babel/plugin-transform-modules-umd": "^7.14.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", - "@babel/plugin-transform-new-target": "^7.12.13", - "@babel/plugin-transform-object-super": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.14.2", - "@babel/plugin-transform-property-literals": "^7.12.13", - "@babel/plugin-transform-regenerator": "^7.13.15", - "@babel/plugin-transform-reserved-words": "^7.12.13", - "@babel/plugin-transform-shorthand-properties": "^7.12.13", - "@babel/plugin-transform-spread": "^7.13.0", - "@babel/plugin-transform-sticky-regex": "^7.12.13", - "@babel/plugin-transform-template-literals": "^7.13.0", - "@babel/plugin-transform-typeof-symbol": "^7.12.13", - "@babel/plugin-transform-unicode-escapes": "^7.12.13", - "@babel/plugin-transform-unicode-regex": "^7.12.13", - "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.14.2", - "babel-plugin-polyfill-corejs2": "^0.2.0", - "babel-plugin-polyfill-corejs3": "^0.2.0", - "babel-plugin-polyfill-regenerator": "^0.2.0", - "core-js-compat": "^3.9.0", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.19.4", + "@babel/plugin-transform-classes": "^7.19.0", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.19.4", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.0", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.19.4", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" }, "dependencies": { @@ -16922,9 +15538,9 @@ } }, "@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", - "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", "requires": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", @@ -16934,507 +15550,1005 @@ } }, "@babel/preset-react": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.13.13.tgz", - "integrity": "sha512-gx+tDLIE06sRjKJkVtpZ/t3mzCDOnPG+ggHZG9lffUbX8+wC739x20YQc9V35Do6ZAxaUc/HhVHIiOzz5MvDmA==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", + "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-transform-react-display-name": "^7.12.13", - "@babel/plugin-transform-react-jsx": "^7.13.12", - "@babel/plugin-transform-react-jsx-development": "^7.12.17", - "@babel/plugin-transform-react-pure-annotations": "^7.12.1" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-react-display-name": "^7.18.6", + "@babel/plugin-transform-react-jsx": "^7.18.6", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-pure-annotations": "^7.18.6" } }, "@babel/preset-typescript": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.13.0.tgz", - "integrity": "sha512-LXJwxrHy0N3f6gIJlYbLta1D9BDtHpQeqwzM0LIfjDlr6UE/D5Mc7W4iDiQzaE+ks0sTjT26ArcHWnJVt0QiHw==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", "requires": { - "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-validator-option": "^7.12.17", - "@babel/plugin-transform-typescript": "^7.13.0" + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" } }, "@babel/runtime": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz", - "integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", + "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz", - "integrity": "sha512-0R0HTZWHLk6G8jIk0FtoX+AatCtKnswS98VhXwGImFc759PJRp4Tru0PQYZofyijTFUr+gT8Mu7sgXVJLQ0ceg==", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.6.tgz", + "integrity": "sha512-oWNn1ZlGde7b4i/3tnixpH9qI0bOAACiUs+KEES4UUCnsPjVWFlWdLV/iwJuPC2qp3EowbAqsm+0XqNwnwYhxA==", "requires": { - "core-js-pure": "^3.0.0", + "core-js-pure": "^3.25.1", "regenerator-runtime": "^0.13.4" } }, "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" } }, "@babel/traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", - "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.2", - "@babel/types": "^7.14.2", + "version": "7.19.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.6.tgz", + "integrity": "sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.19.6", + "@babel/types": "^7.19.4", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", - "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", + "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", "requires": { - "@babel/helper-validator-identifier": "^7.14.0", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "optional": true + }, "@crowdin/cli": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@crowdin/cli/-/cli-3.6.1.tgz", - "integrity": "sha512-RUKFrPCX3R1MJPyRpBSqWFwmjbDlVWLHtXAzx2FPeyjnyMXrXLPJ8YEl4t8YU+96q/0t46qTdmMLeQmYyDEvGQ==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@crowdin/cli/-/cli-3.9.0.tgz", + "integrity": "sha512-4wQjqJZmU/mg3VYfRL6IYXw/pPAL9vdfW3QVSBovYA+bYaEt43ZuGsSrqeBGOhLehasWwRqklXWsl96gxQlLdw==", "requires": { + "njre": "^0.2.0", "shelljs": "^0.8.4" } }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "peer": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "peer": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, "@docsearch/css": { - "version": "3.0.0-alpha.36", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.0.0-alpha.36.tgz", - "integrity": "sha512-zSN2SXuZPDqQaSFzYa1kOwToukqzhLHG7c66iO+/PlmWb6/RZ5cjTkG6VCJynlohRWea7AqZKWS/ptm8kM2Dmg==" + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.2.2.tgz", + "integrity": "sha512-VB0Evx4ikS1ZlW1YVUw+vI9b3H/UXMCo4W/ZWy+n56Sho4KOqyCHcINVays91TJt7HTV/CKO3FCbm2VJg5Wipw==" }, "@docsearch/react": { - "version": "3.0.0-alpha.36", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.0.0-alpha.36.tgz", - "integrity": "sha512-synYZDHalvMzesFiy7kK+uoz4oTdWSTbe2cU+iiUjwFMyQ+WWjWwGVnvcvk+cjj9pRCVaZo5y5WpqNXq1j8k9Q==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.2.2.tgz", + "integrity": "sha512-1Hn2SNQUFVPrzqvaj+vxXZfsfn3rnW8CoyGAJ1LqXMY9py8GbxK8VfmJ5Z6z4LwG9849tGru/N6dp0cQO6r9Ag==", "requires": { - "@algolia/autocomplete-core": "1.0.0-alpha.44", - "@algolia/autocomplete-preset-algolia": "1.0.0-alpha.44", - "@docsearch/css": "3.0.0-alpha.36", + "@algolia/autocomplete-core": "1.7.1", + "@algolia/autocomplete-preset-algolia": "1.7.1", + "@docsearch/css": "3.2.2", "algoliasearch": "^4.0.0" } }, "@docusaurus/core": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.0.0-beta.0.tgz", - "integrity": "sha512-xWwpuEwFRKJmZvNGOpr/dyRDnx/psckLPsozQTg2hu3u81Wqu9gigWgYK/C2fPlEjxMcVw0/2WH+zwpbyWmF2Q==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.2.0.tgz", + "integrity": "sha512-Vd6XOluKQqzG12fEs9prJgDtyn6DPok9vmUWDR2E6/nV5Fl9SVkhEQOBxwObjk3kQh7OY7vguFaLh0jqdApWsA==", "requires": { - "@babel/core": "^7.12.16", - "@babel/generator": "^7.12.15", + "@babel/core": "^7.18.6", + "@babel/generator": "^7.18.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-runtime": "^7.12.15", - "@babel/preset-env": "^7.12.16", - "@babel/preset-react": "^7.12.13", - "@babel/preset-typescript": "^7.12.16", - "@babel/runtime": "^7.12.5", - "@babel/runtime-corejs3": "^7.12.13", - "@babel/traverse": "^7.12.13", - "@docusaurus/cssnano-preset": "2.0.0-beta.0", - "@docusaurus/react-loadable": "5.5.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "@endiliey/static-site-generator-webpack-plugin": "^4.0.0", - "@svgr/webpack": "^5.5.0", - "autoprefixer": "^10.2.5", - "babel-loader": "^8.2.2", - "babel-plugin-dynamic-import-node": "2.3.0", - "boxen": "^5.0.0", - "chalk": "^4.1.0", - "chokidar": "^3.5.1", - "clean-css": "^5.1.1", + "@babel/plugin-transform-runtime": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@babel/runtime": "^7.18.6", + "@babel/runtime-corejs3": "^7.18.6", + "@babel/traverse": "^7.18.8", + "@docusaurus/cssnano-preset": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-common": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "@slorber/static-site-generator-webpack-plugin": "^4.0.7", + "@svgr/webpack": "^6.2.1", + "autoprefixer": "^10.4.7", + "babel-loader": "^8.2.5", + "babel-plugin-dynamic-import-node": "^2.3.3", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "clean-css": "^5.3.0", + "cli-table3": "^0.6.2", + "combine-promises": "^1.1.0", "commander": "^5.1.0", - "copy-webpack-plugin": "^8.1.0", - "core-js": "^3.9.1", - "css-loader": "^5.1.1", - "css-minimizer-webpack-plugin": "^2.0.0", - "cssnano": "^5.0.1", - "del": "^6.0.0", + "copy-webpack-plugin": "^11.0.0", + "core-js": "^3.23.3", + "css-loader": "^6.7.1", + "css-minimizer-webpack-plugin": "^4.0.0", + "cssnano": "^5.1.12", + "del": "^6.1.1", "detect-port": "^1.3.0", - "eta": "^1.12.1", - "express": "^4.17.1", + "escape-html": "^1.0.3", + "eta": "^1.12.3", "file-loader": "^6.2.0", - "fs-extra": "^9.1.0", - "github-slugger": "^1.3.0", - "globby": "^11.0.2", - "html-minifier-terser": "^5.1.1", - "html-tags": "^3.1.0", - "html-webpack-plugin": "^5.2.0", + "fs-extra": "^10.1.0", + "html-minifier-terser": "^6.1.0", + "html-tags": "^3.2.0", + "html-webpack-plugin": "^5.5.0", "import-fresh": "^3.3.0", - "is-root": "^2.1.0", "leven": "^3.1.0", - "lodash": "^4.17.20", - "mini-css-extract-plugin": "^1.4.0", - "module-alias": "^2.2.2", - "nprogress": "^0.2.0", - "postcss": "^8.2.10", - "postcss-loader": "^5.2.0", - "prompts": "^2.4.0", - "react-dev-utils": "^11.0.1", - "react-error-overlay": "^6.0.9", - "react-helmet": "^6.1.0", - "react-loadable": "^5.5.0", + "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.4.14", + "postcss-loader": "^7.0.0", + "prompts": "^2.4.2", + "react-dev-utils": "^12.0.1", + "react-helmet-async": "^1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", "react-loadable-ssr-addon-v5-slorber": "^1.0.1", - "react-router": "^5.2.0", + "react-router": "^5.3.3", "react-router-config": "^5.1.1", - "react-router-dom": "^5.2.0", - "resolve-pathname": "^3.0.0", - "rtl-detect": "^1.0.2", - "semver": "^7.3.4", + "react-router-dom": "^5.3.3", + "rtl-detect": "^1.0.4", + "semver": "^7.3.7", "serve-handler": "^6.1.3", - "shelljs": "^0.8.4", - "std-env": "^2.2.1", - "strip-ansi": "^6.0.0", - "terser-webpack-plugin": "^5.1.1", - "tslib": "^2.1.0", + "shelljs": "^0.8.5", + "terser-webpack-plugin": "^5.3.3", + "tslib": "^2.4.0", "update-notifier": "^5.1.0", "url-loader": "^4.1.1", - "wait-on": "^5.2.1", - "webpack": "^5.28.0", - "webpack-bundle-analyzer": "^4.4.0", - "webpack-dev-server": "^3.11.2", - "webpack-merge": "^5.7.3", - "webpackbar": "^5.0.0-3" + "wait-on": "^6.0.1", + "webpack": "^5.73.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-dev-server": "^4.9.3", + "webpack-merge": "^5.8.0", + "webpackbar": "^5.0.2" }, "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", + "requires": { + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + } + }, + "cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==" + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, "postcss-loader": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-5.3.0.tgz", - "integrity": "sha512-/+Z1RAmssdiSLgIZwnJHwBMnlABPgF7giYzTN2NOfr9D21IJZ4mQC1R2miwp80zno9M4zMD/umGI8cR+2EL5zw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz", + "integrity": "sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==", "requires": { "cosmiconfig": "^7.0.0", - "klona": "^2.0.4", - "semver": "^7.3.4" + "klona": "^2.0.5", + "semver": "^7.3.8" + } + }, + "react-loadable": { + "version": "npm:@docusaurus/react-loadable@5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "requires": { + "@types/react": "*", + "prop-types": "^15.6.2" + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + }, + "widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "requires": { + "string-width": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz", + "integrity": "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" } } } }, "@docusaurus/cssnano-preset": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.0.tgz", - "integrity": "sha512-gqQHeQCDHZDd5NaiKZwDiyg75sBCqDyAsvmFukkDAty8xE7u9IhzbOQKvCAtwseuvzu2BNN41gnJ8bz7vZzQiw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.2.0.tgz", + "integrity": "sha512-mAAwCo4n66TMWBH1kXnHVZsakW9VAXJzTO4yZukuL3ro4F+JtkMwKfh42EG75K/J/YIFQG5I/Bzy0UH/hFxaTg==", + "requires": { + "cssnano-preset-advanced": "^5.3.8", + "postcss": "^8.4.14", + "postcss-sort-media-queries": "^4.2.1", + "tslib": "^2.4.0" + } + }, + "@docusaurus/logger": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.2.0.tgz", + "integrity": "sha512-DF3j1cA5y2nNsu/vk8AG7xwpZu6f5MKkPPMaaIbgXLnWGfm6+wkOeW7kNrxnM95YOhKUkJUophX69nGUnLsm0A==", "requires": { - "cssnano-preset-advanced": "^5.0.0", - "postcss": "^8.2.10", - "postcss-sort-media-queries": "^3.8.9" + "chalk": "^4.1.2", + "tslib": "^2.4.0" } }, "@docusaurus/mdx-loader": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.0.tgz", - "integrity": "sha512-oQLS2ZeUnqw79CV37glglZpaYgFfA5Az5lT83m5tJfMUZjoK4ehG1XWBeUzWy8QQNI452yAID8jz8jihEQeCcw==", - "requires": { - "@babel/parser": "^7.12.16", - "@babel/traverse": "^7.12.13", - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@mdx-js/mdx": "^1.6.21", - "@mdx-js/react": "^1.6.21", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.2.0.tgz", + "integrity": "sha512-X2bzo3T0jW0VhUU+XdQofcEeozXOTmKQMvc8tUnWRdTnCvj4XEcBVdC3g+/jftceluiwSTNRAX4VBOJdNt18jA==", + "requires": { + "@babel/parser": "^7.18.8", + "@babel/traverse": "^7.18.8", + "@docusaurus/logger": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@mdx-js/mdx": "^1.6.22", "escape-html": "^1.0.3", "file-loader": "^6.2.0", - "fs-extra": "^9.1.0", - "github-slugger": "^1.3.0", - "gray-matter": "^4.0.2", + "fs-extra": "^10.1.0", + "image-size": "^1.0.1", "mdast-util-to-string": "^2.0.0", - "remark-emoji": "^2.1.0", + "remark-emoji": "^2.2.0", "stringify-object": "^3.3.0", - "unist-util-visit": "^2.0.2", + "tslib": "^2.4.0", + "unified": "^9.2.2", + "unist-util-visit": "^2.0.3", "url-loader": "^4.1.1", - "webpack": "^5.28.0" + "webpack": "^5.73.0" + }, + "dependencies": { + "unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + } + } + } + }, + "@docusaurus/module-type-aliases": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.2.0.tgz", + "integrity": "sha512-wDGW4IHKoOr9YuJgy7uYuKWrDrSpsUSDHLZnWQYM9fN7D5EpSmYHjFruUpKWVyxLpD/Wh0rW8hYZwdjJIQUQCQ==", + "requires": { + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/types": "2.2.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "@types/react-router-dom": "*", + "react-helmet-async": "*", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" + }, + "dependencies": { + "react-loadable": { + "version": "npm:@docusaurus/react-loadable@5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "requires": { + "@types/react": "*", + "prop-types": "^15.6.2" + } + } } }, "@docusaurus/plugin-content-blog": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.0.tgz", - "integrity": "sha512-lz63i5k/23RJ3Rk/2fIsYAoD8Wua3b5b0AbH2JoOhQu1iAIQiV8m91Z3XALBSzA3nBtAOIweNI7yzWL+JFSTvw==", - "requires": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/mdx-loader": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "chalk": "^4.1.0", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.2.0.tgz", + "integrity": "sha512-0mWBinEh0a5J2+8ZJXJXbrCk1tSTNf7Nm4tYAl5h2/xx+PvH/Bnu0V+7mMljYm/1QlDYALNIIaT/JcoZQFUN3w==", + "requires": { + "@docusaurus/core": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-common": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", - "fs-extra": "^9.1.0", - "globby": "^11.0.2", - "loader-utils": "^2.0.0", - "lodash": "^4.17.20", - "reading-time": "^1.3.0", - "remark-admonitions": "^1.2.1", - "tslib": "^2.1.0", - "webpack": "^5.28.0" + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "reading-time": "^1.5.0", + "tslib": "^2.4.0", + "unist-util-visit": "^2.0.3", + "utility-types": "^3.10.0", + "webpack": "^5.73.0" } }, "@docusaurus/plugin-content-docs": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.0.tgz", - "integrity": "sha512-WdDQUh2rRCbfJswVc0vY9EaAspxgziqpVEZja8+BmQR/TZh7HuLplT6GJbiFbE4RvwM3+PwG/jHMPglYDK60kw==", - "requires": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/mdx-loader": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "chalk": "^4.1.0", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.2.0.tgz", + "integrity": "sha512-BOazBR0XjzsHE+2K1wpNxz5QZmrJgmm3+0Re0EVPYFGW8qndCWGNtXW/0lGKhecVPML8yyFeAmnUCIs7xM2wPw==", + "requires": { + "@docusaurus/core": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/module-type-aliases": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "@types/react-router-config": "^5.0.6", "combine-promises": "^1.1.0", - "execa": "^5.0.0", - "fs-extra": "^9.1.0", - "globby": "^11.0.2", - "import-fresh": "^3.2.2", - "js-yaml": "^4.0.0", - "loader-utils": "^1.2.3", - "lodash": "^4.17.20", - "remark-admonitions": "^1.2.1", - "shelljs": "^0.8.4", - "tslib": "^2.1.0", + "fs-extra": "^10.1.0", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", "utility-types": "^3.10.0", - "webpack": "^5.28.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } + "webpack": "^5.73.0" } }, "@docusaurus/plugin-content-pages": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.0.tgz", - "integrity": "sha512-mk5LVVSvn+HJPKBaAs/Pceq/hTGxF2LVBvJEquuQz0NMAW3QdBWaYRRpOrL9CO8v+ygn5RuLslXsyZBsDNuhww==", - "requires": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/mdx-loader": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "globby": "^11.0.2", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "remark-admonitions": "^1.2.1", - "slash": "^3.0.0", - "tslib": "^2.1.0", - "webpack": "^5.28.0" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.2.0.tgz", + "integrity": "sha512-+OTK3FQHk5WMvdelz8v19PbEbx+CNT6VSpx7nVOvMNs5yJCKvmqBJBQ2ZSxROxhVDYn+CZOlmyrC56NSXzHf6g==", + "requires": { + "@docusaurus/core": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "fs-extra": "^10.1.0", + "tslib": "^2.4.0", + "webpack": "^5.73.0" } }, "@docusaurus/plugin-debug": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.0.tgz", - "integrity": "sha512-m75sZdF8Yccxfih3qfdQg9DucMTrYBnmeTA8GNmdVaK701Ip8t50d1pDJchtu0FSEh6vzVB9C6D2YD5YgVFp8A==", - "requires": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "react-json-view": "^1.21.1", - "tslib": "^2.1.0" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.2.0.tgz", + "integrity": "sha512-p9vOep8+7OVl6r/NREEYxf4HMAjV8JMYJ7Bos5fCFO0Wyi9AZEo0sCTliRd7R8+dlJXZEgcngSdxAUo/Q+CJow==", + "requires": { + "@docusaurus/core": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "fs-extra": "^10.1.0", + "react-json-view": "^1.21.3", + "tslib": "^2.4.0" } }, "@docusaurus/plugin-google-analytics": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.0.tgz", - "integrity": "sha512-7lHrg1L+adc8VbiaLexa15i4fdq4MRPUTLMxRPAWz+QskhisW89Ryi2/gDmfMNqLblX84Qg2RASa+2gqO4wepw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.2.0.tgz", + "integrity": "sha512-+eZVVxVeEnV5nVQJdey9ZsfyEVMls6VyWTIj8SmX0k5EbqGvnIfET+J2pYEuKQnDIHxy+syRMoRM6AHXdHYGIg==", "requires": { - "@docusaurus/core": "2.0.0-beta.0" + "@docusaurus/core": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "tslib": "^2.4.0" } }, "@docusaurus/plugin-google-gtag": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.0.tgz", - "integrity": "sha512-V7zaYbhAMv0jexm5H/5sAnoM1GHibcn9QQk5UWC++x1kE0KRuLDZHV+9OyvW5wr0wWFajod/b88SpUpSMF5u+g==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.0.tgz", + "integrity": "sha512-adj/70DANaQs2+TF/nRdMezDXFAV/O/pjAbUgmKBlyOTq5qoMe0Tk4muvQIwWUmiUQxFJe+sKlZGM771ownyOg==", "requires": { - "@docusaurus/core": "2.0.0-beta.0" + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "tslib": "^2.4.0" + }, + "dependencies": { + "@docusaurus/core": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.4.0.tgz", + "integrity": "sha512-J55/WEoIpRcLf3afO5POHPguVZosKmJEQWKBL+K7TAnfuE7i+Y0NPLlkKtnWCehagGsgTqClfQEexH/UT4kELA==", + "requires": { + "@babel/core": "^7.18.6", + "@babel/generator": "^7.18.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@babel/runtime": "^7.18.6", + "@babel/runtime-corejs3": "^7.18.6", + "@babel/traverse": "^7.18.8", + "@docusaurus/cssnano-preset": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/react-loadable": "5.5.2", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", + "@slorber/static-site-generator-webpack-plugin": "^4.0.7", + "@svgr/webpack": "^6.2.1", + "autoprefixer": "^10.4.7", + "babel-loader": "^8.2.5", + "babel-plugin-dynamic-import-node": "^2.3.3", + "boxen": "^6.2.1", + "chalk": "^4.1.2", + "chokidar": "^3.5.3", + "clean-css": "^5.3.0", + "cli-table3": "^0.6.2", + "combine-promises": "^1.1.0", + "commander": "^5.1.0", + "copy-webpack-plugin": "^11.0.0", + "core-js": "^3.23.3", + "css-loader": "^6.7.1", + "css-minimizer-webpack-plugin": "^4.0.0", + "cssnano": "^5.1.12", + "del": "^6.1.1", + "detect-port": "^1.3.0", + "escape-html": "^1.0.3", + "eta": "^2.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "html-minifier-terser": "^6.1.0", + "html-tags": "^3.2.0", + "html-webpack-plugin": "^5.5.0", + "import-fresh": "^3.3.0", + "leven": "^3.1.0", + "lodash": "^4.17.21", + "mini-css-extract-plugin": "^2.6.1", + "postcss": "^8.4.14", + "postcss-loader": "^7.0.0", + "prompts": "^2.4.2", + "react-dev-utils": "^12.0.1", + "react-helmet-async": "^1.3.0", + "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", + "react-loadable-ssr-addon-v5-slorber": "^1.0.1", + "react-router": "^5.3.3", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.3.3", + "rtl-detect": "^1.0.4", + "semver": "^7.3.7", + "serve-handler": "^6.1.3", + "shelljs": "^0.8.5", + "terser-webpack-plugin": "^5.3.3", + "tslib": "^2.4.0", + "update-notifier": "^5.1.0", + "url-loader": "^4.1.1", + "wait-on": "^6.0.1", + "webpack": "^5.73.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-dev-server": "^4.9.3", + "webpack-merge": "^5.8.0", + "webpackbar": "^5.0.2" + } + }, + "@docusaurus/cssnano-preset": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.0.tgz", + "integrity": "sha512-RmdiA3IpsLgZGXRzqnmTbGv43W4OD44PCo+6Q/aYjEM2V57vKCVqNzuafE94jv0z/PjHoXUrjr69SaRymBKYYw==", + "requires": { + "cssnano-preset-advanced": "^5.3.8", + "postcss": "^8.4.14", + "postcss-sort-media-queries": "^4.2.1", + "tslib": "^2.4.0" + } + }, + "@docusaurus/logger": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.4.0.tgz", + "integrity": "sha512-T8+qR4APN+MjcC9yL2Es+xPJ2923S9hpzDmMtdsOcUGLqpCGBbU1vp3AAqDwXtVgFkq+NsEk7sHdVsfLWR/AXw==", + "requires": { + "chalk": "^4.1.2", + "tslib": "^2.4.0" + } + }, + "@docusaurus/mdx-loader": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.4.0.tgz", + "integrity": "sha512-GWoH4izZKOmFoC+gbI2/y8deH/xKLvzz/T5BsEexBye8EHQlwsA7FMrVa48N063bJBH4FUOiRRXxk5rq9cC36g==", + "requires": { + "@babel/parser": "^7.18.8", + "@babel/traverse": "^7.18.8", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@mdx-js/mdx": "^1.6.22", + "escape-html": "^1.0.3", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "image-size": "^1.0.1", + "mdast-util-to-string": "^2.0.0", + "remark-emoji": "^2.2.0", + "stringify-object": "^3.3.0", + "tslib": "^2.4.0", + "unified": "^9.2.2", + "unist-util-visit": "^2.0.3", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" + } + }, + "@docusaurus/types": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.4.0.tgz", + "integrity": "sha512-xaBXr+KIPDkIaef06c+i2HeTqVNixB7yFut5fBXPGI2f1rrmEV2vLMznNGsFwvZ5XmA3Quuefd4OGRkdo97Dhw==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "commander": "^5.1.0", + "joi": "^17.6.0", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0", + "webpack-merge": "^5.8.0" + } + }, + "@docusaurus/utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.4.0.tgz", + "integrity": "sha512-89hLYkvtRX92j+C+ERYTuSUK6nF9bGM32QThcHPg2EDDHVw6FzYQXmX6/p+pU5SDyyx5nBlE4qXR92RxCAOqfg==", + "requires": { + "@docusaurus/logger": "2.4.0", + "@svgr/webpack": "^6.2.1", + "escape-string-regexp": "^4.0.0", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "github-slugger": "^1.4.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", + "resolve-pathname": "^3.0.0", + "shelljs": "^0.8.5", + "tslib": "^2.4.0", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" + } + }, + "@docusaurus/utils-common": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.4.0.tgz", + "integrity": "sha512-zIMf10xuKxddYfLg5cS19x44zud/E9I7lj3+0bv8UIs0aahpErfNrGhijEfJpAfikhQ8tL3m35nH3hJ3sOG82A==", + "requires": { + "tslib": "^2.4.0" + } + }, + "@docusaurus/utils-validation": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.4.0.tgz", + "integrity": "sha512-IrBsBbbAp6y7mZdJx4S4pIA7dUyWSA0GNosPk6ZJ0fX3uYIEQgcQSGIgTeSC+8xPEx3c16o03en1jSDpgQgz/w==", + "requires": { + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", + "joi": "^17.6.0", + "js-yaml": "^4.1.0", + "tslib": "^2.4.0" + } + }, + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + }, + "boxen": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz", + "integrity": "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==", + "requires": { + "ansi-align": "^3.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.1.2", + "cli-boxes": "^3.0.0", + "string-width": "^5.0.1", + "type-fest": "^2.5.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + } + }, + "cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==" + }, + "cosmiconfig": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", + "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", + "requires": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + } + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "eta": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eta/-/eta-2.0.1.tgz", + "integrity": "sha512-46E2qDPDm7QA+usjffUWz9KfXsxVZclPOuKsXs4ZWZdI/X1wpDF7AO424pt7fdYohCzWsIkXAhNGXSlwo5naAg==" + }, + "postcss-loader": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.2.4.tgz", + "integrity": "sha512-F88rpxxNspo5hatIc+orYwZDtHFaVFOSIVAx+fBfJC1GmhWbVmPWtmg2gXKE1OxJbneOSGn8PWdIwsZFcruS+w==", + "requires": { + "cosmiconfig": "^8.1.3", + "cosmiconfig-typescript-loader": "^4.3.0", + "klona": "^2.0.6", + "semver": "^7.3.8" + } + }, + "react-loadable": { + "version": "npm:@docusaurus/react-loadable@5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "requires": { + "@types/react": "*", + "prop-types": "^15.6.2" + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + }, + "unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + } + }, + "widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "requires": { + "string-width": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } } }, "@docusaurus/plugin-sitemap": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.0.tgz", - "integrity": "sha512-dvmk8Sr+6pBkiKDb7Rjdp0GeFDWPUlayoJWK3fN3g0Fno6uxFfYhNZyXJ+ObyCA7HoW5rzeBMiO+uAja19JXTg==", - "requires": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "fs-extra": "^9.1.0", - "sitemap": "^6.3.6", - "tslib": "^2.1.0" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.2.0.tgz", + "integrity": "sha512-0jAmyRDN/aI265CbWZNZuQpFqiZuo+5otk2MylU9iVrz/4J7gSc+ZJ9cy4EHrEsW7PV8s1w18hIEsmcA1YgkKg==", + "requires": { + "@docusaurus/core": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-common": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "fs-extra": "^10.1.0", + "sitemap": "^7.1.1", + "tslib": "^2.4.0" } }, "@docusaurus/preset-classic": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.0.tgz", - "integrity": "sha512-cFpR0UaAeUt5qVx1bpidhlar6tiRNITIQlxP4bOVsjbxVTZhZ/cNuIz7C+2zFPCuKIflGXdTIQOrucPmd7z51Q==", - "requires": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/plugin-content-blog": "2.0.0-beta.0", - "@docusaurus/plugin-content-docs": "2.0.0-beta.0", - "@docusaurus/plugin-content-pages": "2.0.0-beta.0", - "@docusaurus/plugin-debug": "2.0.0-beta.0", - "@docusaurus/plugin-google-analytics": "2.0.0-beta.0", - "@docusaurus/plugin-google-gtag": "2.0.0-beta.0", - "@docusaurus/plugin-sitemap": "2.0.0-beta.0", - "@docusaurus/theme-classic": "2.0.0-beta.0", - "@docusaurus/theme-search-algolia": "2.0.0-beta.0" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.2.0.tgz", + "integrity": "sha512-yKIWPGNx7BT8v2wjFIWvYrS+nvN04W+UameSFf8lEiJk6pss0kL6SG2MRvyULiI3BDxH+tj6qe02ncpSPGwumg==", + "requires": { + "@docusaurus/core": "2.2.0", + "@docusaurus/plugin-content-blog": "2.2.0", + "@docusaurus/plugin-content-docs": "2.2.0", + "@docusaurus/plugin-content-pages": "2.2.0", + "@docusaurus/plugin-debug": "2.2.0", + "@docusaurus/plugin-google-analytics": "2.2.0", + "@docusaurus/plugin-google-gtag": "2.2.0", + "@docusaurus/plugin-sitemap": "2.2.0", + "@docusaurus/theme-classic": "2.2.0", + "@docusaurus/theme-common": "2.2.0", + "@docusaurus/theme-search-algolia": "2.2.0", + "@docusaurus/types": "2.2.0" + }, + "dependencies": { + "@docusaurus/plugin-google-gtag": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.2.0.tgz", + "integrity": "sha512-6SOgczP/dYdkqUMGTRqgxAS1eTp6MnJDAQMy8VCF1QKbWZmlkx4agHDexihqmYyCujTYHqDAhm1hV26EET54NQ==", + "requires": { + "@docusaurus/core": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "tslib": "^2.4.0" + } + } } }, "@docusaurus/react-loadable": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.0.tgz", - "integrity": "sha512-Ld/kwUE6yATIOTLq3JCsWiTa/drisajwKqBQ2Rw6IcT+sFsKfYek8F2jSH8f68AT73xX97UehduZeCSlnuCBIg==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", + "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", "requires": { + "@types/react": "*", "prop-types": "^15.6.2" } }, "@docusaurus/theme-classic": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.0.tgz", - "integrity": "sha512-cBNtwAyg3be7Gk41FazMtgyibAcfuYaGHhGHIDRsXfc/qp3RhbiGiei7tyh200QT0NgKZxiVQy/r4d0mtjC++Q==", - "requires": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/plugin-content-blog": "2.0.0-beta.0", - "@docusaurus/plugin-content-docs": "2.0.0-beta.0", - "@docusaurus/plugin-content-pages": "2.0.0-beta.0", - "@docusaurus/theme-common": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "@mdx-js/mdx": "^1.6.21", - "@mdx-js/react": "^1.6.21", - "chalk": "^4.1.0", - "clsx": "^1.1.1", - "copy-text-to-clipboard": "^3.0.0", - "fs-extra": "^9.1.0", - "globby": "^11.0.2", - "infima": "0.2.0-alpha.23", - "lodash": "^4.17.20", - "parse-numeric-range": "^1.2.0", - "postcss": "^8.2.10", - "prism-react-renderer": "^1.1.1", - "prismjs": "^1.23.0", - "prop-types": "^15.7.2", - "react-router-dom": "^5.2.0", - "rtlcss": "^3.1.2" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.2.0.tgz", + "integrity": "sha512-kjbg/qJPwZ6H1CU/i9d4l/LcFgnuzeiGgMQlt6yPqKo0SOJIBMPuz7Rnu3r/WWbZFPi//o8acclacOzmXdUUEg==", + "requires": { + "@docusaurus/core": "2.2.0", + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/module-type-aliases": "2.2.0", + "@docusaurus/plugin-content-blog": "2.2.0", + "@docusaurus/plugin-content-docs": "2.2.0", + "@docusaurus/plugin-content-pages": "2.2.0", + "@docusaurus/theme-common": "2.2.0", + "@docusaurus/theme-translations": "2.2.0", + "@docusaurus/types": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-common": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "@mdx-js/react": "^1.6.22", + "clsx": "^1.2.1", + "copy-text-to-clipboard": "^3.0.1", + "infima": "0.2.0-alpha.42", + "lodash": "^4.17.21", + "nprogress": "^0.2.0", + "postcss": "^8.4.14", + "prism-react-renderer": "^1.3.5", + "prismjs": "^1.28.0", + "react-router-dom": "^5.3.3", + "rtlcss": "^3.5.0", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" } }, "@docusaurus/theme-common": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.0.0-beta.0.tgz", - "integrity": "sha512-2rcVmQpvbdAgnzTWuM7Bfpu+2TQm928bhlvxn226jQy7IYz8ySRlIode63HhCtpx03hpdMCkrK6HxhfEcvHjQg==", - "requires": { - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/plugin-content-blog": "2.0.0-beta.0", - "@docusaurus/plugin-content-docs": "2.0.0-beta.0", - "@docusaurus/plugin-content-pages": "2.0.0-beta.0", - "@docusaurus/types": "2.0.0-beta.0", - "tslib": "^2.1.0" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.2.0.tgz", + "integrity": "sha512-R8BnDjYoN90DCL75gP7qYQfSjyitXuP9TdzgsKDmSFPNyrdE3twtPNa2dIN+h+p/pr+PagfxwWbd6dn722A1Dw==", + "requires": { + "@docusaurus/mdx-loader": "2.2.0", + "@docusaurus/module-type-aliases": "2.2.0", + "@docusaurus/plugin-content-blog": "2.2.0", + "@docusaurus/plugin-content-docs": "2.2.0", + "@docusaurus/plugin-content-pages": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router-config": "*", + "clsx": "^1.2.1", + "parse-numeric-range": "^1.3.0", + "prism-react-renderer": "^1.3.5", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" } }, "@docusaurus/theme-search-algolia": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.0.tgz", - "integrity": "sha512-/GhgAm4yuwqTXWTsWnqpFYxpjTv+t45Wk8q/LmTVINa+A7b6jkMkch2lygagIt69/ufDm2Uw6eYhgrmF4DJqfQ==", - "requires": { - "@docsearch/react": "^3.0.0-alpha.33", - "@docusaurus/core": "2.0.0-beta.0", - "@docusaurus/theme-common": "2.0.0-beta.0", - "@docusaurus/utils": "2.0.0-beta.0", - "@docusaurus/utils-validation": "2.0.0-beta.0", - "algoliasearch": "^4.8.4", - "algoliasearch-helper": "^3.3.4", - "clsx": "^1.1.1", - "eta": "^1.12.1", - "lodash": "^4.17.20" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.2.0.tgz", + "integrity": "sha512-2h38B0tqlxgR2FZ9LpAkGrpDWVdXZ7vltfmTdX+4RsDs3A7khiNsmZB+x/x6sA4+G2V2CvrsPMlsYBy5X+cY1w==", + "requires": { + "@docsearch/react": "^3.1.1", + "@docusaurus/core": "2.2.0", + "@docusaurus/logger": "2.2.0", + "@docusaurus/plugin-content-docs": "2.2.0", + "@docusaurus/theme-common": "2.2.0", + "@docusaurus/theme-translations": "2.2.0", + "@docusaurus/utils": "2.2.0", + "@docusaurus/utils-validation": "2.2.0", + "algoliasearch": "^4.13.1", + "algoliasearch-helper": "^3.10.0", + "clsx": "^1.2.1", + "eta": "^1.12.3", + "fs-extra": "^10.1.0", + "lodash": "^4.17.21", + "tslib": "^2.4.0", + "utility-types": "^3.10.0" + } + }, + "@docusaurus/theme-translations": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.2.0.tgz", + "integrity": "sha512-3T140AG11OjJrtKlY4pMZ5BzbGRDjNs2co5hJ6uYJG1bVWlhcaFGqkaZ5lCgKflaNHD7UHBHU9Ec5f69jTdd6w==", + "requires": { + "fs-extra": "^10.1.0", + "tslib": "^2.4.0" } }, "@docusaurus/types": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.0.0-beta.0.tgz", - "integrity": "sha512-z9PI+GbtYwqTXnkX4/a/A6psDX2p8N2uWlN2f4ifrm8WY4WhR9yiTOh0uo0pIqqaUQQvkEq3o5hOXuXLECEs+w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.2.0.tgz", + "integrity": "sha512-b6xxyoexfbRNRI8gjblzVOnLr4peCJhGbYGPpJ3LFqpi5nsFfoK4mmDLvWdeah0B7gmJeXabN7nQkFoqeSdmOw==", "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", "commander": "^5.1.0", - "joi": "^17.4.0", - "querystring": "0.2.0", - "webpack": "^5.28.0", - "webpack-merge": "^5.7.3" + "joi": "^17.6.0", + "react-helmet-async": "^1.3.0", + "utility-types": "^3.10.0", + "webpack": "^5.73.0", + "webpack-merge": "^5.8.0" } }, "@docusaurus/utils": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.0.0-beta.0.tgz", - "integrity": "sha512-bvrT1EQu0maavr0Hb/lke9jmpzgVL/9tn5VQtbyahf472eJFY0bQDExllDrHK+l784SUvucqX0iaQeg0q6ySUw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.2.0.tgz", + "integrity": "sha512-oNk3cjvx7Tt1Lgh/aeZAmFpGV2pDr5nHKrBVx6hTkzGhrnMuQqLt6UPlQjdYQ3QHXwyF/ZtZMO1D5Pfi0lu7SA==", "requires": { - "@docusaurus/types": "2.0.0-beta.0", - "@types/github-slugger": "^1.3.0", - "chalk": "^4.1.0", - "escape-string-regexp": "^4.0.0", - "fs-extra": "^9.1.0", - "gray-matter": "^4.0.2", - "lodash": "^4.17.20", + "@docusaurus/logger": "2.2.0", + "@svgr/webpack": "^6.2.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.1.0", + "github-slugger": "^1.4.0", + "globby": "^11.1.0", + "gray-matter": "^4.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "micromatch": "^4.0.5", "resolve-pathname": "^3.0.0", - "tslib": "^2.1.0" + "shelljs": "^0.8.5", + "tslib": "^2.4.0", + "url-loader": "^4.1.1", + "webpack": "^5.73.0" } }, - "@docusaurus/utils-validation": { - "version": "2.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.0.tgz", - "integrity": "sha512-ELl/FVJ6xBz35TisZ1NmJhjbiVXDeU++K531PEFPCPmwnQPh7S6hZXdPnR71/Kc3BmuN9X2ZkwGOqNKVfys2Bg==", + "@docusaurus/utils-common": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.2.0.tgz", + "integrity": "sha512-qebnerHp+cyovdUseDQyYFvMW1n1nv61zGe5JJfoNQUnjKuApch3IVsz+/lZ9a38pId8kqehC1Ao2bW/s0ntDA==", "requires": { - "@docusaurus/utils": "2.0.0-beta.0", - "chalk": "^4.1.0", - "joi": "^17.4.0", - "tslib": "^2.1.0" + "tslib": "^2.4.0" } }, - "@endiliey/static-site-generator-webpack-plugin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@endiliey/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.0.tgz", - "integrity": "sha512-3MBqYCs30qk1OBRC697NqhGouYbs71D1B8hrk/AFJC6GwF2QaJOQZtA1JYAaGSe650sZ8r5ppRTtCRXepDWlng==", + "@docusaurus/utils-validation": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.2.0.tgz", + "integrity": "sha512-I1hcsG3yoCkasOL5qQAYAfnmVoLei7apugT6m4crQjmDGxq+UkiRrq55UqmDDyZlac/6ax/JC0p+usZ6W4nVyg==", "requires": { - "bluebird": "^3.7.1", - "cheerio": "^0.22.0", - "eval": "^0.1.4", - "url": "^0.11.0", - "webpack-sources": "^1.4.3" + "@docusaurus/logger": "2.2.0", + "@docusaurus/utils": "2.2.0", + "joi": "^17.6.0", + "js-yaml": "^4.1.0", + "tslib": "^2.4.0" } }, "@eslint/eslintrc": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", - "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "requires": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", @@ -17442,51 +16556,141 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "requires": { - "type-fest": "^0.8.1" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + }, + "@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/types": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz", + "integrity": "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==", + "requires": { + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" } } }, - "@hapi/hoek": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz", - "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==" + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, - "@hapi/topo": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", - "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", + "@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "requires": { - "@hapi/hoek": "^9.0.0" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, "@mdx-js/mdx": { "version": "1.6.22", "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", @@ -17548,6 +16752,11 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" } } }, @@ -17563,37 +16772,37 @@ "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==" }, "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "requires": { - "@nodelib/fs.stat": "2.0.4", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" }, "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "requires": { - "@nodelib/fs.scandir": "2.1.4", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "@polka/url": { - "version": "1.0.0-next.12", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.12.tgz", - "integrity": "sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ==" + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==" }, "@sideway/address": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz", - "integrity": "sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", "requires": { "@hapi/hoek": "^9.0.0" } @@ -17608,118 +16817,151 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" }, + "@slorber/static-site-generator-webpack-plugin": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz", + "integrity": "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==", + "requires": { + "eval": "^0.1.8", + "p-map": "^4.0.0", + "webpack-sources": "^3.2.2" + }, + "dependencies": { + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + } + } + }, "@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", + "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==", + "requires": {} }, "@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.5.0.tgz", + "integrity": "sha512-8zYdkym7qNyfXpWvu4yq46k41pyNM9SOstoWhKlm+IfdCE1DdnRKeMUPsWIEO/DEkaWxJ8T9esNdG3QwQ93jBA==", + "requires": {} }, "@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.5.0.tgz", + "integrity": "sha512-NFdxMq3xA42Kb1UbzCVxplUc0iqSyM9X8kopImvFnB+uSDdzIHOdbs1op8ofAvVRtbg4oZiyRl3fTYeKcOe9Iw==", + "requires": {} }, "@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", + "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==", + "requires": {} }, "@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", + "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==", + "requires": {} }, "@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", + "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==", + "requires": {} }, "@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", + "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==", + "requires": {} }, "@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", + "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==", + "requires": {} }, "@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", + "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", "requires": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", + "@svgr/babel-plugin-remove-jsx-attribute": "*", + "@svgr/babel-plugin-remove-jsx-empty-expression": "*", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", + "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", + "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", + "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", + "@svgr/babel-plugin-transform-svg-component": "^6.5.1" } }, "@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", + "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", "requires": { - "@svgr/plugin-jsx": "^5.5.0", + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" + "cosmiconfig": "^7.0.1" } }, "@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", + "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", "requires": { - "@babel/types": "^7.12.6" + "@babel/types": "^7.20.0", + "entities": "^4.4.0" } }, "@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", + "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", "requires": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" + "@babel/core": "^7.19.6", + "@svgr/babel-preset": "^6.5.1", + "@svgr/hast-util-to-babel-ast": "^6.5.1", + "svg-parser": "^2.0.4" } }, "@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", + "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", "requires": { - "cosmiconfig": "^7.0.0", + "cosmiconfig": "^7.0.1", "deepmerge": "^4.2.2", - "svgo": "^1.2.2" + "svgo": "^2.8.0" } }, "@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", + "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", "requires": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" + "@babel/core": "^7.19.6", + "@babel/plugin-transform-react-constant-elements": "^7.18.12", + "@babel/preset-env": "^7.19.4", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@svgr/core": "^6.5.1", + "@svgr/plugin-jsx": "^6.5.1", + "@svgr/plugin-svgo": "^6.5.1" } }, "@szmarczak/http-timer": { @@ -17731,87 +16973,186 @@ } }, "@trysound/sax": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz", - "integrity": "sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow==" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "peer": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "peer": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "peer": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "peer": true + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } }, "@types/eslint": { - "version": "7.2.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.10.tgz", - "integrity": "sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ==", + "version": "8.4.7", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.7.tgz", + "integrity": "sha512-ehM7cCt2RSFs42mb+lcmhFT9ouIlV92PuaeRGn8N8c98oMjG4Z5pJHA9b1QiCcuqnbPSHcyfiD3mlhqMaHsQIw==", "requires": { "@types/estree": "*", "@types/json-schema": "*" } }, "@types/eslint-scope": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz", - "integrity": "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "requires": { "@types/eslint": "*", "@types/estree": "*" } }, "@types/estree": { - "version": "0.0.47", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.47.tgz", - "integrity": "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==" + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" }, - "@types/github-slugger": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz", - "integrity": "sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g==" + "@types/express": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", + "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.31", + "@types/qs": "*", + "@types/serve-static": "*" + } }, - "@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "@types/express-serve-static-core": { + "version": "4.17.32", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz", + "integrity": "sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==", "requires": { - "@types/minimatch": "*", - "@types/node": "*" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" } }, "@types/hast": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.1.tgz", - "integrity": "sha512-viwwrB+6xGzw+G1eWpF9geV3fnsDgXqHG+cqgiHrvQfDUW5hzhCyV7Sy3UJxhfRFBsgky2SSW33qi/YrIkjX5Q==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", "requires": { "@types/unist": "*" } }, + "@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" + }, "@types/html-minifier-terser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", - "integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "requires": { + "@types/istanbul-lib-report": "*" + } }, "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, "@types/katex": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.11.0.tgz", - "integrity": "sha512-27BfE8zASRLYfSBNMk5/+KIjr2CBBrH0i5lhsO04fca4TGirIIMay73v3zNkzqmsaeIa/Mi5kejWDcxPLAmkvA==" + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.11.1.tgz", + "integrity": "sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg==" }, "@types/mdast": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", - "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", + "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", "requires": { "@types/unist": "*" } }, - "@types/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==" + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, "@types/node": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.3.tgz", - "integrity": "sha512-/WbxFeBU+0F79z9RdEOXH4CsDga+ibi5M8uEYr91u3CkT/pdWcV8MCook+4wDPnZBexRdwWS+PiVZ2xJviAzcQ==" + "version": "18.11.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz", + "integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==" }, "@types/parse-json": { "version": "4.0.0", @@ -17824,174 +17165,256 @@ "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" }, "@types/prop-types": { - "version": "15.7.3", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", - "peer": true + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" }, - "@types/q": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", - "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "@types/react": { - "version": "17.0.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.5.tgz", - "integrity": "sha512-bj4biDB9ZJmGAYTWSKJly6bMr4BLUiBrx9ujiJEoP9XIDY9CTaPGxE5QWN/1WjpPLzYF7/jRNnV2nNxNe970sw==", - "peer": true, + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", "requires": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, + "@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "@types/react-router-config": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.6.tgz", + "integrity": "sha512-db1mx37a1EJDf1XeX8jJN7R3PZABmJQXR8r28yUjVMFSjkmnQo6X6pOEEmNl+Tp2gYQOGPdYbFIipBtdElZ3Yg==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, "@types/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-dqYdvN7Sbw8QT/0Ci5rhjE4/iCMJEM0Y9rHpCu+gGXD9Lwbz28t6HI2yegsB6BoV1sShRMU6lAmAcgRjmFy7LA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==", "requires": { "@types/node": "*" } }, "@types/scheduler": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz", - "integrity": "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==", - "peer": true + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "requires": { + "@types/node": "*" + } }, "@types/unist": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", - "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + }, + "@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "requires": { + "@types/node": "*" + } + }, + "@types/yargs": { + "version": "17.0.19", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.19.tgz", + "integrity": "sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "@webassemblyjs/ast": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz", - "integrity": "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "requires": { - "@webassemblyjs/helper-numbers": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0" + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz", - "integrity": "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==" + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" }, "@webassemblyjs/helper-api-error": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz", - "integrity": "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==" + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" }, "@webassemblyjs/helper-buffer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz", - "integrity": "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==" + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" }, "@webassemblyjs/helper-numbers": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz", - "integrity": "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz", - "integrity": "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==" + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz", - "integrity": "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" } }, "@webassemblyjs/ieee754": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz", - "integrity": "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz", - "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz", - "integrity": "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==" + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" }, "@webassemblyjs/wasm-edit": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz", - "integrity": "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/helper-wasm-section": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-opt": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "@webassemblyjs/wast-printer": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz", - "integrity": "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz", - "integrity": "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-buffer": "1.11.0", - "@webassemblyjs/wasm-gen": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz", - "integrity": "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", "requires": { - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/helper-api-error": "1.11.0", - "@webassemblyjs/helper-wasm-bytecode": "1.11.0", - "@webassemblyjs/ieee754": "1.11.0", - "@webassemblyjs/leb128": "1.11.0", - "@webassemblyjs/utf8": "1.11.0" + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz", - "integrity": "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", "requires": { - "@webassemblyjs/ast": "1.11.0", + "@webassemblyjs/ast": "1.11.1", "@xtuc/long": "4.2.2" } }, @@ -18006,12 +17429,12 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "acorn": { @@ -18020,20 +17443,20 @@ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" }, "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "requires": {} }, "acorn-walk": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.0.tgz", - "integrity": "sha512-mjmzmv12YIG/G8JQdQuz2MUDShEJ6teYpT5bmWA4q7iwoGen8xtt3twF3OvzIUl+Q06aWIjvnwQUKvQ6TtMRjg==" + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" }, "address": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", - "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz", + "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==" }, "aggregate-error": { "version": "3.1.0", @@ -18055,11 +17478,31 @@ "uri-js": "^4.2.2" } }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "requires": {} + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } }, "ajv-keywords": { "version": "3.5.2", @@ -18068,111 +17511,56 @@ "requires": {} }, "algoliasearch": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.9.1.tgz", - "integrity": "sha512-EeJUYXzBEhZSsL6tXc3hseLBCtlNLa1MZ4mlMK6EeX38yRjY5vgnFcNNml6uUhlOjvheKxgkKRpPWkxgL8Cqkg==", - "requires": { - "@algolia/cache-browser-local-storage": "4.9.1", - "@algolia/cache-common": "4.9.1", - "@algolia/cache-in-memory": "4.9.1", - "@algolia/client-account": "4.9.1", - "@algolia/client-analytics": "4.9.1", - "@algolia/client-common": "4.9.1", - "@algolia/client-recommendation": "4.9.1", - "@algolia/client-search": "4.9.1", - "@algolia/logger-common": "4.9.1", - "@algolia/logger-console": "4.9.1", - "@algolia/requester-browser-xhr": "4.9.1", - "@algolia/requester-common": "4.9.1", - "@algolia/requester-node-http": "4.9.1", - "@algolia/transporter": "4.9.1" + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.14.2.tgz", + "integrity": "sha512-ngbEQonGEmf8dyEh5f+uOIihv4176dgbuOZspiuhmTTBRBuzWu3KCGHre6uHj5YyuC7pNvQGzB6ZNJyZi0z+Sg==", + "requires": { + "@algolia/cache-browser-local-storage": "4.14.2", + "@algolia/cache-common": "4.14.2", + "@algolia/cache-in-memory": "4.14.2", + "@algolia/client-account": "4.14.2", + "@algolia/client-analytics": "4.14.2", + "@algolia/client-common": "4.14.2", + "@algolia/client-personalization": "4.14.2", + "@algolia/client-search": "4.14.2", + "@algolia/logger-common": "4.14.2", + "@algolia/logger-console": "4.14.2", + "@algolia/requester-browser-xhr": "4.14.2", + "@algolia/requester-common": "4.14.2", + "@algolia/requester-node-http": "4.14.2", + "@algolia/transporter": "4.14.2" } }, "algoliasearch-helper": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.4.4.tgz", - "integrity": "sha512-OjyVLjykaYKCMxxRMZNiwLp8CS310E0qAeIY2NaublcmLAh8/SL19+zYHp7XCLtMem2ZXwl3ywMiA32O9jszuw==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.1.tgz", + "integrity": "sha512-mvsPN3eK4E0bZG0/WlWJjeqe/bUD2KOEVOl0GyL/TGXn6wcpZU8NOuztGHCUKXkyg5gq6YzUakVTmnmSSO5Yiw==", "requires": { - "events": "^1.1.1" + "@algolia/events": "^4.0.1" } }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" - }, "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } + "string-width": "^4.1.0" } }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" - } - } + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "4.3.0", @@ -18192,45 +17580,30 @@ } }, "arg": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.0.tgz", - "integrity": "sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ==" + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" }, "array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" + "is-string": "^1.0.7" } }, "array-union": { @@ -18238,89 +17611,51 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, "array.prototype.flatmap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", - "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "function-bind": "^1.1.1" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" } }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, "at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, "autoprefixer": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.5.tgz", - "integrity": "sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==", - "requires": { - "browserslist": "^4.16.3", - "caniuse-lite": "^1.0.30001196", - "colorette": "^1.2.2", - "fraction.js": "^4.0.13", + "version": "10.4.12", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz", + "integrity": "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001407", + "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", - "postcss-value-parser": "^4.1.0" + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" } }, "axios": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", - "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", "requires": { - "follow-redirects": "^1.10.0" + "follow-redirects": "^1.14.7" } }, "babel-eslint": { @@ -18337,34 +17672,14 @@ } }, "babel-loader": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", - "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "requires": { "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", + "loader-utils": "^2.0.0", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } } }, "babel-plugin-apply-mdx-type-prop": { @@ -18384,9 +17699,9 @@ } }, "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", "requires": { "object.assign": "^4.1.0" } @@ -18407,12 +17722,12 @@ } }, "babel-plugin-polyfill-corejs2": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.0.tgz", - "integrity": "sha512-9bNwiR0dS881c5SHnzCmmGlMkJLl0OUZvxrxHo9w/iNoRuqaPjqlvBf4HrovXtQs/au5yKkpcdgfT1cC5PAZwg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "requires": { - "@babel/compat-data": "^7.13.11", - "@babel/helper-define-polyfill-provider": "^0.2.0", + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" }, "dependencies": { @@ -18424,20 +17739,20 @@ } }, "babel-plugin-polyfill-corejs3": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.0.tgz", - "integrity": "sha512-zZyi7p3BCUyzNxLx8KV61zTINkkV65zVkDAFNZmrTCRVhjo1jAS+YLvDJ9Jgd/w2tsAviCwFHReYfxO3Iql8Yg==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.2.0", - "core-js-compat": "^3.9.1" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.0.tgz", - "integrity": "sha512-J7vKbCuD2Xi/eEHxquHN14bXAW9CXtecwuLrOIDJtcZzTaPzV1VdEfoUf9AzcRBMolKUQKM9/GVojeh0hFiqMg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "requires": { - "@babel/helper-define-polyfill-provider": "^0.2.0" + "@babel/helper-define-polyfill-provider": "^0.3.3" } }, "bail": { @@ -18450,39 +17765,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - } - } - }, "base16": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", - "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, "big.js": { "version": "5.2.2", @@ -18494,37 +17785,30 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "requires": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -18536,45 +17820,36 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", + "array-flatten": "^2.1.2", "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - }, - "dependencies": { - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - } + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "boxen": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", - "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", "requires": { "ansi-align": "^3.0.0", "camelcase": "^6.2.0", "chalk": "^4.1.0", "cli-boxes": "^2.2.1", - "string-width": "^4.2.0", + "string-width": "^4.2.2", "type-fest": "^0.20.2", "widest-line": "^3.1.0", "wrap-ansi": "^7.0.0" @@ -18598,47 +17873,30 @@ } }, "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "requires": { - "caniuse-lite": "^1.0.30001219", - "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", - "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" } }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" }, "cacheable-request": { "version": "6.1.0", @@ -18666,6 +17924,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" } } }, @@ -18693,9 +17956,9 @@ } }, "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" }, "camelcase-css": { "version": "2.0.1", @@ -18714,9 +17977,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001341", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz", - "integrity": "sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==" + "version": "1.0.30001422", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001422.tgz", + "integrity": "sha512-hSesn02u1QacQHhaxl/kNMZwqVG35Sz/8DgvmgedxSH8z9UUpcDYSPYgsj3x5dQNRcNp6BwpSfQfVzYUTm+fog==" }, "ccount": { "version": "1.1.0", @@ -18724,9 +17987,9 @@ "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==" }, "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18748,143 +18011,78 @@ "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" }, "cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "requires": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "dependencies": { + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + } + } + } + }, + "cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "requires": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" } }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" }, "ci-info": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.1.1.tgz", - "integrity": "sha512-kdRWLBIJwdsYJWYJFtAFFYxybguqeF91qpZaggjG5Nf8QKdizFG2hjqvaTXbxFIcYbSaD74KpAXv6BSm17DHEQ==" - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz", + "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==" }, "clean-css": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.2.tgz", - "integrity": "sha512-QcaGg9OuMo+0Ds933yLOY+gHPWbxhxqF0HDexmToPf8pczvmvZGYzd+QqWp9/mkucAOKViI+dSFOqoZIvXbeBw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", "requires": { "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "clean-stack": { @@ -18897,91 +18095,13 @@ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==" }, - "clipboard": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz", - "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==", - "optional": true, - "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - } + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" } }, "clone-deep": { @@ -18991,121 +18111,27 @@ "requires": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "requires": { - "mimic-response": "^1.0.0" - } - }, - "clsx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", - "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } + "shallow-clone": "^3.0.0" + } + }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "requires": { + "mimic-response": "^1.0.0" } }, + "clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + }, "collapse-white-space": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==" }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", - "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.4" - }, - "dependencies": { - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - } - } - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -19119,19 +18145,15 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "color-string": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", - "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" }, "colorette": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", - "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" }, "combine-promises": { "version": "1.1.0", @@ -19143,6 +18165,11 @@ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" }, + "command-exists-promise": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/command-exists-promise/-/command-exists-promise-2.0.2.tgz", + "integrity": "sha512-T6PB6vdFrwnHXg/I0kivM3DqaCGZLjjYSOe0a5WgFKcz1sOnmOeIjnhQPXVXX3QjVbLyTJ85lJkX6lUpukTzaA==" + }, "commander": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", @@ -19151,12 +18178,7 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" }, "compressible": { "version": "2.0.18", @@ -19180,11 +18202,6 @@ "vary": "~1.1.2" }, "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -19196,14 +18213,19 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "configstore": { "version": "5.0.1", @@ -19219,9 +18241,9 @@ } }, "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" }, "consola": { "version": "2.15.3", @@ -19229,11 +18251,11 @@ "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" }, "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" } }, "content-type": { @@ -19242,27 +18264,19 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "requires": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "copy-text-to-clipboard": { "version": "3.0.1", @@ -19270,66 +18284,112 @@ "integrity": "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==" }, "copy-webpack-plugin": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-8.1.1.tgz", - "integrity": "sha512-rYM2uzRxrLRpcyPqGceRBDpxxUV8vcDqIKxAUKfcnFpcrPxT5+XvhTxv7XLjo5AvEJFPdAE3zCogG2JVahqgSQ==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", "requires": { - "fast-glob": "^3.2.5", - "glob-parent": "^5.1.1", - "globby": "^11.0.3", + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", "normalize-path": "^3.0.0", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1" + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" }, "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "globby": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", + "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" } } }, "core-js": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.12.1.tgz", - "integrity": "sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw==" + "version": "3.25.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.5.tgz", + "integrity": "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==" }, "core-js-compat": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.12.1.tgz", - "integrity": "sha512-i6h5qODpw6EsHAoIdQhKoZdWn+dGBF3dSS8m5tif36RlWvW3A6+yu2S16QHUo3CrkzrnEskMAt9f8FxmY9fhWQ==", + "version": "3.25.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.5.tgz", + "integrity": "sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==", "requires": { - "browserslist": "^4.16.6", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" - } + "browserslist": "^4.21.4" } }, "core-js-pure": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz", - "integrity": "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ==" + "version": "3.25.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.5.tgz", + "integrity": "sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg==" }, "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", "requires": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -19338,6 +18398,18 @@ "yaml": "^1.10.0" } }, + "cosmiconfig-typescript-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", + "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "requires": {} + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "peer": true + }, "cross-fetch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", @@ -19361,116 +18433,102 @@ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" }, - "css-color-names": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", - "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==" - }, "css-declaration-sorter": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.0.0.tgz", - "integrity": "sha512-S0TE4E0ha5+tBHdLWPc5n+S8E4dFBS5xScPvgHkLNZwWvX4ISoFGhGeerLC9uS1cKA/sC+K2wHq6qEbcagT/fg==", - "requires": { - "timsort": "^0.3.0" - } + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "requires": {} }, "css-loader": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.4.tgz", - "integrity": "sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", + "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", "requires": { - "camelcase": "^6.2.0", "icss-utils": "^5.1.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.10", + "postcss": "^8.4.19", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.0", "postcss-modules-scope": "^3.0.0", "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.1.0", - "schema-utils": "^3.0.0", - "semver": "^7.3.5" - }, - "dependencies": { - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" } }, "css-minimizer-webpack-plugin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-2.0.0.tgz", - "integrity": "sha512-cG/uc94727tx5pBNtb1Sd7gvUPzwmcQi1lkpfqTpdkuNq75hJCw7bIVsCNijLm4dhDcr1atvuysl2rZqOG8Txw==", - "requires": { - "cssnano": "^5.0.0", - "jest-worker": "^26.3.0", - "p-limit": "^3.0.2", - "postcss": "^8.2.9", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", + "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "requires": { + "cssnano": "^5.1.8", + "jest-worker": "^29.1.2", + "postcss": "^8.4.17", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", "source-map": "^0.6.1" }, "dependencies": { - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } } } }, "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" } }, - "css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" - }, "css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", "requires": { - "mdn-data": "2.0.4", + "mdn-data": "2.0.14", "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" }, "cssesc": { "version": "3.0.0", @@ -19478,68 +18536,68 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, "cssnano": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.2.tgz", - "integrity": "sha512-8JK3EnPsjQsULme9/e5M2hF564f/480hwsdcHvQ7ZtAIMfQ1O3SCfs+b8Mjf5KJxhYApyRshR2QSovEJi2K72Q==", + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.13.tgz", + "integrity": "sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ==", "requires": { - "cosmiconfig": "^7.0.0", - "cssnano-preset-default": "^5.0.1", - "is-resolvable": "^1.1.0" + "cssnano-preset-default": "^5.2.12", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" } }, "cssnano-preset-advanced": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.0.1.tgz", - "integrity": "sha512-g+LB6GcihLXcBEdDh+mzk1qX9jgtBkVpzAg1OlgrH6C+qKIQYRHwAPyaoXy95Ci83sYYXlwJ0OrqLYTIUEBLZQ==", + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.9.tgz", + "integrity": "sha512-njnh4pp1xCsibJcEHnWZb4EEzni0ePMqPuPNyuWT4Z+YeXmsgqNuTPIljXFEXhxGsWs9183JkXgHxc1TcsahIg==", "requires": { - "autoprefixer": "^10.0.2", - "cssnano-preset-default": "^5.0.1", - "postcss-discard-unused": "^5.0.0", - "postcss-merge-idents": "^5.0.0", - "postcss-reduce-idents": "^5.0.0", - "postcss-zindex": "^5.0.0" + "autoprefixer": "^10.4.12", + "cssnano-preset-default": "^5.2.13", + "postcss-discard-unused": "^5.1.0", + "postcss-merge-idents": "^5.1.1", + "postcss-reduce-idents": "^5.2.0", + "postcss-zindex": "^5.1.0" } }, "cssnano-preset-default": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.0.1.tgz", - "integrity": "sha512-cfmfThYODGqhpQKDq9H0MTAqkMvZ3dGbOUTBKw0xWZiIycMqHid22LsJXJl4r1qX4qzDeKxcSyQ/Xb5Mu3Z//Q==", - "requires": { - "css-declaration-sorter": "6.0.0", - "cssnano-utils": "^2.0.0", - "postcss-calc": "^8.0.0", - "postcss-colormin": "^5.0.0", - "postcss-convert-values": "^5.0.0", - "postcss-discard-comments": "^5.0.0", - "postcss-discard-duplicates": "^5.0.0", - "postcss-discard-empty": "^5.0.0", - "postcss-discard-overridden": "^5.0.0", - "postcss-merge-longhand": "^5.0.1", - "postcss-merge-rules": "^5.0.0", - "postcss-minify-font-values": "^5.0.0", - "postcss-minify-gradients": "^5.0.0", - "postcss-minify-params": "^5.0.0", - "postcss-minify-selectors": "^5.0.0", - "postcss-normalize-charset": "^5.0.0", - "postcss-normalize-display-values": "^5.0.0", - "postcss-normalize-positions": "^5.0.0", - "postcss-normalize-repeat-style": "^5.0.0", - "postcss-normalize-string": "^5.0.0", - "postcss-normalize-timing-functions": "^5.0.0", - "postcss-normalize-unicode": "^5.0.0", - "postcss-normalize-url": "^5.0.0", - "postcss-normalize-whitespace": "^5.0.0", - "postcss-ordered-values": "^5.0.0", - "postcss-reduce-initial": "^5.0.0", - "postcss-reduce-transforms": "^5.0.0", - "postcss-svgo": "^5.0.0", - "postcss-unique-selectors": "^5.0.0" + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz", + "integrity": "sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==", + "requires": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.3", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.1", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" } }, "cssnano-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.0.tgz", - "integrity": "sha512-xvxmTszdrvSyTACdPe8VU5J6p4sm3egpgw54dILvNqt5eBUv6TFjACLhSxtRuEsxYrgy8uDy269YjScO5aKbGA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", "requires": {} }, "csso": { @@ -19548,83 +18606,38 @@ "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", "requires": { "css-tree": "^1.1.2" - }, - "dependencies": { - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, - "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "csstype": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", - "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==", - "peer": true + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, "decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", "requires": { "mimic-response": "^1.0.0" } }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" }, "deepmerge": { "version": "4.2.2", @@ -19632,92 +18645,11 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" }, "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - } + "execa": "^5.0.0" } }, "defer-to-connect": { @@ -19725,27 +18657,24 @@ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "del": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", - "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", "requires": { "globby": "^11.0.1", "graceful-fs": "^4.2.4", @@ -19754,24 +18683,18 @@ "is-path-inside": "^3.0.2", "p-map": "^4.0.0", "rimraf": "^3.0.2", - "slash": "^3.0.0" - } - }, - "delegate": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", - "optional": true + "slash": "^3.0.0" + } }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "detab": { "version": "2.0.4", @@ -19782,32 +18705,17 @@ } }, "detect-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.5.tgz", - "integrity": "sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" }, "detect-port": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz", - "integrity": "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", "requires": { "address": "^1.0.1", - "debug": "^2.6.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "debug": "4" } }, "detect-port-alt": { @@ -19830,10 +18738,16 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "peer": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -19845,23 +18759,14 @@ "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" }, "dns-packet": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", - "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", "requires": { - "buffer-indexof": "^1.0.0" + "@leichtgewicht/ip-codec": "^2.0.1" } }, "doctrine": { @@ -19881,34 +18786,36 @@ } }, "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" } }, "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" }, "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "requires": { - "domelementtype": "1" + "domelementtype": "^2.3.0" } }, "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" } }, "dot-case": { @@ -19941,24 +18848,29 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==" + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron-to-chromium": { - "version": "1.3.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz", - "integrity": "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==" + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" }, "emoji-regex": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz", - "integrity": "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "emojis-list": { "version": "3.0.0", @@ -19973,7 +18885,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "end-of-stream": { "version": "1.4.4", @@ -19984,9 +18896,9 @@ } }, "enhanced-resolve": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", - "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -20001,17 +18913,9 @@ } }, "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "requires": { - "prr": "~1.0.1" - } + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" }, "error-ex": { "version": "1.3.2", @@ -20022,32 +18926,48 @@ } }, "es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" } }, "es-module-lexer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz", - "integrity": "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==" + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "requires": { + "has": "^1.0.3" + } }, "es-to-primitive": { "version": "1.2.1", @@ -20072,7 +18992,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "escape-string-regexp": { "version": "4.0.0", @@ -20080,27 +19000,30 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "eslint": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz", - "integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "requires": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.1", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", + "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", @@ -20109,7 +19032,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -20118,7 +19041,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -20145,9 +19068,9 @@ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" }, "globals": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", "requires": { "type-fest": "^0.20.2" } @@ -20164,22 +19087,24 @@ } }, "eslint-plugin-react": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.23.2.tgz", - "integrity": "sha512-AfjgFQB+nYszudkxRkTFu0UR1zEQig0ArVMPloKhxwlwkzaw/fBiH0QWcBBhZONlXqQC51+nfqFrkn4EzHcGBw==", + "version": "7.31.10", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz", + "integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==", "requires": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", + "array-includes": "^3.1.5", + "array.prototype.flatmap": "^1.3.0", "doctrine": "^2.1.0", - "has": "^1.0.3", + "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.3", - "object.fromentries": "^2.0.4", - "object.values": "^1.1.3", - "prop-types": "^15.7.2", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.1", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.4" + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.7" }, "dependencies": { "doctrine": { @@ -20191,13 +19116,19 @@ } }, "resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -20208,6 +19139,13 @@ "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } } }, "eslint-utils": { @@ -20244,13 +19182,6 @@ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" - } } }, "esrecurse": { @@ -20259,19 +19190,12 @@ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" }, "esutils": { "version": "2.0.3", @@ -20279,20 +19203,21 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "eta": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/eta/-/eta-1.12.1.tgz", - "integrity": "sha512-H8npoci2J/7XiPnVcCVulBSPsTNGvGaINyMjQDU8AFqp9LGsEYS88g2CiU+d01Sg44WtX7o4nb8wUJ9vnI+tiA==" + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/eta/-/eta-1.12.3.tgz", + "integrity": "sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg==" }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "eval": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.6.tgz", - "integrity": "sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz", + "integrity": "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==", "requires": { + "@types/node": "*", "require-like": ">= 0.1.1" } }, @@ -20302,22 +19227,14 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - }, - "eventsource": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", - "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", - "requires": { - "original": "^1.0.0" - } + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" }, "execa": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", - "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "requires": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -20330,136 +19247,49 @@ "strip-final-newline": "^2.0.0" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -20471,7 +19301,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, @@ -20483,52 +19313,26 @@ "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", "requires": { "is-extendable": "^0.1.0" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - } - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" + "micromatch": "^4.0.4" } }, "fast-json-stable-stringify": { @@ -20539,28 +19343,28 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "fast-url-parser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", "requires": { "punycode": "^1.3.2" } }, "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "requires": { "reusify": "^1.0.4" } }, "faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "requires": { "websocket-driver": ">=0.5.1" } @@ -20574,17 +19378,17 @@ } }, "fbjs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz", - "integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", + "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", "requires": { - "cross-fetch": "^3.0.4", + "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", "loose-envify": "^1.0.0", "object-assign": "^4.1.0", "promise": "^7.1.1", "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" + "ua-parser-js": "^0.7.30" } }, "fbjs-css-vars": { @@ -20592,6 +19396,14 @@ "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "requires": { + "pend": "~1.2.0" + } + }, "feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -20600,21 +19412,6 @@ "xml-js": "^1.6.11" } }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "requires": { - "escape-string-regexp": "^1.0.5" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - } - } - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -20633,27 +19430,21 @@ }, "dependencies": { "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "requires": { - "@types/json-schema": "^7.0.6", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } } } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, "filesize": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", - "integrity": "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==" + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" }, "fill-range": { "version": "7.0.1", @@ -20664,16 +19455,16 @@ } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "dependencies": { @@ -20688,14 +19479,14 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "requires": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -20717,245 +19508,130 @@ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "requires": { "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" - }, - "flux": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.1.tgz", - "integrity": "sha512-emk4RCvJ8RzNP2lNpphKnG7r18q8elDYNAPx7xn+bDeOIo9FFfxEfIQ2y6YbQNmnsGD3nH1noxtLE64Puz1bRQ==", - "requires": { - "fbemitter": "^3.0.0", - "fbjs": "^3.0.0" - } - }, - "follow-redirects": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz", - "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "fork-ts-checker-webpack-plugin": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz", - "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "chalk": "^2.4.1", - "micromatch": "^3.1.10", - "minimatch": "^3.0.4", - "semver": "^5.6.0", - "tapable": "^1.0.0", - "worker-rpc": "^0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + }, + "flux": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz", + "integrity": "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==", + "requires": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + } + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" } }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - } + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" } }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", "requires": { - "has-flag": "^3.0.0" + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" } }, "tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } } } }, "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fraction.js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.0.tgz", - "integrity": "sha512-o9lSKpK0TDqDwTL24Hxqi6I99s942l6TYkfl6WvGWgLOIFz/YonSGKfiSeMadoiNvTfqnfOa9mjb5SGVbBK9/w==" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "^0.2.2" - } + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "requires": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.2", @@ -20968,29 +19644,40 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "get-own-enumerable-property-symbols": { @@ -21003,28 +19690,29 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, - "github-slugger": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", - "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "requires": { - "emoji-regex": ">=6.0.0 <=6.1.1" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" } }, + "github-slugger": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", + "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==" + }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -21091,34 +19779,25 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" }, "dependencies": { "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" } } }, - "good-listener": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", - "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", - "optional": true, - "requires": { - "delegate": "^3.1.2" - } - }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -21148,9 +19827,9 @@ } }, "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "gray-matter": { "version": "4.0.3", @@ -21183,12 +19862,11 @@ } }, "gzip-size": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", - "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", "requires": { - "duplexer": "^0.1.1", - "pify": "^4.0.1" + "duplexer": "^0.1.2" } }, "handle-thing": { @@ -21205,70 +19883,34 @@ } }, "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" - }, - "has-value": { + "has-property-descriptors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-intrinsic": "^1.1.1" } }, - "has-values": { + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } + "has-symbols": "^1.0.2" } }, "has-yarn": { @@ -21369,11 +20011,6 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" - }, "history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -21398,7 +20035,7 @@ "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "requires": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -21420,6 +20057,11 @@ "util-deprecate": "~1.0.1" } }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -21430,59 +20072,36 @@ } } }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" - }, "html-entities": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", - "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" }, "html-minifier-terser": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", - "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", "requires": { - "camel-case": "^4.1.1", - "clean-css": "^4.2.3", - "commander": "^4.1.1", + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", "he": "^1.2.0", - "param-case": "^3.0.3", + "param-case": "^3.0.4", "relateurl": "^0.2.7", - "terser": "^4.6.3" + "terser": "^5.10.0" }, "dependencies": { - "clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", - "requires": { - "source-map": "~0.6.0" - } - }, "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" } } }, "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==" }, "html-void-elements": { "version": "1.0.5", @@ -21490,28 +20109,26 @@ "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==" }, "html-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-rZsVvPXUYFyME0cuGkyOHfx9hmkFa4pWfxY/mdY38PsBEaVNsRoA+Id+8z6DBDgyv3zaw6XQszdF8HLwfQvcdQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", "requires": { - "@types/html-minifier-terser": "^5.0.0", - "html-minifier-terser": "^5.0.1", - "lodash": "^4.17.20", - "pretty-error": "^2.1.1", + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", "tapable": "^2.0.0" } }, "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" } }, "http-cache-semantics": { @@ -21522,31 +20139,24 @@ "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" }, "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" } }, "http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" }, "http-proxy": { "version": "1.18.1", @@ -21559,114 +20169,21 @@ } }, "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" }, "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - }, - "is-number": { + "is-plain-obj": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - } - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" } } }, @@ -21682,118 +20199,64 @@ "requires": { "safer-buffer": ">= 2.1.2 < 3" } - }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "requires": {} - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" - }, - "immer": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz", - "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==" - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "requires": { - "find-up": "^3.0.0" - } - } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "requires": {} + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" + }, + "image-size": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz", + "integrity": "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==", + "requires": { + "queue": "6.0.2" + } + }, + "immer": { + "version": "9.0.17", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.17.tgz", + "integrity": "sha512-+hBruaLSQvkPfxRiTLK/mi4vLH+/VQS6z2KJahdoxlleFOI8ARqzOF17uy12eFDlqWmPoygwc5evgwcp+dlHhg==" + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==" + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" }, "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" - }, "infima": { - "version": "0.2.0-alpha.23", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.23.tgz", - "integrity": "sha512-V0RTjB1otjpH3E2asbydx3gz7ovdSJsuV7r9JTdBggqRilnelTJUcXxLawBQQKsjQi5qPcRTjxnlaV8xyyKhhw==" + "version": "0.2.0-alpha.42", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.42.tgz", + "integrity": "sha512-ift8OXNbQQwtbIt6z16KnSWP7uJ/SysSMFI4F87MNRTicypfl4Pv3E2OGVv6N3nSZFJvA8imYulCBS64iyHYww==" }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "requires": { "once": "^1.3.0", "wrappy": "1" @@ -21814,15 +20277,6 @@ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - } - }, "internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -21838,34 +20292,19 @@ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "requires": { - "kind-of": "^6.0.0" + "loose-envify": "^1.0.0" } }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + }, "is-alphabetical": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", @@ -21880,23 +20319,18 @@ "is-decimal": "^1.0.0" } }, - "is-arguments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", - "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", - "requires": { - "call-bind": "^1.0.0" - } - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "is-bigint": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", - "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } }, "is-binary-path": { "version": "2.1.0", @@ -21907,11 +20341,12 @@ } }, "is-boolean-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", - "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "requires": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" } }, "is-buffer": { @@ -21920,9 +20355,9 @@ "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" }, "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-ci": { "version": "2.0.0", @@ -21939,62 +20374,27 @@ } } }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - }, - "dependencies": { - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" - } - } - }, "is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "requires": { "has": "^1.0.3" } }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "requires": { - "kind-of": "^6.0.0" + "has-tostringtag": "^1.0.0" } }, - "is-date-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", - "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==" - }, "is-decimal": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -22003,12 +20403,12 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -22016,9 +20416,9 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "requires": { "is-extglob": "^2.1.1" } @@ -22038,9 +20438,9 @@ } }, "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" }, "is-npm": { "version": "5.0.0", @@ -22053,38 +20453,23 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "is-number-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", - "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" }, "is-path-cwd": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "requires": { - "is-path-inside": "^2.1.0" - }, - "dependencies": { - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "requires": { - "path-is-inside": "^1.0.2" - } - } - } - }, "is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -22104,38 +20489,44 @@ } }, "is-regex": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", - "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "requires": { "call-bind": "^1.0.2", - "has-symbols": "^1.0.2" + "has-tostringtag": "^1.0.0" } }, "is-regexp": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" }, "is-root": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } + }, "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, "is-string": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", - "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-symbol": { "version": "1.0.4", @@ -22148,18 +20539,21 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } }, "is-whitespace-character": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==" }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, "is-word-character": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", @@ -22181,36 +20575,60 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" + }, + "jest-util": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", + "integrity": "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==", + "requires": { + "@jest/types": "^29.3.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } }, "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz", + "integrity": "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==", "requires": { "@types/node": "*", + "jest-util": "^29.3.1", "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } } }, "joi": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.0.tgz", - "integrity": "sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg==", + "version": "17.6.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.3.tgz", + "integrity": "sha512-YlQsIaS9MHYekzf1Qe11LjTkNzx9qhYluK3172z38RxYoAUf82XMX1p1DG1H4Wtk2ED/vPdSn9OggqtDu+aTow==", "requires": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.0", + "@sideway/address": "^4.1.3", "@sideway/formula": "^3.0.0", "@sideway/pinpoint": "^2.0.0" } @@ -22236,12 +20654,7 @@ "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==" }, "json-parse-even-better-errors": { "version": "2.3.1", @@ -22256,20 +20669,12 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" - }, - "json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" }, "jsonfile": { "version": "6.1.0", @@ -22281,12 +20686,12 @@ } }, "jsx-ast-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", - "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", "requires": { - "array-includes": "^3.1.2", - "object.assign": "^4.1.2" + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" } }, "katex": { @@ -22312,11 +20717,6 @@ "json-buffer": "3.0.0" } }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" - }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -22328,9 +20728,9 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" }, "klona": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", - "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==" }, "latest-version": { "version": "5.1.0", @@ -22354,20 +20754,25 @@ "type-check": "~0.4.0" } }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" + }, "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" }, "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -22387,110 +20792,40 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" - }, - "lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, "lodash.curry": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", - "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "lodash.flow": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", - "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" - }, - "lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" - }, - "lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" - }, - "lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" - }, - "lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" - }, - "lodash.toarray": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", - "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" - }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=" + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==" }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "loglevel": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", - "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==" + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, "loose-envify": { "version": "1.4.0", @@ -22536,18 +20871,11 @@ } } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "peer": true }, "markdown-escapes": { "version": "1.0.4", @@ -22591,57 +20919,32 @@ "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==" }, "mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" }, "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "memfs": { + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz", + "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==", "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } + "fs-monkey": "^1.0.3" } }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, "merge-stream": { "version": "2.0.0", @@ -22656,20 +20959,15 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "microevent.ts": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", - "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "mime": { @@ -22678,16 +20976,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.47.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -22700,33 +20998,47 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, - "mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "requires": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - } - }, "mini-css-extract-plugin": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.0.tgz", - "integrity": "sha512-nPFKI7NSy6uONUo9yn2hIfb9vyYvkFu95qki0e21DQ9uaqNKDP15DGpK0KnV6wDroWxPHtExrdEwx/yDQ8nVRw==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", + "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "webpack-sources": "^1.1.0" + "schema-utils": "^4.0.0" }, "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" } } } @@ -22737,46 +21049,54 @@ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" }, "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } }, - "module-alias": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", - "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" + "mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==" }, "ms": { "version": "2.1.2", @@ -22784,86 +21104,44 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "requires": { - "dns-packet": "^1.3.1", + "dns-packet": "^5.2.2", "thunky": "^1.0.2" } }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" - }, - "nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", - "optional": true - }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "njre": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/njre/-/njre-0.2.0.tgz", + "integrity": "sha512-+Wq8R6VmjK+jI8a9NdzfU6Vh50r3tjsdvl5KJE1OyHeH8I/nx5Ptm12qpO3qNUbstXuZfBDgDL0qQZw9JyjhMw==", + "requires": { + "command-exists-promise": "^2.0.2", + "node-fetch": "^2.5.0", + "tar": "^4.4.8", + "yauzl": "^2.10.0" + } }, "no-case": { "version": "3.0.4", @@ -22875,11 +21153,11 @@ } }, "node-emoji": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", - "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", "requires": { - "lodash.toarray": "^4.4.0" + "lodash": "^4.17.21" } }, "node-fetch": { @@ -22891,14 +21169,14 @@ } }, "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" }, "node-releases": { - "version": "1.1.72", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", - "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, "normalize-path": { "version": "3.0.0", @@ -22908,12 +21186,12 @@ "normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" }, "normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" }, "npm-run-path": { "version": "4.0.1", @@ -22926,174 +21204,79 @@ "nprogress": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" }, "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "requires": { - "boolbase": "~1.0.0" + "boolbase": "^1.0.0" } }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", - "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==" - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - } - }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, "object.entries": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", - "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" - } - }, - "object.fromentries": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", - "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" + "es-abstract": "^1.19.1" } }, - "object.getownpropertydescriptors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", - "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", + "object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2" + "es-abstract": "^1.19.1" } }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "object.hasown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", + "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", "requires": { - "isobject": "^3.0.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "object.values": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", - "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" + "es-abstract": "^1.19.1" } }, "obuf": { @@ -23102,9 +21285,9 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" }, "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } @@ -23117,7 +21300,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "requires": { "wrappy": "1" } @@ -23131,12 +21314,13 @@ } }, "open": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" } }, "opener": { @@ -23144,21 +21328,6 @@ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "requires": { - "is-wsl": "^1.1.0" - }, - "dependencies": { - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" - } - } - }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -23172,30 +21341,17 @@ "word-wrap": "^1.2.3" } }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "requires": { - "url-parse": "^1.4.3" - } - }, "p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "requires": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" } }, "p-locate": { @@ -23204,16 +21360,6 @@ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "requires": { "p-limit": "^2.2.0" - }, - "dependencies": { - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - } } }, "p-map": { @@ -23225,11 +21371,12 @@ } }, "p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "requires": { - "retry": "^0.12.0" + "@types/retry": "0.12.0", + "retry": "^0.13.1" } }, "p-try": { @@ -23297,15 +21444,34 @@ } }, "parse-numeric-range": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.2.0.tgz", - "integrity": "sha512-1q2tXpAOplPxcl8vrIGPWz1dJxxfmdRkCFcpxxMBerDnGuuHalOWF/xj9L8Nn5XoTUoB/6F0CeQBp2fMgkOYFg==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" }, "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, + "parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "requires": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "dependencies": { + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + } + } + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -23320,16 +21486,6 @@ "tslib": "^2.0.3" } }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -23338,12 +21494,12 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" }, "path-key": { "version": "3.1.1", @@ -23358,35 +21514,27 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, - "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==" - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "requires": { - "pinkie": "^2.0.0" - } + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "pkg-dir": { "version": "4.2.0", @@ -23421,14 +21569,6 @@ "path-exists": "^3.0.0" } }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, "p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -23440,117 +21580,79 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - } - } - }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" } } }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - }, "postcss": { - "version": "8.2.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz", - "integrity": "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==", + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", "requires": { - "colorette": "^1.2.2", - "nanoid": "^3.1.23", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" } }, "postcss-calc": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.0.0.tgz", - "integrity": "sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", "requires": { - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" } }, "postcss-colormin": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.0.0.tgz", - "integrity": "sha512-Yt84+5V6CgS/AhK7d7MA58vG8dSZ7+ytlRtWLaQhag3HXOncTfmYpuUOX4cDoXjvLfw1sHRCHMiBjYhc35CymQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", "requires": { - "browserslist": "^4.16.0", - "color": "^3.1.1", - "postcss-value-parser": "^4.1.0" + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" } }, "postcss-convert-values": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.0.tgz", - "integrity": "sha512-V5kmYm4xoBAjNs+eHY/6XzXJkkGeg4kwNf2ocfqhLb1WBPEa4oaSmoi1fnVO7Dkblqvus9h+AenDvhCKUCK7uQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", "requires": { - "postcss-value-parser": "^4.1.0" + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" } }, "postcss-discard-comments": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.0.tgz", - "integrity": "sha512-Umig6Gxs8m20RihiXY6QkePd6mp4FxkA1Dg+f/Kd6uw0gEMfKRjDeQOyFkLibexbJJGHpE3lrN/Q0R9SMrUMbQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", "requires": {} }, "postcss-discard-duplicates": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.0.tgz", - "integrity": "sha512-vEJJ+Y3pFUnO1FyCBA6PSisGjHtnphL3V6GsNvkASq/VkP3OX5/No5RYXXLxHa2QegStNzg6HYrYdo71uR4caQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", "requires": {} }, "postcss-discard-empty": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.0.tgz", - "integrity": "sha512-+wigy099Y1xZxG36WG5L1f2zeH1oicntkJEW4TDIqKKDO2g9XVB3OhoiHTu08rDEjLnbcab4rw0BAccwi2VjiQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", "requires": {} }, "postcss-discard-overridden": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.0.tgz", - "integrity": "sha512-hybnScTaZM2iEA6kzVQ6Spozy7kVdLw+lGw8hftLlBEzt93uzXoltkYp9u0tI8xbfhxDLTOOzHsHQCkYdmzRUg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", "requires": {} }, "postcss-discard-unused": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.0.0.tgz", - "integrity": "sha512-C+bchjnGRoGlSQjACMts/FlpY3LMDEUS5+9rHKxvl/NFUY/5OYWjkA1AEUo9HDWnFB44CFgcm6khLMSIbrjVEQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", + "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", "requires": { - "postcss-selector-parser": "^6.0.4" + "postcss-selector-parser": "^6.0.5" } }, "postcss-loader": { @@ -23566,11 +21668,11 @@ }, "dependencies": { "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "requires": { - "@types/json-schema": "^7.0.6", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } @@ -23578,85 +21680,68 @@ } }, "postcss-merge-idents": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.0.0.tgz", - "integrity": "sha512-s8wwhAB/SJDPkcVxj31s2SGzgrO66ktUYjWh6j4qwY67Mzxx3/TkK+m/+v6tU/xyW4TmGd4yuyTXsHaaLC0jLg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", + "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", "requires": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" } }, "postcss-merge-longhand": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.1.tgz", - "integrity": "sha512-H1RO8le5deFGumQzuhJjuL0bIXPRysa+w7xtk5KrHe38oiaSS9ksPXDo24+IOS3SETPhip0J5+1uCOW+ALs3Yw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", "requires": { - "css-color-names": "^1.0.1", - "postcss-value-parser": "^4.1.0", - "stylehacks": "^5.0.0" + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" } }, "postcss-merge-rules": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.0.tgz", - "integrity": "sha512-TfsXbKjNYCGfUPEXGIGPySnMiJbdS+3gcVeV8gwmJP4RajyKZHW8E0FYDL1WmggTj3hi+m+WUCAvqRpX2ut4Kg==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz", + "integrity": "sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==", "requires": { - "browserslist": "^4.16.0", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", - "cssnano-utils": "^2.0.0", - "postcss-selector-parser": "^6.0.4", - "vendors": "^1.0.3" + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" } }, "postcss-minify-font-values": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.0.tgz", - "integrity": "sha512-zi2JhFaMOcIaNxhndX5uhsqSY1rexKDp23wV8EOmC9XERqzLbHsoRye3aYF716Zm+hkcR4loqKDt8LZlmihwAg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", "requires": { - "postcss-value-parser": "^4.1.0" + "postcss-value-parser": "^4.2.0" } }, "postcss-minify-gradients": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.0.tgz", - "integrity": "sha512-/jPtNgs6JySMwgsE5dPOq8a2xEopWTW3RyqoB9fLqxgR+mDUNLSi7joKd+N1z7FXWgVkc4l/dEBMXHgNAaUbvg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", "requires": { - "cssnano-utils": "^2.0.0", - "is-color-stop": "^1.1.0", - "postcss-value-parser": "^4.1.0" + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" } }, "postcss-minify-params": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.0.tgz", - "integrity": "sha512-KvZYIxTPBVKjdd+XgObq9A+Sfv8lMkXTpbZTsjhr42XbfWIeLaTItMlygsDWfjArEc3muUfDaUFgNSeDiJ5jug==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", "requires": { - "alphanum-sort": "^1.0.2", - "browserslist": "^4.16.0", - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0", - "uniqs": "^2.0.0" + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" } }, "postcss-minify-selectors": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.0.0.tgz", - "integrity": "sha512-cEM0O0eWwFIvmo6nfB0lH0vO/XFwgqIvymODbfPXZ1gTA3i76FKnb7TGUrEpiTxaXH6tgYQ6DcTHwRiRS+YQLQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", "requires": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^3.1.2" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } + "postcss-selector-parser": "^6.0.5" } }, "postcss-modules-extract-imports": { @@ -23692,268 +21777,155 @@ } }, "postcss-normalize-charset": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.0.tgz", - "integrity": "sha512-pqsCkgo9KmQP0ew6DqSA+uP9YN6EfsW20pQ3JU5JoQge09Z6Too4qU0TNDsTNWuEaP8SWsMp+19l15210MsDZQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", "requires": {} }, "postcss-normalize-display-values": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.0.tgz", - "integrity": "sha512-t4f2d//gH1f7Ns0Jq3eNdnWuPT7TeLuISZ6RQx4j8gpl5XrhkdshdNcOnlrEK48YU6Tcb6jqK7dorME3N4oOGA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", "requires": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" + "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-positions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.0.tgz", - "integrity": "sha512-0o6/qU5ky74X/eWYj/tv4iiKCm3YqJnrhmVADpIMNXxzFZywsSQxl8F7cKs8jQEtF3VrJBgcDHTexZy1zgDoYg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", "requires": { - "postcss-value-parser": "^4.1.0" + "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-repeat-style": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.0.tgz", - "integrity": "sha512-KRT14JbrXKcFMYuc4q7lh8lvv8u22wLyMrq+UpHKLtbx2H/LOjvWXYdoDxmNrrrJzomAWL+ViEXr48/IhSUJnQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", "requires": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" + "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.0.tgz", - "integrity": "sha512-wSO4pf7GNcDZpmelREWYADF1+XZWrAcbFLQCOqoE92ZwYgaP/RLumkUTaamEzdT2YKRZAH8eLLKGWotU/7FNPw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", "requires": { - "postcss-value-parser": "^4.1.0" + "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-timing-functions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.0.tgz", - "integrity": "sha512-TwPaDX+wl9wO3MUm23lzGmOzGCGKnpk+rSDgzB2INpakD5dgWR3L6bJq1P1LQYzBAvz8fRIj2NWdnZdV4EV98Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", "requires": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" + "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-unicode": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.0.tgz", - "integrity": "sha512-2CpVoz/67rXU5s9tsPZDxG1YGS9OFHwoY9gsLAzrURrCxTAb0H7Vp87/62LvVPgRWTa5ZmvgmqTp2rL8tlm72A==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", "requires": { - "browserslist": "^4.16.0", - "postcss-value-parser": "^4.1.0" + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.0.tgz", - "integrity": "sha512-ICDaGFBqLgA3dlrCIRuhblLl80D13YtgEV9NJPTYJtgR72vu61KgxAHv+z/lKMs1EbwfSQa3ALjOFLSmXiE34A==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", "requires": { - "is-absolute-url": "^3.0.3", - "normalize-url": "^4.5.0", - "postcss-value-parser": "^4.1.0" + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" } }, "postcss-normalize-whitespace": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.0.tgz", - "integrity": "sha512-KRnxQvQAVkJfaeXSz7JlnD9nBN9sFZF9lrk9452Q2uRoqrRSkinqifF8Iex7wZGei2DZVG/qpmDFDmRvbNAOGA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", "requires": { - "postcss-value-parser": "^4.1.0" + "postcss-value-parser": "^4.2.0" } }, "postcss-ordered-values": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.0.tgz", - "integrity": "sha512-dPr+SRObiHueCIc4IUaG0aOGQmYkuNu50wQvdXTGKy+rzi2mjmPsbeDsheLk5WPb9Zyf2tp8E+I+h40cnivm6g==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", "requires": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" } }, "postcss-reduce-idents": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.0.0.tgz", - "integrity": "sha512-wDth7wkXAZ91i7GNe+/PJKyC9NOR2n04U0t5nnqlvlkKhMhnRn/8NJLYQRa7ZZHPGOZcOfvugrhblioTTg2X8A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", + "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", "requires": { - "postcss-value-parser": "^4.1.0" + "postcss-value-parser": "^4.2.0" } }, "postcss-reduce-initial": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.0.tgz", - "integrity": "sha512-wR6pXUaFbSMG1oCKx8pKVA+rnSXCHlca5jMrlmkmif+uig0HNUTV9oGN5kjKsM3mATQAldv2PF9Tbl2vqLFjnA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz", + "integrity": "sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==", "requires": { - "browserslist": "^4.16.0", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0" } }, "postcss-reduce-transforms": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.0.tgz", - "integrity": "sha512-iHdGODW4YzM3WjVecBhPQt6fpJC4lGQZxJKjkBNHpp2b8dzmvj0ogKThqya+IRodQEFzjfXgYeESkf172FH5Lw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", "requires": { - "cssnano-utils": "^2.0.0", - "postcss-value-parser": "^4.1.0" + "postcss-value-parser": "^4.2.0" } }, "postcss-selector-parser": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", - "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "postcss-sort-media-queries": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-3.9.10.tgz", - "integrity": "sha512-pyCWbMrpQq4WjcYFrcVAvxS/+iHnXK5pxa1SAm1s9U4HZjGYU4gkCHwbHbzJ2ZFiiRYpRNRp85QuFvg6ZyKHxw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.3.0.tgz", + "integrity": "sha512-jAl8gJM2DvuIJiI9sL1CuiHtKM4s5aEIomkU8G3LFvbP+p8i7Sz8VV63uieTgoewGqKbi+hxBTiOKJlB35upCg==", "requires": { - "sort-css-media-queries": "1.5.4" + "sort-css-media-queries": "2.1.0" } }, "postcss-svgo": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.0.tgz", - "integrity": "sha512-M3/VS4sFI1Yp9g0bPL+xzzCNz5iLdRUztoFaugMit5a8sMfkVzzhwqbsOlD8IFFymCdJDmXmh31waYHWw1K4BA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", "requires": { - "postcss-value-parser": "^4.1.0", - "svgo": "^2.3.0" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - }, - "css-select": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz", - "integrity": "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^4.0.0", - "domhandler": "^4.0.0", - "domutils": "^2.4.3", - "nth-check": "^2.0.0" - } - }, - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, - "css-what": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz", - "integrity": "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==" - }, - "dom-serializer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", - "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" - }, - "domhandler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz", - "integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==", - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz", - "integrity": "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==", - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" - }, - "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "nth-check": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz", - "integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==", - "requires": { - "boolbase": "^1.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "svgo": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.3.0.tgz", - "integrity": "sha512-fz4IKjNO6HDPgIQxu4IxwtubtbSfGEAJUq/IXyTPIkGhWck/faiiwfkvsB8LnBkKLvSoyNNIY6d13lZprJMc9Q==", - "requires": { - "@trysound/sax": "0.1.1", - "chalk": "^4.1.0", - "commander": "^7.1.0", - "css-select": "^3.1.2", - "css-tree": "^1.1.2", - "csso": "^4.2.0", - "stable": "^0.1.8" - } - } + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" } }, "postcss-unique-selectors": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.0.tgz", - "integrity": "sha512-o9l4pF8SRn7aCMTmzb/kNv/kjV7wPZpZ8Nlb1Gq8v/Qvw969K1wanz1RVA0ehHzWe9+wHXaC2DvZlak/gdMJ5w==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", "requires": { - "alphanum-sort": "^1.0.2", - "postcss-selector-parser": "^6.0.2", - "uniqs": "^2.0.0" + "postcss-selector-parser": "^6.0.5" } }, "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "postcss-zindex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.0.0.tgz", - "integrity": "sha512-thJp90qNZedxzfljsAnu7V35L/Zue/nVvWzPDLKZuqHmwDuy1vd3xkFVYfEa8WZZQaetvHtsi3uwjVD3UJAVeg==", - "requires": { - "has": "^1.0.3", - "uniqs": "^2.0.0" - } + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", + "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", + "requires": {} }, "prelude-ls": { "version": "1.2.1", @@ -23963,20 +21935,20 @@ "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==" }, "prettier": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz", - "integrity": "sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==" + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==" }, "pretty-error": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", - "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", "requires": { "lodash": "^4.17.20", - "renderkid": "^2.0.4" + "renderkid": "^3.0.0" } }, "pretty-time": { @@ -23985,18 +21957,15 @@ "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" }, "prism-react-renderer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.2.0.tgz", - "integrity": "sha512-GHqzxLYImx1iKN1jJURcuRoA/0ygCcNhfGw1IT8nPIMzarmKQ3Nc+JcG0gi8JXQzuh0C5ShE4npMIoqNin40hg==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", + "integrity": "sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==", "requires": {} }, "prismjs": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", - "integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", - "requires": { - "clipboard": "^2.0.0" - } + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" }, "process-nextick-args": { "version": "2.0.1", @@ -24017,22 +21986,22 @@ } }, "prompts": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", - "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "requires": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "property-information": { @@ -24044,19 +22013,21 @@ } }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + } } }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -24069,7 +22040,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" }, "pupa": { "version": "2.1.1", @@ -24082,27 +22053,23 @@ "pure-color": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", - "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + "queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "requires": { + "inherits": "~2.0.3" + } }, "queue-microtask": { "version": "1.2.3", @@ -24123,14 +22090,21 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + } } }, "rc": { @@ -24147,7 +22121,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" } } }, @@ -24164,7 +22138,7 @@ "react-base16-styling": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", - "integrity": "sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", "requires": { "base16": "^1.0.0", "lodash.curry": "^4.0.1", @@ -24173,136 +22147,72 @@ } }, "react-dev-utils": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz", - "integrity": "sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A==", - "requires": { - "@babel/code-frame": "7.10.4", - "address": "1.1.2", - "browserslist": "4.14.2", - "chalk": "2.4.2", - "cross-spawn": "7.0.3", - "detect-port-alt": "1.1.6", - "escape-string-regexp": "2.0.0", - "filesize": "6.1.0", - "find-up": "4.1.0", - "fork-ts-checker-webpack-plugin": "4.1.6", - "global-modules": "2.0.0", - "globby": "11.0.1", - "gzip-size": "5.1.1", - "immer": "8.0.1", - "is-root": "2.1.0", - "loader-utils": "2.0.0", - "open": "^7.0.2", - "pkg-up": "3.1.0", - "prompts": "2.4.0", - "react-error-overlay": "^6.0.9", - "recursive-readdir": "2.2.2", - "shell-quote": "1.7.2", - "strip-ansi": "6.0.0", - "text-table": "0.2.0" + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "requires": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "requires": { - "@babel/highlight": "^7.10.4" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, - "ansi-styles": { + "loader-utils": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "browserslist": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz", - "integrity": "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==", - "requires": { - "caniuse-lite": "^1.0.30001125", - "electron-to-chromium": "^1.3.564", - "escalade": "^3.0.2", - "node-releases": "^1.1.61" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" }, - "globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "p-locate": "^5.0.0" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" - }, - "prompts": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", - "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "yocto-queue": "^0.1.0" } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "requires": { - "has-flag": "^3.0.0" + "p-limit": "^3.0.2" } } } @@ -24319,24 +22229,25 @@ } }, "react-error-overlay": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", - "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, "react-fast-compare": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, - "react-helmet": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", - "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", + "react-helmet-async": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", "requires": { - "object-assign": "^4.1.1", + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", "prop-types": "^15.7.2", - "react-fast-compare": "^3.1.1", - "react-side-effect": "^2.1.0" + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" } }, "react-is": { @@ -24364,6 +22275,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/react-loadable/-/react-loadable-5.5.0.tgz", "integrity": "sha512-C8Aui0ZpMd4KokxRdVAm2bQtI03k2RMRNzOB+IipV3yxFTSVICv7WoUr5L9ALB5BmKO1iHgZtWM8EvYG83otdg==", + "peer": true, "requires": { "prop-types": "^15.5.0" } @@ -24377,15 +22289,14 @@ } }, "react-router": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", - "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", + "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", "requires": { - "@babel/runtime": "^7.1.2", + "@babel/runtime": "^7.12.13", "history": "^4.9.0", "hoist-non-react-statics": "^3.1.0", "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", "path-to-regexp": "^1.7.0", "prop-types": "^15.6.2", "react-is": "^16.6.0", @@ -24396,7 +22307,7 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" }, "path-to-regexp": { "version": "1.8.0", @@ -24417,33 +22328,27 @@ } }, "react-router-dom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", - "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", + "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", "requires": { - "@babel/runtime": "^7.1.2", + "@babel/runtime": "^7.12.13", "history": "^4.9.0", "loose-envify": "^1.3.1", "prop-types": "^15.6.2", - "react-router": "5.2.0", + "react-router": "5.3.4", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" } }, - "react-side-effect": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz", - "integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==", - "requires": {} - }, "react-textarea-autosize": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.2.tgz", - "integrity": "sha512-JrMWVgQSaExQByP3ggI1eA8zF4mF0+ddVuX7acUeK2V7bmrpjVOY72vmLz2IXFJSAXoY3D80nEzrn0GWajWK3Q==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.0.tgz", + "integrity": "sha512-YrTFaEHLgJsi8sJVYHBzYn+mkP3prGkmP2DKb/tm0t7CLJY5t1Rxix8070LAKb0wby7bl/lf2EeHkuMihMZMwQ==", "requires": { "@babel/runtime": "^7.10.2", - "use-composed-ref": "^1.0.0", - "use-latest": "^1.0.0" + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" } }, "readable-stream": { @@ -24457,32 +22362,32 @@ } }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "requires": { "picomatch": "^2.2.1" } }, "reading-time": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.3.0.tgz", - "integrity": "sha512-RJ8J5O6UvrclfZpcPSPuKusrdRfoY7uXXoYOOdeswZNtSkQaewT3919yz6RyloDBR+iwcUyz5zGOUjhgvfuv3g==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", + "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "requires": { "resolve": "^1.1.6" } }, "recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.5" } }, "regenerate": { @@ -24491,87 +22396,60 @@ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "requires": { - "regenerate": "^1.4.0" + "regenerate": "^1.4.2" } }, "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" }, "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", "requires": { "@babel/runtime": "^7.8.4" } }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" } }, "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" }, "regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", + "integrity": "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==", "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" } }, "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", "requires": { - "rc": "^1.2.8" + "rc": "1.2.8" } }, "registry-url": { @@ -24583,14 +22461,14 @@ } }, "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==" }, "regjsparser": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", - "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "requires": { "jsesc": "~0.5.0" }, @@ -24598,7 +22476,7 @@ "jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" } } }, @@ -24620,77 +22498,15 @@ "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz", "integrity": "sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==", "requires": { - "hast-util-from-parse5": "^6.0.0", - "parse5": "^6.0.0" - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" - }, - "remark-admonitions": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/remark-admonitions/-/remark-admonitions-1.2.1.tgz", - "integrity": "sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow==", - "requires": { - "rehype-parse": "^6.0.2", - "unified": "^8.4.2", - "unist-util-visit": "^2.0.1" - }, - "dependencies": { - "hast-util-from-parse5": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz", - "integrity": "sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA==", - "requires": { - "ccount": "^1.0.3", - "hastscript": "^5.0.0", - "property-information": "^5.0.0", - "web-namespaces": "^1.1.2", - "xtend": "^4.0.1" - } - }, - "hastscript": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz", - "integrity": "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==", - "requires": { - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" - } - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" - }, - "rehype-parse": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.2.tgz", - "integrity": "sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug==", - "requires": { - "hast-util-from-parse5": "^5.0.0", - "parse5": "^5.0.0", - "xtend": "^4.0.0" - } - }, - "unified": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-8.4.2.tgz", - "integrity": "sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA==", - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - } - } + "hast-util-from-parse5": "^6.0.0", + "parse5": "^6.0.0" } }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, "remark-emoji": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz", @@ -24776,6 +22592,11 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" } } }, @@ -24810,77 +22631,80 @@ "mdast-squeeze-paragraphs": "^4.0.0" } }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, "renderkid": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz", - "integrity": "sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", "requires": { - "css-select": "^2.0.2", - "dom-converter": "^0.2", - "htmlparser2": "^3.10.1", - "lodash": "^4.17.20", - "strip-ansi": "^3.0.0" + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "requires": { "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" } }, - "css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } }, "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" } }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", "requires": { - "ansi-regex": "^2.0.0" + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" } } } }, - "repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" - }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" }, "require-from-string": { "version": "2.0.2", @@ -24890,40 +22714,21 @@ "require-like": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz", - "integrity": "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==" }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "requires": { - "resolve-from": "^3.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" - } + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-from": { @@ -24936,44 +22741,24 @@ "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, "responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", "requires": { "lowercase-keys": "^1.0.0" } }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" - }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -24983,19 +22768,18 @@ } }, "rtl-detect": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.3.tgz", - "integrity": "sha512-2sMcZO60tL9YDEFe24gqddg3hJ+xSmJFN8IExcQUxeHxQzydQrN6GHPL+yAWgzItXSI7es53hcZC9pJneuZDKA==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz", + "integrity": "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==" }, "rtlcss": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.1.2.tgz", - "integrity": "sha512-b04YSX37siupPOWUEguEBReWX2w4QT89C0PI9g2JzZycbq7zrgPmTr1DA1pizSWpKRFdCjjnrx/SSvU4fOHmGg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz", + "integrity": "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==", "requires": { - "chalk": "^4.1.0", "find-up": "^5.0.0", - "mkdirp": "^1.0.4", - "postcss": "^8.2.4", + "picocolors": "^1.0.0", + "postcss": "^8.3.11", "strip-json-comments": "^3.1.1" }, "dependencies": { @@ -25016,6 +22800,14 @@ "p-locate": "^5.0.0" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, "p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", @@ -25035,31 +22827,26 @@ } }, "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } + "tslib": "^2.1.0" } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", "requires": { - "ret": "~0.1.10" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" } }, "safer-buffer": { @@ -25100,29 +22887,23 @@ "kind-of": "^6.0.0" } }, - "select": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", - "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", - "optional": true - }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" }, "selfsigned": { - "version": "1.10.11", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz", - "integrity": "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", "requires": { - "node-forge": "^0.10.0" + "node-forge": "^1" } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "requires": { "lru-cache": "^6.0.0" } @@ -25143,23 +22924,23 @@ } }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "dependencies": { "debug": { @@ -25173,21 +22954,21 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" } } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "requires": { "randombytes": "^2.1.0" } @@ -25207,15 +22988,10 @@ "range-parser": "1.2.0" }, "dependencies": { - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==" }, "mime-db": { "version": "1.33.0", @@ -25230,6 +23006,14 @@ "mime-db": "~1.33.0" } }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, "path-to-regexp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", @@ -25238,14 +23022,14 @@ "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==" } } }, "serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "requires": { "accepts": "~1.3.4", "batch": "0.6.1", @@ -25264,10 +23048,15 @@ "ms": "2.0.0" } }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "requires": { "depd": "~1.1.2", "inherits": "2.0.3", @@ -25278,56 +23067,45 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" } } }, "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "send": "0.18.0" } }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "shallow-clone": { "version": "3.0.1", @@ -25337,6 +23115,11 @@ "kind-of": "^6.0.2" } }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -25351,14 +23134,14 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "shell-quote": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==" }, "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "requires": { "glob": "^7.0.0", "interpret": "^1.0.0", @@ -25376,40 +23159,18 @@ } }, "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - } - } + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "sirv": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.11.tgz", - "integrity": "sha512-SR36i3/LSWja7AJNRBz4fF/Xjpn7lQFI30tZ434dIy+bitLYSP+ZEenHg36i23V2SGEz+kqjksg0uOGZ5LPiqg==", + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", "requires": { - "@polka/url": "^1.0.0-next.9", - "mime": "^2.3.1", + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", "totalist": "^1.0.0" - }, - "dependencies": { - "mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" - } } }, "sisteransi": { @@ -25418,20 +23179,20 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "sitemap": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-6.4.0.tgz", - "integrity": "sha512-DoPKNc2/apQZTUnfiOONWctwq7s6dZVspxAZe2VPMNtoqNq7HgXRvlRnbIpKjf+8+piQdWncwcy+YhhTGY5USQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", + "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", "requires": { - "@types/node": "^14.14.28", + "@types/node": "^17.0.5", "@types/sax": "^1.2.1", "arg": "^5.0.0", "sax": "^1.2.4" }, "dependencies": { "@types/node": { - "version": "14.14.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.45.tgz", - "integrity": "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==" + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" } } }, @@ -25450,224 +23211,40 @@ "is-fullwidth-code-point": "^3.0.0" } }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "sockjs": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", - "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==", + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "requires": { "faye-websocket": "^0.11.3", - "uuid": "^3.4.0", + "uuid": "^8.3.2", "websocket-driver": "^0.7.4" } }, - "sockjs-client": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.1.tgz", - "integrity": "sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ==", - "requires": { - "debug": "^3.2.6", - "eventsource": "^1.0.7", - "faye-websocket": "^0.11.3", - "inherits": "^2.0.4", - "json3": "^3.3.3", - "url-parse": "^1.5.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, "sort-css-media-queries": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-1.5.4.tgz", - "integrity": "sha512-YP5W/h4Sid/YP7Lp87ejJ5jP13/Mtqt2vx33XyhO+IAugKlufRPbOrPlIiEUuxmpNBSBd3EeeQpFhdu3RfI2Ag==" - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", + "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==" }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" - }, "space-separated-tokens": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", @@ -25698,37 +23275,10 @@ "wbuf": "^1.7.3" } }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "requires": { - "extend-shallow": "^3.0.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "stable": { "version": "0.1.8", @@ -25740,93 +23290,10 @@ "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==" }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "std-env": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-2.3.0.tgz", - "integrity": "sha512-4qT5B45+Kjef2Z6pE0BkskzsH0GO7GrND0wGlTM1ioUe3v0dGYx9ZJH0Aro/YyA8fqQ5EyIKDRjZojJYMFTflw==", - "requires": { - "ci-info": "^3.0.0" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, "string_decoder": { "version": "1.3.0", @@ -25834,62 +23301,51 @@ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } } }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - } + "strip-ansi": "^6.0.1" } }, "string.prototype.matchall": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz", - "integrity": "sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has-symbols": "^1.0.1", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.1", "side-channel": "^1.0.4" } }, "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" } }, "stringify-object": { @@ -25903,22 +23359,17 @@ } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=" - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==" }, "strip-final-newline": { "version": "2.0.0", @@ -25939,11 +23390,11 @@ } }, "stylehacks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.0.tgz", - "integrity": "sha512-QOWm6XivDLb+fqffTZP8jrmPmPITVChl2KCY2R05nsCWwLi3VGhCdVc3IVGNwd1zzTt1jPd67zIKjpQfxzQZeA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", "requires": { - "browserslist": "^4.16.0", + "browserslist": "^4.21.4", "postcss-selector-parser": "^6.0.4" } }, @@ -25955,149 +23406,98 @@ "has-flag": "^4.0.0" } }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, "svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" }, "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" }, "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "requires": { "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" } }, - "css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "domelementtype": "^2.2.0" } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "requires": { - "minimist": "^1.2.5" + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" } } }, "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", "requires": { "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "dependencies": { "ajv": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.3.0.tgz", - "integrity": "sha512-RYE7B5An83d7eWnDR8kbdaIFqmKCNsP16ay1hDbJEU+sa0e3H9SebskCt0Uufem6cfAVu7Col6ubcn/W+Sm8/Q==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "requires": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -26113,80 +23513,92 @@ } }, "tapable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", - "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + }, + "tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "requires": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } }, "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "source-map-support": "~0.5.20" }, "dependencies": { + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, "terser-webpack-plugin": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz", - "integrity": "sha512-6QhDaAiVHIQr5Ab3XUWZyDmrIPCHMiqJVljMF91YKyqwKkL5QHnYMkrMBy96v9Z7ev1hGhSEw1HQZc2p/s5Z8Q==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "requires": { - "jest-worker": "^26.6.2", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "source-map": "^0.6.1", - "terser": "^5.7.0" + "@jridgewell/trace-mapping": "^0.3.14", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.14.1" }, "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } }, "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "requires": { - "@types/json-schema": "^7.0.6", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "terser": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz", - "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==", + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "requires": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - } + "has-flag": "^4.0.0" } } } @@ -26194,28 +23606,17 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, "thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" - }, - "tiny-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", - "optional": true - }, "tiny-invariant": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", - "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" }, "tiny-warning": { "version": "1.0.3", @@ -26225,66 +23626,13 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" }, "to-readable-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -26294,9 +23642,9 @@ } }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "totalist": { "version": "1.1.0", @@ -26306,12 +23654,12 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + "integrity": "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==" }, "trim-trailing-lines": { "version": "1.1.4", @@ -26323,15 +23671,45 @@ "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" }, - "ts-essentials": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-2.0.12.tgz", - "integrity": "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==" + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "peer": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "peer": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "peer": true + } + } }, "tslib": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", - "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, "type-check": { "version": "0.4.0", @@ -26363,19 +23741,25 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "peer": true + }, "ua-parser-js": { - "version": "0.7.28", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz", - "integrity": "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==" + "version": "0.7.32", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz", + "integrity": "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==" }, "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" } }, @@ -26389,28 +23773,28 @@ } }, "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" }, "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" } }, "unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" }, "unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" }, "unified": { "version": "9.2.0", @@ -26425,27 +23809,6 @@ "vfile": "^4.0.0" } }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" - }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -26533,54 +23896,17 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - } + "escalade": "^3.1.1", + "picocolors": "^1.0.0" } }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" - }, "update-notifier": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", @@ -26617,27 +23943,6 @@ } } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - } - } - }, "url-loader": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", @@ -26649,81 +23954,54 @@ }, "dependencies": { "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "requires": { - "@types/json-schema": "^7.0.6", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } } } }, - "url-parse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", - "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "url-parse-lax": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", "requires": { "prepend-http": "^2.0.0" } }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, "use-composed-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.1.0.tgz", - "integrity": "sha512-my1lNHGWsSDAhhVAT4MKs6IjBUtG6ZG11uUqexPH9PptiIZDQOzaF4f5tEbJ2+7qvNbtXNBbU3SfmN+fXlWDhg==", - "requires": { - "ts-essentials": "^2.0.3" - } + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "requires": {} }, "use-isomorphic-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz", - "integrity": "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", "requires": {} }, "use-latest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz", - "integrity": "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", "requires": { - "use-isomorphic-layout-effect": "^1.0.0" + "use-isomorphic-layout-effect": "^1.1.1" } }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utila": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" }, "utility-types": { "version": "3.10.0", @@ -26733,18 +24011,24 @@ "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "peer": true + }, "value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", @@ -26753,12 +24037,7 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "vendors": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", - "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "vfile": { "version": "4.2.1", @@ -26786,21 +24065,21 @@ } }, "wait-on": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-5.3.0.tgz", - "integrity": "sha512-DwrHrnTK+/0QFaB9a8Ol5Lna3k7WvUR4jzSKmz0YaPBpuN2sACyiPVKVfj6ejnjcajAcvn3wlbTyMIn9AZouOg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz", + "integrity": "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==", "requires": { - "axios": "^0.21.1", - "joi": "^17.3.0", + "axios": "^0.25.0", + "joi": "^17.6.0", "lodash": "^4.17.21", "minimist": "^1.2.5", - "rxjs": "^6.6.3" + "rxjs": "^7.5.4" } }, "watchpack": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", - "integrity": "sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "requires": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -26822,83 +24101,76 @@ "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webpack": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.37.0.tgz", - "integrity": "sha512-yvdhgcI6QkQkDe1hINBAJ1UNevqNGTVaCkD2SSJcB8rcrNNl922RI8i2DXUAuNfANoxwsiXXEA4ZPZI9q2oGLA==", - "requires": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.47", - "@webassemblyjs/ast": "1.11.0", - "@webassemblyjs/wasm-edit": "1.11.0", - "@webassemblyjs/wasm-parser": "1.11.0", - "acorn": "^8.2.1", + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.0", - "es-module-lexer": "^0.4.0", - "eslint-scope": "^5.1.1", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.4", - "json-parse-better-errors": "^1.0.2", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.0.0", + "schema-utils": "^3.1.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.1", - "watchpack": "^2.0.0", - "webpack-sources": "^2.1.1" + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" }, "dependencies": { "acorn": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", - "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==" + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "requires": {} }, "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "requires": { - "@types/json-schema": "^7.0.6", + "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, "webpack-sources": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz", - "integrity": "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==", - "requires": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - } + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" } } }, "webpack-bundle-analyzer": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.1.tgz", - "integrity": "sha512-j5m7WgytCkiVBoOGavzNokBOqxe6Mma13X1asfVYtKWM3wxBiRRu1u1iG0Iol5+qp9WgyhkMmBAcvjEfJ2bdDw==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.1.tgz", + "integrity": "sha512-oKz9Oz9j3rUciLNfpGFjOb49/jEpXNmWdVH8Ls//zNcnLlQdTGXQQMsBbb/gR7Zl8WNLxVCq+0Hqbx3zv6twBw==", "requires": { "acorn": "^8.0.4", "acorn-walk": "^8.0.0", "chalk": "^4.1.0", - "commander": "^6.2.0", + "commander": "^7.2.0", "gzip-size": "^6.0.0", "lodash": "^4.17.20", "opener": "^1.5.2", @@ -26907,462 +24179,170 @@ }, "dependencies": { "acorn": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz", - "integrity": "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==" + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" }, "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" - }, - "gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "requires": { - "duplexer": "^0.1.2" - } + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" } } }, "webpack-dev-middleware": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", - "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" + "schema-utils": "^4.0.0" }, "dependencies": { - "mime": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", - "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "requires": { - "minimist": "^1.2.5" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" } } } }, "webpack-dev-server": { - "version": "3.11.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz", - "integrity": "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==", - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", + "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.3.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.8", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.26", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.8", - "semver": "^6.3.0", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", "serve-index": "^1.9.1", - "sockjs": "^0.3.21", - "sockjs-client": "^1.5.0", + "sockjs": "^0.3.24", "spdy": "^4.0.2", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "^13.3.2" + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "requires": { - "array-uniq": "^1.0.1" - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - } - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" } }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "requires": { - "glob": "^7.1.3" + "fast-deep-equal": "^3.1.3" } }, - "schema-utils": { + "json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" } }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} } } }, "webpack-merge": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz", - "integrity": "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "requires": { "clone-deep": "^4.0.1", "wildcard": "^2.0.0" } }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, "webpackbar": { - "version": "5.0.0-3", - "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz", - "integrity": "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz", + "integrity": "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==", "requires": { - "ansi-escapes": "^4.3.1", "chalk": "^4.1.0", - "consola": "^2.15.0", - "figures": "^3.2.0", + "consola": "^2.15.3", "pretty-time": "^1.1.0", - "std-env": "^2.2.1", - "text-table": "^0.2.0", - "wrap-ansi": "^7.0.0" + "std-env": "^3.0.1" + }, + "dependencies": { + "std-env": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.0.tgz", + "integrity": "sha512-cNNS+VYsXIs5gI6gJipO4qZ8YYT274JHvNnQ1/R/x8Q8mdP0qj0zoMchRXmBNPqp/0eOEhX+3g7g6Fgb7meLIQ==" + } } }, "websocket-driver": { @@ -27383,7 +24363,7 @@ "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -27409,11 +24389,6 @@ "is-symbol": "^1.0.3" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, "widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -27432,14 +24407,6 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, - "worker-rpc": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", - "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", - "requires": { - "microevent.ts": "~0.1.1" - } - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -27453,7 +24420,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "write-file-atomic": { "version": "3.0.3", @@ -27467,9 +24434,9 @@ } }, "ws": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz", - "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "requires": {} }, "xdg-basedir": { @@ -27490,11 +24457,6 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -27505,111 +24467,20 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" } }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } - } + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "peer": true }, "yocto-queue": { "version": "0.1.0", diff --git a/docs/package.json b/docs/package.json index 12a6ee16d8b831..2be6040c558f80 100644 --- a/docs/package.json +++ b/docs/package.json @@ -21,11 +21,12 @@ }, "dependencies": { "@crowdin/cli": "^3.6.1", - "@docusaurus/core": "^2.0.0-beta.0", - "@docusaurus/preset-classic": "^2.0.0-beta.0", - "@docusaurus/theme-search-algolia": "^2.0.0-beta.0", + "@docusaurus/core": "^2.2.0", + "@docusaurus/plugin-google-gtag": "^2.4.0", + "@docusaurus/preset-classic": "^2.2.0", + "@docusaurus/theme-search-algolia": "^2.2.0", "babel-eslint": "^10.1.0", - "clsx": "^1.1.1", + "clsx": "^1.2.1", "eslint": "^7.3.1", "eslint-plugin-react": "^7.20.0", "postcss": "^8.2.13", diff --git a/docs/publish-docs.sh b/docs/publish-docs.sh index c16425818819d1..0ae2d927162acf 100755 --- a/docs/publish-docs.sh +++ b/docs/publish-docs.sh @@ -28,23 +28,25 @@ fi cat > "$CONFIG_FILE" < + +## getConfirmedBlock + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0. +**Please use [getBlock](#getblock) instead** +::: + +Returns identity and transaction information about a confirmed block in the ledger + + + + + +### Parameters: + + + slot number, as u64 integer + + + + +Configuration object containing the following fields: + + + + + level of transaction detail to return, either "full", "signatures", or "none" + + + + whether to populate the `rewards` array. + + + + +Encoding format for Account data + + + +
+ +- `jsonParsed` encoding attempts to use program-specific instruction parsers to return + more human-readable and explicit data in the `transaction.message.instructions` list. +- If `jsonParsed` is requested but a parser cannot be found, the instruction + falls back to regular JSON encoding (`accounts`, `data`, and `programIdIndex` fields). + +
+ +
+ +
+ +### Result: + +The result field will be an object with the following fields: + +- `` - if specified block is not confirmed +- `` - if block is confirmed, an object with the following fields: + - `blockhash: ` - the blockhash of this block, as base-58 encoded string + - `previousBlockhash: ` - the blockhash of this block's parent, as base-58 encoded string; if the parent block is not available due to ledger cleanup, this field will return "11111111111111111111111111111111" + - `parentSlot: ` - the slot index of this block's parent + - `transactions: ` - present if "full" transaction details are requested; an array of JSON objects containing: + - `transaction: ` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter + - `meta: ` - transaction status metadata object, containing `null` or: + - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) + - `fee: ` - fee this transaction was charged, as u64 integer + - `preBalances: ` - array of u64 account balances from before the transaction was processed + - `postBalances: ` - array of u64 account balances after the transaction was processed + - `innerInstructions: ` - List of [inner instructions](#inner-instructions-structure) or `null` if inner instruction recording was not enabled during this transaction + - `preTokenBalances: ` - List of [token balances](#token-balances-structure) from before the transaction was processed or omitted if token balance recording was not yet enabled during this transaction + - `postTokenBalances: ` - List of [token balances](#token-balances-structure) from after the transaction was processed or omitted if token balance recording was not yet enabled during this transaction + - `logMessages: ` - array of string log messages or `null` if log message recording was not enabled during this transaction + - DEPRECATED: `status: ` - Transaction status + - `"Ok": ` - Transaction was successful + - `"Err": ` - Transaction failed with TransactionError + - `signatures: ` - present if "signatures" are requested for transaction details; an array of signatures strings, corresponding to the transaction order in the block + - `rewards: ` - present if rewards are requested; an array of JSON objects containing: + - `pubkey: ` - The public key, as base-58 encoded string, of the account that received the reward + - `lamports: `- number of reward lamports credited or debited by the account, as a i64 + - `postBalance: ` - account balance in lamports after the reward was applied + - `rewardType: ` - type of reward: "fee", "rent", "voting", "staking" + - `commission: ` - vote account commission when the reward was credited, only present for voting and staking rewards + - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch). null if not available + +#### For more details on returned data: + +- [Transaction Structure](#transaction-structure) +- [Inner Instructions Structure](#inner-instructions-structure) +- [Token Balances Structure](#token-balances-structure) + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getConfirmedBlock", + "params": [430, "base64"] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "blockTime": null, + "blockhash": "3Eq21vXNB5s86c62bVuUfTeaMif1N2kUqRPBmGRJhyTA", + "parentSlot": 429, + "previousBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B", + "rewards": [], + "transactions": [ + { + "meta": { + "err": null, + "fee": 5000, + "innerInstructions": [], + "logMessages": [], + "postBalances": [499998932500, 26858640, 1, 1, 1], + "postTokenBalances": [], + "preBalances": [499998937500, 26858640, 1, 1, 1], + "preTokenBalances": [], + "status": { + "Ok": null + } + }, + "transaction": [ + "AVj7dxHlQ9IrvdYVIjuiRFs1jLaDMHixgrv+qtHBwz51L4/ImLZhszwiyEJDIp7xeBSpm/TX5B7mYzxa+fPOMw0BAAMFJMJVqLw+hJYheizSoYlLm53KzgT82cDVmazarqQKG2GQsLgiqktA+a+FDR4/7xnDX7rsusMwryYVUdixfz1B1Qan1RcZLwqvxvJl4/t3zHragsUp0L47E24tAFUgAAAABqfVFxjHdMkoVmOYaR1etoteuKObS21cc1VbIQAAAAAHYUgdNXR0u3xNdiTr072z2DVec9EQQ/wNo1OAAAAAAAtxOUhPBp2WSjUNJEgfvy70BbxI00fZyEPvFHNfxrtEAQQEAQIDADUCAAAAAQAAAAAAAACtAQAAAAAAAAdUE18R96XTJCe+YfRfUp6WP+YKCy/72ucOL8AoBFSpAA==", + "base64" + ] + } + ] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/deprecated/_getConfirmedBlocks.mdx b/docs/src/api/deprecated/_getConfirmedBlocks.mdx new file mode 100644 index 00000000000000..5a6b21c12aa8a8 --- /dev/null +++ b/docs/src/api/deprecated/_getConfirmedBlocks.mdx @@ -0,0 +1,71 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getConfirmedBlocks + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0 +**Please use [getBlocks](#getblocks) instead** +::: + +Returns a list of confirmed blocks between two slots + + + + + +### Parameters: + + + start_slot, as u64 integer + + + + +Configuration object containing the following fields: + + + + + +### Result: + +The result field will be an array of u64 integers listing confirmed blocks +between `start_slot` and either `end_slot` - if provided, or latest confirmed block, +inclusive. Max range allowed is 500,000 slots. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc": "2.0","id":1,"method":"getConfirmedBlocks","params":[5, 10]} +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": [5, 6, 7, 8, 9, 10], "id": 1 } +``` + + + + diff --git a/docs/src/api/deprecated/_getConfirmedBlocksWithLimit.mdx b/docs/src/api/deprecated/_getConfirmedBlocksWithLimit.mdx new file mode 100644 index 00000000000000..3daec0abc25a37 --- /dev/null +++ b/docs/src/api/deprecated/_getConfirmedBlocksWithLimit.mdx @@ -0,0 +1,78 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getConfirmedBlocksWithLimit + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0 +**Please use [getBlocksWithLimit](#getblockswithlimit) instead** +::: + +Returns a list of confirmed blocks starting at the given slot + + + + + +### Parameters: + + + start_slot, as u64 integer + + + + limit, as u64 integer + + + + +Configuration object containing the following fields: + + + + + +### Result: + +The result field will be an array of u64 integers listing confirmed blocks +starting at `start_slot` for up to `limit` blocks, inclusive. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getConfirmedBlocksWithLimit", + "params": [5, 3] + } +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": [5, 6, 7], "id": 1 } +``` + + + + diff --git a/docs/src/api/deprecated/_getConfirmedSignaturesForAddress2.mdx b/docs/src/api/deprecated/_getConfirmedSignaturesForAddress2.mdx new file mode 100644 index 00000000000000..7d8c803d061544 --- /dev/null +++ b/docs/src/api/deprecated/_getConfirmedSignaturesForAddress2.mdx @@ -0,0 +1,114 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getConfirmedSignaturesForAddress2 + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0 +**Please use [getSignaturesForAddress](#getsignaturesforaddress) instead** +::: + +Returns signatures for confirmed transactions that include the given address in +their `accountKeys` list. Returns signatures backwards in time from the +provided signature or most recent confirmed block + + + + + +### Parameters: + + + account address, as base-58 encoded string + + + +Configuration object containing the following fields: + + + + + maximum transaction signatures to return (between 1 and 1,000, default: + 1,000). + + + + start searching backwards from this transaction signature. (If not provided + the search starts from the top of the highest max confirmed block.) + + + + search until this transaction signature, if found before limit reached. + + + + +### Result: + +The result field will be an array of ``, ordered +from newest to oldest transaction, containing transaction signature information with the following fields: + +- `signature: ` - transaction signature as base-58 encoded string +- `slot: ` - The slot that contains the block with the transaction +- `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) +- `memo: ` - Memo associated with the transaction, null if no memo is present +- `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch) of when transaction was processed. null if not available. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getConfirmedSignaturesForAddress2", + "params": [ + "Vote111111111111111111111111111111111111111", + { + "limit": 1 + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": [ + { + "err": null, + "memo": null, + "signature": "5h6xBEauJ3PK6SWCZ1PGjBvj8vDdWG3KpwATGy1ARAXFSDwt8GFXM7W5Ncn16wmqokgpiKRLuS83KUxyZyv2sUYv", + "slot": 114, + "blockTime": null + } + ], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/deprecated/_getConfirmedTransaction.mdx b/docs/src/api/deprecated/_getConfirmedTransaction.mdx new file mode 100644 index 00000000000000..9586975df62c6e --- /dev/null +++ b/docs/src/api/deprecated/_getConfirmedTransaction.mdx @@ -0,0 +1,133 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getConfirmedTransaction + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0 +**Please use [getTransaction](#gettransaction) instead** +::: + +Returns transaction details for a confirmed transaction + + + + + +### Parameters: + + + transaction signature, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + +Encoding format for Account data + + + +
+ +- `base58` is slow and limited to less than 129 bytes of Account data. +- `jsonParsed` encoding attempts to use program-specific instruction parsers + to return more human-readable and explicit data in the `transaction.message.instructions` list. +- If `jsonParsed` is requested but a parser cannot be found, the instruction + falls back to regular `json` encoding (`accounts`, `data`, and `programIdIndex` fields). + +
+ +
+ +
+ +### Result: + +- `` - if transaction is not found or not confirmed +- `` - if transaction is confirmed, an object with the following fields: + - `slot: ` - the slot this transaction was processed in + - `transaction: ` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter + - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch) of when the transaction was processed. null if not available + - `meta: ` - transaction status metadata object: + - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://docs.rs/solana-sdk/VERSION_FOR_DOCS_RS/solana_sdk/transaction/enum.TransactionError.html) + - `fee: ` - fee this transaction was charged, as u64 integer + - `preBalances: ` - array of u64 account balances from before the transaction was processed + - `postBalances: ` - array of u64 account balances after the transaction was processed + - `innerInstructions: ` - List of [inner instructions](#inner-instructions-structure) or `null` if inner instruction recording was not enabled during this transaction + - `preTokenBalances: ` - List of [token balances](#token-balances-structure) from before the transaction was processed or omitted if token balance recording was not yet enabled during this transaction + - `postTokenBalances: ` - List of [token balances](#token-balances-structure) from after the transaction was processed or omitted if token balance recording was not yet enabled during this transaction + - `logMessages: ` - array of string log messages or `null` if log message recording was not enabled during this transaction + - DEPRECATED: `status: ` - Transaction status + - `"Ok": ` - Transaction was successful + - `"Err": ` - Transaction failed with TransactionError + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getConfirmedTransaction", + "params": [ + "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv", + "base64" + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "meta": { + "err": null, + "fee": 5000, + "innerInstructions": [], + "postBalances": [499998932500, 26858640, 1, 1, 1], + "postTokenBalances": [], + "preBalances": [499998937500, 26858640, 1, 1, 1], + "preTokenBalances": [], + "status": { + "Ok": null + } + }, + "slot": 430, + "transaction": [ + "AVj7dxHlQ9IrvdYVIjuiRFs1jLaDMHixgrv+qtHBwz51L4/ImLZhszwiyEJDIp7xeBSpm/TX5B7mYzxa+fPOMw0BAAMFJMJVqLw+hJYheizSoYlLm53KzgT82cDVmazarqQKG2GQsLgiqktA+a+FDR4/7xnDX7rsusMwryYVUdixfz1B1Qan1RcZLwqvxvJl4/t3zHragsUp0L47E24tAFUgAAAABqfVFxjHdMkoVmOYaR1etoteuKObS21cc1VbIQAAAAAHYUgdNXR0u3xNdiTr072z2DVec9EQQ/wNo1OAAAAAAAtxOUhPBp2WSjUNJEgfvy70BbxI00fZyEPvFHNfxrtEAQQEAQIDADUCAAAAAQAAAAAAAACtAQAAAAAAAAdUE18R96XTJCe+YfRfUp6WP+YKCy/72ucOL8AoBFSpAA==", + "base64" + ] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/deprecated/_getFeeCalculatorForBlockhash.mdx b/docs/src/api/deprecated/_getFeeCalculatorForBlockhash.mdx new file mode 100644 index 00000000000000..66b0d954ee5581 --- /dev/null +++ b/docs/src/api/deprecated/_getFeeCalculatorForBlockhash.mdx @@ -0,0 +1,97 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getFeeCalculatorForBlockhash + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0 +**Please use [isBlockhashValid](#isblockhashvalid) or [getFeeForMessage](#getfeeformessage) instead** +::: + +Returns the fee calculator associated with the query blockhash, or `null` if the blockhash has expired + + + + + +### Parameters: + + + query blockhash, as a base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to: + +- `` - if the query blockhash has expired; or +- `` - otherwise, a JSON object containing: + - `feeCalculator: ` - `FeeCalculator` object describing the cluster fee rate at the queried blockhash + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getFeeCalculatorForBlockhash", + "params": [ + "GJxqhuxcgfn5Tcj6y3f8X4FeCDd2RQ6SnEMo1AAxrPRZ" + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 221 + }, + "value": { + "feeCalculator": { + "lamportsPerSignature": 5000 + } + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/deprecated/_getFeeRateGovernor.mdx b/docs/src/api/deprecated/_getFeeRateGovernor.mdx new file mode 100644 index 00000000000000..e7b87cda19b981 --- /dev/null +++ b/docs/src/api/deprecated/_getFeeRateGovernor.mdx @@ -0,0 +1,76 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getFeeRateGovernor + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0 +::: + +Returns the fee rate governor information from the root bank + + + + + +### Parameters: + +**None** + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to an `object` with the following fields: + +- `burnPercent: ` - Percentage of fees collected to be destroyed +- `maxLamportsPerSignature: ` - Largest value `lamportsPerSignature` can attain for the next slot +- `minLamportsPerSignature: ` - Smallest value `lamportsPerSignature` can attain for the next slot +- `targetLamportsPerSignature: ` - Desired fee rate for the cluster +- `targetSignaturesPerSlot: ` - Desired signature rate for the cluster + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getFeeRateGovernor"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 54 + }, + "value": { + "feeRateGovernor": { + "burnPercent": 50, + "maxLamportsPerSignature": 100000, + "minLamportsPerSignature": 5000, + "targetLamportsPerSignature": 10000, + "targetSignaturesPerSlot": 20000 + } + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/deprecated/_getFees.mdx b/docs/src/api/deprecated/_getFees.mdx new file mode 100644 index 00000000000000..62cd33745483ef --- /dev/null +++ b/docs/src/api/deprecated/_getFees.mdx @@ -0,0 +1,92 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getFees + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0 +**Please use [getFeeForMessage](#getfeeformessage) instead** +::: + +Returns a recent block hash from the ledger, a fee schedule that can be used to +compute the cost of submitting a transaction using it, and the last slot in +which the blockhash will be valid. + + + + + +### Parameters: + + + Pubkey of account to query, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + +### Result: + +The result will be an RpcResponse JSON object with `value` set to a JSON object with the following fields: + +- `blockhash: ` - a Hash as base-58 encoded string +- `feeCalculator: ` - FeeCalculator object, the fee schedule for this block hash +- `lastValidSlot: ` - DEPRECATED - this value is inaccurate and should not be relied upon +- `lastValidBlockHeight: ` - last [block height](../../terminology.md#block-height) at which the blockhash will be valid + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { "jsonrpc":"2.0", "id": 1, "method":"getFees"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1 + }, + "value": { + "blockhash": "CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR", + "feeCalculator": { + "lamportsPerSignature": 5000 + }, + "lastValidSlot": 297, + "lastValidBlockHeight": 296 + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/deprecated/_getRecentBlockhash.mdx b/docs/src/api/deprecated/_getRecentBlockhash.mdx new file mode 100644 index 00000000000000..456685c3ddf603 --- /dev/null +++ b/docs/src/api/deprecated/_getRecentBlockhash.mdx @@ -0,0 +1,87 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getRecentBlockhash + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0 +**Please use [getLatestBlockhash](#getlatestblockhash) instead** +::: + +Returns a recent block hash from the ledger, and a fee schedule that can be used to compute the cost of submitting a transaction using it. + + + + + +### Parameters: + + + Pubkey of account to query, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + +### Result: + +An RpcResponse containing a JSON object consisting of a string blockhash and FeeCalculator JSON object. + +- `RpcResponse` - RpcResponse JSON object with `value` field set to a JSON object including: +- `blockhash: ` - a Hash as base-58 encoded string +- `feeCalculator: ` - FeeCalculator object, the fee schedule for this block hash + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getRecentBlockhash"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1 + }, + "value": { + "blockhash": "CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR", + "feeCalculator": { + "lamportsPerSignature": 5000 + } + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/deprecated/_getSnapshotSlot.mdx b/docs/src/api/deprecated/_getSnapshotSlot.mdx new file mode 100644 index 00000000000000..42ee186d5c3325 --- /dev/null +++ b/docs/src/api/deprecated/_getSnapshotSlot.mdx @@ -0,0 +1,64 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getSnapshotSlot + +:::warning DEPRECATED +This method is expected to be removed in solana-core v2.0 +**Please use [getHighestSnapshotSlot](#gethighestsnapshotslot) instead** +::: + +Returns the highest slot that the node has a snapshot for + + + + + +### Parameters: + +**None** + +### Result: + +`` - Snapshot slot + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getSnapshotSlot"} +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 100, "id": 1 } +``` + +Result when the node has no snapshot: + +```json +{ + "jsonrpc": "2.0", + "error": { "code": -32008, "message": "No snapshot" }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/http.md b/docs/src/api/http.md new file mode 100644 index 00000000000000..63163fbfa4ccdf --- /dev/null +++ b/docs/src/api/http.md @@ -0,0 +1,419 @@ +--- +title: JSON RPC HTTP Methods +displayed_sidebar: apiHttpMethodsSidebar +hide_table_of_contents: true +--- + +Solana nodes accept HTTP requests using the [JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification. + +:::info +For JavaScript applications, use the [@solana/web3.js](https://github.com/solana-labs/solana-web3.js) library as a convenient interface for the RPC methods to interact with a Solana node. + +For an PubSub connection to a Solana node, use the [Websocket API](./websocket.md). +::: + +## RPC HTTP Endpoint + +**Default port:** 8899 e.g. [http://localhost:8899](http://localhost:8899), [http://192.168.1.88:8899](http://192.168.1.88:8899) + +## Request Formatting + +To make a JSON-RPC request, send an HTTP POST request with a `Content-Type: application/json` header. The JSON request data should contain 4 fields: + +- `jsonrpc: ` - set to `"2.0"` +- `id: ` - a unique client-generated identifying integer +- `method: ` - a string containing the method to be invoked +- `params: ` - a JSON array of ordered parameter values + +Example using curl: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getBalance", + "params": [ + "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri" + ] + } +' +``` + +The response output will be a JSON object with the following fields: + +- `jsonrpc: ` - matching the request specification +- `id: ` - matching the request identifier +- `result: ` - requested data or success confirmation + +Requests can be sent in batches by sending an array of JSON-RPC request objects as the data for a single POST. + +## Definitions + +- Hash: A SHA-256 hash of a chunk of data. +- Pubkey: The public key of a Ed25519 key-pair. +- Transaction: A list of Solana instructions signed by a client keypair to authorize those actions. +- Signature: An Ed25519 signature of transaction's payload data including instructions. This can be used to identify transactions. + +## Configuring State Commitment + +For preflight checks and transaction processing, Solana nodes choose which bank +state to query based on a commitment requirement set by the client. The +commitment describes how finalized a block is at that point in time. When +querying the ledger state, it's recommended to use lower levels of commitment +to report progress and higher levels to ensure the state will not be rolled back. + +In descending order of commitment (most finalized to least finalized), clients +may specify: + +- `"finalized"` - the node will query the most recent block confirmed by supermajority + of the cluster as having reached maximum lockout, meaning the cluster has + recognized this block as finalized +- `"confirmed"` - the node will query the most recent block that has been voted on by supermajority of the cluster. + - It incorporates votes from gossip and replay. + - It does not count votes on descendants of a block, only direct votes on that block. + - This confirmation level also upholds "optimistic confirmation" guarantees in + release 1.3 and onwards. +- `"processed"` - the node will query its most recent block. Note that the block + may still be skipped by the cluster. + +For processing many dependent transactions in series, it's recommended to use +`"confirmed"` commitment, which balances speed with rollback safety. +For total safety, it's recommended to use`"finalized"` commitment. + +#### Example + +The commitment parameter should be included as the last element in the `params` array: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getBalance", + "params": [ + "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri", + { + "commitment": "finalized" + } + ] + } +' +``` + +#### Default: + +If commitment configuration is not provided, the node will default to `"finalized"` commitment + +Only methods that query bank state accept the commitment parameter. They are indicated in the API Reference below. + +#### RpcResponse Structure + +Many methods that take a commitment parameter return an RpcResponse JSON object comprised of two parts: + +- `context` : An RpcResponseContext JSON structure including a `slot` field at which the operation was evaluated. +- `value` : The value returned by the operation itself. + +#### Parsed Responses + +Some methods support an `encoding` parameter, and can return account or +instruction data in parsed JSON format if `"encoding":"jsonParsed"` is requested +and the node has a parser for the owning program. Solana nodes currently support +JSON parsing for the following native and SPL programs: + +| Program | Account State | Instructions | +| ---------------------------- | ------------- | ------------ | +| Address Lookup | v1.15.0 | v1.15.0 | +| BPF Loader | n/a | stable | +| BPF Upgradeable Loader | stable | stable | +| Config | stable | | +| SPL Associated Token Account | n/a | stable | +| SPL Memo | n/a | stable | +| SPL Token | stable | stable | +| SPL Token 2022 | stable | stable | +| Stake | stable | stable | +| Vote | stable | stable | + +The list of account parsers can be found [here](https://github.com/solana-labs/solana/blob/master/account-decoder/src/parse_account_data.rs), and instruction parsers [here](https://github.com/solana-labs/solana/blob/master/transaction-status/src/parse_instruction.rs). + +## Filter criteria + +Some methods support providing a `filters` object to enable pre-filtering the data returned within the RpcResponse JSON object. The following filters exist: + +- `memcmp: object` - compares a provided series of bytes with program account data at a particular offset. Fields: + + - `offset: usize` - offset into program account data to start comparison + - `bytes: string` - data to match, as encoded string + - `encoding: string` - encoding for filter `bytes` data, either "base58" or "base64". Data is limited in size to 128 or fewer decoded bytes.
+ **NEW: This field, and base64 support generally, is only available in solana-core v1.14.0 or newer. Please omit when querying nodes on earlier versions** + +- `dataSize: u64` - compares the program account data length with the provided data size + +## Health Check + +Although not a JSON RPC API, a `GET /health` at the RPC HTTP Endpoint provides a +health-check mechanism for use by load balancers or other network +infrastructure. This request will always return a HTTP 200 OK response with a body of +"ok", "behind" or "unknown" based on the following conditions: + +1. If one or more `--known-validator` arguments are provided to `solana-validator` - "ok" is returned + when the node has within `HEALTH_CHECK_SLOT_DISTANCE` slots of the highest + known validator, otherwise "behind". "unknown" is returned when no slot + information from known validators is not yet available. +2. "ok" is always returned if no known validators are provided. + +## JSON RPC API Reference + +import GetAccountInfo from "./methods/\_getAccountInfo.mdx" + + + +import GetBalance from "./methods/\_getBalance.mdx" + + + +import GetBlock from "./methods/\_getBlock.mdx" + + + +import GetBlockHeight from "./methods/\_getBlockHeight.mdx" + + + +import GetBlockProduction from "./methods/\_getBlockProduction.mdx" + + + +import GetBlockCommitment from "./methods/\_getBlockCommitment.mdx" + + + +import GetBlocks from "./methods/\_getBlocks.mdx" + + + +import GetBlocksWithLimit from "./methods/\_getBlocksWithLimit.mdx" + + + +import GetBlockTime from "./methods/\_getBlockTime.mdx" + + + +import GetClusterNodes from "./methods/\_getClusterNodes.mdx" + + + +import GetEpochInfo from "./methods/\_getEpochInfo.mdx" + + + +import GetEpochSchedule from "./methods/\_getEpochSchedule.mdx" + + + +import GetFeeForMessage from "./methods/\_getFeeForMessage.mdx" + + + +import GetFirstAvailableBlock from "./methods/\_getFirstAvailableBlock.mdx" + + + +import GetGenesisHash from "./methods/\_getGenesisHash.mdx" + + + +import GetHealth from "./methods/\_getHealth.mdx" + + + +import GetHighestSnapshotSlot from "./methods/\_getHighestSnapshotSlot.mdx" + + + +import GetIdentity from "./methods/\_getIdentity.mdx" + + + +import GetInflationGovernor from "./methods/\_getInflationGovernor.mdx" + + + +import GetInflationRate from "./methods/\_getInflationRate.mdx" + + + +import GetInflationReward from "./methods/\_getInflationReward.mdx" + + + +import GetLargestAccounts from "./methods/\_getLargestAccounts.mdx" + + + +import GetLatestBlockhash from "./methods/\_getLatestBlockhash.mdx" + + + +import GetLeaderSchedule from "./methods/\_getLeaderSchedule.mdx" + + + +import GetMaxRetransmitSlot from "./methods/\_getMaxRetransmitSlot.mdx" + + + +import GetMaxShredInsertSlot from "./methods/\_getMaxShredInsertSlot.mdx" + + + +import GetMinimumBalanceForRentExemption from "./methods/\_getMinimumBalanceForRentExemption.mdx" + + + +import GetMultipleAccounts from "./methods/\_getMultipleAccounts.mdx" + + + +import GetProgramAccounts from "./methods/\_getProgramAccounts.mdx" + + + +import GetRecentPerformanceSamples from "./methods/\_getRecentPerformanceSamples.mdx" + + + +import GetRecentPrioritizationFees from "./methods/\_getRecentPrioritizationFees.mdx" + + + +import GetSignaturesForAddress from "./methods/\_getSignaturesForAddress.mdx" + + + +import GetSignatureStatuses from "./methods/\_getSignatureStatuses.mdx" + + + +import GetSlot from "./methods/\_getSlot.mdx" + + + +import GetSlotLeader from "./methods/\_getSlotLeader.mdx" + + + +import GetSlotLeaders from "./methods/\_getSlotLeaders.mdx" + + + +import GetStakeActivation from "./methods/\_getStakeActivation.mdx" + + + +import GetStakeMinimumDelegation from "./methods/\_getStakeMinimumDelegation.mdx" + + + +import GetSupply from "./methods/\_getSupply.mdx" + + + +import GetTokenAccountBalance from "./methods/\_getTokenAccountBalance.mdx" + + + +import GetTokenAccountsByDelegate from "./methods/\_getTokenAccountsByDelegate.mdx" + + + +import GetTokenAccountsByOwner from "./methods/\_getTokenAccountsByOwner.mdx" + + + +import GetTokenLargestAccounts from "./methods/\_getTokenLargestAccounts.mdx" + + + +import GetTokenSupply from "./methods/\_getTokenSupply.mdx" + + + +import GetTransaction from "./methods/\_getTransaction.mdx" + + + +import GetTransactionCount from "./methods/\_getTransactionCount.mdx" + + + +import GetVersion from "./methods/\_getVersion.mdx" + + + +import GetVoteAccounts from "./methods/\_getVoteAccounts.mdx" + + + +import IsBlockhashValid from "./methods/\_isBlockhashValid.mdx" + + + +import MinimumLedgerSlot from "./methods/\_minimumLedgerSlot.mdx" + + + +import RequestAirdrop from "./methods/\_requestAirdrop.mdx" + + + +import SendTransaction from "./methods/\_sendTransaction.mdx" + + + +import SimulateTransaction from "./methods/\_simulateTransaction.mdx" + + + +## JSON RPC API Deprecated Methods + +import GetConfirmedBlock from "./deprecated/\_getConfirmedBlock.mdx" + + + +import GetConfirmedBlocks from "./deprecated/\_getConfirmedBlocks.mdx" + + + +import GetConfirmedBlocksWithLimit from "./deprecated/\_getConfirmedBlocksWithLimit.mdx" + + + +import GetConfirmedSignaturesForAddress2 from "./deprecated/\_getConfirmedSignaturesForAddress2.mdx" + + + +import GetConfirmedTransaction from "./deprecated/\_getConfirmedTransaction.mdx" + + + +import GetFeeCalculatorForBlockhash from "./deprecated/\_getFeeCalculatorForBlockhash.mdx" + + + +import GetFeeRateGovernor from "./deprecated/\_getFeeRateGovernor.mdx" + + + +import GetFees from "./deprecated/\_getFees.mdx" + + + +import GetRecentBlockhash from "./deprecated/\_getRecentBlockhash.mdx" + + + +import GetSnapshotSlot from "./deprecated/\_getSnapshotSlot.mdx" + + diff --git a/docs/src/api/methods/_getAccountInfo.mdx b/docs/src/api/methods/_getAccountInfo.mdx new file mode 100644 index 00000000000000..18a5080758de4a --- /dev/null +++ b/docs/src/api/methods/_getAccountInfo.mdx @@ -0,0 +1,136 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getAccountInfo + +Returns all information associated with the account of provided Pubkey + + + + + +### Parameters: + + + Pubkey of account to query, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + +Encoding format for Account data + + + +
+ +- `base58` is slow and limited to less than 129 bytes of Account data. +- `base64` will return base64 encoded data for Account data of any size. +- `base64+zstd` compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) + and base64-encodes the result. +- `jsonParsed` encoding attempts to use program-specific state parsers to return + more human-readable and explicit account state data. +- If `jsonParsed` is requested but a parser cannot be found, the field falls + back to `base64` encoding, detectable when the `data` field is type `string`. + +
+ +
+ + + limit the returned account data using the provided "offset: <usize>" and + "length: <usize>" fields +
  • + only available for base58, base64 or{" "} + base64+zstd encodings. +
  • +
    + + + The minimum slot that the request can be evaluated at + + +
    + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to: + +- `` - if the requested account doesn't exist +- `` - otherwise, a JSON object containing: + - `lamports: ` - number of lamports assigned to this account, as a u64 + - `owner: ` - base-58 encoded Pubkey of the program this account has been assigned to + - `data: <[string, encoding]|object>` - data associated with the account, either as encoded binary data or JSON format `{: }` - depending on encoding parameter + - `executable: ` - boolean indicating if the account contains a program \(and is strictly read-only\) + - `rentEpoch: ` - the epoch at which this account will next owe rent, as u64 + - `size: ` - the data size of the account + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getAccountInfo", + "params": [ + "vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg", + { + "encoding": "base58" + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1 + }, + "value": { + "data": [ + "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHRTPuR3oZ1EioKtYGiYxpxMG5vpbZLsbcBYBEmZZcMKaSoGx9JZeAuWf", + "base58" + ], + "executable": false, + "lamports": 1000000000, + "owner": "11111111111111111111111111111111", + "rentEpoch": 2, + "space": 80 + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getBalance.mdx b/docs/src/api/methods/_getBalance.mdx new file mode 100644 index 00000000000000..0cd0f0c6d0ef23 --- /dev/null +++ b/docs/src/api/methods/_getBalance.mdx @@ -0,0 +1,78 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getBalance + +Returns the balance of the account of provided Pubkey + + + + + +### Parameters: + + + Pubkey of account to query, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +`RpcResponse` - RpcResponse JSON object with `value` field set to the balance + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getBalance", + "params": [ + "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri" + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { "context": { "slot": 1 }, "value": 0 }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getBlock.mdx b/docs/src/api/methods/_getBlock.mdx new file mode 100644 index 00000000000000..6ced795c2eee8f --- /dev/null +++ b/docs/src/api/methods/_getBlock.mdx @@ -0,0 +1,288 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getBlock + +Returns identity and transaction information about a confirmed block in the ledger + + + + + +### Parameters: + + + slot number, as u64 integer + + + + +Configuration object containing the following fields: + + +
  • + the default is finalized +
  • +
  • + processed is not supported. +
  • +
    + + + +encoding format for each returned Transaction + + + +
    + +- `jsonParsed` attempts to use program-specific instruction parsers to return + more human-readable and explicit data in the `transaction.message.instructions` list. +- If `jsonParsed` is requested but a parser cannot be found, the instruction + falls back to regular JSON encoding (`accounts`, `data`, and `programIdIndex` fields). + +
    + +
    + + + +level of transaction detail to return + + + +
    + +- If `accounts` are requested, transaction details only include signatures and + an annotated list of accounts in each transaction. +- Transaction metadata is limited to only: fee, err, pre_balances, + post_balances, pre_token_balances, and post_token_balances. + +
    + +
    + + + +the max transaction version to return in responses. + +
    + +- If the requested block contains a transaction with a higher version, an + error will be returned. +- If this parameter is omitted, only legacy transactions will be returned, and + a block containing any versioned transaction will prompt the error. + +
    + +
    + + + whether to populate the `rewards` array. If parameter not provided, the + default includes rewards. + + +
    + +### Result: + +The result field will be an object with the following fields: + +- `` - if specified block is not confirmed +- `` - if block is confirmed, an object with the following fields: + - `blockhash: ` - the blockhash of this block, as base-58 encoded string + - `previousBlockhash: ` - the blockhash of this block's parent, as base-58 encoded string; if the parent block is not available due to ledger cleanup, this field will return "11111111111111111111111111111111" + - `parentSlot: ` - the slot index of this block's parent + - `transactions: ` - present if "full" transaction details are requested; an array of JSON objects containing: + - `transaction: ` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter + - `meta: ` - transaction status metadata object, containing `null` or: + - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) + - `fee: ` - fee this transaction was charged, as u64 integer + - `preBalances: ` - array of u64 account balances from before the transaction was processed + - `postBalances: ` - array of u64 account balances after the transaction was processed + - `innerInstructions: ` - List of [inner instructions](#inner-instructions-structure) or `null` if inner instruction recording was not enabled during this transaction + - `preTokenBalances: ` - List of [token balances](#token-balances-structure) from before the transaction was processed or omitted if token balance recording was not yet enabled during this transaction + - `postTokenBalances: ` - List of [token balances](#token-balances-structure) from after the transaction was processed or omitted if token balance recording was not yet enabled during this transaction + - `logMessages: ` - array of string log messages or `null` if log message recording was not enabled during this transaction + - `rewards: ` - transaction-level rewards, populated if rewards are requested; an array of JSON objects containing: + - `pubkey: ` - The public key, as base-58 encoded string, of the account that received the reward + - `lamports: `- number of reward lamports credited or debited by the account, as a i64 + - `postBalance: ` - account balance in lamports after the reward was applied + - `rewardType: ` - type of reward: "fee", "rent", "voting", "staking" + - `commission: ` - vote account commission when the reward was credited, only present for voting and staking rewards + - DEPRECATED: `status: ` - Transaction status + - `"Ok": ` - Transaction was successful + - `"Err": ` - Transaction failed with TransactionError + - `loadedAddresses: ` - Transaction addresses loaded from address lookup tables. Undefined if `maxSupportedTransactionVersion` is not set in request params, or if `jsonParsed` encoding is set in request params. + - `writable: ` - Ordered list of base-58 encoded addresses for writable loaded accounts + - `readonly: ` - Ordered list of base-58 encoded addresses for readonly loaded accounts + - `returnData: ` - the most-recent return data generated by an instruction in the transaction, with the following fields: + - `programId: ` - the program that generated the return data, as base-58 encoded Pubkey + - `data: <[string, encoding]>` - the return data itself, as base-64 encoded binary data + - `computeUnitsConsumed: ` - number of [compute units](developing/programming-model/runtime.md#compute-budget) consumed by the transaction + - `version: <"legacy"|number|undefined>` - Transaction version. Undefined if `maxSupportedTransactionVersion` is not set in request params. + - `signatures: ` - present if "signatures" are requested for transaction details; an array of signatures strings, corresponding to the transaction order in the block + - `rewards: ` - block-level rewards, present if rewards are requested; an array of JSON objects containing: + - `pubkey: ` - The public key, as base-58 encoded string, of the account that received the reward + - `lamports: `- number of reward lamports credited or debited by the account, as a i64 + - `postBalance: ` - account balance in lamports after the reward was applied + - `rewardType: ` - type of reward: "fee", "rent", "voting", "staking" + - `commission: ` - vote account commission when the reward was credited, only present for voting and staking rewards + - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch). null if not available + - `blockHeight: ` - the number of blocks beneath this block + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0","id":1, + "method":"getBlock", + "params": [ + 430, + { + "encoding": "json", + "maxSupportedTransactionVersion":0, + "transactionDetails":"full", + "rewards":false + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "blockHeight": 428, + "blockTime": null, + "blockhash": "3Eq21vXNB5s86c62bVuUfTeaMif1N2kUqRPBmGRJhyTA", + "parentSlot": 429, + "previousBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B", + "transactions": [ + { + "meta": { + "err": null, + "fee": 5000, + "innerInstructions": [], + "logMessages": [], + "postBalances": [499998932500, 26858640, 1, 1, 1], + "postTokenBalances": [], + "preBalances": [499998937500, 26858640, 1, 1, 1], + "preTokenBalances": [], + "rewards": null, + "status": { + "Ok": null + } + }, + "transaction": { + "message": { + "accountKeys": [ + "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe", + "AjozzgE83A3x1sHNUR64hfH7zaEBWeMaFuAN9kQgujrc", + "SysvarS1otHashes111111111111111111111111111", + "SysvarC1ock11111111111111111111111111111111", + "Vote111111111111111111111111111111111111111" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 3, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [1, 2, 3, 0], + "data": "37u9WtQpcm6ULa3WRQHmj49EPs4if7o9f1jSRVZpm2dvihR9C8jY4NqEwXUbLwx15HBSNcP1", + "programIdIndex": 4 + } + ], + "recentBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B" + }, + "signatures": [ + "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv" + ] + } + } + ] + }, + "id": 1 +} +``` + + + + +--- + +#### Transaction Structure + +Transactions are quite different from those on other blockchains. Be sure to review [Anatomy of a Transaction](developing/programming-model/transactions.md) to learn about transactions on Solana. + +The JSON structure of a transaction is defined as follows: + +- `signatures: ` - A list of base-58 encoded signatures applied to the transaction. The list is always of length `message.header.numRequiredSignatures` and not empty. The signature at index `i` corresponds to the public key at index `i` in `message.accountKeys`. The first one is used as the [transaction id](../../terminology.md#transaction-id). +- `message: ` - Defines the content of the transaction. + - `accountKeys: ` - List of base-58 encoded public keys used by the transaction, including by the instructions and for signatures. The first `message.header.numRequiredSignatures` public keys must sign the transaction. + - `header: ` - Details the account types and signatures required by the transaction. + - `numRequiredSignatures: ` - The total number of signatures required to make the transaction valid. The signatures must match the first `numRequiredSignatures` of `message.accountKeys`. + - `numReadonlySignedAccounts: ` - The last `numReadonlySignedAccounts` of the signed keys are read-only accounts. Programs may process multiple transactions that load read-only accounts within a single PoH entry, but are not permitted to credit or debit lamports or modify account data. Transactions targeting the same read-write account are evaluated sequentially. + - `numReadonlyUnsignedAccounts: ` - The last `numReadonlyUnsignedAccounts` of the unsigned keys are read-only accounts. + - `recentBlockhash: ` - A base-58 encoded hash of a recent block in the ledger used to prevent transaction duplication and to give transactions lifetimes. + - `instructions: ` - List of program instructions that will be executed in sequence and committed in one atomic transaction if all succeed. + - `programIdIndex: ` - Index into the `message.accountKeys` array indicating the program account that executes this instruction. + - `accounts: ` - List of ordered indices into the `message.accountKeys` array indicating which accounts to pass to the program. + - `data: ` - The program input data encoded in a base-58 string. + - `addressTableLookups: ` - List of address table lookups used by a transaction to dynamically load addresses from on-chain address lookup tables. Undefined if `maxSupportedTransactionVersion` is not set. + - `accountKey: ` - base-58 encoded public key for an address lookup table account. + - `writableIndexes: ` - List of indices used to load addresses of writable accounts from a lookup table. + - `readonlyIndexes: ` - List of indices used to load addresses of readonly accounts from a lookup table. + +#### Inner Instructions Structure + +The Solana runtime records the cross-program instructions that are invoked during transaction processing and makes these available for greater transparency of what was executed on-chain per transaction instruction. Invoked instructions are grouped by the originating transaction instruction and are listed in order of processing. + +The JSON structure of inner instructions is defined as a list of objects in the following structure: + +- `index: number` - Index of the transaction instruction from which the inner instruction(s) originated +- `instructions: ` - Ordered list of inner program instructions that were invoked during a single transaction instruction. + - `programIdIndex: ` - Index into the `message.accountKeys` array indicating the program account that executes this instruction. + - `accounts: ` - List of ordered indices into the `message.accountKeys` array indicating which accounts to pass to the program. + - `data: ` - The program input data encoded in a base-58 string. + +#### Token Balances Structure + +The JSON structure of token balances is defined as a list of objects in the following structure: + +- `accountIndex: ` - Index of the account in which the token balance is provided for. +- `mint: ` - Pubkey of the token's mint. +- `owner: ` - Pubkey of token balance's owner. +- `programId: ` - Pubkey of the Token program that owns the account. +- `uiTokenAmount: ` - + - `amount: ` - Raw amount of tokens as a string, ignoring decimals. + - `decimals: ` - Number of decimals configured for token's mint. + - `uiAmount: ` - Token amount as a float, accounting for decimals. **DEPRECATED** + - `uiAmountString: ` - Token amount as a string, accounting for decimals. + + diff --git a/docs/src/api/methods/_getBlockCommitment.mdx b/docs/src/api/methods/_getBlockCommitment.mdx new file mode 100644 index 00000000000000..c12fc186cfca4b --- /dev/null +++ b/docs/src/api/methods/_getBlockCommitment.mdx @@ -0,0 +1,70 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getBlockCommitment + +Returns commitment for particular block + + + + + +### Parameters: + + + block number, identified by Slot + + +### Result: + +The result field will be a JSON object containing: + +- `commitment` - commitment, comprising either: + - `` - Unknown block + - `` - commitment, array of u64 integers logging the amount of cluster stake in lamports that has voted on the block at each depth from 0 to `MAX_LOCKOUT_HISTORY` + 1 +- `totalStake` - total active stake, in lamports, of the current epoch + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getBlockCommitment", + "params":[5] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "commitment": [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 10, 32 + ], + "totalStake": 42 + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getBlockHeight.mdx b/docs/src/api/methods/_getBlockHeight.mdx new file mode 100644 index 00000000000000..9b8a07d0fd6a2b --- /dev/null +++ b/docs/src/api/methods/_getBlockHeight.mdx @@ -0,0 +1,73 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getBlockHeight + +Returns the current block height of the node + + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +- `` - Current block height + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc":"2.0","id":1, + "method":"getBlockHeight" + } +' +``` + +Result: + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": 1233, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getBlockProduction.mdx b/docs/src/api/methods/_getBlockProduction.mdx new file mode 100644 index 00000000000000..eed1b5e6fbd3b2 --- /dev/null +++ b/docs/src/api/methods/_getBlockProduction.mdx @@ -0,0 +1,97 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getBlockProduction + +Returns recent block production information from the current or previous epoch. + + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + Only return results for this validator identity (base-58 encoded) + + + +Slot range to return block production for. If parameter not provided, defaults to current epoch. + +- `firstSlot: ` - first slot to return block production information for (inclusive) +- (optional) `lastSlot: ` - last slot to return block production information for (inclusive). If parameter not provided, defaults to the highest slot + + + + + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to: + +- `` + - `byIdentity: ` - a dictionary of validator identities, + as base-58 encoded strings. Value is a two element array containing the + number of leader slots and the number of blocks produced. + - `range: ` - Block production slot range + - `firstSlot: ` - first slot of the block production information (inclusive) + - `lastSlot: ` - last slot of block production information (inclusive) + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getBlockProduction"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 9887 + }, + "value": { + "byIdentity": { + "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr": [9888, 9886] + }, + "range": { + "firstSlot": 0, + "lastSlot": 9887 + } + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getBlockTime.mdx b/docs/src/api/methods/_getBlockTime.mdx new file mode 100644 index 00000000000000..7fc991b5b8690e --- /dev/null +++ b/docs/src/api/methods/_getBlockTime.mdx @@ -0,0 +1,81 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getBlockTime + +Returns the estimated production time of a block. + +:::info +Each validator reports their UTC time to the ledger on a regular interval by +intermittently adding a timestamp to a Vote for a particular block. A requested +block's time is calculated from the stake-weighted mean of the Vote timestamps +in a set of recent blocks recorded on the ledger. +::: + + + + + +### Parameters: + + + block number, identified by Slot + + +### Result: + +- `` - estimated production time, as Unix timestamp (seconds since the Unix epoch) + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc":"2.0", "id":1, + "method": "getBlockTime", + "params":[5] + } +' +``` + +### Response: + +When a block time is available: + +```json +{ + "jsonrpc": "2.0", + "result": 1574721591, + "id": 1 +} +``` + +When a block time is not available: + +```json +{ + "jsonrpc": "2.0", + "error": { + "code": -32004, + "message": "Block not available for slot 150" + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getBlocks.mdx b/docs/src/api/methods/_getBlocks.mdx new file mode 100644 index 00000000000000..59e816d6e1af26 --- /dev/null +++ b/docs/src/api/methods/_getBlocks.mdx @@ -0,0 +1,86 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getBlocks + +Returns a list of confirmed blocks between two slots + + + + + +### Parameters: + + + end_slot, as u64 integer + + + + start_slot, as u64 integer (must be no more than 500,000 blocks + higher than the `start_slot`) + + + + +Configuration object containing the following fields: + + + +- "processed" is not supported + + + + + +### Result: + +The result field will be an array of u64 integers listing confirmed blocks +between `start_slot` and either `end_slot` - if provided, or latest confirmed block, +inclusive. Max range allowed is 500,000 slots. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getBlocks", + "params": [ + 5, 10 + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": [5, 6, 7, 8, 9, 10], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getBlocksWithLimit.mdx b/docs/src/api/methods/_getBlocksWithLimit.mdx new file mode 100644 index 00000000000000..6080a027c5926a --- /dev/null +++ b/docs/src/api/methods/_getBlocksWithLimit.mdx @@ -0,0 +1,84 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getBlocksWithLimit + +Returns a list of confirmed blocks starting at the given slot + + + + + +### Parameters: + + + start_slot, as u64 integer + + + + limit, as u64 integer (must be no more than 500,000 blocks higher + than the start_slot) + + + + +Configuration object containing the following field: + + + +- "processed" is not supported + + + + + +### Result: + +The result field will be an array of u64 integers listing confirmed blocks +starting at `start_slot` for up to `limit` blocks, inclusive. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id":1, + "method":"getBlocksWithLimit", + "params":[5, 3] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": [5, 6, 7], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getClusterNodes.mdx b/docs/src/api/methods/_getClusterNodes.mdx new file mode 100644 index 00000000000000..735e3aff27fc8e --- /dev/null +++ b/docs/src/api/methods/_getClusterNodes.mdx @@ -0,0 +1,71 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getClusterNodes + +Returns information about all the nodes participating in the cluster + + + + +### Parameters: + +**None** + +### Result: + +The result field will be an array of JSON objects, each with the following sub fields: + +- `pubkey: ` - Node public key, as base-58 encoded string +- `gossip: ` - Gossip network address for the node +- `tpu: ` - TPU network address for the node +- `rpc: ` - JSON RPC network address for the node, or `null` if the JSON RPC service is not enabled +- `version: ` - The software version of the node, or `null` if the version information is not available +- `featureSet: ` - The unique identifier of the node's feature set +- `shredVersion: ` - The shred version the node has been configured to use + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getClusterNodes" + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": [ + { + "gossip": "10.239.6.48:8001", + "pubkey": "9QzsJf7LPLj8GkXbYT3LFDKqsj2hHG7TA3xinJHu8epQ", + "rpc": "10.239.6.48:8899", + "tpu": "10.239.6.48:8856", + "version": "1.0.0 c375ce1f" + } + ], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getEpochInfo.mdx b/docs/src/api/methods/_getEpochInfo.mdx new file mode 100644 index 00000000000000..4b8cb1c23b6449 --- /dev/null +++ b/docs/src/api/methods/_getEpochInfo.mdx @@ -0,0 +1,81 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getEpochInfo + +Returns information about the current epoch + + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +The result field will be an object with the following fields: + +- `absoluteSlot: ` - the current slot +- `blockHeight: ` - the current block height +- `epoch: ` - the current epoch +- `slotIndex: ` - the current slot relative to the start of the current epoch +- `slotsInEpoch: ` - the number of slots in this epoch +- `transactionCount: ` - total number of transactions processed without error since genesis + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getEpochInfo"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "absoluteSlot": 166598, + "blockHeight": 166500, + "epoch": 27, + "slotIndex": 2790, + "slotsInEpoch": 8192, + "transactionCount": 22661093 + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getEpochSchedule.mdx b/docs/src/api/methods/_getEpochSchedule.mdx new file mode 100644 index 00000000000000..2e11e8a4be0d93 --- /dev/null +++ b/docs/src/api/methods/_getEpochSchedule.mdx @@ -0,0 +1,67 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getEpochSchedule + +Returns the epoch schedule information from this cluster's genesis config + + + + +### Parameters: + +**None** + +### Result: + +The result field will be an object with the following fields: + +- `slotsPerEpoch: ` - the maximum number of slots in each epoch +- `leaderScheduleSlotOffset: ` - the number of slots before beginning of an epoch to calculate a leader schedule for that epoch +- `warmup: ` - whether epochs start short and grow +- `firstNormalEpoch: ` - first normal-length epoch, log2(slotsPerEpoch) - log2(MINIMUM_SLOTS_PER_EPOCH) +- `firstNormalSlot: ` - MINIMUM_SLOTS_PER_EPOCH \* (2.pow(firstNormalEpoch) - 1) + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc":"2.0","id":1, + "method":"getEpochSchedule" + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "firstNormalEpoch": 8, + "firstNormalSlot": 8160, + "leaderScheduleSlotOffset": 8192, + "slotsPerEpoch": 8192, + "warmup": true + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getFeeForMessage.mdx b/docs/src/api/methods/_getFeeForMessage.mdx new file mode 100644 index 00000000000000..85a81f589b0fc9 --- /dev/null +++ b/docs/src/api/methods/_getFeeForMessage.mdx @@ -0,0 +1,86 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getFeeForMessage + +Get the fee the network will charge for a particular Message + +:::caution +**NEW: This method is only available in solana-core v1.9 or newer. Please use +[getFees](#getFees) for solana-core v1.8** +::: + + + + +### Parameters: + + + Base-64 encoded Message + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +- `` - Fee corresponding to the message at the specified blockhash + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' +{ + "id":1, + "jsonrpc":"2.0", + "method":"getFeeForMessage", + "params":[ + "AQABAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAA", + { + "commitment":"processed" + } + ] +} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { "context": { "slot": 5068 }, "value": 5000 }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getFirstAvailableBlock.mdx b/docs/src/api/methods/_getFirstAvailableBlock.mdx new file mode 100644 index 00000000000000..97139e17b5bf60 --- /dev/null +++ b/docs/src/api/methods/_getFirstAvailableBlock.mdx @@ -0,0 +1,50 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getFirstAvailableBlock + +Returns the slot of the lowest confirmed block that has not been purged from the ledger + + + + +### Parameters: + +**None** + +### Result: + +- `` - Slot + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc":"2.0","id":1, + "method":"getFirstAvailableBlock" + } +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 250000, "id": 1 } +``` + + + + diff --git a/docs/src/api/methods/_getGenesisHash.mdx b/docs/src/api/methods/_getGenesisHash.mdx new file mode 100644 index 00000000000000..4a7802d8b1b266 --- /dev/null +++ b/docs/src/api/methods/_getGenesisHash.mdx @@ -0,0 +1,51 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getGenesisHash + +Returns the genesis hash + + + + +### Parameters: + +**None** + +### Result: + +- `` - a Hash as base-58 encoded string + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getGenesisHash"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": "GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC", + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getHealth.mdx b/docs/src/api/methods/_getHealth.mdx new file mode 100644 index 00000000000000..482a2ff62a076e --- /dev/null +++ b/docs/src/api/methods/_getHealth.mdx @@ -0,0 +1,88 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getHealth + +Returns the current health of the node. + +:::caution +If one or more `--known-validator` arguments are provided to `solana-validator` - "ok" is returned +when the node has within `HEALTH_CHECK_SLOT_DISTANCE` slots of the highest known validator, +otherwise an error is returned. "ok" is always returned if no known validators are provided. +::: + + + + +### Parameters: + +**None** + +### Result: + +If the node is healthy: "ok" + +If the node is unhealthy, a JSON RPC error response is returned. The specifics of the error response are **UNSTABLE** and may change in the future + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getHealth"} +' +``` + +### Response: + +Healthy Result: + +```json +{ "jsonrpc": "2.0", "result": "ok", "id": 1 } +``` + +Unhealthy Result (generic): + +```json +{ + "jsonrpc": "2.0", + "error": { + "code": -32005, + "message": "Node is unhealthy", + "data": {} + }, + "id": 1 +} +``` + +Unhealthy Result (if additional information is available) + +```json +{ + "jsonrpc": "2.0", + "error": { + "code": -32005, + "message": "Node is behind by 42 slots", + "data": { + "numSlotsBehind": 42 + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getHighestSnapshotSlot.mdx b/docs/src/api/methods/_getHighestSnapshotSlot.mdx new file mode 100644 index 00000000000000..5028f77e6feeca --- /dev/null +++ b/docs/src/api/methods/_getHighestSnapshotSlot.mdx @@ -0,0 +1,78 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getHighestSnapshotSlot + +Returns the highest slot information that the node has snapshots for. + +This will find the highest full snapshot slot, and the highest incremental +snapshot slot _based on_ the full snapshot slot, if there is one. + +:::caution +NEW: This method is only available in solana-core v1.9 or newer. Please use +[getSnapshotSlot](/api/http#getsnapshotslot) for solana-core v1.8 +::: + + + + +### Parameters: + +**None** + +### Result: + +When the node has a snapshot, this returns a JSON object with the following fields: + +- `full: ` - Highest full snapshot slot +- `incremental: ` - Highest incremental snapshot slot _based on_ `full` + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1,"method":"getHighestSnapshotSlot"} +' +``` + +### Response: + +Result when the node has a snapshot: + +```json +{ + "jsonrpc": "2.0", + "result": { + "full": 100, + "incremental": 110 + }, + "id": 1 +} +``` + +Result when the node has no snapshot: + +```json +{ + "jsonrpc": "2.0", + "error": { "code": -32008, "message": "No snapshot" }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getIdentity.mdx b/docs/src/api/methods/_getIdentity.mdx new file mode 100644 index 00000000000000..263ebb28d7f824 --- /dev/null +++ b/docs/src/api/methods/_getIdentity.mdx @@ -0,0 +1,55 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getIdentity + +Returns the identity pubkey for the current node + + + + +### Parameters: + +**None** + +### Result: + +The result field will be a JSON object with the following fields: + +- `identity` - the identity pubkey of the current node \(as a base-58 encoded string\) + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getIdentity"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "identity": "2r1F4iWqVcb8M1DbAjQuFpebkQHY9hcVU4WuW2DJBppN" + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getInflationGovernor.mdx b/docs/src/api/methods/_getInflationGovernor.mdx new file mode 100644 index 00000000000000..206fa9a60c498a --- /dev/null +++ b/docs/src/api/methods/_getInflationGovernor.mdx @@ -0,0 +1,75 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getInflationGovernor + +Returns the current inflation governor + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + +### Result: + +The result field will be a JSON object with the following fields: + +- `initial: ` - the initial inflation percentage from time 0 +- `terminal: ` - terminal inflation percentage +- `taper: ` - rate per year at which inflation is lowered. (Rate reduction is derived using the target slot time in genesis config) +- `foundation: ` - percentage of total inflation allocated to the foundation +- `foundationTerm: ` - duration of foundation pool inflation in years + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getInflationGovernor"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "foundation": 0.05, + "foundationTerm": 7, + "initial": 0.15, + "taper": 0.15, + "terminal": 0.015 + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getInflationRate.mdx b/docs/src/api/methods/_getInflationRate.mdx new file mode 100644 index 00000000000000..1cc987aab13c1c --- /dev/null +++ b/docs/src/api/methods/_getInflationRate.mdx @@ -0,0 +1,62 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getInflationRate + +Returns the specific inflation values for the current epoch + + + + +### Parameters: + +**None** + +### Result: + +The result field will be a JSON object with the following fields: + +- `total: ` - total inflation +- `validator: ` -inflation allocated to validators +- `foundation: ` - inflation allocated to the foundation +- `epoch: ` - epoch for which these values are valid + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getInflationRate"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "epoch": 100, + "foundation": 0.001, + "total": 0.149, + "validator": 0.148 + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getInflationReward.mdx b/docs/src/api/methods/_getInflationReward.mdx new file mode 100644 index 00000000000000..840a6f520fa34c --- /dev/null +++ b/docs/src/api/methods/_getInflationReward.mdx @@ -0,0 +1,101 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getInflationReward + +Returns the inflation / staking reward for a list of addresses for an epoch + + + + +### Parameters: + + + An array of addresses to query, as base-58 encoded strings + + + + +Configuration object containing the following fields: + + + + + An epoch for which the reward occurs. If omitted, the previous epoch will be + used + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +The result field will be a JSON array with the following fields: + +- `epoch: ` - epoch for which reward occured +- `effectiveSlot: ` - the slot in which the rewards are effective +- `amount: ` - reward amount in lamports +- `postBalance: ` - post balance of the account in lamports +- `commission: ` - vote account commission when the reward was credited + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getInflationReward", + "params": [ + [ + "6dmNQ5jwLeLk5REvio1JcMshcbvkYMwy26sJ8pbkvStu", + "BGsqMegLpV6n6Ve146sSX2dTjUMj3M92HnU8BbNRMhF2" + ], + {"epoch": 2} + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": [ + { + "amount": 2500, + "effectiveSlot": 224, + "epoch": 2, + "postBalance": 499999442500 + }, + null + ], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getLargestAccounts.mdx b/docs/src/api/methods/_getLargestAccounts.mdx new file mode 100644 index 00000000000000..aef3e9f8202c57 --- /dev/null +++ b/docs/src/api/methods/_getLargestAccounts.mdx @@ -0,0 +1,150 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getLargestAccounts + +Returns the 20 largest accounts, by lamport balance (results may be cached up to two hours) + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + filter results by account type + + + + + + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to an array of `` containing: + +- `address: ` - base-58 encoded address of the account +- `lamports: ` - number of lamports in the account, as a u64 + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getLargestAccounts"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 54 + }, + "value": [ + { + "lamports": 999974, + "address": "99P8ZgtJYe1buSK8JXkvpLh8xPsCFuLYhz9hQFNw93WJ" + }, + { + "lamports": 42, + "address": "uPwWLo16MVehpyWqsLkK3Ka8nLowWvAHbBChqv2FZeL" + }, + { + "lamports": 42, + "address": "aYJCgU7REfu3XF8b3QhkqgqQvLizx8zxuLBHA25PzDS" + }, + { + "lamports": 42, + "address": "CTvHVtQ4gd4gUcw3bdVgZJJqApXE9nCbbbP4VTS5wE1D" + }, + { + "lamports": 20, + "address": "4fq3xJ6kfrh9RkJQsmVd5gNMvJbuSHfErywvEjNQDPxu" + }, + { + "lamports": 4, + "address": "AXJADheGVp9cruP8WYu46oNkRbeASngN5fPCMVGQqNHa" + }, + { + "lamports": 2, + "address": "8NT8yS6LiwNprgW4yM1jPPow7CwRUotddBVkrkWgYp24" + }, + { + "lamports": 1, + "address": "SysvarEpochSchedu1e111111111111111111111111" + }, + { + "lamports": 1, + "address": "11111111111111111111111111111111" + }, + { + "lamports": 1, + "address": "Stake11111111111111111111111111111111111111" + }, + { + "lamports": 1, + "address": "SysvarC1ock11111111111111111111111111111111" + }, + { + "lamports": 1, + "address": "StakeConfig11111111111111111111111111111111" + }, + { + "lamports": 1, + "address": "SysvarRent111111111111111111111111111111111" + }, + { + "lamports": 1, + "address": "Config1111111111111111111111111111111111111" + }, + { + "lamports": 1, + "address": "SysvarStakeHistory1111111111111111111111111" + }, + { + "lamports": 1, + "address": "SysvarRecentB1ockHashes11111111111111111111" + }, + { + "lamports": 1, + "address": "SysvarFees111111111111111111111111111111111" + }, + { + "lamports": 1, + "address": "Vote111111111111111111111111111111111111111" + } + ] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getLatestBlockhash.mdx b/docs/src/api/methods/_getLatestBlockhash.mdx new file mode 100644 index 00000000000000..85724a785c71ec --- /dev/null +++ b/docs/src/api/methods/_getLatestBlockhash.mdx @@ -0,0 +1,92 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getLatestBlockhash + +Returns the latest blockhash + +:::caution +NEW: This method is only available in solana-core v1.9 or newer. Please use +[getRecentBlockhash](#getrecentblockhash) for solana-core v1.8 +::: + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +`RpcResponse` - RpcResponse JSON object with `value` field set to a JSON object including: + +- `blockhash: ` - a Hash as base-58 encoded string +- `lastValidBlockHeight: ` - last [block height](../../terminology.md#block-height) at which the blockhash will be valid + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "id":1, + "jsonrpc":"2.0", + "method":"getLatestBlockhash", + "params":[ + { + "commitment":"processed" + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 2792 + }, + "value": { + "blockhash": "EkSnNWid2cvwEVnVx9aBqawnmiCNiDgp3gUdkDPTKN1N", + "lastValidBlockHeight": 3090 + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getLeaderSchedule.mdx b/docs/src/api/methods/_getLeaderSchedule.mdx new file mode 100644 index 00000000000000..ee6803cb0ed49f --- /dev/null +++ b/docs/src/api/methods/_getLeaderSchedule.mdx @@ -0,0 +1,96 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getLeaderSchedule + +Returns the leader schedule for an epoch + + + + +### Parameters: + + +Fetch the leader schedule for the epoch that corresponds to the provided slot. + +
  • If unspecified, the leader schedule for the current epoch is fetched
  • + +
    + + + +Configuration object containing the following fields: + + + + + Only return results for this validator identity (base-58 encoded) + + + + +### Result: + +Returns a result with one of the two following values: + +- `` - if requested epoch is not found, or +- `` - the result field will be a dictionary of validator identities, + as base-58 encoded strings, and their corresponding leader slot indices as values + (indices are relative to the first slot in the requested epoch) + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getLeaderSchedule", + "params": [ + null, + { + "identity": "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F" + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F": [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63 + ] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getMaxRetransmitSlot.mdx b/docs/src/api/methods/_getMaxRetransmitSlot.mdx new file mode 100644 index 00000000000000..17a104750e5cd6 --- /dev/null +++ b/docs/src/api/methods/_getMaxRetransmitSlot.mdx @@ -0,0 +1,48 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getMaxRetransmitSlot + +Get the max slot seen from retransmit stage. + + + + +### Parameters: + +**None** + +### Result: + +`` - Slot number + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getMaxRetransmitSlot"} +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 1234, "id": 1 } +``` + + + + diff --git a/docs/src/api/methods/_getMaxShredInsertSlot.mdx b/docs/src/api/methods/_getMaxShredInsertSlot.mdx new file mode 100644 index 00000000000000..d776870ed9cc2c --- /dev/null +++ b/docs/src/api/methods/_getMaxShredInsertSlot.mdx @@ -0,0 +1,48 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getMaxShredInsertSlot + +Get the max slot seen from after shred insert. + + + + +### Parameters: + +**None** + +### Result: + +`` - Slot number + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getMaxShredInsertSlot"} +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 1234, "id": 1 } +``` + + + + diff --git a/docs/src/api/methods/_getMinimumBalanceForRentExemption.mdx b/docs/src/api/methods/_getMinimumBalanceForRentExemption.mdx new file mode 100644 index 00000000000000..3f528284413647 --- /dev/null +++ b/docs/src/api/methods/_getMinimumBalanceForRentExemption.mdx @@ -0,0 +1,67 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getMinimumBalanceForRentExemption + +Returns minimum balance required to make account rent exempt. + + + + +### Parameters: + + + the Account's data length + + + + +Configuration object containing the following fields: + + + + + +### Result: + +`` - minimum lamports required in the Account to remain rent free + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getMinimumBalanceForRentExemption", + "params": [50] + } +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 500, "id": 1 } +``` + + + + diff --git a/docs/src/api/methods/_getMultipleAccounts.mdx b/docs/src/api/methods/_getMultipleAccounts.mdx new file mode 100644 index 00000000000000..207828fb4e5c39 --- /dev/null +++ b/docs/src/api/methods/_getMultipleAccounts.mdx @@ -0,0 +1,143 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getMultipleAccounts + +Returns the account information for a list of Pubkeys. + + + + +### Parameters: + + + An array of Pubkeys to query, as base-58 encoded strings (up to a maximum of + 100) + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +limit the returned account data using the provided `offset: ` and `length: ` fields; only available for "base58", "base64" or "base64+zstd" encodings. + + + + + +encoding format for the returned Account data + + + +
    + +- `base58` is slow and limited to less than 129 bytes of Account data. +- `base64` will return base64 encoded data for Account data of any size. +- `base64+zstd` compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) + and base64-encodes the result. +- [`jsonParsed` encoding](/api/http#parsed-responses) attempts to use program-specific state parsers to + return more human-readable and explicit account state data. +- If `jsonParsed` is requested but a parser cannot be found, the field falls back to `base64` + encoding, detectable when the `data` field is type ``. + +
    + +
    + +
    + +### Result: + +The result will be a JSON object with `value` equal to an array of: + +- `` - if the account at that Pubkey doesn't exist, or +- `` - a JSON object containing: + - `lamports: ` - number of lamports assigned to this account, as a u64 + - `owner: ` - base-58 encoded Pubkey of the program this account has been assigned to + - `data: <[string, encoding]|object>` - data associated with the account, either as encoded binary data or JSON format `{: }` - depending on encoding parameter + - `executable: ` - boolean indicating if the account contains a program \(and is strictly read-only\) + - `rentEpoch: ` - the epoch at which this account will next owe rent, as u64 + - `size: ` - the data size of the account + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getMultipleAccounts", + "params": [ + [ + "vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg", + "4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA" + ], + { + "encoding": "base58" + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1 + }, + "value": [ + { + "data": ["", "base64"], + "executable": false, + "lamports": 1000000000, + "owner": "11111111111111111111111111111111", + "rentEpoch": 2, + "space": 16 + }, + { + "data": ["", "base64"], + "executable": false, + "lamports": 5000000000, + "owner": "11111111111111111111111111111111", + "rentEpoch": 2, + "space": 0 + } + ] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getProgramAccounts.mdx b/docs/src/api/methods/_getProgramAccounts.mdx new file mode 100644 index 00000000000000..7b623849dd7a09 --- /dev/null +++ b/docs/src/api/methods/_getProgramAccounts.mdx @@ -0,0 +1,160 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getProgramAccounts + +Returns all accounts owned by the provided program Pubkey + + + + +### Parameters: + + + Pubkey of program, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + wrap the result in an RpcResponse JSON object + + + + +encoding format for the returned Account data + + + +
    + +- `base58` is slow and limited to less than 129 bytes of Account data. +- `base64` will return base64 encoded data for Account data of any size. +- `base64+zstd` compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and + base64-encodes the result. +- [`jsonParsed` encoding](/api/http#parsed-responses) attempts to use program-specific state + parsers to return more human-readable and explicit account state data. +- If `jsonParsed` is requested but a parser cannot be found, the field falls back + to `base64` encoding, detectable when the `data` field is type ``. + +
    + +
    + + +limit the returned account data using the provided `offset: usize` and `length: usize` fields; + +- only available for "base58", "base64" or "base64+zstd" encodings. + + + + + +filter results using up to 4 filter objects + +:::info +The resultant account(s) must meet **ALL** filter criteria to be included in the returned results +::: + + + +
    + +### Result: + +By default, the result field will be an array of JSON objects. + +:::info +If `withContext` flag is set the array will be wrapped in an RpcResponse JSON object. +::: + +The resultant response array will contain: + +- `pubkey: ` - the account Pubkey as base-58 encoded string +- `account: ` - a JSON object, with the following sub fields: + - `lamports: ` - number of lamports assigned to this account, as a u64 + - `owner: ` - base-58 encoded Pubkey of the program this account has been assigned to + - `data: <[string,encoding]|object>` - data associated with the account, either as encoded binary data or JSON format `{: }` - depending on encoding parameter + - `executable: ` - boolean indicating if the account contains a program \(and is strictly read-only\) + - `rentEpoch: ` - the epoch at which this account will next owe rent, as u64 + - `size: ` - the data size of the account + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getProgramAccounts", + "params": [ + "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", + { + "filters": [ + { + "dataSize": 17 + }, + { + "memcmp": { + "offset": 4, + "bytes": "3Mc6vR" + } + } + ] + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": [ + { + "account": { + "data": "2R9jLfiAQ9bgdcw6h8s44439", + "executable": false, + "lamports": 15298080, + "owner": "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", + "rentEpoch": 28, + "space": 42 + }, + "pubkey": "CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY" + } + ], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getRecentPerformanceSamples.mdx b/docs/src/api/methods/_getRecentPerformanceSamples.mdx new file mode 100644 index 00000000000000..131cc80ba794d9 --- /dev/null +++ b/docs/src/api/methods/_getRecentPerformanceSamples.mdx @@ -0,0 +1,103 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getRecentPerformanceSamples + +Returns a list of recent performance samples, in reverse slot order. Performance samples are taken every 60 seconds and +include the number of transactions and slots that occur in a given time window. + + + + +### Parameters: + + + +number of samples to return (maximum 720) + + + +### Result: + +An array of `RpcPerfSample` with the following fields: + +- `slot: ` - Slot in which sample was taken at +- `numTransactions: ` - Number of transactions in sample +- `numSlots: ` - Number of slots in sample +- `samplePeriodSecs: ` - Number of seconds in a sample window +- `numNonVoteTransaction: ` - Number of non-vote transactions in + sample. + +:::info +`numNonVoteTransaction` is present starting with v1.15. + +To get a number of voting transactions compute:
    +`numTransactions - numNonVoteTransaction` +::: + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc":"2.0", "id":1, + "method": "getRecentPerformanceSamples", + "params": [4]} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": [ + { + "numSlots": 126, + "numTransactions": 126, + "numNonVoteTransaction": 1, + "samplePeriodSecs": 60, + "slot": 348125 + }, + { + "numSlots": 126, + "numTransactions": 126, + "numNonVoteTransaction": 1, + "samplePeriodSecs": 60, + "slot": 347999 + }, + { + "numSlots": 125, + "numTransactions": 125, + "numNonVoteTransaction": 0, + "samplePeriodSecs": 60, + "slot": 347873 + }, + { + "numSlots": 125, + "numTransactions": 125, + "numNonVoteTransaction": 0, + "samplePeriodSecs": 60, + "slot": 347748 + } + ], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getRecentPrioritizationFees.mdx b/docs/src/api/methods/_getRecentPrioritizationFees.mdx new file mode 100644 index 00000000000000..7d7fcc791de6ea --- /dev/null +++ b/docs/src/api/methods/_getRecentPrioritizationFees.mdx @@ -0,0 +1,95 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getRecentPrioritizationFees + +Returns a list of prioritization fees from recent blocks. + +:::info +Currently, a node's prioritization-fee cache stores data from up to 150 blocks. +::: + + + + +### Parameters: + + + +An array of Account addresses (up to a maximum of 128 addresses), as base-58 encoded strings + +:::note +If this parameter is provided, the response will reflect a fee to land a transaction locking all of the provided accounts as writable. +::: + + + +### Result: + +An array of `RpcPrioritizationFee` with the following fields: + +- `slot: ` - slot in which the fee was observed +- `prioritizationFee: ` - the per-compute-unit fee paid by at least + one successfully landed transaction, specified in increments of 0.000001 lamports + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc":"2.0", "id":1, + "method": "getRecentPrioritizationFees", + "params": [ + ["CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY"] + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": [ + { + "slot": 348125, + "prioritizationFee": 0 + }, + { + "slot": 348126, + "prioritizationFee": 1000 + }, + { + "slot": 348127, + "prioritizationFee": 500 + }, + { + "slot": 348128, + "prioritizationFee": 0 + }, + { + "slot": 348129, + "prioritizationFee": 1234 + } + ], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getSignatureStatuses.mdx b/docs/src/api/methods/_getSignatureStatuses.mdx new file mode 100644 index 00000000000000..7c52c85dcdb8eb --- /dev/null +++ b/docs/src/api/methods/_getSignatureStatuses.mdx @@ -0,0 +1,114 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getSignatureStatuses + +Returns the statuses of a list of signatures. Each signature must be a [txid](/terminology#transaction-id), the first signature of a transaction. + +:::info +Unless the `searchTransactionHistory` configuration parameter is included, +this method only searches the recent status cache of signatures, which +retains statuses for all active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots. +::: + + + + +### Parameters: + + + An array of transaction signatures to confirm, as base-58 encoded strings (up + to a maximum of 256) + + + + +Configuration object containing the following fields: + + + +if `true` - a Solana node will search its ledger cache for any signatures not +found in the recent status cache + + + + + +### Result: + +An array of `RpcResponse` consisting of either: + +- `` - Unknown transaction, or +- `` + - `slot: ` - The slot the transaction was processed + - `confirmations: ` - Number of blocks since signature confirmation, null if rooted, as well as finalized by a supermajority of the cluster + - `err: ` - Error if transaction failed, null if transaction succeeded. + See [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) + - `confirmationStatus: ` - The transaction's cluster confirmation status; + Either `processed`, `confirmed`, or `finalized`. See [Commitment](/api/http#configuring-state-commitment) for more on optimistic confirmation. + - DEPRECATED: `status: ` - Transaction status + - `"Ok": ` - Transaction was successful + - `"Err": ` - Transaction failed with TransactionError + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getSignatureStatuses", + "params": [ + [ + "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW" + ], + { + "searchTransactionHistory": true + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 82 + }, + "value": [ + { + "slot": 48, + "confirmations": null, + "err": null, + "status": { + "Ok": null + }, + "confirmationStatus": "finalized" + }, + null + ] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getSignaturesForAddress.mdx b/docs/src/api/methods/_getSignaturesForAddress.mdx new file mode 100644 index 00000000000000..dc0517058d34c7 --- /dev/null +++ b/docs/src/api/methods/_getSignaturesForAddress.mdx @@ -0,0 +1,117 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getSignaturesForAddress + +Returns signatures for confirmed transactions that include the given address in +their `accountKeys` list. Returns signatures backwards in time from the +provided signature or most recent confirmed block + + + + +### Parameters: + + + Account address as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + maximum transaction signatures to return (between 1 and 1,000). + + + + start searching backwards from this transaction signature. If not provided the + search starts from the top of the highest max confirmed block. + + + + search until this transaction signature, if found before limit reached + + + + +### Result: + +An array of ``, ordered from **newest** to **oldest** transaction, containing transaction +signature information with the following fields: + +- `signature: ` - transaction signature as base-58 encoded string +- `slot: ` - The slot that contains the block with the transaction +- `err: ` - Error if transaction failed, null if transaction succeeded. + See [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) + for more info. +- `memo: ` - Memo associated with the transaction, null if no memo is present +- `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch) + of when transaction was processed. null if not available. +- `confirmationStatus: ` - The transaction's cluster confirmation status; + Either `processed`, `confirmed`, or `finalized`. See [Commitment](/api/http#configuring-state-commitment) + for more on optimistic confirmation. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getSignaturesForAddress", + "params": [ + "Vote111111111111111111111111111111111111111", + { + "limit": 1 + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": [ + { + "err": null, + "memo": null, + "signature": "5h6xBEauJ3PK6SWCZ1PGjBvj8vDdWG3KpwATGy1ARAXFSDwt8GFXM7W5Ncn16wmqokgpiKRLuS83KUxyZyv2sUYv", + "slot": 114, + "blockTime": null + } + ], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getSlot.mdx b/docs/src/api/methods/_getSlot.mdx new file mode 100644 index 00000000000000..8693f8f3bb038e --- /dev/null +++ b/docs/src/api/methods/_getSlot.mdx @@ -0,0 +1,63 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getSlot + +Returns the slot that has reached the [given or default commitment level](/api/http#configuring-state-commitment) + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +`` - Current slot + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getSlot"} +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 1234, "id": 1 } +``` + + + + diff --git a/docs/src/api/methods/_getSlotLeader.mdx b/docs/src/api/methods/_getSlotLeader.mdx new file mode 100644 index 00000000000000..7f8550ee1b8bab --- /dev/null +++ b/docs/src/api/methods/_getSlotLeader.mdx @@ -0,0 +1,67 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getSlotLeader + +Returns the current slot leader + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +`` - Node identity Pubkey as base-58 encoded string + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getSlotLeader"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": "ENvAW7JScgYq6o4zKZwewtkzzJgDzuJAFxYasvmEQdpS", + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getSlotLeaders.mdx b/docs/src/api/methods/_getSlotLeaders.mdx new file mode 100644 index 00000000000000..386a74fb44de94 --- /dev/null +++ b/docs/src/api/methods/_getSlotLeaders.mdx @@ -0,0 +1,77 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getSlotLeaders + +Returns the slot leaders for a given slot range + + + + +### Parameters: + + + Start slot, as u64 integer + + + + Limit, as u64 integer (between 1 and 5,000) + + +### Result: + +`` - array of Node identity public keys as base-58 encoded strings + + + + + +### Code sample: + +If the current slot is `#99` - query the next `10` leaders with the following request: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc":"2.0", "id": 1, + "method": "getSlotLeaders", + "params": [100, 10] + } +' +``` + +### Response: + +The first leader returned is the leader for slot `#100`: + +```json +{ + "jsonrpc": "2.0", + "result": [ + "ChorusmmK7i1AxXeiTtQgQZhQNiXYU84ULeaYF1EH15n", + "ChorusmmK7i1AxXeiTtQgQZhQNiXYU84ULeaYF1EH15n", + "ChorusmmK7i1AxXeiTtQgQZhQNiXYU84ULeaYF1EH15n", + "ChorusmmK7i1AxXeiTtQgQZhQNiXYU84ULeaYF1EH15n", + "Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM", + "Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM", + "Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM", + "Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM", + "DWvDTSh3qfn88UoQTEKRV2JnLt5jtJAVoiCo3ivtMwXP", + "DWvDTSh3qfn88UoQTEKRV2JnLt5jtJAVoiCo3ivtMwXP" + ], + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getStakeActivation.mdx b/docs/src/api/methods/_getStakeActivation.mdx new file mode 100644 index 00000000000000..abd1d0bafb5966 --- /dev/null +++ b/docs/src/api/methods/_getStakeActivation.mdx @@ -0,0 +1,95 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getStakeActivation + +Returns epoch activation information for a stake account + + + + +### Parameters: + + + Pubkey of stake Account to query, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + epoch for which to calculate activation details. If parameter not provided, + defaults to current epoch. + + + + +### Result: + +The result will be a JSON object with the following fields: + +- `state: ` - the stake account's activation state, + either: `active`, `inactive`, `activating`, or `deactivating` +- `active: ` - stake active during the epoch +- `inactive: ` - stake inactive during the epoch + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getStakeActivation", + "params": [ + "CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT", + { + "epoch": 4 + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "active": 124429280, + "inactive": 73287840, + "state": "activating" + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getStakeMinimumDelegation.mdx b/docs/src/api/methods/_getStakeMinimumDelegation.mdx new file mode 100644 index 00000000000000..94e01ac87052e9 --- /dev/null +++ b/docs/src/api/methods/_getStakeMinimumDelegation.mdx @@ -0,0 +1,73 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getStakeMinimumDelegation + +Returns the stake minimum delegation, in lamports. + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to: + +- `` - The stake minimum delegation, in lamports + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc":"2.0", "id":1, + "method": "getStakeMinimumDelegation" + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 501 + }, + "value": 1000000000 + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getSupply.mdx b/docs/src/api/methods/_getSupply.mdx new file mode 100644 index 00000000000000..a1d8915a1b87d1 --- /dev/null +++ b/docs/src/api/methods/_getSupply.mdx @@ -0,0 +1,87 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getSupply + +Returns information about the current supply. + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + exclude non circulating accounts list from response + + + + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to a JSON object containing: + +- `total: ` - Total supply in lamports +- `circulating: ` - Circulating supply in lamports +- `nonCirculating: ` - Non-circulating supply in lamports +- `nonCirculatingAccounts: ` - an array of account addresses of non-circulating accounts, as strings. If `excludeNonCirculatingAccountsList` is enabled, the returned array will be empty. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0", "id":1, "method":"getSupply"} +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1114 + }, + "value": { + "circulating": 16000, + "nonCirculating": 1000000, + "nonCirculatingAccounts": [ + "FEy8pTbP5fEoqMV1GdTz83byuA8EKByqYat1PKDgVAq5", + "9huDUZfxoJ7wGMTffUE7vh1xePqef7gyrLJu9NApncqA", + "3mi1GmwEE3zo2jmfDuzvjSX9ovRXsDUKHvsntpkhuLJ9", + "BYxEJTDerkaRWBem3XgnVcdhppktBXa2HbkHPKj2Ui4Z" + ], + "total": 1016000 + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getTokenAccountBalance.mdx b/docs/src/api/methods/_getTokenAccountBalance.mdx new file mode 100644 index 00000000000000..e0cb785f7d6038 --- /dev/null +++ b/docs/src/api/methods/_getTokenAccountBalance.mdx @@ -0,0 +1,91 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getTokenAccountBalance + +Returns the token balance of an SPL Token account. + + + + +### Parameters: + + + Pubkey of Token account to query, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to a JSON object containing: + +- `amount: ` - the raw balance without decimals, a string representation of u64 +- `decimals: ` - number of base 10 digits to the right of the decimal place +- `uiAmount: ` - the balance, using mint-prescribed decimals **DEPRECATED** +- `uiAmountString: ` - the balance as a string, using mint-prescribed decimals + +For more details on returned data, the [Token Balances Structure](#token-balances-structure) +response from [getBlock](#getblock) follows a similar structure. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getTokenAccountBalance", + "params": [ + "7fUAJdStEuGbc3sM84cKRL6yYaaSstyLSU4ve5oovLS7" + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1114 + }, + "value": { + "amount": "9864", + "decimals": 2, + "uiAmount": 98.64, + "uiAmountString": "98.64" + }, + "id": 1 + } +} +``` + + + + diff --git a/docs/src/api/methods/_getTokenAccountsByDelegate.mdx b/docs/src/api/methods/_getTokenAccountsByDelegate.mdx new file mode 100644 index 00000000000000..be47c1305eeaa4 --- /dev/null +++ b/docs/src/api/methods/_getTokenAccountsByDelegate.mdx @@ -0,0 +1,177 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getTokenAccountsByDelegate + +Returns all SPL Token accounts by approved Delegate. + + + + +### Parameters: + + + Pubkey of account delegate to query, as base-58 encoded string + + + + +A JSON object with one of the following fields: + +- `mint: ` - Pubkey of the specific token Mint to limit accounts to, as base-58 encoded string; or +- `programId: ` - Pubkey of the Token program that owns the accounts, as base-58 encoded string + + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +limit the returned account data using the provided `offset: ` +and `length: ` fields; only available for `base58`, +`base64` or `base64+zstd` encodings. + + + + + +Encoding format for Account data + + + +
    + +- `base58` is slow and limited to less than 129 bytes of Account data. +- `base64` will return base64 encoded data for Account data of any size. +- `base64+zstd` compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) + and base64-encodes the result. +- `jsonParsed` encoding attempts to use program-specific state parsers to return + more human-readable and explicit account state data. +- If `jsonParsed` is requested but a parser cannot be found, the field falls + back to `base64` encoding, detectable when the `data` field is type `string`. + +
    + +
    + +
    + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to an array of JSON objects, which will contain: + +- `pubkey: ` - the account Pubkey as base-58 encoded string +- `account: ` - a JSON object, with the following sub fields: + - `lamports: ` - number of lamports assigned to this account, as a u64 + - `owner: ` - base-58 encoded Pubkey of the program this account has been assigned to + - `data: ` - Token state data associated with the account, either as encoded binary data or in JSON format `{: }` + - `executable: ` - boolean indicating if the account contains a program (and is strictly read-only\) + - `rentEpoch: ` - the epoch at which this account will next owe rent, as u64 + - `size: ` - the data size of the account + +When the data is requested with the `jsonParsed` encoding a format similar to that of the +[Token Balances Structure](#token-balances-structure) can be expected inside the structure, +both for the `tokenAmount` and the `delegatedAmount` - with the latter being an optional object. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getTokenAccountsByDelegate", + "params": [ + "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", + { + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "encoding": "jsonParsed" + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1114 + }, + "value": [ + { + "account": { + "data": { + "program": "spl-token", + "parsed": { + "info": { + "tokenAmount": { + "amount": "1", + "decimals": 1, + "uiAmount": 0.1, + "uiAmountString": "0.1" + }, + "delegate": "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", + "delegatedAmount": { + "amount": "1", + "decimals": 1, + "uiAmount": 0.1, + "uiAmountString": "0.1" + }, + "state": "initialized", + "isNative": false, + "mint": "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E", + "owner": "CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD" + }, + "type": "account" + }, + "space": 165 + }, + "executable": false, + "lamports": 1726080, + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "rentEpoch": 4, + "space": 165 + }, + "pubkey": "28YTZEwqtMHWrhWcvv34se7pjS7wctgqzCPB3gReCFKp" + } + ] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getTokenAccountsByOwner.mdx b/docs/src/api/methods/_getTokenAccountsByOwner.mdx new file mode 100644 index 00000000000000..06a7e5fe4bbd80 --- /dev/null +++ b/docs/src/api/methods/_getTokenAccountsByOwner.mdx @@ -0,0 +1,176 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getTokenAccountsByOwner + +Returns all SPL Token accounts by token owner. + + + + +### Parameters: + + + Pubkey of account delegate to query, as base-58 encoded string + + + + +A JSON object with one of the following fields: + +- `mint: ` - Pubkey of the specific token Mint to limit accounts to, as base-58 encoded string; or +- `programId: ` - Pubkey of the Token program that owns the accounts, as base-58 encoded string + + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +limit the returned account data using the provided `offset: ` +and `length: ` fields; only available for +`base58`, `base64`, or `base64+zstd` encodings. + + + + + +Encoding format for Account data + + + +
    + +- `base58` is slow and limited to less than 129 bytes of Account data. +- `base64` will return base64 encoded data for Account data of any size. +- `base64+zstd` compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) + and base64-encodes the result. +- `jsonParsed` encoding attempts to use program-specific state parsers to return + more human-readable and explicit account state data. +- If `jsonParsed` is requested but a parser cannot be found, the field falls + back to `base64` encoding, detectable when the `data` field is type `string`. + +
    + +
    + +
    + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to an array of JSON objects, which will contain: + +- `pubkey: ` - the account Pubkey as base-58 encoded string +- `account: ` - a JSON object, with the following sub fields: + - `lamports: ` - number of lamports assigned to this account, as a u64 + - `owner: ` - base-58 encoded Pubkey of the program this account has been assigned to + - `data: ` - Token state data associated with the account, either as encoded binary data or in JSON format `{: }` + - `executable: ` - boolean indicating if the account contains a program \(and is strictly read-only\) + - `rentEpoch: ` - the epoch at which this account will next owe rent, as u64 + - `size: ` - the data size of the account + +When the data is requested with the `jsonParsed` encoding a format similar to that of the [Token Balances Structure](/api/http#token-balances-structure) can be expected inside the structure, both for the `tokenAmount` and the `delegatedAmount` - with the latter being an optional object. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getTokenAccountsByOwner", + "params": [ + "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F", + { + "mint": "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E" + }, + { + "encoding": "jsonParsed" + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1114 + }, + "value": [ + { + "account": { + "data": { + "program": "spl-token", + "parsed": { + "accountType": "account", + "info": { + "tokenAmount": { + "amount": "1", + "decimals": 1, + "uiAmount": 0.1, + "uiAmountString": "0.1" + }, + "delegate": "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", + "delegatedAmount": { + "amount": "1", + "decimals": 1, + "uiAmount": 0.1, + "uiAmountString": "0.1" + }, + "state": "initialized", + "isNative": false, + "mint": "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E", + "owner": "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F" + }, + "type": "account" + }, + "space": 165 + }, + "executable": false, + "lamports": 1726080, + "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "rentEpoch": 4, + "space": 165 + }, + "pubkey": "C2gJg6tKpQs41PRS1nC8aw3ZKNZK3HQQZGVrDFDup5nx" + } + ] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getTokenLargestAccounts.mdx b/docs/src/api/methods/_getTokenLargestAccounts.mdx new file mode 100644 index 00000000000000..387f00ad2e1bd4 --- /dev/null +++ b/docs/src/api/methods/_getTokenLargestAccounts.mdx @@ -0,0 +1,99 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getTokenLargestAccounts + +Returns the 20 largest accounts of a particular SPL Token type. + + + + +### Parameters: + + + Pubkey of the token Mint to query, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to an array of JSON objects containing: + +- `address: ` - the address of the token account +- `amount: ` - the raw token account balance without decimals, a string representation of u64 +- `decimals: ` - number of base 10 digits to the right of the decimal place +- `uiAmount: ` - the token account balance, using mint-prescribed decimals **DEPRECATED** +- `uiAmountString: ` - the token account balance as a string, using mint-prescribed decimals + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getTokenLargestAccounts", + "params": [ + "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E" + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1114 + }, + "value": [ + { + "address": "FYjHNoFtSQ5uijKrZFyYAxvEr87hsKXkXcxkcmkBAf4r", + "amount": "771", + "decimals": 2, + "uiAmount": 7.71, + "uiAmountString": "7.71" + }, + { + "address": "BnsywxTcaYeNUtzrPxQUvzAWxfzZe3ZLUJ4wMMuLESnu", + "amount": "229", + "decimals": 2, + "uiAmount": 2.29, + "uiAmountString": "2.29" + } + ] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getTokenSupply.mdx b/docs/src/api/methods/_getTokenSupply.mdx new file mode 100644 index 00000000000000..af42feee0683ac --- /dev/null +++ b/docs/src/api/methods/_getTokenSupply.mdx @@ -0,0 +1,88 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getTokenSupply + +Returns the total supply of an SPL Token type. + + + + +### Parameters: + + + Pubkey of the token Mint to query, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + +### Result: + +The result will be an RpcResponse JSON object with `value` equal to a JSON object containing: + +- `amount: ` - the raw total token supply without decimals, a string representation of u64 +- `decimals: ` - number of base 10 digits to the right of the decimal place +- `uiAmount: ` - the total token supply, using mint-prescribed decimals **DEPRECATED** +- `uiAmountString: ` - the total token supply as a string, using mint-prescribed decimals + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getTokenSupply", + "params": [ + "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E" + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 1114 + }, + "value": { + "amount": "100000", + "decimals": 2, + "uiAmount": 1000, + "uiAmountString": "1000" + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getTransaction.mdx b/docs/src/api/methods/_getTransaction.mdx new file mode 100644 index 00000000000000..8a70a3219b1414 --- /dev/null +++ b/docs/src/api/methods/_getTransaction.mdx @@ -0,0 +1,172 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getTransaction + +Returns transaction details for a confirmed transaction + + + + +### Parameters: + + + Transaction signature, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + Set the max transaction version to return in responses. If the requested + transaction is a higher version, an error will be returned. If this parameter + is omitted, only legacy transactions will be returned, and any versioned + transaction will prompt the error. + + + + +Encoding for the returned Transaction + + + +
    + +- `jsonParsed` encoding attempts to use program-specific state parsers to return + more human-readable and explicit data in the `transaction.message.instructions` list. +- If `jsonParsed` is requested but a parser cannot be found, the instruction + falls back to regular JSON encoding (`accounts`, `data`, and `programIdIndex` fields). + +
    + +
    + +
    + +### Result: + +- `` - if transaction is not found or not confirmed +- `` - if transaction is confirmed, an object with the following fields: + - `slot: ` - the slot this transaction was processed in + - `transaction: ` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter + - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch) of when the transaction was processed. null if not available + - `meta: ` - transaction status metadata object: + - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://docs.rs/solana-sdk/VERSION_FOR_DOCS_RS/solana_sdk/transaction/enum.TransactionError.html) + - `fee: ` - fee this transaction was charged, as u64 integer + - `preBalances: ` - array of u64 account balances from before the transaction was processed + - `postBalances: ` - array of u64 account balances after the transaction was processed + - `innerInstructions: ` - List of [inner instructions](#inner-instructions-structure) or `null` if inner instruction recording was not enabled during this transaction + - `preTokenBalances: ` - List of [token balances](#token-balances-structure) from before the transaction was processed or omitted if token balance recording was not yet enabled during this transaction + - `postTokenBalances: ` - List of [token balances](#token-balances-structure) from after the transaction was processed or omitted if token balance recording was not yet enabled during this transaction + - `logMessages: ` - array of string log messages or `null` if log message recording was not enabled during this transaction + - DEPRECATED: `status: ` - Transaction status + - `"Ok": ` - Transaction was successful + - `"Err": ` - Transaction failed with TransactionError + - `rewards: ` - transaction-level rewards, populated if rewards are requested; an array of JSON objects containing: + - `pubkey: ` - The public key, as base-58 encoded string, of the account that received the reward + - `lamports: `- number of reward lamports credited or debited by the account, as a i64 + - `postBalance: ` - account balance in lamports after the reward was applied + - `rewardType: ` - type of reward: currently only "rent", other types may be added in the future + - `commission: ` - vote account commission when the reward was credited, only present for voting and staking rewards + - `loadedAddresses: ` - Transaction addresses loaded from address lookup tables. Undefined if `maxSupportedTransactionVersion` is not set in request params, or if `jsonParsed` encoding is set in request params. + - `writable: ` - Ordered list of base-58 encoded addresses for writable loaded accounts + - `readonly: ` - Ordered list of base-58 encoded addresses for readonly loaded accounts + - `returnData: ` - the most-recent return data generated by an instruction in the transaction, with the following fields: + - `programId: ` - the program that generated the return data, as base-58 encoded Pubkey + - `data: <[string, encoding]>` - the return data itself, as base-64 encoded binary data + - `computeUnitsConsumed: ` - number of [compute units](developing/programming-model/runtime.md#compute-budget) consumed by the transaction + - `version: <"legacy"|number|undefined>` - Transaction version. Undefined if `maxSupportedTransactionVersion` is not set in request params. + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getTransaction", + "params": [ + "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv", + "json" + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "meta": { + "err": null, + "fee": 5000, + "innerInstructions": [], + "postBalances": [499998932500, 26858640, 1, 1, 1], + "postTokenBalances": [], + "preBalances": [499998937500, 26858640, 1, 1, 1], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "slot": 430, + "transaction": { + "message": { + "accountKeys": [ + "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe", + "AjozzgE83A3x1sHNUR64hfH7zaEBWeMaFuAN9kQgujrc", + "SysvarS1otHashes111111111111111111111111111", + "SysvarC1ock11111111111111111111111111111111", + "Vote111111111111111111111111111111111111111" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 3, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [1, 2, 3, 0], + "data": "37u9WtQpcm6ULa3WRQHmj49EPs4if7o9f1jSRVZpm2dvihR9C8jY4NqEwXUbLwx15HBSNcP1", + "programIdIndex": 4 + } + ], + "recentBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B" + }, + "signatures": [ + "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv" + ] + } + }, + "blockTime": null, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_getTransactionCount.mdx b/docs/src/api/methods/_getTransactionCount.mdx new file mode 100644 index 00000000000000..3a966a1f66dbf2 --- /dev/null +++ b/docs/src/api/methods/_getTransactionCount.mdx @@ -0,0 +1,63 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getTransactionCount + +Returns the current Transaction count from the ledger + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +`` - the current Transaction count from the ledger + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getTransactionCount"} +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 268, "id": 1 } +``` + + + + diff --git a/docs/src/api/methods/_getVersion.mdx b/docs/src/api/methods/_getVersion.mdx new file mode 100644 index 00000000000000..0ed4d94020fd2a --- /dev/null +++ b/docs/src/api/methods/_getVersion.mdx @@ -0,0 +1,51 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getVersion + +Returns the current Solana version running on the node + + + + +### Parameters: + +**None** + +### Result: + +The result field will be a JSON object with the following fields: + +- `solana-core` - software version of solana-core +- `feature-set` - unique identifier of the current software's feature set + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getVersion"} +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": { "solana-core": "1.15.0" }, "id": 1 } +``` + + + + diff --git a/docs/src/api/methods/_getVoteAccounts.mdx b/docs/src/api/methods/_getVoteAccounts.mdx new file mode 100644 index 00000000000000..04a57a3aa42207 --- /dev/null +++ b/docs/src/api/methods/_getVoteAccounts.mdx @@ -0,0 +1,114 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## getVoteAccounts + +Returns the account info and associated stake for all the voting accounts in the current bank. + + + + +### Parameters: + + + +Configuration object containing the following fields: + + + + + Only return results for this validator vote address (base-58 encoded) + + + + Do not filter out delinquent validators with no stake + + + + Specify the number of slots behind the tip that a validator must fall to be + considered delinquent. **NOTE:** For the sake of consistency between ecosystem + products, _it is **not** recommended that this argument be specified._ + + + + +### Result: + +The result field will be a JSON object of `current` and `delinquent` accounts, +each containing an array of JSON objects with the following sub fields: + +- `votePubkey: ` - Vote account address, as base-58 encoded string +- `nodePubkey: ` - Validator identity, as base-58 encoded string +- `activatedStake: ` - the stake, in lamports, delegated to this vote account and active in this epoch +- `epochVoteAccount: ` - bool, whether the vote account is staked for this epoch +- `commission: ` - percentage (0-100) of rewards payout owed to the vote account +- `lastVote: ` - Most recent slot voted on by this vote account +- `epochCredits: ` - Latest history of earned credits for up to five epochs, as an array of arrays containing: `[epoch, credits, previousCredits]`. +- `rootSlot: ` - Current root slot for this vote account + + + + + +### Code sample: + +Restrict results to a single validator vote account: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "getVoteAccounts", + "params": [ + { + "votePubkey": "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw" + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "current": [ + { + "commission": 0, + "epochVoteAccount": true, + "epochCredits": [ + [1, 64, 0], + [2, 192, 64] + ], + "nodePubkey": "B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD", + "lastVote": 147, + "activatedStake": 42, + "votePubkey": "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw" + } + ], + "delinquent": [] + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_isBlockhashValid.mdx b/docs/src/api/methods/_isBlockhashValid.mdx new file mode 100644 index 00000000000000..d8903dc0ee44eb --- /dev/null +++ b/docs/src/api/methods/_isBlockhashValid.mdx @@ -0,0 +1,89 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## isBlockhashValid + +Returns whether a blockhash is still valid or not + +:::caution +NEW: This method is only available in solana-core v1.9 or newer. Please use +[getFeeCalculatorForBlockhash](#getfeecalculatorforblockhash) for solana-core v1.8 +::: + + + + +### Parameters: + + + the blockhash of the block to evauluate, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + The minimum slot that the request can be evaluated at + + + + +### Result: + +`` - `true` if the blockhash is still valid + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "id":45, + "jsonrpc":"2.0", + "method":"isBlockhashValid", + "params":[ + "J7rBdM6AecPDEZp8aPq5iPSNKVkU5Q76F3oAV4eW5wsW", + {"commitment":"processed"} + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 2483 + }, + "value": false + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_minimumLedgerSlot.mdx b/docs/src/api/methods/_minimumLedgerSlot.mdx new file mode 100644 index 00000000000000..1ac63315d972ed --- /dev/null +++ b/docs/src/api/methods/_minimumLedgerSlot.mdx @@ -0,0 +1,52 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## minimumLedgerSlot + +Returns the lowest slot that the node has information about in its ledger. + +:::info +This value may increase over time if the node is configured to purge older ledger data +::: + + + + +### Parameters: + +**None** + +### Result: + +`u64` - Minimum ledger slot number + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"minimumLedgerSlot"} +' +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 1234, "id": 1 } +``` + + + + diff --git a/docs/src/api/methods/_requestAirdrop.mdx b/docs/src/api/methods/_requestAirdrop.mdx new file mode 100644 index 00000000000000..7a9cf3527f919d --- /dev/null +++ b/docs/src/api/methods/_requestAirdrop.mdx @@ -0,0 +1,78 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## requestAirdrop + +Requests an airdrop of lamports to a Pubkey + + + + +### Parameters: + + + Pubkey of account to receive lamports, as a base-58 encoded string + + + + lamports to airdrop, as a "u64" + + + + +Configuration object containing the following fields: + + + + + +### Result: + +`` - Transaction Signature of the airdrop, as a base-58 encoded string + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "requestAirdrop", + "params": [ + "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri", + 1000000000 + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_sendTransaction.mdx b/docs/src/api/methods/_sendTransaction.mdx new file mode 100644 index 00000000000000..fc9978aaeea0c6 --- /dev/null +++ b/docs/src/api/methods/_sendTransaction.mdx @@ -0,0 +1,124 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## sendTransaction + +Submits a signed transaction to the cluster for processing. + +This method does not alter the transaction in any way; it relays the +transaction created by clients to the node as-is. + +If the node's rpc service receives the transaction, this method immediately +succeeds, without waiting for any confirmations. A successful response from +this method does not guarantee the transaction is processed or confirmed by the +cluster. + +While the rpc service will reasonably retry to submit it, the transaction +could be rejected if transaction's `recent_blockhash` expires before it lands. + +Use [`getSignatureStatuses`](#getsignaturestatuses) to ensure a transaction is processed and confirmed. + +Before submitting, the following preflight checks are performed: + +1. The transaction signatures are verified +2. The transaction is simulated against the bank slot specified by the preflight + commitment. On failure an error will be returned. Preflight checks may be + disabled if desired. It is recommended to specify the same commitment and + preflight commitment to avoid confusing behavior. + +The returned signature is the first signature in the transaction, which +is used to identify the transaction ([transaction id](../../terminology.md#transaction-id)). +This identifier can be easily extracted from the transaction data before +submission. + + + + +### Parameters: + + + Fully-signed Transaction, as encoded string. + + + + +Configuration object containing the following optional fields: + + + +Encoding used for the transaction data. + +Values: `base58` (_slow_, **DEPRECATED**), or `base64`. + + + + + if "true", skip the preflight transaction checks + + + + Commitment level to use for preflight. + + + + Maximum number of times for the RPC node to retry sending the transaction to + the leader. If this parameter not provided, the RPC node will retry the + transaction until it is finalized or until the blockhash expires. + + + + set the minimum slot at which to perform preflight transaction checks + + + + +### Result: + +`` - First Transaction Signature embedded in the transaction, as base-58 encoded string ([transaction id](../../terminology.md#transaction-id)) + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "sendTransaction", + "params": [ + "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT" + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": "2id3YC2jK9G5Wo2phDx4gJVAew8DcY5NAojnVuao8rkxwPYPe8cSwE5GzhEgJA2y8fVjDEo6iR6ykBvDxrTQrtpb", + "id": 1 +} +``` + + + + diff --git a/docs/src/api/methods/_simulateTransaction.mdx b/docs/src/api/methods/_simulateTransaction.mdx new file mode 100644 index 00000000000000..fbae901244a97b --- /dev/null +++ b/docs/src/api/methods/_simulateTransaction.mdx @@ -0,0 +1,174 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## simulateTransaction + +Simulate sending a transaction + + + + +### Parameters: + + + +Transaction, as an encoded string. + +:::note +The transaction must have a valid blockhash, but is not required to be signed. +::: + + + + + +Configuration object containing the following fields: + + + Commitment level to simulate the transaction at + + + + if `true` the transaction signatures will be verified (conflicts with + `replaceRecentBlockhash`) + + + + if `true` the transaction recent blockhash will be replaced with the most + recent blockhash. (conflicts with `sigVerify`) + + + + the minimum slot that the request can be evaluated at + + + + +Encoding used for the transaction data. + +Values: `base58` (_slow_, **DEPRECATED**), or `base64`. + + + + + +Accounts configuration object containing the following fields: + + + An `array` of accounts to return, as base-58 encoded strings + + + + +encoding for returned Account data + + + +
    + +- `jsonParsed` encoding attempts to use program-specific state + parsers to return more human-readable and explicit account state data. +- If `jsonParsed` is requested but a parser cannot be found, the field falls + back to binary encoding, detectable when the `data` field is type `string`. + +
    + +
    + +
    + +
    + +### Result: + +The result will be an RpcResponse JSON object with `value` set to a JSON object with the following fields: + +- `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) +- `logs: ` - Array of log messages the transaction instructions output during execution, null if simulation failed before the transaction was able to execute (for example due to an invalid blockhash or signature verification failure) +- `accounts: ` - array of accounts with the same length as the `accounts.addresses` array in the request + - `` - if the account doesn't exist or if `err` is not null + - `` - otherwise, a JSON object containing: + - `lamports: ` - number of lamports assigned to this account, as a u64 + - `owner: ` - base-58 encoded Pubkey of the program this account has been assigned to + - `data: <[string, encoding]|object>` - data associated with the account, either as encoded binary data or JSON format `{: }` - depending on encoding parameter + - `executable: ` - boolean indicating if the account contains a program \(and is strictly read-only\) + - `rentEpoch: ` - the epoch at which this account will next owe rent, as u64 +- `unitsConsumed: ` - The number of compute budget units consumed during the processing of this transaction +- `returnData: ` - the most-recent return data generated by an instruction in the transaction, with the following fields: + - `programId: ` - the program that generated the return data, as base-58 encoded Pubkey + - `data: <[string, encoding]>` - the return data itself, as base-64 encoded binary data + + + + + +### Code sample: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", + "id": 1, + "method": "simulateTransaction", + "params": [ + "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEDArczbMia1tLmq7zz4DinMNN0pJ1JtLdqIJPUw3YrGCzYAMHBsgN27lcgB6H2WQvFgyZuJYHa46puOQo9yQ8CVQbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCp20C7Wj2aiuk5TReAXo+VTVg8QTHjs0UjNMMKCvpzZ+ABAgEBARU=", + { + "encoding":"base64", + } + ] + } +' +``` + +### Response: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 218 + }, + "value": { + "err": null, + "accounts": null, + "logs": [ + "Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri invoke [1]", + "Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri consumed 2366 of 1400000 compute units", + "Program return: 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri KgAAAAAAAAA=", + "Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri success" + ], + "returnData": { + "data": ["Kg==", "base64"], + "programId": "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri" + }, + "unitsConsumed": 2366 + } + }, + "id": 1 +} +``` + + + + diff --git a/docs/src/api/websocket.md b/docs/src/api/websocket.md new file mode 100644 index 00000000000000..d409072f462901 --- /dev/null +++ b/docs/src/api/websocket.md @@ -0,0 +1,91 @@ +--- +title: RPC Websocket API +displayed_sidebar: apiWebsocketMethodsSidebar +hide_table_of_contents: true +--- + +After connecting to the RPC PubSub websocket at `ws://
    /`: + +- Submit subscription requests to the websocket using the methods below +- Multiple subscriptions may be active at once +- Many subscriptions take the optional [`commitment` parameter](/api/http#configuring-state-commitment), defining how finalized a change should be to trigger a notification. For subscriptions, if commitment is unspecified, the default value is `finalized`. + +## RPC PubSub WebSocket Endpoint + +**Default port:** 8900 e.g. ws://localhost:8900, [http://192.168.1.88:8900](http://192.168.1.88:8900) + +## Methods + +The following methods are supported in the RPC Websocket API: + +import AccountSubscribe from "./websocket/\_accountSubscribe.mdx" + + + +import AccountUnsubscribe from "./websocket/\_accountUnsubscribe.mdx" + + + +import BlockSubscribe from "./websocket/\_blockSubscribe.mdx" + + + +import BlockUnsubscribe from "./websocket/\_blockUnsubscribe.mdx" + + + +import LogsSubscribe from "./websocket/\_logsSubscribe.mdx" + + + +import LogsUnsubscribe from "./websocket/\_logsUnsubscribe.mdx" + + + +import ProgramSubscribe from "./websocket/\_programSubscribe.mdx" + + + +import ProgramUnsubscribe from "./websocket/\_programUnsubscribe.mdx" + + + +import SignatureSubscribe from "./websocket/\_signatureSubscribe.mdx" + + + +import SignatureUnsubscribe from "./websocket/\_signatureUnsubscribe.mdx" + + + +import SlotSubscribe from "./websocket/\_slotSubscribe.mdx" + + + +import SlotUnsubscribe from "./websocket/\_slotUnsubscribe.mdx" + + + +import SlotsUpdatesSubscribe from "./websocket/\_slotsUpdatesSubscribe.mdx" + + + +import SlotsUpdatesUnsubscribe from "./websocket/\_slotsUpdatesUnsubscribe.mdx" + + + +import RootSubscribe from "./websocket/\_rootSubscribe.mdx" + + + +import RootUnsubscribe from "./websocket/\_rootUnsubscribe.mdx" + + + +import VoteSubscribe from "./websocket/\_voteSubscribe.mdx" + + + +import VoteUnsubscribe from "./websocket/\_voteUnsubscribe.mdx" + + diff --git a/docs/src/api/websocket/_accountSubscribe.mdx b/docs/src/api/websocket/_accountSubscribe.mdx new file mode 100644 index 00000000000000..f86e214a64aa92 --- /dev/null +++ b/docs/src/api/websocket/_accountSubscribe.mdx @@ -0,0 +1,160 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## accountSubscribe + +Subscribe to an account to receive notifications when the lamports or data for a given account public key changes + + + + +### Parameters: + + + Account Pubkey, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + +Encoding format for Account data + + + +
    + +- `base58` is slow. +- `jsonParsed` encoding attempts to use program-specific state parsers to return more + human-readable and explicit account state data +- If `jsonParsed` is requested but a parser cannot be found, the field falls back to + binary encoding, detectable when the `data`field is type`string`. + +
    + +
    + +
    + +### Result: + +`` - Subscription id \(needed to unsubscribe\) + +
    + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "accountSubscribe", + "params": [ + "CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12", + { + "encoding": "jsonParsed", + "commitment": "finalized" + } + ] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 23784, "id": 1 } +``` + + +
    + +#### Notification Format: + +The notification format is the same as seen in the [getAccountInfo](#getAccountInfo) RPC HTTP method. + +Base58 encoding: + +```json +{ + "jsonrpc": "2.0", + "method": "accountNotification", + "params": { + "result": { + "context": { + "slot": 5199307 + }, + "value": { + "data": [ + "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHPXHRDEHrBesJhZyqnnq9qJeUuF7WHxiuLuL5twc38w2TXNLxnDbjmuR", + "base58" + ], + "executable": false, + "lamports": 33594, + "owner": "11111111111111111111111111111111", + "rentEpoch": 635, + "space": 80 + } + }, + "subscription": 23784 + } +} +``` + +Parsed-JSON encoding: + +```json +{ + "jsonrpc": "2.0", + "method": "accountNotification", + "params": { + "result": { + "context": { + "slot": 5199307 + }, + "value": { + "data": { + "program": "nonce", + "parsed": { + "type": "initialized", + "info": { + "authority": "Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX", + "blockhash": "LUaQTmM7WbMRiATdMMHaRGakPtCkc2GHtH57STKXs6k", + "feeCalculator": { + "lamportsPerSignature": 5000 + } + } + } + }, + "executable": false, + "lamports": 33594, + "owner": "11111111111111111111111111111111", + "rentEpoch": 635, + "space": 80 + } + }, + "subscription": 23784 + } +} +``` + +
    diff --git a/docs/src/api/websocket/_accountUnsubscribe.mdx b/docs/src/api/websocket/_accountUnsubscribe.mdx new file mode 100644 index 00000000000000..d3a90de9ab9132 --- /dev/null +++ b/docs/src/api/websocket/_accountUnsubscribe.mdx @@ -0,0 +1,53 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## accountUnsubscribe + +Unsubscribe from account change notifications + + + + +### Parameters: + + + id of the account Subscription to cancel + + +### Result: + +`` - unsubscribe success message + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "accountUnsubscribe", + "params": [0] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": true, "id": 1 } +``` + + + + diff --git a/docs/src/api/websocket/_blockSubscribe.mdx b/docs/src/api/websocket/_blockSubscribe.mdx new file mode 100644 index 00000000000000..078f7585846e7f --- /dev/null +++ b/docs/src/api/websocket/_blockSubscribe.mdx @@ -0,0 +1,342 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## blockSubscribe + +Subscribe to receive notification anytime a new block is Confirmed or Finalized. + +:::caution +This subscription is **unstable** and only available if the validator was started +with the `--rpc-pubsub-enable-block-subscription` flag. + +**NOTE: The format of this subscription may change in the future** +::: + + + + +### Parameters: + + + +filter criteria for the logs to receive results by account type; currently supported: + + + all - include all transactions in block + + + + +A JSON object with the following field: + +- `mentionsAccountOrProgram: ` - return only transactions that mention the provided public key (as base-58 encoded string). If no mentions in a given block, then no notification will be sent. + + + + + + + +Configuration object containing the following fields: + + + + + level of transaction detail to return, either "full", "signatures", or "none". + + + + whether to populate the `rewards` array. + + + + +Encoding format for Account data + + + +
    + +- `base58` is slow +- `jsonParsed` encoding attempts to use program-specific state parsers to return + more human-readable and explicit account state data. +- If `jsonParsed` is requested but a parser cannot be found, the field falls back + to `base64` encoding, detectable when the `data` field is type `string`. + +
    + +
    + +
    + +### Result: + +`integer` - subscription id \(needed to unsubscribe\) + +
    + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": "1", + "method": "blockSubscribe", + "params": ["all"] +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": "1", + "method": "blockSubscribe", + "params": [ + { + "mentionsAccountOrProgram": "LieKvPRE8XeX3Y2xVNHjKlpAScD12lYySBVQ4HqoJ5op" + }, + { + "commitment": "confirmed", + "encoding": "base64", + "showRewards": true, + "transactionDetails": "full" + } + ] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 0, "id": 1 } +``` + + +
    + +#### Notification Format: + +The notification will be an object with the following fields: + +- `slot: ` - The corresponding slot. +- `err: ` - Error if something went wrong publishing the notification otherwise null. +- `block: ` - A block object as seen in the [getBlock](/api/http#getblock) RPC HTTP method. + +```json +{ + "jsonrpc": "2.0", + "method": "blockNotification", + "params": { + "result": { + "context": { + "slot": 112301554 + }, + "value": { + "slot": 112301554, + "block": { + "previousBlockhash": "GJp125YAN4ufCSUvZJVdCyWQJ7RPWMmwxoyUQySydZA", + "blockhash": "6ojMHjctdqfB55JDpEpqfHnP96fiaHEcvzEQ2NNcxzHP", + "parentSlot": 112301553, + "transactions": [ + { + "transaction": [ + "OpltwoUvWxYi1P2U8vbIdE/aPntjYo5Aa0VQ2JJyeJE2g9Vvxk8dDGgFMruYfDu8/IfUWb0REppTe7IpAuuLRgIBAAkWnj4KHRpEWWW7gvO1c0BHy06wZi2g7/DLqpEtkRsThAXIdBbhXCLvltw50ZnjDx2hzw74NVn49kmpYj2VZHQJoeJoYJqaKcvuxCi/2i4yywedcVNDWkM84Iuw+cEn9/ROCrXY4qBFI9dveEERQ1c4kdU46xjxj9Vi+QXkb2Kx45QFVkG4Y7HHsoS6WNUiw2m4ffnMNnOVdF9tJht7oeuEfDMuUEaO7l9JeUxppCvrGk3CP45saO51gkwVYEgKzhpKjCx3rgsYxNR81fY4hnUQXSbbc2Y55FkwgRBpVvQK7/+clR4Gjhd3L4y+OtPl7QF93Akg1LaU9wRMs5nvfDFlggqI9PqJl+IvVWrNRdBbPS8LIIhcwbRTkSbqlJQWxYg3Bo2CTVbw7rt1ZubuHWWp0mD/UJpLXGm2JprWTePNULzHu67sfqaWF99LwmwjTyYEkqkRt1T0Je5VzHgJs0N5jY4iIU9K3lMqvrKOIn/2zEMZ+ol2gdgjshx+sphIyhw65F3J/Dbzk04LLkK+CULmN571Y+hFlXF2ke0BIuUG6AUF+4214Cu7FXnqo3rkxEHDZAk0lRrAJ8X/Z+iwuwI5cgbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpDLAp8axcEkaQkLDKRoWxqp8XLNZSKial7Rk+ELAVVKWoWLRXRZ+OIggu0OzMExvVLE5VHqy71FNHq4gGitkiKYNFWSLIE4qGfdFLZXy/6hwS+wq9ewjikCpd//C9BcCL7Wl0iQdUslxNVCBZHnCoPYih9JXvGefOb9WWnjGy14sG9j70+RSVx6BlkFELWwFvIlWR/tHn3EhHAuL0inS2pwX7ZQTAU6gDVaoqbR2EiJ47cKoPycBNvHLoKxoY9AZaBjPl6q8SKQJSFyFd9n44opAgI6zMTjYF/8Ok4VpXEESp3QaoUyTI9sOJ6oFP6f4dwnvQelgXS+AEfAsHsKXxGAIUDQENAgMEBQAGBwgIDg8IBJCER3QXl1AVDBADCQoOAAQLERITDAjb7ugh3gOuTy==", + "base64" + ], + "meta": { + "err": null, + "status": { + "Ok": null + }, + "fee": 5000, + "preBalances": [ + 1758510880, 2067120, 1566000, 1461600, 2039280, 2039280, + 1900080, 1865280, 0, 3680844220, 2039280 + ], + "postBalances": [ + 1758505880, 2067120, 1566000, 1461600, 2039280, 2039280, + 1900080, 1865280, 0, 3680844220, 2039280 + ], + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "programIdIndex": 13, + "accounts": [1, 15, 3, 4, 2, 14], + "data": "21TeLgZXNbtHXVBzCaiRmH" + }, + { + "programIdIndex": 14, + "accounts": [3, 4, 1], + "data": "6qfC8ic7Aq99" + }, + { + "programIdIndex": 13, + "accounts": [1, 15, 3, 5, 2, 14], + "data": "21TeLgZXNbsn4QEpaSEr3q" + }, + { + "programIdIndex": 14, + "accounts": [3, 5, 1], + "data": "6LC7BYyxhFRh" + } + ] + }, + { + "index": 1, + "instructions": [ + { + "programIdIndex": 14, + "accounts": [4, 3, 0], + "data": "7aUiLHFjSVdZ" + }, + { + "programIdIndex": 19, + "accounts": [17, 18, 16, 9, 11, 12, 14], + "data": "8kvZyjATKQWYxaKR1qD53V" + }, + { + "programIdIndex": 14, + "accounts": [9, 11, 18], + "data": "6qfC8ic7Aq99" + } + ] + } + ], + "logMessages": [ + "Program QMNeHCGYnLVDn1icRAfQZpjPLBNkfGbSKRB83G5d8KB invoke [1]", + "Program QMWoBmAyJLAsA1Lh9ugMTw2gciTihncciphzdNzdZYV invoke [2]" + ], + "preTokenBalances": [ + { + "accountIndex": 4, + "mint": "iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u", + "uiTokenAmount": { + "uiAmount": null, + "decimals": 6, + "amount": "0", + "uiAmountString": "0" + }, + "owner": "LieKvPRE8XeX3Y2xVNHjKlpAScD12lYySBVQ4HqoJ5op", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "accountIndex": 5, + "mint": "iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u", + "uiTokenAmount": { + "uiAmount": 11513.0679, + "decimals": 6, + "amount": "11513067900", + "uiAmountString": "11513.0679" + }, + "owner": "rXhAofQCT7NN9TUqigyEAUzV1uLL4boeD8CRkNBSkYk", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "accountIndex": 10, + "mint": "Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1", + "uiTokenAmount": { + "uiAmount": null, + "decimals": 6, + "amount": "0", + "uiAmountString": "0" + }, + "owner": "CL9wkGFT3SZRRNa9dgaovuRV7jrVVigBUZ6DjcgySsCU", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "accountIndex": 11, + "mint": "Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1", + "uiTokenAmount": { + "uiAmount": 15138.514093, + "decimals": 6, + "amount": "15138514093", + "uiAmountString": "15138.514093" + }, + "owner": "LieKvPRE8XeX3Y2xVNHjKlpAScD12lYySBVQ4HqoJ5op", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + } + ], + "postTokenBalances": [ + { + "accountIndex": 4, + "mint": "iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u", + "uiTokenAmount": { + "uiAmount": null, + "decimals": 6, + "amount": "0", + "uiAmountString": "0" + }, + "owner": "LieKvPRE8XeX3Y2xVNHjKlpAScD12lYySBVQ4HqoJ5op", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "accountIndex": 5, + "mint": "iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u", + "uiTokenAmount": { + "uiAmount": 11513.103028, + "decimals": 6, + "amount": "11513103028", + "uiAmountString": "11513.103028" + }, + "owner": "rXhAofQCT7NN9TUqigyEAUzV1uLL4boeD8CRkNBSkYk", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "accountIndex": 10, + "mint": "Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1", + "uiTokenAmount": { + "uiAmount": null, + "decimals": 6, + "amount": "0", + "uiAmountString": "0" + }, + "owner": "CL9wkGFT3SZRRNa9dgaovuRV7jrVVigBUZ6DjcgySsCU", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + }, + { + "accountIndex": 11, + "mint": "Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1", + "uiTokenAmount": { + "uiAmount": 15489.767829, + "decimals": 6, + "amount": "15489767829", + "uiAmountString": "15489.767829" + }, + "owner": "BeiHVPRE8XeX3Y2xVNrSsTpAScH94nYySBVQ4HqgN9at", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + } + ], + "rewards": [] + } + } + ], + "blockTime": 1639926816, + "blockHeight": 101210751 + }, + "err": null + } + }, + "subscription": 14 + } +} +``` + +
    diff --git a/docs/src/api/websocket/_blockUnsubscribe.mdx b/docs/src/api/websocket/_blockUnsubscribe.mdx new file mode 100644 index 00000000000000..a16b73ca639aa4 --- /dev/null +++ b/docs/src/api/websocket/_blockUnsubscribe.mdx @@ -0,0 +1,53 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## blockUnsubscribe + +Unsubscribe from block notifications + + + + +### Parameters: + + + subscription id to cancel + + +### Result: + +`` - unsubscribe success message + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "blockUnsubscribe", + "params": [0] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": true, "id": 1 } +``` + + + + diff --git a/docs/src/api/websocket/_logsSubscribe.mdx b/docs/src/api/websocket/_logsSubscribe.mdx new file mode 100644 index 00000000000000..6955004489cda9 --- /dev/null +++ b/docs/src/api/websocket/_logsSubscribe.mdx @@ -0,0 +1,146 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## logsSubscribe + +Subscribe to transaction logging + + + + +### Parameters: + + + filter criteria for the logs to receive results by account type. The following filters types are currently supported: + + + +A string with one of the following values: + +- `all` - subscribe to all transactions except for simple vote transactions +- `allWithVotes` - subscribe to all transactions, including simple vote + transactions + + + + + +An object with the following field: + +- `mentions: [ ]` - array containing a single Pubkey (as base-58 + encoded string); if present, subscribe to only transactions mentioning this address + +:::caution + +Currently, the `mentions` field +[only supports one](https://github.com/solana-labs/solana/blob/master/rpc/src/rpc_pubsub.rs#L481) +Pubkey string per method call. Listing additional addresses will result in an +error. + +::: + + + + + + + +Configuration object containing the following fields: + + + + + +### Result: + +`` - Subscription id \(needed to unsubscribe\) + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "logsSubscribe", + "params": [ + { + "mentions": [ "11111111111111111111111111111111" ] + }, + { + "commitment": "finalized" + } + ] +} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "logsSubscribe", + "params": [ "all" ] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 24040, "id": 1 } +``` + + + + +#### Notification Format: + +The notification will be an RpcResponse JSON object with value equal to: + +- `signature: ` - The transaction signature base58 encoded. +- `err: ` - Error if transaction failed, null if transaction + succeeded. + [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) +- `logs: ` - Array of log messages the transaction instructions + output during execution, null if simulation failed before the transaction was + able to execute (for example due to an invalid blockhash or signature + verification failure) + +Example: + +```json +{ + "jsonrpc": "2.0", + "method": "logsNotification", + "params": { + "result": { + "context": { + "slot": 5208469 + }, + "value": { + "signature": "5h6xBEauJ3PK6SWCZ1PGjBvj8vDdWG3KpwATGy1ARAXFSDwt8GFXM7W5Ncn16wmqokgpiKRLuS83KUxyZyv2sUYv", + "err": null, + "logs": [ + "SBF program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri success" + ] + } + }, + "subscription": 24040 + } +} +``` + + diff --git a/docs/src/api/websocket/_logsUnsubscribe.mdx b/docs/src/api/websocket/_logsUnsubscribe.mdx new file mode 100644 index 00000000000000..6a75606eb02b06 --- /dev/null +++ b/docs/src/api/websocket/_logsUnsubscribe.mdx @@ -0,0 +1,53 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## logsUnsubscribe + +Unsubscribe from transaction logging + + + + +### Parameters: + + + subscription id to cancel + + +### Result: + +`` - unsubscribe success message + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "logsUnsubscribe", + "params": [0] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": true, "id": 1 } +``` + + + + diff --git a/docs/src/api/websocket/_programSubscribe.mdx b/docs/src/api/websocket/_programSubscribe.mdx new file mode 100644 index 00000000000000..bea83bac6c0fad --- /dev/null +++ b/docs/src/api/websocket/_programSubscribe.mdx @@ -0,0 +1,205 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## programSubscribe + +Subscribe to a program to receive notifications when the lamports or data for an account owned by the given program changes + + + + +### Parameters: + + + +Pubkey of the `program_id`, as base-58 encoded string + + + + + +Configuration object containing the following fields: + + + + + +filter results using various [filter objects](/api/http#filter-criteria) + +:::info +The resultant account must meet **ALL** filter criteria to be included in the returned results +::: + + + + + +Encoding format for Account data + + + +
    + +- `base58` is slow. +- [`jsonParsed`](/api/http#parsed-responses">) encoding attempts to use program-specific + state parsers to return more human-readable and explicit account state data. +- If `jsonParsed` is requested but a parser cannot be found, the field falls + back to `base64` encoding, detectable when the `data` field is type `string`. + +
    + +
    + +
    + +### Result: + +`` - Subscription id \(needed to unsubscribe\) + +
    + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "programSubscribe", + "params": [ + "11111111111111111111111111111111", + { + "encoding": "base64", + "commitment": "finalized" + } + ] +} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "programSubscribe", + "params": [ + "11111111111111111111111111111111", + { + "encoding": "jsonParsed" + } + ] +} +{ + "jsonrpc": "2.0", + "id": 1, + "method": "programSubscribe", + "params": [ + "11111111111111111111111111111111", + { + "encoding": "base64", + "filters": [ + { + "dataSize": 80 + } + ] + } + ] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 24040, "id": 1 } +``` + + +
    + +#### Notification format + +The notification format is a single program account object as seen in the [getProgramAccounts](/api/http#getprogramaccounts) RPC HTTP method. + +Base58 encoding: + +```json +{ + "jsonrpc": "2.0", + "method": "programNotification", + "params": { + "result": { + "context": { + "slot": 5208469 + }, + "value": { + "pubkey": "H4vnBqifaSACnKa7acsxstsY1iV1bvJNxsCY7enrd1hq", + "account": { + "data": [ + "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHPXHRDEHrBesJhZyqnnq9qJeUuF7WHxiuLuL5twc38w2TXNLxnDbjmuR", + "base58" + ], + "executable": false, + "lamports": 33594, + "owner": "11111111111111111111111111111111", + "rentEpoch": 636, + "space": 80 + } + } + }, + "subscription": 24040 + } +} +``` + +Parsed-JSON encoding: + +```json +{ + "jsonrpc": "2.0", + "method": "programNotification", + "params": { + "result": { + "context": { + "slot": 5208469 + }, + "value": { + "pubkey": "H4vnBqifaSACnKa7acsxstsY1iV1bvJNxsCY7enrd1hq", + "account": { + "data": { + "program": "nonce", + "parsed": { + "type": "initialized", + "info": { + "authority": "Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX", + "blockhash": "LUaQTmM7WbMRiATdMMHaRGakPtCkc2GHtH57STKXs6k", + "feeCalculator": { + "lamportsPerSignature": 5000 + } + } + } + }, + "executable": false, + "lamports": 33594, + "owner": "11111111111111111111111111111111", + "rentEpoch": 636, + "space": 80 + } + } + }, + "subscription": 24040 + } +} +``` + +
    diff --git a/docs/src/api/websocket/_programUnsubscribe.mdx b/docs/src/api/websocket/_programUnsubscribe.mdx new file mode 100644 index 00000000000000..b3decdcb9a50fe --- /dev/null +++ b/docs/src/api/websocket/_programUnsubscribe.mdx @@ -0,0 +1,53 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## programUnsubscribe + +Unsubscribe from program-owned account change notifications + + + + +### Parameters: + + + id of account Subscription to cancel + + +### Result: + +`` - unsubscribe success message + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "programUnsubscribe", + "params": [0] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": true, "id": 1 } +``` + + + + diff --git a/docs/src/api/websocket/_rootSubscribe.mdx b/docs/src/api/websocket/_rootSubscribe.mdx new file mode 100644 index 00000000000000..98fd59407f023b --- /dev/null +++ b/docs/src/api/websocket/_rootSubscribe.mdx @@ -0,0 +1,62 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## rootSubscribe + +Subscribe to receive notification anytime a new root is set by the validator. + + + + +### Parameters: + +**None** + +### Result: + +`integer` - subscription id \(needed to unsubscribe\) + + + + + +### Code sample: + +```json +{ "jsonrpc": "2.0", "id": 1, "method": "rootSubscribe" } +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 0, "id": 1 } +``` + + + + +#### Notification Format: + +The result is the latest root slot number. + +```json +{ + "jsonrpc": "2.0", + "method": "rootNotification", + "params": { + "result": 42, + "subscription": 0 + } +} +``` + + diff --git a/docs/src/api/websocket/_rootUnsubscribe.mdx b/docs/src/api/websocket/_rootUnsubscribe.mdx new file mode 100644 index 00000000000000..8d4085f183678f --- /dev/null +++ b/docs/src/api/websocket/_rootUnsubscribe.mdx @@ -0,0 +1,53 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## rootUnsubscribe + +Unsubscribe from root notifications + + + + +### Parameters: + + + subscription id to cancel + + +### Result: + +`` - unsubscribe success message + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "rootUnsubscribe", + "params": [0] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": true, "id": 1 } +``` + + + + diff --git a/docs/src/api/websocket/_signatureSubscribe.mdx b/docs/src/api/websocket/_signatureSubscribe.mdx new file mode 100644 index 00000000000000..d918c027d79635 --- /dev/null +++ b/docs/src/api/websocket/_signatureSubscribe.mdx @@ -0,0 +1,99 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## signatureSubscribe + +Subscribe to a transaction signature to receive notification when a given transaction is committed. On `signatureNotification` - the subscription is automatically cancelled. +The signature must be a [txid](/terminology#transaction-id), the first signature of a transaction. + + + + +### Parameters: + + + Transaction Signature, as base-58 encoded string + + + + +Configuration object containing the following fields: + + + + + +### Result: + +`` - subscription id (needed to unsubscribe) + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "signatureSubscribe", + "params": [ + "2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b", + { + "commitment": "finalized" + } + ] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 0, "id": 1 } +``` + + + + +#### Notification Format: + +The notification will be an RpcResponse JSON object with value containing an object with: + +- `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) + +Example: + +```json +{ + "jsonrpc": "2.0", + "method": "signatureNotification", + "params": { + "result": { + "context": { + "slot": 5207624 + }, + "value": { + "err": null + } + }, + "subscription": 24006 + } +} +``` + + diff --git a/docs/src/api/websocket/_signatureUnsubscribe.mdx b/docs/src/api/websocket/_signatureUnsubscribe.mdx new file mode 100644 index 00000000000000..880efed7c49fe7 --- /dev/null +++ b/docs/src/api/websocket/_signatureUnsubscribe.mdx @@ -0,0 +1,53 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## signatureUnsubscribe + +Unsubscribe from signature confirmation notification + + + + +### Parameters: + + + subscription id to cancel + + +### Result: + +`` - unsubscribe success message + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "signatureUnsubscribe", + "params": [0] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": true, "id": 1 } +``` + + + + diff --git a/docs/src/api/websocket/_slotSubscribe.mdx b/docs/src/api/websocket/_slotSubscribe.mdx new file mode 100644 index 00000000000000..c746ff060e5b22 --- /dev/null +++ b/docs/src/api/websocket/_slotSubscribe.mdx @@ -0,0 +1,72 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## slotSubscribe + +Subscribe to receive notification anytime a slot is processed by the validator + + + + +### Parameters: + +**None** + +### Result: + +`` - Subscription id \(needed to unsubscribe\) + + + + + +### Code sample: + +```json +{ "jsonrpc": "2.0", "id": 1, "method": "slotSubscribe" } +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 0, "id": 1 } +``` + + + + +#### Notification Format: + +The notification will be an object with the following fields: + +- `parent: ` - The parent slot +- `root: ` - The current root slot +- `slot: ` - The newly set slot value + +Example: + +```json +{ + "jsonrpc": "2.0", + "method": "slotNotification", + "params": { + "result": { + "parent": 75, + "root": 44, + "slot": 76 + }, + "subscription": 0 + } +} +``` + + diff --git a/docs/src/api/websocket/_slotUnsubscribe.mdx b/docs/src/api/websocket/_slotUnsubscribe.mdx new file mode 100644 index 00000000000000..0ce506163c87ca --- /dev/null +++ b/docs/src/api/websocket/_slotUnsubscribe.mdx @@ -0,0 +1,53 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## slotUnsubscribe + +Unsubscribe from slot notifications + + + + +### Parameters: + + + subscription id to cancel + + +### Result: + +`` - unsubscribe success message + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "slotUnsubscribe", + "params": [0] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": true, "id": 1 } +``` + + + + diff --git a/docs/src/api/websocket/_slotsUpdatesSubscribe.mdx b/docs/src/api/websocket/_slotsUpdatesSubscribe.mdx new file mode 100644 index 00000000000000..efb639b2034958 --- /dev/null +++ b/docs/src/api/websocket/_slotsUpdatesSubscribe.mdx @@ -0,0 +1,86 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## slotsUpdatesSubscribe + +Subscribe to receive a notification from the validator on a variety of updates +on every slot + +:::caution +This subscription is unstable + +**NOTE: the format of this subscription may change in the future and it may not always be supported** +::: + + + + +### Parameters: + +**None** + +### Result: + +`` - Subscription id (needed to unsubscribe) + + + + + +### Code sample: + +```json +{ "jsonrpc": "2.0", "id": 1, "method": "slotsUpdatesSubscribe" } +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 0, "id": 1 } +``` + + + + +#### Notification Format: + +The notification will be an object with the following fields: + +- `parent: ` - The parent slot +- `slot: ` - The newly updated slot +- `timestamp: ` - The Unix timestamp of the update +- `type: ` - The update type, one of: + - "firstShredReceived" + - "completed" + - "createdBank" + - "frozen" + - "dead" + - "optimisticConfirmation" + - "root" + +```bash +{ + "jsonrpc": "2.0", + "method": "slotsUpdatesNotification", + "params": { + "result": { + "parent": 75, + "slot": 76, + "timestamp": 1625081266243, + "type": "optimisticConfirmation" + }, + "subscription": 0 + } +} +``` + + diff --git a/docs/src/api/websocket/_slotsUpdatesUnsubscribe.mdx b/docs/src/api/websocket/_slotsUpdatesUnsubscribe.mdx new file mode 100644 index 00000000000000..8169e52118781b --- /dev/null +++ b/docs/src/api/websocket/_slotsUpdatesUnsubscribe.mdx @@ -0,0 +1,53 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## slotsUpdatesUnsubscribe + +Unsubscribe from slot-update notifications + + + + +### Parameters: + + + subscription id to cancel + + +### Result: + +`` - unsubscribe success message + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "slotsUpdatesUnsubscribe", + "params": [0] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": true, "id": 1 } +``` + + + + diff --git a/docs/src/api/websocket/_voteSubscribe.mdx b/docs/src/api/websocket/_voteSubscribe.mdx new file mode 100644 index 00000000000000..4f6f9cc87be982 --- /dev/null +++ b/docs/src/api/websocket/_voteSubscribe.mdx @@ -0,0 +1,79 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## voteSubscribe + +Subscribe to receive notification anytime a new vote is observed in gossip. +These votes are pre-consensus therefore there is no guarantee these votes will +enter the ledger. + +:::caution +This subscription is unstable and only available if the validator was started +with the `--rpc-pubsub-enable-vote-subscription` flag. The format of this +subscription may change in the future +::: + + + + +### Parameters: + +**None** + +### Result: + +`` - subscription id (needed to unsubscribe) + + + + + +### Code sample: + +```json +{ "jsonrpc": "2.0", "id": 1, "method": "voteSubscribe" } +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": 0, "id": 1 } +``` + + + + +#### Notification Format: + +The notification will be an object with the following fields: + +- `hash: ` - The vote hash +- `slots: ` - The slots covered by the vote, as an array of u64 integers +- `timestamp: ` - The timestamp of the vote +- `signature: ` - The signature of the transaction that contained this vote + +```json +{ + "jsonrpc": "2.0", + "method": "voteNotification", + "params": { + "result": { + "hash": "8Rshv2oMkPu5E4opXTRyuyBeZBqQ4S477VG26wUTFxUM", + "slots": [1, 2], + "timestamp": null + }, + "subscription": 0 + } +} +``` + + diff --git a/docs/src/api/websocket/_voteUnsubscribe.mdx b/docs/src/api/websocket/_voteUnsubscribe.mdx new file mode 100644 index 00000000000000..db37b4205479a1 --- /dev/null +++ b/docs/src/api/websocket/_voteUnsubscribe.mdx @@ -0,0 +1,53 @@ +import { + DocBlock, + DocSideBySide, + CodeParams, + Parameter, + Field, + Values, + CodeSnippets, +} from "../../../components/CodeDocBlock"; + + + +## voteUnsubscribe + +Unsubscribe from vote notifications + + + + +### Parameters: + + + subscription id to cancel + + +### Result: + +`` - unsubscribe success message + + + + + +### Code sample: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "voteUnsubscribe", + "params": [0] +} +``` + +### Response: + +```json +{ "jsonrpc": "2.0", "result": true, "id": 1 } +``` + + + + diff --git a/docs/src/cli/deploy-a-program.md b/docs/src/cli/deploy-a-program.md index 0c6b693c0e0680..2a8af6660737de 100644 --- a/docs/src/cli/deploy-a-program.md +++ b/docs/src/cli/deploy-a-program.md @@ -6,7 +6,7 @@ Developers can deploy on-chain [programs](terminology.md#program) (often called smart contracts elsewhere) with the Solana tools. To learn about developing and executing programs on Solana, start with the -[overview](developing/programming-model/overview.md) and then dig into the +[intro to Solana programs](developing/intro/programs.md) and then dig into the details of [on-chain programs](developing/on-chain-programs/overview.md). To deploy a program, use the Solana tools to interact with the on-chain loader @@ -284,7 +284,7 @@ solana program write-buffer Buffer accounts support authorities like program accounts: ```bash -solana program set-buffer-authority --new-upgrade-authority +solana program set-buffer-authority --new-buffer-authority ``` One exception is that buffer accounts cannot be marked immutable like program diff --git a/docs/src/cli/install-solana-cli-tools.md b/docs/src/cli/install-solana-cli-tools.md index 467ed94b6b6506..690dad059ef2b9 100644 --- a/docs/src/cli/install-solana-cli-tools.md +++ b/docs/src/cli/install-solana-cli-tools.md @@ -107,7 +107,7 @@ manually download and install the binaries. Download the binaries by navigating to [https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest), -download **solana-release-x86_64-unknown-linux-msvc.tar.bz2**, then extract the +download **solana-release-x86_64-unknown-linux-gnu.tar.bz2**, then extract the archive: ```bash diff --git a/docs/src/cluster/commitments.md b/docs/src/cluster/commitments.md new file mode 100644 index 00000000000000..6b7edd1c214525 --- /dev/null +++ b/docs/src/cluster/commitments.md @@ -0,0 +1,27 @@ +--- +title: Commitment Status +description: "Processed, confirmed, and finalized. Learn the differences between the different commitment statuses on the Solana blockchain." +keywords: + - processed + - confirmed + - finalized + - stake level + - block + - blockhash +--- + +The [commitment](./../terminology.md#commitment) metric gives clients a standard measure of the network confirmation for the block. Clients can then use this information to derive their own measures of commitment. + +There are three specific commitment statuses: + +- Processed +- Confirmed +- Finalized + +| Property | Processed | Confirmed | Finalized | +| ------------------------------------- | --------- | --------- | --------- | +| Received block | X | X | X | +| Block on majority fork | X | X | X | +| Block contains target tx | X | X | X | +| 66%+ stake voted on block | - | X | X | +| 31+ confirmed blocks built atop block | - | - | X | diff --git a/docs/src/cluster/rpc-endpoints.md b/docs/src/cluster/rpc-endpoints.md index 4b1c6f10b1723a..50173fa9087334 100644 --- a/docs/src/cluster/rpc-endpoints.md +++ b/docs/src/cluster/rpc-endpoints.md @@ -2,7 +2,7 @@ title: Solana Cluster RPC Endpoints --- -Solana maintains dedicated api nodes to fulfill [JSON-RPC](developing/clients/jsonrpc-api.md) +Solana maintains dedicated api nodes to fulfill [JSON-RPC](/api) requests for each public cluster, and third parties may as well. Here are the public RPC endpoints currently available and recommended for each public cluster: @@ -36,10 +36,9 @@ public RPC endpoints currently available and recommended for each public cluster ## Mainnet Beta -#### Endpoints* +#### Endpoints\* - `https://api.mainnet-beta.solana.com` - Solana-hosted api node cluster, backed by a load balancer; rate-limited -- `https://solana-api.projectserum.com` - Project Serum-hosted api node #### Rate Limits @@ -49,7 +48,7 @@ public RPC endpoints currently available and recommended for each public cluster - Maximum connection rate per 10 seconds per IP: 40 - Maximum amount of data per 30 second: 100 MB -*The public RPC endpoints are not intended for production applications. Please +\*The public RPC endpoints are not intended for production applications. Please use dedicated/private RPC servers when you launch your application, drop NFTs, etc. The public services are subject to abuse and rate limits may change without prior notice. Likewise, high-traffic websites may be blocked without @@ -59,6 +58,6 @@ prior notice. - 403 -- Your IP address or website has been blocked. It is time to run your own RPC server(s) or find a private service. - 429 -- Your IP address is exceeding the rate limits. Slow down! Use the -[Retry-After](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) -HTTP response header to determine how long to wait before making another -request. + [Retry-After](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) + HTTP response header to determine how long to wait before making another + request. diff --git a/docs/src/cluster/turbine-block-propagation.md b/docs/src/cluster/turbine-block-propagation.md index 5c79b89d11209a..8cc30acdc20c6b 100644 --- a/docs/src/cluster/turbine-block-propagation.md +++ b/docs/src/cluster/turbine-block-propagation.md @@ -2,41 +2,49 @@ title: Turbine Block Propagation --- -A Solana cluster uses a multi-layer block propagation mechanism called _Turbine_ to broadcast transaction shreds to all nodes with minimal amount of duplicate messages. The cluster divides itself into small collections of nodes, called _neighborhoods_. Each node is responsible for sharing any data it receives with the other nodes in its neighborhood, as well as propagating the data on to a small set of nodes in other neighborhoods. This way each node only has to communicate with a small number of nodes. - -During its slot, the leader node distributes shreds between the validator nodes in the first neighborhood \(layer 0\). Each validator shares its data within its neighborhood, but also retransmits the shreds to one node in some neighborhoods in the next layer \(layer 1\). The layer-1 nodes each share their data with their neighborhood peers, and retransmit to nodes in the next layer, etc, until all nodes in the cluster have received all the shreds. +A Solana cluster uses a multi-layer block propagation mechanism called _Turbine_ to broadcast transaction shreds to all nodes with minimal amount of duplicate messages. The cluster divides itself into small collections of nodes, called _neighborhoods_. Each node is responsible for propagating any data it receives on to a small set of nodes in downstream neighborhoods and possibly sharing data with the other nodes in its neighborhood. This way each node only has to communicate with a small number of nodes. ## Neighborhood Assignment - Weighted Selection -In order for data plane fanout to work, the entire cluster must agree on how the cluster is divided into neighborhoods. To achieve this, all the recognized validator nodes \(the TVU peers\) are sorted by stake and stored in a list. This list is then indexed in different ways to figure out neighborhood boundaries and retransmit peers. For example, the leader will simply select the first nodes to make up layer 0. These will automatically be the highest stake holders, allowing the heaviest votes to come back to the leader first. Layer 0 and lower-layer nodes use the same logic to find their neighbors and next layer peers. +In order for data plane fanout to work, the entire cluster must agree on how the cluster is divided into neighborhoods. To achieve this, all the recognized validator nodes \(the TVU peers\) are sorted by stake and stored in a list. This list is then indexed in different ways to figure out neighborhood boundaries and retransmit peers. For example, the leader will simply select the first `DATA_PLANE_FANOUT` nodes to make up layer 1. These will automatically be the highest stake holders, allowing the heaviest votes to come back to the leader first. Layer 1 and lower-layer nodes use the same logic to find their neighbors and next layer peers. -To reduce the possibility of attack vectors, each shred is transmitted over a random tree of neighborhoods. Each node uses the same set of nodes representing the cluster. A random tree is generated from the set for each shred using a seed derived from the leader id, slot and shred index. +To reduce the possibility of attack vectors, each shred is transmitted over a random tree of neighborhoods. Each node uses the same set of nodes representing the cluster. A random tree is generated from the set for each shred using a seed derived from the slot leader id, slot, shred index, and shred type. ## Layer and Neighborhood Structure -The current leader makes its initial broadcasts to at most `DATA_PLANE_FANOUT` nodes. If this layer 0 is smaller than the number of nodes in the cluster, then the data plane fanout mechanism adds layers below. Subsequent layers follow these constraints to determine layer-capacity: Each neighborhood contains `DATA_PLANE_FANOUT` nodes. Layer 0 starts with 1 neighborhood with fanout nodes. The number of nodes in each additional layer grows by a factor of fanout. +The leader can be thought of as layer 0 and communicates with layer 1, which is made up of at most `DATA_PLANE_FANOUT` nodes. If this layer 1 is smaller than the number of nodes in the cluster, then the data plane fanout mechanism adds layers below. Subsequent layers follow these constraints to determine layer-capacity: Each neighborhood contains `DATA_PLANE_FANOUT` nodes. Layer 1 starts with 1 neighborhood. The number of nodes in each additional neighborhood/layer grows by a factor of `DATA_PLANE_FANOUT`. -As mentioned above, each node in a layer only has to broadcast its shreds to its neighbors and to exactly 1 node in some next-layer neighborhoods, instead of to every TVU peer in the cluster. A good way to think about this is, layer 0 starts with 1 neighborhood with fanout nodes, layer 1 adds fanout neighborhoods, each with fanout nodes and layer 2 will have `fanout * number of nodes in layer 1` and so on. +A good way to think about this is, layer 1 starts with 1 neighborhood with fanout nodes, layer 2 adds fanout neighborhoods, each with fanout nodes and layer 3 will have `fanout * number of nodes in layer 2` and so on. -This way each node only has to communicate with a maximum of `2 * DATA_PLANE_FANOUT - 1` nodes. +The following diagram shows a three layer cluster with a fanout of 2. -The following diagram shows how the Leader sends shreds with a fanout of 2 to Neighborhood 0 in Layer 0 and how the nodes in Neighborhood 0 share their data with each other. +![Two layer cluster with a Fanout of 2](/img/data-plane.svg) -![Leader sends shreds to Neighborhood 0 in Layer 0](/img/data-plane-seeding.svg) +### Configuration Values -The following diagram shows how Neighborhood 0 fans out to Neighborhoods 1 and 2. +`DATA_PLANE_FANOUT` - Determines the size of layer 1. Subsequent layers grow by a factor of `DATA_PLANE_FANOUT`. The number of nodes in a neighborhood is equal to the fanout value. Neighborhoods will fill to capacity before new ones are added, i.e if a neighborhood isn't full, it _must_ be the last one. -![Neighborhood 0 Fanout to Neighborhood 1 and 2](/img/data-plane-fanout.svg) +Currently, configuration is set when the cluster is launched. In the future, these parameters may be hosted on-chain, allowing modification on the fly as the cluster sizes change. -Finally, the following diagram shows a two layer cluster with a fanout of 2. +## Shred Propagation Flow -![Two layer cluster with a Fanout of 2](/img/data-plane.svg) +During its slot, the leader node \(layer 0\) makes its initial broadcasts to a special root node sitting atop the turbine tree. This root node is rotated every shred. The root shares data within its neighborhood \(layer 1\). Nodes in this neighborhood then retransmit shreds to one node in some neighborhoods in the next layer \(layer 2\). In general, the layer-1 root/anchor node (first node in the neighborhood, rotated on every shred) shares their data with their neighborhood peers, and every node in layer-1 retransmits to nodes in the next layer, etc, until all nodes in the cluster have received all the shreds. -### Configuration Values +As mentioned above, each node in a layer only has to broadcast its shreds to exactly 1 node in some next-layer neighborhoods (and to its neighbors if it is the anchor node), instead of to every TVU peer in the cluster. In this way, each node only has to communicate with a maximum of `2 * DATA_PLANE_FANOUT - 1` nodes if it is the anchor node and `DATA_PLANE_FANOUT` if it is not the anchor node. -`DATA_PLANE_FANOUT` - Determines the size of layer 0. Subsequent layers grow by a factor of `DATA_PLANE_FANOUT`. The number of nodes in a neighborhood is equal to the fanout value. Neighborhoods will fill to capacity before new ones are added, i.e if a neighborhood isn't full, it _must_ be the last one. +The following diagram shows how the leader sends shreds with a fanout of 2 to the root from Neighborhood 0 in Layer 1 and how the root from Neighborhood 0 shares its data with its neighbors. -Currently, configuration is set when the cluster is launched. In the future, these parameters may be hosted on-chain, allowing modification on the fly as the cluster sizes change. +![Leader sends shreds to Neighborhood 0 in Layer 1](/img/data-plane-seeding.svg) + +The following diagram shows how Neighborhood 0 fans out to Neighborhoods 1 and 2. + +![Neighborhood 0 Fanout to Neighborhood 1 and 2](/img/data-plane-fanout.svg) + +### Neighborhood Interaction + +The following diagram shows how two neighborhoods in different layers interact. To cripple a neighborhood, enough nodes \(erasure codes +1\) from the neighborhood above need to fail. Since each neighborhood receives shreds from multiple nodes in a neighborhood in the upper layer, we'd need a big network failure in the upper layers to end up with incomplete data. + +![Inner workings of a neighborhood](/img/data-plane-neighborhood.svg) ## Calculating the required FEC rate @@ -56,7 +64,7 @@ on repair to fixup the blocks. The probability of the shred group failing can be computed using the binomial distribution. If the FEC rate is `16:4`, then the group size is 20, and at least 4 of the shreds must fail for the group to fail. -Which is equal to the sum of the probability of 4 or more trails failing +Which is equal to the sum of the probability of 4 or more trials failing out of 20. Probability of a block succeeding in turbine: @@ -64,7 +72,7 @@ Probability of a block succeeding in turbine: - Probability of packet failure: `P = 1 - (1 - network_packet_loss_rate)^2` - FEC rate: `K:M` - Number of trials: `N = K + M` -- Shred group failure rate: `S = SUM of i=0 -> M for binomial(prob_failure = P, trials = N, failures = i)` +- Shred group failure rate: `S = 1 - (SUM of i=0 -> M for binomial(prob_failure = P, trials = N, failures = i))` - Shreds per block: `G` - Block success rate: `B = (1 - S) ^ (G / N)` - Binomial distribution for exactly `i` results with probability of P in N trials is defined as `(N choose i) * P^i * (1 - P)^(N-i)` @@ -79,23 +87,17 @@ With a FEC rate: `16:4` - `G = 8000` - `P = 1 - 0.85 * 0.85 = 1 - 0.7225 = 0.2775` -- `S = SUM of i=0 -> 4 for binomial(prob_failure = 0.2775, trials = 20, failures = i) = 0.689414` +- `S = 1 - (SUM of i=0 -> 4 for binomial(prob_failure = 0.2775, trials = 20, failures = i)) = 0.689414` - `B = (1 - 0.689) ^ (8000 / 20) = 10^-203` With FEC rate of `16:16` - `G = 12800` -- `S = SUM of i=0 -> 32 for binomial(prob_failure = 0.2775, trials = 64, failures = i) = 0.002132` +- `S = 1 - (SUM of i=0 -> 16 for binomial(prob_failure = 0.2775, trials = 32, failures = i)) = 0.002132` - `B = (1 - 0.002132) ^ (12800 / 32) = 0.42583` With FEC rate of `32:32` - `G = 12800` -- `S = SUM of i=0 -> 32 for binomial(prob_failure = 0.2775, trials = 64, failures = i) = 0.000048` +- `S = 1 - (SUM of i=0 -> 32 for binomial(prob_failure = 0.2775, trials = 64, failures = i)) = 0.000048` - `B = (1 - 0.000048) ^ (12800 / 64) = 0.99045` - -## Neighborhoods - -The following diagram shows how two neighborhoods in different layers interact. To cripple a neighborhood, enough nodes \(erasure codes +1\) from the neighborhood above need to fail. Since each neighborhood receives shreds from multiple nodes in a neighborhood in the upper layer, we'd need a big network failure in the upper layers to end up with incomplete data. - -![Inner workings of a neighborhood](/img/data-plane-neighborhood.svg) diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 39cf3913447af9..35cc4a0d71f066 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -7,7 +7,7 @@ /* You can override the default Infima variables here. */ -@import url('https://fonts.googleapis.com/css2?family=Roboto'); +@import url("https://fonts.googleapis.com/css2?family=Roboto"); :root { --ifm-color-primary: #25c2a0; @@ -19,15 +19,18 @@ --ifm-color-primary-lightest: #abd5c6; --ifm-code-font-size: 95%; --ifm-spacing-horizontal: 1em; - --ifm-font-family-base: "Roboto", system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + --ifm-font-family-base: "Roboto", system-ui, -apple-system, Segoe UI, Roboto, + Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, "Segoe UI", + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol"; --ifm-footer-background-color: #232323; } - - - @keyframes fadeInUp { - 0% { opacity: 0; transform: translateY(1.5rem); } + /* 0% { + opacity: 0; + transform: translateY(1.5rem); + } */ } main { @@ -41,6 +44,26 @@ main { padding: 0 var(--ifm-pre-padding); } +.button { + background-color: var(--ifm-link-color); + color: var(--ifm-background-surface-color) !important; + padding: 0.5em 0.8em; + border: 0px solid red; + font-size: 1em !important; +} + +.container__spacer { + margin: 0em 1em 3em 1em; +} + +.cards__container { + /* margin-bottom: 0; */ +} + +.cards__container .col { + margin-bottom: 3em; +} + .card { padding: 1rem; margin-top: 0rem; @@ -48,16 +71,23 @@ main { animation-delay: 150ms; transition-property: all; transition-duration: 200ms; - box-shadow: 0 8px 28px 4px rgba(86,91,115,0.15); + align-items: start; + height: 100%; + /* box-shadow: 0 8px 15px 2px rgba(86, 91, 115, 0.1); */ + border: 1px solid #ccc; } +[data-theme="dark"] .card { + border-color: #444; +} .card a { text-decoration: none; } .card:hover { - transform: translate(0px, -5px); + /* transform: translate(0px, -5px); */ + border-color: var(--ifm-color-primary); } .footer--dark { @@ -69,5 +99,32 @@ footer .text--center { } .card__header h3 { - color: #1DD79B; -} \ No newline at end of file + color: #1dd79b; +} + +.header-link-icon:before { + content: ""; + display: flex; + height: 24px; + width: 24px; + background-position: center center; +} +.header-link-icon { + padding: 0.4em !important; +} +[data-theme="dark"] .header-github-link:before { + background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") + no-repeat; +} +.header-github-link:before { + background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") + no-repeat; +} +[data-theme="dark"] .header-discord-link:before { + background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 640 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' d='M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.085,69.137a1.712,1.712,0,0,0-.788.676C39.068,183.651,18.186,294.69,28.43,404.354a2.016,2.016,0,0,0,.765,1.375A487.666,487.666,0,0,0,176.02,479.918a1.9,1.9,0,0,0,2.063-.676A348.2,348.2,0,0,0,208.12,430.4a1.86,1.86,0,0,0-1.019-2.588,321.173,321.173,0,0,1-45.868-21.853,1.885,1.885,0,0,1-.185-3.126c3.082-2.309,6.166-4.711,9.109-7.137a1.819,1.819,0,0,1,1.9-.256c96.229,43.917,200.41,43.917,295.5,0a1.812,1.812,0,0,1,1.924.233c2.944,2.426,6.027,4.851,9.132,7.16a1.884,1.884,0,0,1-.162,3.126,301.407,301.407,0,0,1-45.89,21.83,1.875,1.875,0,0,0-1,2.611,391.055,391.055,0,0,0,30.014,48.815,1.864,1.864,0,0,0,2.063.7A486.048,486.048,0,0,0,610.7,405.729a1.882,1.882,0,0,0,.765-1.352C623.729,277.594,590.933,167.465,524.531,69.836ZM222.491,337.58c-28.972,0-52.844-26.587-52.844-59.239S193.056,219.1,222.491,219.1c29.665,0,53.306,26.82,52.843,59.239C275.334,310.993,251.924,337.58,222.491,337.58Zm195.38,0c-28.971,0-52.843-26.587-52.843-59.239S388.437,219.1,417.871,219.1c29.667,0,53.307,26.82,52.844,59.239C470.715,310.993,447.538,337.58,417.871,337.58Z'/%3E%3C/svg%3E") + no-repeat center; +} +.header-discord-link:before { + background: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 640 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.085,69.137a1.712,1.712,0,0,0-.788.676C39.068,183.651,18.186,294.69,28.43,404.354a2.016,2.016,0,0,0,.765,1.375A487.666,487.666,0,0,0,176.02,479.918a1.9,1.9,0,0,0,2.063-.676A348.2,348.2,0,0,0,208.12,430.4a1.86,1.86,0,0,0-1.019-2.588,321.173,321.173,0,0,1-45.868-21.853,1.885,1.885,0,0,1-.185-3.126c3.082-2.309,6.166-4.711,9.109-7.137a1.819,1.819,0,0,1,1.9-.256c96.229,43.917,200.41,43.917,295.5,0a1.812,1.812,0,0,1,1.924.233c2.944,2.426,6.027,4.851,9.132,7.16a1.884,1.884,0,0,1-.162,3.126,301.407,301.407,0,0,1-45.89,21.83,1.875,1.875,0,0,0-1,2.611,391.055,391.055,0,0,0,30.014,48.815,1.864,1.864,0,0,0,2.063.7A486.048,486.048,0,0,0,610.7,405.729a1.882,1.882,0,0,0,.765-1.352C623.729,277.594,590.933,167.465,524.531,69.836ZM222.491,337.58c-28.972,0-52.844-26.587-52.844-59.239S193.056,219.1,222.491,219.1c29.665,0,53.306,26.82,52.843,59.239C275.334,310.993,251.924,337.58,222.491,337.58Zm195.38,0c-28.971,0-52.843-26.587-52.843-59.239S388.437,219.1,417.871,219.1c29.667,0,53.307,26.82,52.844,59.239C470.715,310.993,447.538,337.58,417.871,337.58Z'/%3E%3C/svg%3E") + no-repeat center; +} diff --git a/docs/src/developing/backwards-compatibility.md b/docs/src/developing/backwards-compatibility.md index 769fbba8fa0980..252bbd2d9f6740 100644 --- a/docs/src/developing/backwards-compatibility.md +++ b/docs/src/developing/backwards-compatibility.md @@ -149,3 +149,9 @@ software releases. If a new attack vector is discovered in existing code, the above processes may be circumvented in order to rapidly deploy a fix, depending on the severity of the issue. + +#### CLI Tooling Output + +CLI tooling json output (`output --json`) compatibility will be preserved; however, output directed +for a human reader is subject to change. This includes output as well as potential help, warning, or +error messages. diff --git a/docs/src/developing/clients/javascript-api.md b/docs/src/developing/clients/javascript-api.md index 66b05b5a6696fa..8dad0f46a00698 100644 --- a/docs/src/developing/clients/javascript-api.md +++ b/docs/src/developing/clients/javascript-api.md @@ -4,19 +4,19 @@ title: Web3 JavaScript API ## What is Solana-Web3.js? -The Solana-Web3.js library aims to provide complete coverage of Solana. The library was built on top of the [Solana JSON RPC API](https://docs.solana.com/developing/clients/jsonrpc-api). +The Solana-Web3.js library aims to provide complete coverage of Solana. The library was built on top of the [Solana JSON RPC API](/api). You can find the full documentation for the `@solana/web3.js` library [here](https://solana-labs.github.io/solana-web3.js/). ## Common Terminology -| Term | Definition | -|-------------|------------------------| -| Program | Stateless executable code written to interpret instructions. Programs are capable of performing actions based on the instructions provided. | +| Term | Definition | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Program | Stateless executable code written to interpret instructions. Programs are capable of performing actions based on the instructions provided. | | Instruction | The smallest unit of a program that a client can include in a transaction. Within its processing code, an instruction may contain one or more cross-program invocations. | -| Transaction | One or more instructions signed by the client using one or more Keypairs and executed atomically with only two possible outcomes: success or failure. | +| Transaction | One or more instructions signed by the client using one or more Keypairs and executed atomically with only two possible outcomes: success or failure. | -For the full list of terms, see [Solana terminology](https://docs.solana.com/terminology#cross-program-invocation) +For the full list of terms, see [Solana terminology](../../terminology#cross-program-invocation) ## Getting Started @@ -49,19 +49,17 @@ $ npm install --save @solana/web3.js #### Javascript ```javascript -const solanaWeb3 = require('@solana/web3.js'); +const solanaWeb3 = require("@solana/web3.js"); console.log(solanaWeb3); ``` - #### ES6 ```javascript -import * as solanaWeb3 from '@solana/web3.js'; +import * as solanaWeb3 from "@solana/web3.js"; console.log(solanaWeb3); ``` - #### Browser Bundle ```javascript @@ -76,13 +74,14 @@ console.log(solanaWeb3); To allow users to use your dApp or application on Solana, they will need to get access to their Keypair. A Keypair is a private key with a matching public key, used to sign transactions. There are two ways to obtain a Keypair: + 1. Generate a new Keypair 2. Obtain a Keypair using the secret key You can obtain a new Keypair with the following: ```javascript -const {Keypair} = require("@solana/web3.js"); +const { Keypair } = require("@solana/web3.js"); let keypair = Keypair.generate(); ``` @@ -92,15 +91,13 @@ This will generate a brand new Keypair for a user to fund and use within your ap You can allow entry of the secretKey using a textbox, and obtain the Keypair with `Keypair.fromSecretKey(secretKey)`. ```javascript -const {Keypair} = require("@solana/web3.js"); +const { Keypair } = require("@solana/web3.js"); let secretKey = Uint8Array.from([ - 202, 171, 192, 129, 150, 189, 204, 241, 142, 71, 205, - 2, 81, 97, 2, 176, 48, 81, 45, 1, 96, 138, - 220, 132, 231, 131, 120, 77, 66, 40, 97, 172, 91, - 245, 84, 221, 157, 190, 9, 145, 176, 130, 25, 43, - 72, 107, 190, 229, 75, 88, 191, 136, 7, 167, 109, - 91, 170, 164, 186, 15, 142, 36, 12, 23 + 202, 171, 192, 129, 150, 189, 204, 241, 142, 71, 205, 2, 81, 97, 2, 176, 48, + 81, 45, 1, 96, 138, 220, 132, 231, 131, 120, 77, 66, 40, 97, 172, 91, 245, 84, + 221, 157, 190, 9, 145, 176, 130, 25, 43, 72, 107, 190, 229, 75, 88, 191, 136, + 7, 167, 109, 91, 170, 164, 186, 15, 142, 36, 12, 23, ]); let keypair = Keypair.fromSecretKey(secretKey); @@ -117,7 +114,12 @@ A transaction in Solana-Web3.js is created using the [`Transaction`](javascript- Take the example of a transfer transaction: ```javascript -const {Keypair, Transaction, SystemProgram, LAMPORTS_PER_SOL} = require("@solana/web3.js"); +const { + Keypair, + Transaction, + SystemProgram, + LAMPORTS_PER_SOL, +} = require("@solana/web3.js"); let fromKeypair = Keypair.generate(); let toKeypair = Keypair.generate(); @@ -127,8 +129,8 @@ transaction.add( SystemProgram.transfer({ fromPubkey: fromKeypair.publicKey, toPubkey: toKeypair.publicKey, - lamports: LAMPORTS_PER_SOL - }) + lamports: LAMPORTS_PER_SOL, + }), ); ``` @@ -137,16 +139,16 @@ The above code achieves creating a transaction ready to be signed and broadcaste All that is left is to sign the transaction with keypair and send it over the network. You can accomplish sending a transaction by using `sendAndConfirmTransaction` if you wish to alert the user or do something after a transaction is finished, or use `sendTransaction` if you don't need to wait for the transaction to be confirmed. ```javascript -const {sendAndConfirmTransaction, clusterApiUrl, Connection} = require("@solana/web3.js"); +const { + sendAndConfirmTransaction, + clusterApiUrl, + Connection, +} = require("@solana/web3.js"); let keypair = Keypair.generate(); -let connection = new Connection(clusterApiUrl('testnet')); +let connection = new Connection(clusterApiUrl("testnet")); -sendAndConfirmTransaction( - connection, - transaction, - [keypair] -); +sendAndConfirmTransaction(connection, transaction, [keypair]); ``` The above code takes in a `TransactionInstruction` using `SystemProgram`, creates a `Transaction`, and sends it over the network. You use `Connection` in order to define which Solana network you are connecting to, namely `mainnet-beta`, `testnet`, or `devnet`. @@ -175,23 +177,23 @@ Let's look at how to call this instruction using solana-web3.js: ```javascript let keypair = web3.Keypair.generate(); let payer = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('testnet')); +let connection = new web3.Connection(web3.clusterApiUrl("testnet")); let airdropSignature = await connection.requestAirdrop( payer.publicKey, web3.LAMPORTS_PER_SOL, ); -await connection.confirmTransaction(airdropSignature); +await connection.confirmTransaction({ signature: airdropSignature }); ``` First, we set up the account Keypair and connection so that we have an account to make allocate on the testnet. We also create a payer Keypair and airdrop some sol so we can pay for the allocate transaction. ```javascript let allocateTransaction = new web3.Transaction({ - feePayer: payer.publicKey - }); -let keys = [{pubkey: keypair.publicKey, isSigner: true, isWritable: true}]; + feePayer: payer.publicKey, +}); +let keys = [{ pubkey: keypair.publicKey, isSigner: true, isWritable: true }]; let params = { space: 100 }; ``` @@ -200,10 +202,7 @@ We create the transaction `allocateTransaction`, keys, and params objects. `feeP ```javascript let allocateStruct = { index: 8, - layout: struct([ - u32('instruction'), - ns64('space'), - ]) + layout: struct([u32("instruction"), ns64("space")]), }; ``` @@ -270,20 +269,25 @@ The `layout` in the allocate struct must always have `u32('instruction')` first ```javascript let data = Buffer.alloc(allocateStruct.layout.span); -let layoutFields = Object.assign({instruction: allocateStruct.index}, params); +let layoutFields = Object.assign({ instruction: allocateStruct.index }, params); allocateStruct.layout.encode(layoutFields, data); ``` Using the previously created bufferLayout, we can allocate a data buffer. We then assign our params `{ space: 100 }` so that it maps correctly to the layout, and encode it to the data buffer. Now the data is ready to be sent to the program. ```javascript -allocateTransaction.add(new web3.TransactionInstruction({ - keys, - programId: web3.SystemProgram.programId, - data, -})); +allocateTransaction.add( + new web3.TransactionInstruction({ + keys, + programId: web3.SystemProgram.programId, + data, + }), +); -await web3.sendAndConfirmTransaction(connection, allocateTransaction, [payer, keypair]); +await web3.sendAndConfirmTransaction(connection, allocateTransaction, [ + payer, + keypair, +]); ``` Finally, we add the transaction instruction with all the account keys, payer, data, and programId and broadcast the transaction to the network. @@ -291,45 +295,47 @@ Finally, we add the transaction instruction with all the account keys, payer, da The full code can be found below. ```javascript -const {struct, u32, ns64} = require("@solana/buffer-layout"); -const {Buffer} = require('buffer'); +const { struct, u32, ns64 } = require("@solana/buffer-layout"); +const { Buffer } = require("buffer"); const web3 = require("@solana/web3.js"); let keypair = web3.Keypair.generate(); let payer = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('testnet')); +let connection = new web3.Connection(web3.clusterApiUrl("testnet")); let airdropSignature = await connection.requestAirdrop( payer.publicKey, web3.LAMPORTS_PER_SOL, ); -await connection.confirmTransaction(airdropSignature); +await connection.confirmTransaction({ signature: airdropSignature }); let allocateTransaction = new web3.Transaction({ - feePayer: payer.publicKey + feePayer: payer.publicKey, }); -let keys = [{pubkey: keypair.publicKey, isSigner: true, isWritable: true}]; +let keys = [{ pubkey: keypair.publicKey, isSigner: true, isWritable: true }]; let params = { space: 100 }; let allocateStruct = { index: 8, - layout: struct([ - u32('instruction'), - ns64('space'), - ]) + layout: struct([u32("instruction"), ns64("space")]), }; let data = Buffer.alloc(allocateStruct.layout.span); -let layoutFields = Object.assign({instruction: allocateStruct.index}, params); +let layoutFields = Object.assign({ instruction: allocateStruct.index }, params); allocateStruct.layout.encode(layoutFields, data); -allocateTransaction.add(new web3.TransactionInstruction({ - keys, - programId: web3.SystemProgram.programId, - data, -})); +allocateTransaction.add( + new web3.TransactionInstruction({ + keys, + programId: web3.SystemProgram.programId, + data, + }), +); -await web3.sendAndConfirmTransaction(connection, allocateTransaction, [payer, keypair]); +await web3.sendAndConfirmTransaction(connection, allocateTransaction, [ + payer, + keypair, +]); ``` diff --git a/docs/src/developing/clients/javascript-reference.md b/docs/src/developing/clients/javascript-reference.md index 57612c87b375dd..4d7dfd6c9a962b 100644 --- a/docs/src/developing/clients/javascript-reference.md +++ b/docs/src/developing/clients/javascript-reference.md @@ -4,7 +4,7 @@ title: Web3 API Reference ## Web3 API Reference Guide -The `@solana/web3.js` library is a package that has coverage over the [Solana JSON RPC API](https://docs.solana.com/developing/clients/jsonrpc-api). +The `@solana/web3.js` library is a package that has coverage over the [Solana JSON RPC API](/api). You can find the full documentation for the `@solana/web3.js` library [here](https://solana-labs.github.io/solana-web3.js/). @@ -14,7 +14,7 @@ You can find the full documentation for the `@solana/web3.js` library [here](htt [Source Documentation](https://solana-labs.github.io/solana-web3.js/classes/Connection.html) -Connection is used to interact with the [Solana JSON RPC](https://docs.solana.com/developing/clients/jsonrpc-api). You can use Connection to confirm transactions, get account info, and more. +Connection is used to interact with the [Solana JSON RPC](/api). You can use Connection to confirm transactions, get account info, and more. You create a connection by defining the JSON RPC cluster endpoint and the desired commitment. Once this is complete, you can use this connection object to interact with any of the Solana JSON RPC API. @@ -23,7 +23,7 @@ You create a connection by defining the JSON RPC cluster endpoint and the desire ```javascript const web3 = require("@solana/web3.js"); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let slot = await connection.getSlot(); console.log(slot); @@ -64,19 +64,19 @@ A transaction is used to interact with programs on the Solana blockchain. These #### Example Usage ```javascript -const web3 = require('@solana/web3.js'); -const nacl = require('tweetnacl'); +const web3 = require("@solana/web3.js"); +const nacl = require("tweetnacl"); // Airdrop SOL for paying transactions let payer = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - payer.publicKey, - web3.LAMPORTS_PER_SOL, + payer.publicKey, + web3.LAMPORTS_PER_SOL, ); -await connection.confirmTransaction(airdropSignature); +await connection.confirmTransaction({ signature: airdropSignature }); let toAccount = web3.Keypair.generate(); @@ -84,27 +84,31 @@ let toAccount = web3.Keypair.generate(); let transaction = new web3.Transaction(); // Add an instruction to execute -transaction.add(web3.SystemProgram.transfer({ +transaction.add( + web3.SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: toAccount.publicKey, lamports: 1000, -})); + }), +); // Send and confirm transaction // Note: feePayer is by default the first signer, or payer, if the parameter is not set -await web3.sendAndConfirmTransaction(connection, transaction, [payer]) +await web3.sendAndConfirmTransaction(connection, transaction, [payer]); // Alternatively, manually construct the transaction let recentBlockhash = await connection.getRecentBlockhash(); let manualTransaction = new web3.Transaction({ - recentBlockhash: recentBlockhash.blockhash, - feePayer: payer.publicKey + recentBlockhash: recentBlockhash.blockhash, + feePayer: payer.publicKey, }); -manualTransaction.add(web3.SystemProgram.transfer({ +manualTransaction.add( + web3.SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: toAccount.publicKey, lamports: 1000, -})); + }), +); let transactionBuffer = manualTransaction.serializeMessage(); let signature = nacl.sign.detached(transactionBuffer, payer.secretKey); @@ -112,7 +116,7 @@ let signature = nacl.sign.detached(transactionBuffer, payer.secretKey); manualTransaction.addSignature(payer.publicKey, signature); let isVerifiedSignature = manualTransaction.verifySignatures(); -console.log(`The signatures were verifed: ${isVerifiedSignature}`) +console.log(`The signatures were verifed: ${isVerifiedSignature}`); // The signatures were verified: true @@ -130,7 +134,7 @@ The keypair is used to create an account with a public key and secret key within #### Example Usage ```javascript -const {Keypair} = require("@solana/web3.js") +const { Keypair } = require("@solana/web3.js"); let account = Keypair.generate(); @@ -147,8 +151,10 @@ console.log(account.secretKey); // 205, 189, 165, 112, 32, 200, 116, 164, 234 // ] - -let seed = Uint8Array.from([70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100]); +let seed = Uint8Array.from([ + 70, 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, 70, + 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, +]); let accountFromSeed = Keypair.fromSeed(seed); console.log(accountFromSeed.publicKey.toBase58()); @@ -164,7 +170,6 @@ console.log(accountFromSeed.secretKey); // 227, 60, 72, 215, 47, 208, 209, 162, 59 // ] - let accountFromSecret = Keypair.fromSecretKey(account.secretKey); console.log(accountFromSecret.publicKey.toBase58()); @@ -196,25 +201,33 @@ A PublicKey can be created with a base58 encoded string, buffer, Uint8Array, num #### Example Usage ```javascript -const {Buffer} = require('buffer'); -const web3 = require('@solana/web3.js'); -const crypto = require('crypto'); +const { Buffer } = require("buffer"); +const web3 = require("@solana/web3.js"); +const crypto = require("crypto"); // Create a PublicKey with a base58 encoded string -let base58publicKey = new web3.PublicKey('5xot9PVkphiX2adznghwrAuxGs2zeWisNSxMW6hU6Hkj'); +let base58publicKey = new web3.PublicKey( + "5xot9PVkphiX2adznghwrAuxGs2zeWisNSxMW6hU6Hkj", +); console.log(base58publicKey.toBase58()); // 5xot9PVkphiX2adznghwrAuxGs2zeWisNSxMW6hU6Hkj // Create a Program Address let highEntropyBuffer = crypto.randomBytes(31); -let programAddressFromKey = await web3.PublicKey.createProgramAddress([highEntropyBuffer.slice(0, 31)], base58publicKey); +let programAddressFromKey = await web3.PublicKey.createProgramAddress( + [highEntropyBuffer.slice(0, 31)], + base58publicKey, +); console.log(`Generated Program Address: ${programAddressFromKey.toBase58()}`); // Generated Program Address: 3thxPEEz4EDWHNxo1LpEpsAxZryPAHyvNVXJEJWgBgwJ // Find Program address given a PublicKey -let validProgramAddress = await web3.PublicKey.findProgramAddress([Buffer.from('', 'utf8')], programAddressFromKey); +let validProgramAddress = await web3.PublicKey.findProgramAddress( + [Buffer.from("", "utf8")], + programAddressFromKey, +); console.log(`Valid Program Address: ${validProgramAddress}`); // Valid Program Address: C14Gs3oyeXbASzwUpqSymCKpEyccfEuSe8VRar9vJQRE,253 @@ -233,75 +246,86 @@ const web3 = require("@solana/web3.js"); // Airdrop SOL for paying transactions let payer = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - payer.publicKey, - web3.LAMPORTS_PER_SOL, + payer.publicKey, + web3.LAMPORTS_PER_SOL, ); -await connection.confirmTransaction(airdropSignature); +await connection.confirmTransaction({ signature: airdropSignature }); // Allocate Account Data let allocatedAccount = web3.Keypair.generate(); let allocateInstruction = web3.SystemProgram.allocate({ - accountPubkey: allocatedAccount.publicKey, - space: 100, -}) + accountPubkey: allocatedAccount.publicKey, + space: 100, +}); let transaction = new web3.Transaction().add(allocateInstruction); -await web3.sendAndConfirmTransaction(connection, transaction, [payer, allocatedAccount]) +await web3.sendAndConfirmTransaction(connection, transaction, [ + payer, + allocatedAccount, +]); // Create Nonce Account let nonceAccount = web3.Keypair.generate(); -let minimumAmountForNonceAccount = await connection.getMinimumBalanceForRentExemption( - web3.NONCE_ACCOUNT_LENGTH, -); +let minimumAmountForNonceAccount = + await connection.getMinimumBalanceForRentExemption(web3.NONCE_ACCOUNT_LENGTH); let createNonceAccountTransaction = new web3.Transaction().add( -web3.SystemProgram.createNonceAccount({ + web3.SystemProgram.createNonceAccount({ fromPubkey: payer.publicKey, noncePubkey: nonceAccount.publicKey, authorizedPubkey: payer.publicKey, lamports: minimumAmountForNonceAccount, -}), + }), ); -await web3.sendAndConfirmTransaction(connection, createNonceAccountTransaction, [payer, nonceAccount]) +await web3.sendAndConfirmTransaction( + connection, + createNonceAccountTransaction, + [payer, nonceAccount], +); // Advance nonce - Used to create transactions as an account custodian let advanceNonceTransaction = new web3.Transaction().add( - web3.SystemProgram.nonceAdvance({ - noncePubkey: nonceAccount.publicKey, - authorizedPubkey: payer.publicKey, - }), + web3.SystemProgram.nonceAdvance({ + noncePubkey: nonceAccount.publicKey, + authorizedPubkey: payer.publicKey, + }), ); -await web3.sendAndConfirmTransaction(connection, advanceNonceTransaction, [payer]) +await web3.sendAndConfirmTransaction(connection, advanceNonceTransaction, [ + payer, +]); // Transfer lamports between accounts let toAccount = web3.Keypair.generate(); let transferTransaction = new web3.Transaction().add( -web3.SystemProgram.transfer({ + web3.SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: toAccount.publicKey, lamports: 1000, -}), + }), ); -await web3.sendAndConfirmTransaction(connection, transferTransaction, [payer]) +await web3.sendAndConfirmTransaction(connection, transferTransaction, [payer]); // Assign a new account to a program let programId = web3.Keypair.generate(); let assignedAccount = web3.Keypair.generate(); let assignTransaction = new web3.Transaction().add( -web3.SystemProgram.assign({ + web3.SystemProgram.assign({ accountPubkey: assignedAccount.publicKey, programId: programId.publicKey, -}), + }), ); -await web3.sendAndConfirmTransaction(connection, assignTransaction, [payer, assignedAccount]); +await web3.sendAndConfirmTransaction(connection, assignTransaction, [ + payer, + assignedAccount, +]); ``` ### Secp256k1Program @@ -313,49 +337,53 @@ The Secp256k1Program is used to verify Secp256k1 signatures, which are used by b #### Example Usage ```javascript -const {keccak_256} = require('js-sha3'); +const { keccak_256 } = require("js-sha3"); const web3 = require("@solana/web3.js"); -const secp256k1 = require('secp256k1'); +const secp256k1 = require("secp256k1"); // Create a Ethereum Address from secp256k1 let secp256k1PrivateKey; do { - secp256k1PrivateKey = web3.Keypair.generate().secretKey.slice(0, 32); + secp256k1PrivateKey = web3.Keypair.generate().secretKey.slice(0, 32); } while (!secp256k1.privateKeyVerify(secp256k1PrivateKey)); -let secp256k1PublicKey = secp256k1.publicKeyCreate(secp256k1PrivateKey, false).slice(1); +let secp256k1PublicKey = secp256k1 + .publicKeyCreate(secp256k1PrivateKey, false) + .slice(1); -let ethAddress = web3.Secp256k1Program.publicKeyToEthAddress(secp256k1PublicKey); -console.log(`Ethereum Address: 0x${ethAddress.toString('hex')}`); +let ethAddress = + web3.Secp256k1Program.publicKeyToEthAddress(secp256k1PublicKey); +console.log(`Ethereum Address: 0x${ethAddress.toString("hex")}`); // Ethereum Address: 0xadbf43eec40694eacf36e34bb5337fba6a2aa8ee // Fund a keypair to create instructions let fromPublicKey = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - fromPublicKey.publicKey, - web3.LAMPORTS_PER_SOL, + fromPublicKey.publicKey, + web3.LAMPORTS_PER_SOL, ); -await connection.confirmTransaction(airdropSignature); + +await connection.confirmTransaction({ signature: airdropSignature }); // Sign Message with Ethereum Key -let plaintext = Buffer.from('string address'); +let plaintext = Buffer.from("string address"); let plaintextHash = Buffer.from(keccak_256.update(plaintext).digest()); -let {signature, recid: recoveryId} = secp256k1.ecdsaSign( - plaintextHash, - secp256k1PrivateKey +let { signature, recid: recoveryId } = secp256k1.ecdsaSign( + plaintextHash, + secp256k1PrivateKey, ); // Create transaction to verify the signature let transaction = new Transaction().add( - web3.Secp256k1Program.createInstructionWithEthAddress({ - ethAddress: ethAddress.toString('hex'), - plaintext, - signature, - recoveryId, - }), + web3.Secp256k1Program.createInstructionWithEthAddress({ + ethAddress: ethAddress.toString("hex"), + plaintext, + signature, + recoveryId, + }), ); // Transaction will succeed if the message is verified to be signed by the address @@ -371,61 +399,57 @@ Message is used as another way to construct transactions. You can construct a me #### Example Usage ```javascript -const {Buffer} = require("buffer"); -const bs58 = require('bs58'); -const web3 = require('@solana/web3.js'); +const { Buffer } = require("buffer"); +const bs58 = require("bs58"); +const web3 = require("@solana/web3.js"); let toPublicKey = web3.Keypair.generate().publicKey; let fromPublicKey = web3.Keypair.generate(); -let connection = new web3.Connection( - web3.clusterApiUrl('devnet'), - 'confirmed' -); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - fromPublicKey.publicKey, - web3.LAMPORTS_PER_SOL, + fromPublicKey.publicKey, + web3.LAMPORTS_PER_SOL, ); -await connection.confirmTransaction(airdropSignature); +await connection.confirmTransaction({ signature: airdropSignature }); let type = web3.SYSTEM_INSTRUCTION_LAYOUTS.Transfer; let data = Buffer.alloc(type.layout.span); -let layoutFields = Object.assign({instruction: type.index}); +let layoutFields = Object.assign({ instruction: type.index }); type.layout.encode(layoutFields, data); let recentBlockhash = await connection.getRecentBlockhash(); let messageParams = { - accountKeys: [ - fromPublicKey.publicKey.toString(), - toPublicKey.toString(), - web3.SystemProgram.programId.toString() - ], - header: { - numReadonlySignedAccounts: 0, - numReadonlyUnsignedAccounts: 1, - numRequiredSignatures: 1, + accountKeys: [ + fromPublicKey.publicKey.toString(), + toPublicKey.toString(), + web3.SystemProgram.programId.toString(), + ], + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 1, + numRequiredSignatures: 1, + }, + instructions: [ + { + accounts: [0, 1], + data: bs58.encode(data), + programIdIndex: 2, }, - instructions: [ - { - accounts: [0, 1], - data: bs58.encode(data), - programIdIndex: 2, - }, - ], - recentBlockhash, + ], + recentBlockhash, }; let message = new web3.Message(messageParams); -let transaction = web3.Transaction.populate( - message, - [fromPublicKey.publicKey.toString()] -); +let transaction = web3.Transaction.populate(message, [ + fromPublicKey.publicKey.toString(), +]); -await web3.sendAndConfirmTransaction(connection, transaction, [fromPublicKey]) +await web3.sendAndConfirmTransaction(connection, transaction, [fromPublicKey]); ``` ### Struct @@ -437,6 +461,7 @@ The struct class is used to create Rust compatible structs in javascript. This c #### Example Usage Struct in Rust: + ```rust pub struct Fee { pub denominator: u64, @@ -445,9 +470,10 @@ pub struct Fee { ``` Using web3: + ```javascript -import BN from 'bn.js'; -import {Struct} from '@solana/web3.js'; +import BN from "bn.js"; +import { Struct } from "@solana/web3.js"; export class Fee extends Struct { denominator: BN; @@ -464,6 +490,7 @@ The Enum class is used to represent a Rust compatible Enum in javascript. The en #### Example Usage Rust: + ```rust pub enum AccountType { Uninitialized, @@ -473,8 +500,9 @@ pub enum AccountType { ``` Web3: + ```javascript -import {Enum} from '@solana/web3.js'; +import { Enum } from "@solana/web3.js"; export class AccountType extends Enum {} ``` @@ -490,13 +518,10 @@ You can create a nonce account by first creating a normal account, then using `S #### Example Usage ```javascript -const web3 = require('@solana/web3.js'); +const web3 = require("@solana/web3.js"); // Create connection -let connection = new web3.Connection( - web3.clusterApiUrl('devnet'), - 'confirmed', -); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); // Generate accounts let account = web3.Keypair.generate(); @@ -504,36 +529,35 @@ let nonceAccount = web3.Keypair.generate(); // Fund account let airdropSignature = await connection.requestAirdrop( - account.publicKey, - web3.LAMPORTS_PER_SOL, + account.publicKey, + web3.LAMPORTS_PER_SOL, ); -await connection.confirmTransaction(airdropSignature); +await connection.confirmTransaction({ signature: airdropSignature }); // Get Minimum amount for rent exemption let minimumAmount = await connection.getMinimumBalanceForRentExemption( - web3.NONCE_ACCOUNT_LENGTH, + web3.NONCE_ACCOUNT_LENGTH, ); // Form CreateNonceAccount transaction let transaction = new web3.Transaction().add( -web3.SystemProgram.createNonceAccount({ + web3.SystemProgram.createNonceAccount({ fromPubkey: account.publicKey, noncePubkey: nonceAccount.publicKey, authorizedPubkey: account.publicKey, lamports: minimumAmount, -}), + }), ); // Create Nonce Account -await web3.sendAndConfirmTransaction( - connection, - transaction, - [account, nonceAccount] -); +await web3.sendAndConfirmTransaction(connection, transaction, [ + account, + nonceAccount, +]); let nonceAccountData = await connection.getNonce( - nonceAccount.publicKey, - 'confirmed', + nonceAccount.publicKey, + "confirmed", ); console.log(nonceAccountData); @@ -546,12 +570,12 @@ console.log(nonceAccountData); // } let nonceAccountInfo = await connection.getAccountInfo( - nonceAccount.publicKey, - 'confirmed' + nonceAccount.publicKey, + "confirmed", ); let nonceAccountFromInfo = web3.NonceAccount.fromAccountData( - nonceAccountInfo.data + nonceAccountInfo.data, ); console.log(nonceAccountFromInfo); @@ -575,10 +599,12 @@ Vote account is an object that grants the capability of decoding vote accounts f #### Example Usage ```javascript -const web3 = require('@solana/web3.js'); +const web3 = require("@solana/web3.js"); let voteAccountInfo = await connection.getProgramAccounts(web3.VOTE_PROGRAM_ID); -let voteAccountFromData = web3.VoteAccount.fromAccountData(voteAccountInfo[0].account.data); +let voteAccountFromData = web3.VoteAccount.fromAccountData( + voteAccountInfo[0].account.data, +); console.log(voteAccountFromData); /* VoteAccount { @@ -650,33 +676,42 @@ const web3 = require("@solana/web3.js"); // Fund a key to create transactions let fromPublicKey = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - fromPublicKey.publicKey, - web3.LAMPORTS_PER_SOL, + fromPublicKey.publicKey, + web3.LAMPORTS_PER_SOL, ); -await connection.confirmTransaction(airdropSignature); +await connection.confirmTransaction({ signature: airdropSignature }); // Create Account let stakeAccount = web3.Keypair.generate(); let authorizedAccount = web3.Keypair.generate(); /* Note: This is the minimum amount for a stake account -- Add additional Lamports for staking For example, we add 50 lamports as part of the stake */ -let lamportsForStakeAccount = (await connection.getMinimumBalanceForRentExemption(web3.StakeProgram.space)) + 50; +let lamportsForStakeAccount = + (await connection.getMinimumBalanceForRentExemption( + web3.StakeProgram.space, + )) + 50; let createAccountTransaction = web3.StakeProgram.createAccount({ - fromPubkey: fromPublicKey.publicKey, - authorized: new web3.Authorized(authorizedAccount.publicKey, authorizedAccount.publicKey), - lamports: lamportsForStakeAccount, - lockup: new web3.Lockup(0, 0, fromPublicKey.publicKey), - stakePubkey: stakeAccount.publicKey + fromPubkey: fromPublicKey.publicKey, + authorized: new web3.Authorized( + authorizedAccount.publicKey, + authorizedAccount.publicKey, + ), + lamports: lamportsForStakeAccount, + lockup: new web3.Lockup(0, 0, fromPublicKey.publicKey), + stakePubkey: stakeAccount.publicKey, }); -await web3.sendAndConfirmTransaction(connection, createAccountTransaction, [fromPublicKey, stakeAccount]); +await web3.sendAndConfirmTransaction(connection, createAccountTransaction, [ + fromPublicKey, + stakeAccount, +]); // Check that stake is available let stakeBalance = await connection.getBalance(stakeAccount.publicKey); -console.log(`Stake balance: ${stakeBalance}`) +console.log(`Stake balance: ${stakeBalance}`); // Stake balance: 2282930 // We can verify the state of our stake. This may take some time to become active @@ -686,35 +721,42 @@ console.log(`Stake state: ${stakeState.state}`); // To delegate our stake, we get the current vote accounts and choose the first let voteAccounts = await connection.getVoteAccounts(); -let voteAccount = voteAccounts.current.concat( - voteAccounts.delinquent, -)[0]; +let voteAccount = voteAccounts.current.concat(voteAccounts.delinquent)[0]; let votePubkey = new web3.PublicKey(voteAccount.votePubkey); // We can then delegate our stake to the voteAccount let delegateTransaction = web3.StakeProgram.delegate({ - stakePubkey: stakeAccount.publicKey, - authorizedPubkey: authorizedAccount.publicKey, - votePubkey: votePubkey, + stakePubkey: stakeAccount.publicKey, + authorizedPubkey: authorizedAccount.publicKey, + votePubkey: votePubkey, }); -await web3.sendAndConfirmTransaction(connection, delegateTransaction, [fromPublicKey, authorizedAccount]); +await web3.sendAndConfirmTransaction(connection, delegateTransaction, [ + fromPublicKey, + authorizedAccount, +]); // To withdraw our funds, we first have to deactivate the stake let deactivateTransaction = web3.StakeProgram.deactivate({ - stakePubkey: stakeAccount.publicKey, - authorizedPubkey: authorizedAccount.publicKey, + stakePubkey: stakeAccount.publicKey, + authorizedPubkey: authorizedAccount.publicKey, }); -await web3.sendAndConfirmTransaction(connection, deactivateTransaction, [fromPublicKey, authorizedAccount]); +await web3.sendAndConfirmTransaction(connection, deactivateTransaction, [ + fromPublicKey, + authorizedAccount, +]); // Once deactivated, we can withdraw our funds let withdrawTransaction = web3.StakeProgram.withdraw({ - stakePubkey: stakeAccount.publicKey, - authorizedPubkey: authorizedAccount.publicKey, - toPubkey: fromPublicKey.publicKey, - lamports: stakeBalance, + stakePubkey: stakeAccount.publicKey, + authorizedPubkey: authorizedAccount.publicKey, + toPubkey: fromPublicKey.publicKey, + lamports: stakeBalance, }); -await web3.sendAndConfirmTransaction(connection, withdrawTransaction, [fromPublicKey, authorizedAccount]); +await web3.sendAndConfirmTransaction(connection, withdrawTransaction, [ + fromPublicKey, + authorizedAccount, +]); ``` ### Authorized @@ -734,7 +776,12 @@ Lockup is used in conjunction with the [StakeProgram](javascript-api.md#StakePro #### Example Usage ```javascript -const {Authorized, Keypair, Lockup, StakeProgram} = require("@solana/web3.js"); +const { + Authorized, + Keypair, + Lockup, + StakeProgram, +} = require("@solana/web3.js"); let account = Keypair.generate(); let stakeAccount = Keypair.generate(); @@ -742,13 +789,14 @@ let authorized = new Authorized(account.publicKey, account.publicKey); let lockup = new Lockup(0, 0, account.publicKey); let createStakeAccountInstruction = StakeProgram.createAccount({ - fromPubkey: account.publicKey, - authorized: authorized, - lamports: 1000, - lockup: lockup, - stakePubkey: stakeAccount.publicKey + fromPubkey: account.publicKey, + authorized: authorized, + lamports: 1000, + lockup: lockup, + stakePubkey: stakeAccount.publicKey, }); ``` + The above code creates a `createStakeAccountInstruction` to be used when creating an account with the `StakeProgram`. The Lockup is set to 0 for both the epoch and Unix timestamp, disabling lockup for the account. See [StakeProgram](javascript-api.md#StakeProgram) for more. diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md deleted file mode 100644 index 5e4507a3951579..00000000000000 --- a/docs/src/developing/clients/jsonrpc-api.md +++ /dev/null @@ -1,5373 +0,0 @@ ---- -title: JSON RPC API ---- - -Solana nodes accept HTTP requests using the [JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification. - -To interact with a Solana node inside a JavaScript application, use the -[solana-web3.js](https://github.com/solana-labs/solana-web3.js) library, which -gives a convenient interface for the RPC methods. - -## RPC HTTP Endpoint - -**Default port:** 8899 e.g. [http://localhost:8899](http://localhost:8899), [http://192.168.1.88:8899](http://192.168.1.88:8899) - -## RPC PubSub WebSocket Endpoint - -**Default port:** 8900 e.g. ws://localhost:8900, [http://192.168.1.88:8900](http://192.168.1.88:8900) - -## Methods - -- [getAccountInfo](jsonrpc-api.md#getaccountinfo) -- [getBalance](jsonrpc-api.md#getbalance) -- [getBlock](jsonrpc-api.md#getblock) -- [getBlockHeight](jsonrpc-api.md#getblockheight) -- [getBlockProduction](jsonrpc-api.md#getblockproduction) -- [getBlockCommitment](jsonrpc-api.md#getblockcommitment) -- [getBlocks](jsonrpc-api.md#getblocks) -- [getBlocksWithLimit](jsonrpc-api.md#getblockswithlimit) -- [getBlockTime](jsonrpc-api.md#getblocktime) -- [getClusterNodes](jsonrpc-api.md#getclusternodes) -- [getEpochInfo](jsonrpc-api.md#getepochinfo) -- [getEpochSchedule](jsonrpc-api.md#getepochschedule) -- [getFeeForMessage](jsonrpc-api.md#getfeeformessage) -- [getFirstAvailableBlock](jsonrpc-api.md#getfirstavailableblock) -- [getGenesisHash](jsonrpc-api.md#getgenesishash) -- [getHealth](jsonrpc-api.md#gethealth) -- [getHighestSnapshotSlot](jsonrpc-api.md#gethighestsnapshotslot) -- [getIdentity](jsonrpc-api.md#getidentity) -- [getInflationGovernor](jsonrpc-api.md#getinflationgovernor) -- [getInflationRate](jsonrpc-api.md#getinflationrate) -- [getInflationReward](jsonrpc-api.md#getinflationreward) -- [getLargestAccounts](jsonrpc-api.md#getlargestaccounts) -- [getLatestBlockhash](jsonrpc-api.md#getlatestblockhash) -- [getLeaderSchedule](jsonrpc-api.md#getleaderschedule) -- [getMaxRetransmitSlot](jsonrpc-api.md#getmaxretransmitslot) -- [getMaxShredInsertSlot](jsonrpc-api.md#getmaxshredinsertslot) -- [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption) -- [getMultipleAccounts](jsonrpc-api.md#getmultipleaccounts) -- [getProgramAccounts](jsonrpc-api.md#getprogramaccounts) -- [getRecentPerformanceSamples](jsonrpc-api.md#getrecentperformancesamples) -- [getSignaturesForAddress](jsonrpc-api.md#getsignaturesforaddress) -- [getSignatureStatuses](jsonrpc-api.md#getsignaturestatuses) -- [getSlot](jsonrpc-api.md#getslot) -- [getSlotLeader](jsonrpc-api.md#getslotleader) -- [getSlotLeaders](jsonrpc-api.md#getslotleaders) -- [getStakeActivation](jsonrpc-api.md#getstakeactivation) -- [getStakeMinimumDelegation](jsonrpc-api.md#getstakeminimumdelegation) -- [getSupply](jsonrpc-api.md#getsupply) -- [getTokenAccountBalance](jsonrpc-api.md#gettokenaccountbalance) -- [getTokenAccountsByDelegate](jsonrpc-api.md#gettokenaccountsbydelegate) -- [getTokenAccountsByOwner](jsonrpc-api.md#gettokenaccountsbyowner) -- [getTokenLargestAccounts](jsonrpc-api.md#gettokenlargestaccounts) -- [getTokenSupply](jsonrpc-api.md#gettokensupply) -- [getTransaction](jsonrpc-api.md#gettransaction) -- [getTransactionCount](jsonrpc-api.md#gettransactioncount) -- [getVersion](jsonrpc-api.md#getversion) -- [getVoteAccounts](jsonrpc-api.md#getvoteaccounts) -- [isBlockhashValid](jsonrpc-api.md#isblockhashvalid) -- [minimumLedgerSlot](jsonrpc-api.md#minimumledgerslot) -- [requestAirdrop](jsonrpc-api.md#requestairdrop) -- [sendTransaction](jsonrpc-api.md#sendtransaction) -- [simulateTransaction](jsonrpc-api.md#simulatetransaction) -- [Subscription Websocket](jsonrpc-api.md#subscription-websocket) - - [accountSubscribe](jsonrpc-api.md#accountsubscribe) - - [accountUnsubscribe](jsonrpc-api.md#accountunsubscribe) - - [logsSubscribe](jsonrpc-api.md#logssubscribe) - - [logsUnsubscribe](jsonrpc-api.md#logsunsubscribe) - - [programSubscribe](jsonrpc-api.md#programsubscribe) - - [programUnsubscribe](jsonrpc-api.md#programunsubscribe) - - [signatureSubscribe](jsonrpc-api.md#signaturesubscribe) - - [signatureUnsubscribe](jsonrpc-api.md#signatureunsubscribe) - - [slotSubscribe](jsonrpc-api.md#slotsubscribe) - - [slotUnsubscribe](jsonrpc-api.md#slotunsubscribe) - -### Unstable Methods - -Unstable methods may see breaking changes in patch releases and may not be supported in perpetuity. - -- [blockSubscribe](jsonrpc-api.md#blocksubscribe---unstable-disabled-by-default) -- [blockUnsubscribe](jsonrpc-api.md#blockunsubscribe) -- [slotsUpdatesSubscribe](jsonrpc-api.md#slotsupdatessubscribe---unstable) -- [slotsUpdatesUnsubscribe](jsonrpc-api.md#slotsupdatesunsubscribe) -- [voteSubscribe](jsonrpc-api.md#votesubscribe---unstable-disabled-by-default) -- [voteUnsubscribe](jsonrpc-api.md#voteunsubscribe) - -### Deprecated Methods - -- [getConfirmedBlock](jsonrpc-api.md#getconfirmedblock) -- [getConfirmedBlocks](jsonrpc-api.md#getconfirmedblocks) -- [getConfirmedBlocksWithLimit](jsonrpc-api.md#getconfirmedblockswithlimit) -- [getConfirmedSignaturesForAddress2](jsonrpc-api.md#getconfirmedsignaturesforaddress2) -- [getConfirmedTransaction](jsonrpc-api.md#getconfirmedtransaction) -- [getFeeCalculatorForBlockhash](jsonrpc-api.md#getfeecalculatorforblockhash) -- [getFeeRateGovernor](jsonrpc-api.md#getfeerategovernor) -- [getFees](jsonrpc-api.md#getfees) -- [getRecentBlockhash](jsonrpc-api.md#getrecentblockhash) -- [getSnapshotSlot](jsonrpc-api.md#getsnapshotslot) - -## Request Formatting - -To make a JSON-RPC request, send an HTTP POST request with a `Content-Type: -application/json` header. The JSON request data should contain 4 fields: - -- `jsonrpc: `, set to `"2.0"` -- `id: `, a unique client-generated identifying integer -- `method: `, a string containing the method to be invoked -- `params: `, a JSON array of ordered parameter values - -Example using curl: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getBalance", - "params": [ - "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri" - ] - } -' -``` - -The response output will be a JSON object with the following fields: - -- `jsonrpc: `, matching the request specification -- `id: `, matching the request identifier -- `result: `, requested data or success confirmation - -Requests can be sent in batches by sending an array of JSON-RPC request objects as the data for a single POST. - -## Definitions - -- Hash: A SHA-256 hash of a chunk of data. -- Pubkey: The public key of a Ed25519 key-pair. -- Transaction: A list of Solana instructions signed by a client keypair to authorize those actions. -- Signature: An Ed25519 signature of transaction's payload data including instructions. This can be used to identify transactions. - -## Configuring State Commitment - -For preflight checks and transaction processing, Solana nodes choose which bank -state to query based on a commitment requirement set by the client. The -commitment describes how finalized a block is at that point in time. When -querying the ledger state, it's recommended to use lower levels of commitment -to report progress and higher levels to ensure the state will not be rolled back. - -In descending order of commitment (most finalized to least finalized), clients -may specify: - -- `"finalized"` - the node will query the most recent block confirmed by supermajority - of the cluster as having reached maximum lockout, meaning the cluster has - recognized this block as finalized -- `"confirmed"` - the node will query the most recent block that has been voted on by supermajority of the cluster. - - It incorporates votes from gossip and replay. - - It does not count votes on descendants of a block, only direct votes on that block. - - This confirmation level also upholds "optimistic confirmation" guarantees in - release 1.3 and onwards. -- `"processed"` - the node will query its most recent block. Note that the block - may still be skipped by the cluster. - -For processing many dependent transactions in series, it's recommended to use -`"confirmed"` commitment, which balances speed with rollback safety. -For total safety, it's recommended to use`"finalized"` commitment. - -#### Example - -The commitment parameter should be included as the last element in the `params` array: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getBalance", - "params": [ - "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri", - { - "commitment": "finalized" - } - ] - } -' -``` - -#### Default: - -If commitment configuration is not provided, the node will default to `"finalized"` commitment - -Only methods that query bank state accept the commitment parameter. They are indicated in the API Reference below. - -#### RpcResponse Structure - -Many methods that take a commitment parameter return an RpcResponse JSON object comprised of two parts: - -- `context` : An RpcResponseContext JSON structure including a `slot` field at which the operation was evaluated. -- `value` : The value returned by the operation itself. - -## Health Check - -Although not a JSON RPC API, a `GET /health` at the RPC HTTP Endpoint provides a -health-check mechanism for use by load balancers or other network -infrastructure. This request will always return a HTTP 200 OK response with a body of -"ok", "behind" or "unknown" based on the following conditions: - -1. If one or more `--known-validator` arguments are provided to `solana-validator`, "ok" is returned - when the node has within `HEALTH_CHECK_SLOT_DISTANCE` slots of the highest - known validator, otherwise "behind". "unknown" is returned when no slot - information from known validators is not yet available. -2. "ok" is always returned if no known validators are provided. - -## JSON RPC API Reference - -### getAccountInfo - -Returns all information associated with the account of provided Pubkey - -#### Parameters: - -- `` - Pubkey of account to query, as base-58 encoded string -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `encoding: ` - encoding for Account data, either "base58" (_slow_), "base64", "base64+zstd", or "jsonParsed". - "base58" is limited to Account data of less than 129 bytes. - "base64" will return base64 encoded data for Account data of any size. - "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. - "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to "base64" encoding, detectable when the `data` field is type ``. - - (optional) `dataSlice: ` - limit the returned account data using the provided `offset: ` and `length: ` fields; only available for "base58", "base64" or "base64+zstd" encodings. - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to: - -- `` - if the requested account doesn't exist -- `` - otherwise, a JSON object containing: - - `lamports: `, number of lamports assigned to this account, as a u64 - - `owner: `, base-58 encoded Pubkey of the program this account has been assigned to - - `data: <[string, encoding]|object>`, data associated with the account, either as encoded binary data or JSON format `{: }`, depending on encoding parameter - - `executable: `, boolean indicating if the account contains a program \(and is strictly read-only\) - - `rentEpoch: `, the epoch at which this account will next owe rent, as u64 - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getAccountInfo", - "params": [ - "vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg", - { - "encoding": "base58" - } - ] - } -' -``` - -Response: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1 - }, - "value": { - "data": [ - "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHRTPuR3oZ1EioKtYGiYxpxMG5vpbZLsbcBYBEmZZcMKaSoGx9JZeAuWf", - "base58" - ], - "executable": false, - "lamports": 1000000000, - "owner": "11111111111111111111111111111111", - "rentEpoch": 2 - } - }, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getAccountInfo", - "params": [ - "4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA", - { - "encoding": "jsonParsed" - } - ] - } -' -``` - -Response: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1 - }, - "value": { - "data": { - "nonce": { - "initialized": { - "authority": "Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX", - "blockhash": "3xLP3jK6dVJwpeGeTDYTwdDK3TKchUf1gYYGHa4sF3XJ", - "feeCalculator": { - "lamportsPerSignature": 5000 - } - } - } - }, - "executable": false, - "lamports": 1000000000, - "owner": "11111111111111111111111111111111", - "rentEpoch": 2 - } - }, - "id": 1 -} -``` - -### getBalance - -Returns the balance of the account of provided Pubkey - -#### Parameters: - -- `` - Pubkey of account to query, as base-58 encoded string -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -- `RpcResponse` - RpcResponse JSON object with `value` field set to the balance - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0", "id":1, "method":"getBalance", "params":["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { "context": { "slot": 1 }, "value": 0 }, - "id": 1 -} -``` - -### getBlock - -Returns identity and transaction information about a confirmed block in the ledger - -#### Parameters: - -- `` - slot, as u64 integer -- `` - (optional) Configuration object containing the following optional fields: - - (optional) `encoding: ` - encoding for each returned Transaction, either "json", "jsonParsed", "base58" (_slow_), "base64". If parameter not provided, the default encoding is "json". - "jsonParsed" encoding attempts to use program-specific instruction parsers to return more human-readable and explicit data in the `transaction.message.instructions` list. If "jsonParsed" is requested but a parser cannot be found, the instruction falls back to regular JSON encoding (`accounts`, `data`, and `programIdIndex` fields). - - (optional) `transactionDetails: ` - level of transaction detail to return, either "full", "signatures", or "none". If parameter not provided, the default detail level is "full". - - (optional) `rewards: bool` - whether to populate the `rewards` array. If parameter not provided, the default includes rewards. - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". - - (optional) `maxSupportedTransactionVersion: ` - set the max transaction version to return in responses. If the requested block contains a transaction with a higher version, an error will be returned. If this parameter is omitted, only legacy transactions will be returned, and a block containing any versioned transaction will prompt the error. - -#### Results: - -The result field will be an object with the following fields: - -- `` - if specified block is not confirmed -- `` - if block is confirmed, an object with the following fields: - - `blockhash: ` - the blockhash of this block, as base-58 encoded string - - `previousBlockhash: ` - the blockhash of this block's parent, as base-58 encoded string; if the parent block is not available due to ledger cleanup, this field will return "11111111111111111111111111111111" - - `parentSlot: ` - the slot index of this block's parent - - `transactions: ` - present if "full" transaction details are requested; an array of JSON objects containing: - - `transaction: ` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter - - `meta: ` - transaction status metadata object, containing `null` or: - - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) - - `fee: ` - fee this transaction was charged, as u64 integer - - `preBalances: ` - array of u64 account balances from before the transaction was processed - - `postBalances: ` - array of u64 account balances after the transaction was processed - - `innerInstructions: ` - List of [inner instructions](#inner-instructions-structure) or `null` if inner instruction recording was not enabled during this transaction - - `preTokenBalances: ` - List of [token balances](#token-balances-structure) from before the transaction was processed or omitted if token balance recording was not yet enabled during this transaction - - `postTokenBalances: ` - List of [token balances](#token-balances-structure) from after the transaction was processed or omitted if token balance recording was not yet enabled during this transaction - - `logMessages: ` - array of string log messages or `null` if log message recording was not enabled during this transaction - - DEPRECATED: `status: ` - Transaction status - - `"Ok": ` - Transaction was successful - - `"Err": ` - Transaction failed with TransactionError - - `loadedAddresses: ` - Transaction addresses loaded from address lookup tables. Undefined if `maxSupportedTransactionVersion` is not set in request params. - - `writable: ` - Ordered list of base-58 encoded addresses for writable loaded accounts - - `readonly: ` - Ordered list of base-58 encoded addresses for readonly loaded accounts - - `version: <"legacy"|number|undefined>` - Transaction version. Undefined if `maxSupportedTransactionVersion` is not set in request params. - - `signatures: ` - present if "signatures" are requested for transaction details; an array of signatures strings, corresponding to the transaction order in the block - - `rewards: ` - present if rewards are requested; an array of JSON objects containing: - - `pubkey: ` - The public key, as base-58 encoded string, of the account that received the reward - - `lamports: `- number of reward lamports credited or debited by the account, as a i64 - - `postBalance: ` - account balance in lamports after the reward was applied - - `rewardType: ` - type of reward: "fee", "rent", "voting", "staking" - - `commission: ` - vote account commission when the reward was credited, only present for voting and staking rewards - - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch). null if not available - - `blockHeight: ` - the number of blocks beneath this block - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc": "2.0","id":1,"method":"getBlock","params":[430, {"encoding": "json","transactionDetails":"full","rewards":false}]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "blockHeight": 428, - "blockTime": null, - "blockhash": "3Eq21vXNB5s86c62bVuUfTeaMif1N2kUqRPBmGRJhyTA", - "parentSlot": 429, - "previousBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B", - "transactions": [ - { - "meta": { - "err": null, - "fee": 5000, - "innerInstructions": [], - "logMessages": [], - "postBalances": [499998932500, 26858640, 1, 1, 1], - "postTokenBalances": [], - "preBalances": [499998937500, 26858640, 1, 1, 1], - "preTokenBalances": [], - "status": { - "Ok": null - } - }, - "transaction": { - "message": { - "accountKeys": [ - "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe", - "AjozzgE83A3x1sHNUR64hfH7zaEBWeMaFuAN9kQgujrc", - "SysvarS1otHashes111111111111111111111111111", - "SysvarC1ock11111111111111111111111111111111", - "Vote111111111111111111111111111111111111111" - ], - "header": { - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 3, - "numRequiredSignatures": 1 - }, - "instructions": [ - { - "accounts": [1, 2, 3, 0], - "data": "37u9WtQpcm6ULa3WRQHmj49EPs4if7o9f1jSRVZpm2dvihR9C8jY4NqEwXUbLwx15HBSNcP1", - "programIdIndex": 4 - } - ], - "recentBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B" - }, - "signatures": [ - "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv" - ] - } - } - ] - }, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc": "2.0","id":1,"method":"getBlock","params":[430, "base64"]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "blockHeight": 428, - "blockTime": null, - "blockhash": "3Eq21vXNB5s86c62bVuUfTeaMif1N2kUqRPBmGRJhyTA", - "parentSlot": 429, - "previousBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B", - "rewards": [], - "transactions": [ - { - "meta": { - "err": null, - "fee": 5000, - "innerInstructions": null, - "logMessages": null, - "postBalances": [499998932500, 26858640, 1, 1, 1], - "postTokenBalances": [], - "preBalances": [499998937500, 26858640, 1, 1, 1], - "preTokenBalances": [], - "status": { - "Ok": null - } - }, - "transaction": [ - "AVj7dxHlQ9IrvdYVIjuiRFs1jLaDMHixgrv+qtHBwz51L4/ImLZhszwiyEJDIp7xeBSpm/TX5B7mYzxa+fPOMw0BAAMFJMJVqLw+hJYheizSoYlLm53KzgT82cDVmazarqQKG2GQsLgiqktA+a+FDR4/7xnDX7rsusMwryYVUdixfz1B1Qan1RcZLwqvxvJl4/t3zHragsUp0L47E24tAFUgAAAABqfVFxjHdMkoVmOYaR1etoteuKObS21cc1VbIQAAAAAHYUgdNXR0u3xNdiTr072z2DVec9EQQ/wNo1OAAAAAAAtxOUhPBp2WSjUNJEgfvy70BbxI00fZyEPvFHNfxrtEAQQEAQIDADUCAAAAAQAAAAAAAACtAQAAAAAAAAdUE18R96XTJCe+YfRfUp6WP+YKCy/72ucOL8AoBFSpAA==", - "base64" - ] - } - ] - }, - "id": 1 -} -``` - -#### Transaction Structure - -Transactions are quite different from those on other blockchains. Be sure to review [Anatomy of a Transaction](developing/programming-model/transactions.md) to learn about transactions on Solana. - -The JSON structure of a transaction is defined as follows: - -- `signatures: ` - A list of base-58 encoded signatures applied to the transaction. The list is always of length `message.header.numRequiredSignatures` and not empty. The signature at index `i` corresponds to the public key at index `i` in `message.account_keys`. The first one is used as the [transaction id](../../terminology.md#transaction-id). -- `message: ` - Defines the content of the transaction. - - `accountKeys: ` - List of base-58 encoded public keys used by the transaction, including by the instructions and for signatures. The first `message.header.numRequiredSignatures` public keys must sign the transaction. - - `header: ` - Details the account types and signatures required by the transaction. - - `numRequiredSignatures: ` - The total number of signatures required to make the transaction valid. The signatures must match the first `numRequiredSignatures` of `message.account_keys`. - - `numReadonlySignedAccounts: ` - The last `numReadonlySignedAccounts` of the signed keys are read-only accounts. Programs may process multiple transactions that load read-only accounts within a single PoH entry, but are not permitted to credit or debit lamports or modify account data. Transactions targeting the same read-write account are evaluated sequentially. - - `numReadonlyUnsignedAccounts: ` - The last `numReadonlyUnsignedAccounts` of the unsigned keys are read-only accounts. - - `recentBlockhash: ` - A base-58 encoded hash of a recent block in the ledger used to prevent transaction duplication and to give transactions lifetimes. - - `instructions: ` - List of program instructions that will be executed in sequence and committed in one atomic transaction if all succeed. - - `programIdIndex: ` - Index into the `message.accountKeys` array indicating the program account that executes this instruction. - - `accounts: ` - List of ordered indices into the `message.accountKeys` array indicating which accounts to pass to the program. - - `data: ` - The program input data encoded in a base-58 string. - - `addressTableLookups: ` - List of address table lookups used by a transaction to dynamically load addresses from on-chain address lookup tables. Undefined if `maxSupportedTransactionVersion` is not set. - - `accountKey: ` - base-58 encoded public key for an address lookup table account. - - `writableIndexes: ` - List of indices used to load addresses of writable accounts from a lookup table. - - `readonlyIndexes: ` - List of indices used to load addresses of readonly accounts from a lookup table. - -#### Inner Instructions Structure - -The Solana runtime records the cross-program instructions that are invoked during transaction processing and makes these available for greater transparency of what was executed on-chain per transaction instruction. Invoked instructions are grouped by the originating transaction instruction and are listed in order of processing. - -The JSON structure of inner instructions is defined as a list of objects in the following structure: - -- `index: number` - Index of the transaction instruction from which the inner instruction(s) originated -- `instructions: ` - Ordered list of inner program instructions that were invoked during a single transaction instruction. - - `programIdIndex: ` - Index into the `message.accountKeys` array indicating the program account that executes this instruction. - - `accounts: ` - List of ordered indices into the `message.accountKeys` array indicating which accounts to pass to the program. - - `data: ` - The program input data encoded in a base-58 string. - -#### Token Balances Structure - -The JSON structure of token balances is defined as a list of objects in the following structure: - -- `accountIndex: ` - Index of the account in which the token balance is provided for. -- `mint: ` - Pubkey of the token's mint. -- `owner: ` - Pubkey of token balance's owner. -- `programId: ` - Pubkey of the Token program that owns the account. -- `uiTokenAmount: ` - - - `amount: ` - Raw amount of tokens as a string, ignoring decimals. - - `decimals: ` - Number of decimals configured for token's mint. - - `uiAmount: ` - Token amount as a float, accounting for decimals. **DEPRECATED** - - `uiAmountString: ` - Token amount as a string, accounting for decimals. - -### getBlockHeight - -Returns the current block height of the node - -#### Parameters: - -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -- `` - Current block height - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getBlockHeight"} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 1233, "id": 1 } -``` - -### getBlockProduction - -Returns recent block production information from the current or previous epoch. - -#### Parameters: - -- `` - (optional) Configuration object containing the following optional fields: - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `range: ` - Slot range to return block production for. If parameter not provided, defaults to current epoch. - - `firstSlot: ` - first slot to return block production information for (inclusive) - - (optional) `lastSlot: ` - last slot to return block production information for (inclusive). If parameter not provided, defaults to the highest slot - - (optional) `identity: ` - Only return results for this validator identity (base-58 encoded) - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to: - -- `` - - `byIdentity: ` - a dictionary of validator identities, - as base-58 encoded strings. Value is a two element array containing the - number of leader slots and the number of blocks produced. - - `range: ` - Block production slot range - - `firstSlot: ` - first slot of the block production information (inclusive) - - `lastSlot: ` - last slot of block production information (inclusive) - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getBlockProduction"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 9887 - }, - "value": { - "byIdentity": { - "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr": [9888, 9886] - }, - "range": { - "firstSlot": 0, - "lastSlot": 9887 - } - } - }, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getBlockProduction", - "params": [ - { - "identity": "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr", - "range": { - "firstSlot": 40, - "lastSlot": 50 - } - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 10102 - }, - "value": { - "byIdentity": { - "85iYT5RuzRTDgjyRa3cP8SYhM2j21fj7NhfJ3peu1DPr": [11, 11] - }, - "range": { - "firstSlot": 50, - "lastSlot": 40 - } - } - }, - "id": 1 -} -``` - -### getBlockCommitment - -Returns commitment for particular block - -#### Parameters: - -- `` - block, identified by Slot - -#### Results: - -The result field will be a JSON object containing: - -- `commitment` - commitment, comprising either: - - `` - Unknown block - - `` - commitment, array of u64 integers logging the amount of cluster stake in lamports that has voted on the block at each depth from 0 to `MAX_LOCKOUT_HISTORY` + 1 -- `totalStake` - total active stake, in lamports, of the current epoch - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getBlockCommitment","params":[5]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "commitment": [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 10, 32 - ], - "totalStake": 42 - }, - "id": 1 -} -``` - -### getBlocks - -Returns a list of confirmed blocks between two slots - -#### Parameters: - -- `` - start_slot, as u64 integer -- `` - (optional) end_slot, as u64 integer (must be no more than 500,000 blocks higher than the `start_slot`) -- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". - -#### Results: - -The result field will be an array of u64 integers listing confirmed blocks -between `start_slot` and either `end_slot`, if provided, or latest confirmed block, -inclusive. Max range allowed is 500,000 slots. - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc": "2.0","id":1,"method":"getBlocks","params":[5, 10]} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": [5, 6, 7, 8, 9, 10], "id": 1 } -``` - -### getBlocksWithLimit - -Returns a list of confirmed blocks starting at the given slot - -#### Parameters: - -- `` - start_slot, as u64 integer -- `` - limit, as u64 integer (must be no more than 500,000 blocks higher than the `start_slot`) -- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". - -#### Results: - -The result field will be an array of u64 integers listing confirmed blocks -starting at `start_slot` for up to `limit` blocks, inclusive. - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc": "2.0","id":1,"method":"getBlocksWithLimit","params":[5, 3]} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": [5, 6, 7], "id": 1 } -``` - -### getBlockTime - -Returns the estimated production time of a block. - -Each validator reports their UTC time to the ledger on a regular interval by -intermittently adding a timestamp to a Vote for a particular block. A requested -block's time is calculated from the stake-weighted mean of the Vote timestamps -in a set of recent blocks recorded on the ledger. - -#### Parameters: - -- `` - block, identified by Slot - -#### Results: - -- `` - estimated production time, as Unix timestamp (seconds since the Unix epoch) -- `` - timestamp is not available for this block - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getBlockTime","params":[5]} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 1574721591, "id": 1 } -``` - -### getClusterNodes - -Returns information about all the nodes participating in the cluster - -#### Parameters: - -None - -#### Results: - -The result field will be an array of JSON objects, each with the following sub fields: - -- `pubkey: ` - Node public key, as base-58 encoded string -- `gossip: ` - Gossip network address for the node -- `tpu: ` - TPU network address for the node -- `rpc: ` - JSON RPC network address for the node, or `null` if the JSON RPC service is not enabled -- `version: ` - The software version of the node, or `null` if the version information is not available -- `featureSet: ` - The unique identifier of the node's feature set -- `shredVersion: ` - The shred version the node has been configured to use - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0", "id":1, "method":"getClusterNodes"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": [ - { - "gossip": "10.239.6.48:8001", - "pubkey": "9QzsJf7LPLj8GkXbYT3LFDKqsj2hHG7TA3xinJHu8epQ", - "rpc": "10.239.6.48:8899", - "tpu": "10.239.6.48:8856", - "version": "1.0.0 c375ce1f" - } - ], - "id": 1 -} -``` - -### getEpochInfo - -Returns information about the current epoch - -#### Parameters: - -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -The result field will be an object with the following fields: - -- `absoluteSlot: `, the current slot -- `blockHeight: `, the current block height -- `epoch: `, the current epoch -- `slotIndex: `, the current slot relative to the start of the current epoch -- `slotsInEpoch: `, the number of slots in this epoch -- `transactionCount: `, total number of transactions processed without error since genesis - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getEpochInfo"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "absoluteSlot": 166598, - "blockHeight": 166500, - "epoch": 27, - "slotIndex": 2790, - "slotsInEpoch": 8192, - "transactionCount": 22661093 - }, - "id": 1 -} -``` - -### getEpochSchedule - -Returns epoch schedule information from this cluster's genesis config - -#### Parameters: - -None - -#### Results: - -The result field will be an object with the following fields: - -- `slotsPerEpoch: `, the maximum number of slots in each epoch -- `leaderScheduleSlotOffset: `, the number of slots before beginning of an epoch to calculate a leader schedule for that epoch -- `warmup: `, whether epochs start short and grow -- `firstNormalEpoch: `, first normal-length epoch, log2(slotsPerEpoch) - log2(MINIMUM_SLOTS_PER_EPOCH) -- `firstNormalSlot: `, MINIMUM_SLOTS_PER_EPOCH \* (2.pow(firstNormalEpoch) - 1) - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getEpochSchedule"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "firstNormalEpoch": 8, - "firstNormalSlot": 8160, - "leaderScheduleSlotOffset": 8192, - "slotsPerEpoch": 8192, - "warmup": true - }, - "id": 1 -} -``` - -### getFeeForMessage - -**NEW: This method is only available in solana-core v1.9 or newer. Please use -[getFees](jsonrpc-api.md#getfees) for solana-core v1.8** - -Get the fee the network will charge for a particular Message - -#### Parameters: - -- `message: ` - Base-64 encoded Message -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) (used for retrieving blockhash) - -#### Results: - -- `` - Fee corresponding to the message at the specified blockhash - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' -{ - "id":1, - "jsonrpc":"2.0", - "method":"getFeeForMessage", - "params":[ - "AQABAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAA", - { - "commitment":"processed" - } - ] -} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { "context": { "slot": 5068 }, "value": 5000 }, - "id": 1 -} -``` - -### getFirstAvailableBlock - -Returns the slot of the lowest confirmed block that has not been purged from the ledger - -#### Parameters: - -None - -#### Results: - -- `` - Slot - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getFirstAvailableBlock"} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 250000, "id": 1 } -``` - -### getGenesisHash - -Returns the genesis hash - -#### Parameters: - -None - -#### Results: - -- `` - a Hash as base-58 encoded string - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getGenesisHash"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": "GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC", - "id": 1 -} -``` - -### getHealth - -Returns the current health of the node. - -If one or more `--known-validator` arguments are provided to -`solana-validator`, "ok" is returned when the node has within -`HEALTH_CHECK_SLOT_DISTANCE` slots of the highest known validator, otherwise -an error is returned. "ok" is always returned if no known validators are -provided. - -#### Parameters: - -None - -#### Results: - -If the node is healthy: "ok" -If the node is unhealthy, a JSON RPC error response is returned. The specifics -of the error response are **UNSTABLE** and may change in the future - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getHealth"} -' -``` - -Healthy Result: - -```json -{ "jsonrpc": "2.0", "result": "ok", "id": 1 } -``` - -Unhealthy Result (generic): - -```json -{ - "jsonrpc": "2.0", - "error": { - "code": -32005, - "message": "Node is unhealthy", - "data": {} - }, - "id": 1 -} -``` - -Unhealthy Result (if additional information is available) - -```json -{ - "jsonrpc": "2.0", - "error": { - "code": -32005, - "message": "Node is behind by 42 slots", - "data": { - "numSlotsBehind": 42 - } - }, - "id": 1 -} -``` - -### getHighestSnapshotSlot - -**NEW: This method is only available in solana-core v1.9 or newer. Please use -[getSnapshotSlot](jsonrpc-api.md#getsnapshotslot) for solana-core v1.8** - -Returns the highest slot information that the node has snapshots for. - -This will find the highest full snapshot slot, and the highest incremental -snapshot slot _based on_ the full snapshot slot, if there is one. - -#### Parameters: - -None - -#### Results: - -- `` - - `full: ` - Highest full snapshot slot - - `incremental: ` - Highest incremental snapshot slot _based on_ `full` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1,"method":"getHighestSnapshotSlot"} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": { "full": 100, "incremental": 110 }, "id": 1 } -``` - -Result when the node has no snapshot: - -```json -{ - "jsonrpc": "2.0", - "error": { "code": -32008, "message": "No snapshot" }, - "id": 1 -} -``` - -### getIdentity - -Returns the identity pubkey for the current node - -#### Parameters: - -None - -#### Results: - -The result field will be a JSON object with the following fields: - -- `identity`, the identity pubkey of the current node \(as a base-58 encoded string\) - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getIdentity"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { "identity": "2r1F4iWqVcb8M1DbAjQuFpebkQHY9hcVU4WuW2DJBppN" }, - "id": 1 -} -``` - -### getInflationGovernor - -Returns the current inflation governor - -#### Parameters: - -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -The result field will be a JSON object with the following fields: - -- `initial: `, the initial inflation percentage from time 0 -- `terminal: `, terminal inflation percentage -- `taper: `, rate per year at which inflation is lowered. - Rate reduction is derived using the target slot time in genesis config -- `foundation: `, percentage of total inflation allocated to the foundation -- `foundationTerm: `, duration of foundation pool inflation in years - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getInflationGovernor"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "foundation": 0.05, - "foundationTerm": 7, - "initial": 0.15, - "taper": 0.15, - "terminal": 0.015 - }, - "id": 1 -} -``` - -### getInflationRate - -Returns the specific inflation values for the current epoch - -#### Parameters: - -None - -#### Results: - -The result field will be a JSON object with the following fields: - -- `total: `, total inflation -- `validator: `, inflation allocated to validators -- `foundation: `, inflation allocated to the foundation -- `epoch: `, epoch for which these values are valid - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getInflationRate"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "epoch": 100, - "foundation": 0.001, - "total": 0.149, - "validator": 0.148 - }, - "id": 1 -} -``` - -### getInflationReward - -Returns the inflation / staking reward for a list of addresses for an epoch - -#### Parameters: - -- `` - An array of addresses to query, as base-58 encoded strings -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `epoch: ` - An epoch for which the reward occurs. If omitted, the previous epoch will be used - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results - -The result field will be a JSON array with the following fields: - -- `epoch: `, epoch for which reward occured -- `effectiveSlot: `, the slot in which the rewards are effective -- `amount: `, reward amount in lamports -- `postBalance: `, post balance of the account in lamports -- `commission: ` - vote account commission when the reward was credited - -#### Example - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getInflationReward", - "params": [ - ["6dmNQ5jwLeLk5REvio1JcMshcbvkYMwy26sJ8pbkvStu", "BGsqMegLpV6n6Ve146sSX2dTjUMj3M92HnU8BbNRMhF2"], {"epoch": 2} - ] - } -' -``` - -Response: - -```json -{ - "jsonrpc": "2.0", - "result": [ - { - "amount": 2500, - "effectiveSlot": 224, - "epoch": 2, - "postBalance": 499999442500 - }, - null - ], - "id": 1 -} -``` - -### getLargestAccounts - -Returns the 20 largest accounts, by lamport balance (results may be cached up to two hours) - -#### Parameters: - -- `` - (optional) Configuration object containing the following optional fields: - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `filter: ` - filter results by account type; currently supported: `circulating|nonCirculating` - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to an array of: - -- `` - otherwise, a JSON object containing: - - `address: `, base-58 encoded address of the account - - `lamports: `, number of lamports in the account, as a u64 - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getLargestAccounts"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 54 - }, - "value": [ - { - "lamports": 999974, - "address": "99P8ZgtJYe1buSK8JXkvpLh8xPsCFuLYhz9hQFNw93WJ" - }, - { - "lamports": 42, - "address": "uPwWLo16MVehpyWqsLkK3Ka8nLowWvAHbBChqv2FZeL" - }, - { - "lamports": 42, - "address": "aYJCgU7REfu3XF8b3QhkqgqQvLizx8zxuLBHA25PzDS" - }, - { - "lamports": 42, - "address": "CTvHVtQ4gd4gUcw3bdVgZJJqApXE9nCbbbP4VTS5wE1D" - }, - { - "lamports": 20, - "address": "4fq3xJ6kfrh9RkJQsmVd5gNMvJbuSHfErywvEjNQDPxu" - }, - { - "lamports": 4, - "address": "AXJADheGVp9cruP8WYu46oNkRbeASngN5fPCMVGQqNHa" - }, - { - "lamports": 2, - "address": "8NT8yS6LiwNprgW4yM1jPPow7CwRUotddBVkrkWgYp24" - }, - { - "lamports": 1, - "address": "SysvarEpochSchedu1e111111111111111111111111" - }, - { - "lamports": 1, - "address": "11111111111111111111111111111111" - }, - { - "lamports": 1, - "address": "Stake11111111111111111111111111111111111111" - }, - { - "lamports": 1, - "address": "SysvarC1ock11111111111111111111111111111111" - }, - { - "lamports": 1, - "address": "StakeConfig11111111111111111111111111111111" - }, - { - "lamports": 1, - "address": "SysvarRent111111111111111111111111111111111" - }, - { - "lamports": 1, - "address": "Config1111111111111111111111111111111111111" - }, - { - "lamports": 1, - "address": "SysvarStakeHistory1111111111111111111111111" - }, - { - "lamports": 1, - "address": "SysvarRecentB1ockHashes11111111111111111111" - }, - { - "lamports": 1, - "address": "SysvarFees111111111111111111111111111111111" - }, - { - "lamports": 1, - "address": "Vote111111111111111111111111111111111111111" - } - ] - }, - "id": 1 -} -``` - -### getLatestBlockhash - -**NEW: This method is only available in solana-core v1.9 or newer. Please use -[getRecentBlockhash](jsonrpc-api.md#getrecentblockhash) for solana-core v1.8** - -Returns the latest blockhash - -#### Parameters: - -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) (used for retrieving blockhash) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -- `RpcResponse` - RpcResponse JSON object with `value` field set to a JSON object including: -- `blockhash: ` - a Hash as base-58 encoded string -- `lastValidBlockHeight: ` - last [block height](../../terminology.md#block-height) at which the blockhash will be valid - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "id":1, - "jsonrpc":"2.0", - "method":"getLatestBlockhash", - "params":[ - { - "commitment":"processed" - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 2792 - }, - "value": { - "blockhash": "EkSnNWid2cvwEVnVx9aBqawnmiCNiDgp3gUdkDPTKN1N", - "lastValidBlockHeight": 3090 - } - }, - "id": 1 -} -``` - -### getLeaderSchedule - -Returns the leader schedule for an epoch - -#### Parameters: - -- `` - (optional) Fetch the leader schedule for the epoch that corresponds to the provided slot. - If unspecified, the leader schedule for the current epoch is fetched -- `` - (optional) Configuration object containing the following field: - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `identity: ` - Only return results for this validator identity (base-58 encoded) - -#### Results: - -- `` - if requested epoch is not found -- `` - otherwise, the result field will be a dictionary of validator identities, - as base-58 encoded strings, and their corresponding leader slot indices as values - (indices are relative to the first slot in the requested epoch) - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F": [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, 63 - ] - }, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getLeaderSchedule", - "params": [ - null, - { - "identity": "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F" - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F": [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, 63 - ] - }, - "id": 1 -} -``` - -### getMaxRetransmitSlot - -Get the max slot seen from retransmit stage. - -#### Results: - -- `` - Slot - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getMaxRetransmitSlot"} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 1234, "id": 1 } -``` - -### getMaxShredInsertSlot - -Get the max slot seen from after shred insert. - -#### Results: - -- `` - Slot - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getMaxShredInsertSlot"} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 1234, "id": 1 } -``` - -### getMinimumBalanceForRentExemption - -Returns minimum balance required to make account rent exempt. - -#### Parameters: - -- `` - account data length -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -- `` - minimum lamports required in account - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0", "id":1, "method":"getMinimumBalanceForRentExemption", "params":[50]} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 500, "id": 1 } -``` - -### getMultipleAccounts - -Returns the account information for a list of Pubkeys. - -#### Parameters: - -- `` - An array of Pubkeys to query, as base-58 encoded strings (up to a maximum of 100). -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `encoding: ` - encoding for Account data, either "base58" (_slow_), "base64", "base64+zstd", or "jsonParsed". - "base58" is limited to Account data of less than 129 bytes. - "base64" will return base64 encoded data for Account data of any size. - "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. - "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to "base64" encoding, detectable when the `data` field is type ``. - - (optional) `dataSlice: ` - limit the returned account data using the provided `offset: ` and `length: ` fields; only available for "base58", "base64" or "base64+zstd" encodings. - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to: - -An array of: - -- `` - if the account at that Pubkey doesn't exist -- `` - otherwise, a JSON object containing: - - `lamports: `, number of lamports assigned to this account, as a u64 - - `owner: `, base-58 encoded Pubkey of the program this account has been assigned to - - `data: <[string, encoding]|object>`, data associated with the account, either as encoded binary data or JSON format `{: }`, depending on encoding parameter - - `executable: `, boolean indicating if the account contains a program \(and is strictly read-only\) - - `rentEpoch: `, the epoch at which this account will next owe rent, as u64 - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getMultipleAccounts", - "params": [ - [ - "vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg", - "4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA" - ], - { - "dataSlice": { - "offset": 0, - "length": 0 - } - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1 - }, - "value": [ - { - "data": ["AAAAAAEAAAACtzNsyJrW0g==", "base64"], - "executable": false, - "lamports": 1000000000, - "owner": "11111111111111111111111111111111", - "rentEpoch": 2 - }, - { - "data": ["", "base64"], - "executable": false, - "lamports": 5000000000, - "owner": "11111111111111111111111111111111", - "rentEpoch": 2 - } - ] - }, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getMultipleAccounts", - "params": [ - [ - "vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg", - "4fYNw3dojWmQ4dXtSGE9epjRGy9pFSx62YypT7avPYvA" - ], - { - "encoding": "base58" - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1 - }, - "value": [ - { - "data": [ - "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHRTPuR3oZ1EioKtYGiYxpxMG5vpbZLsbcBYBEmZZcMKaSoGx9JZeAuWf", - "base58" - ], - "executable": false, - "lamports": 1000000000, - "owner": "11111111111111111111111111111111", - "rentEpoch": 2 - }, - { - "data": ["", "base58"], - "executable": false, - "lamports": 5000000000, - "owner": "11111111111111111111111111111111", - "rentEpoch": 2 - } - ] - }, - "id": 1 -} -``` - -### getProgramAccounts - -Returns all accounts owned by the provided program Pubkey - -#### Parameters: - -- `` - Pubkey of program, as base-58 encoded string -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `encoding: ` - encoding for Account data, either "base58" (_slow_), "base64", "base64+zstd", or "jsonParsed". - "base58" is limited to Account data of less than 129 bytes. - "base64" will return base64 encoded data for Account data of any size. - "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. - "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to "base64" encoding, detectable when the `data` field is type ``. - - (optional) `dataSlice: ` - limit the returned account data using the provided `offset: ` and `length: ` fields; only available for "base58", "base64" or "base64+zstd" encodings. - - (optional) `filters: ` - filter results using up to 4 [filter objects](jsonrpc-api.md#filters); account must meet all filter criteria to be included in results - - (optional) `withContext: bool` - wrap the result in an RpcResponse JSON object. - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -##### Filters: - -- `memcmp: ` - compares a provided series of bytes with program account data at a particular offset. Fields: - - - `offset: ` - offset into program account data to start comparison - - `bytes: ` - data to match, as encoded string - - `encoding: ` - encoding for filter `bytes` data, either "base58" or "base64". Data is limited in size to 128 or fewer decoded bytes. - **NEW: This field, and base64 support generally, is only available in solana-core v1.11.6 or newer. Please omit when querying nodes on earlier versions** - -- `dataSize: ` - compares the program account data length with the provided data size - -#### Results: - -By default the result field will be an array of JSON objects. If `withContext` flag is set the array will be wrapped in an RpcResponse JSON object. - -The array will contain: - -- `pubkey: ` - the account Pubkey as base-58 encoded string -- `account: ` - a JSON object, with the following sub fields: - - `lamports: `, number of lamports assigned to this account, as a u64 - - `owner: `, base-58 encoded Pubkey of the program this account has been assigned to - - `data: <[string,encoding]|object>`, data associated with the account, either as encoded binary data or JSON format `{: }`, depending on encoding parameter - - `executable: `, boolean indicating if the account contains a program \(and is strictly read-only\) - - `rentEpoch: `, the epoch at which this account will next owe rent, as u64 - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0", "id":1, "method":"getProgramAccounts", "params":["4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T"]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": [ - { - "account": { - "data": "2R9jLfiAQ9bgdcw6h8s44439", - "executable": false, - "lamports": 15298080, - "owner": "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", - "rentEpoch": 28 - }, - "pubkey": "CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY" - } - ], - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getProgramAccounts", - "params": [ - "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", - { - "filters": [ - { - "dataSize": 17 - }, - { - "memcmp": { - "offset": 4, - "bytes": "3Mc6vR" - } - } - ] - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": [ - { - "account": { - "data": "2R9jLfiAQ9bgdcw6h8s44439", - "executable": false, - "lamports": 15298080, - "owner": "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", - "rentEpoch": 28 - }, - "pubkey": "CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY" - } - ], - "id": 1 -} -``` - -### getRecentPerformanceSamples - -Returns a list of recent performance samples, in reverse slot order. Performance samples are taken every 60 seconds and -include the number of transactions and slots that occur in a given time window. - -#### Parameters: - -- `limit: ` - (optional) number of samples to return (maximum 720) - -#### Results: - -An array of: - -- `RpcPerfSample` - - `slot: ` - Slot in which sample was taken at - - `numTransactions: ` - Number of transactions in sample - - `numSlots: ` - Number of slots in sample - - `samplePeriodSecs: ` - Number of seconds in a sample window - -#### Example: - -Request: - -```bash -// Request -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0", "id":1, "method":"getRecentPerformanceSamples", "params": [4]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": [ - { - "numSlots": 126, - "numTransactions": 126, - "samplePeriodSecs": 60, - "slot": 348125 - }, - { - "numSlots": 126, - "numTransactions": 126, - "samplePeriodSecs": 60, - "slot": 347999 - }, - { - "numSlots": 125, - "numTransactions": 125, - "samplePeriodSecs": 60, - "slot": 347873 - }, - { - "numSlots": 125, - "numTransactions": 125, - "samplePeriodSecs": 60, - "slot": 347748 - } - ], - "id": 1 -} -``` - -### getSignaturesForAddress - -Returns signatures for confirmed transactions that include the given address in -their `accountKeys` list. Returns signatures backwards in time from the -provided signature or most recent confirmed block - -#### Parameters: - -- `` - account address as base-58 encoded string -- `` - (optional) Configuration object containing the following fields: - - (optional) `limit: ` - maximum transaction signatures to return (between 1 and 1,000, default: 1,000). - - (optional) `before: ` - start searching backwards from this transaction signature. - If not provided the search starts from the top of the highest max confirmed block. - - (optional) `until: ` - search until this transaction signature, if found before limit reached. - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -The result field will be an array of transaction signature information, ordered -from newest to oldest transaction: - -- `` - - `signature: ` - transaction signature as base-58 encoded string - - `slot: ` - The slot that contains the block with the transaction - - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) - - `memo: ` - Memo associated with the transaction, null if no memo is present - - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch) of when transaction was processed. null if not available. - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getSignaturesForAddress", - "params": [ - "Vote111111111111111111111111111111111111111", - { - "limit": 1 - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": [ - { - "err": null, - "memo": null, - "signature": "5h6xBEauJ3PK6SWCZ1PGjBvj8vDdWG3KpwATGy1ARAXFSDwt8GFXM7W5Ncn16wmqokgpiKRLuS83KUxyZyv2sUYv", - "slot": 114, - "blockTime": null - } - ], - "id": 1 -} -``` - -### getSignatureStatuses - -Returns the statuses of a list of signatures. Unless the -`searchTransactionHistory` configuration parameter is included, this method only -searches the recent status cache of signatures, which retains statuses for all -active slots plus `MAX_RECENT_BLOCKHASHES` rooted slots. - -#### Parameters: - -- `` - An array of transaction signatures to confirm, as base-58 encoded strings (up to a maximum of 256) -- `` - (optional) Configuration object containing the following field: - - `searchTransactionHistory: ` - if true, a Solana node will search its ledger cache for any signatures not found in the recent status cache - -#### Results: - -An RpcResponse containing a JSON object consisting of an array of TransactionStatus objects. - -- `RpcResponse` - RpcResponse JSON object with `value` field: - -An array of: - -- `` - Unknown transaction -- `` - - `slot: ` - The slot the transaction was processed - - `confirmations: ` - Number of blocks since signature confirmation, null if rooted, as well as finalized by a supermajority of the cluster - - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) - - `confirmationStatus: ` - The transaction's cluster confirmation status; either `processed`, `confirmed`, or `finalized`. See [Commitment](jsonrpc-api.md#configuring-state-commitment) for more on optimistic confirmation. - - DEPRECATED: `status: ` - Transaction status - - `"Ok": ` - Transaction was successful - - `"Err": ` - Transaction failed with TransactionError - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getSignatureStatuses", - "params": [ - [ - "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", - "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7" - ] - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 82 - }, - "value": [ - { - "slot": 72, - "confirmations": 10, - "err": null, - "status": { - "Ok": null - }, - "confirmationStatus": "confirmed" - }, - null - ] - }, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getSignatureStatuses", - "params": [ - [ - "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW" - ], - { - "searchTransactionHistory": true - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 82 - }, - "value": [ - { - "slot": 48, - "confirmations": null, - "err": null, - "status": { - "Ok": null - }, - "confirmationStatus": "finalized" - }, - null - ] - }, - "id": 1 -} -``` - -### getSlot - -Returns the slot that has reached the [given or default commitment level](jsonrpc-api.md#configuring-state-commitment) - -#### Parameters: - -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -- `` - Current slot - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getSlot"} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 1234, "id": 1 } -``` - -### getSlotLeader - -Returns the current slot leader - -#### Parameters: - -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -- `` - Node identity Pubkey as base-58 encoded string - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getSlotLeader"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": "ENvAW7JScgYq6o4zKZwewtkzzJgDzuJAFxYasvmEQdpS", - "id": 1 -} -``` - -### getSlotLeaders - -Returns the slot leaders for a given slot range - -#### Parameters: - -- `` - Start slot, as u64 integer -- `` - Limit, as u64 integer (between 1 and 5,000) - -#### Results: - -- `` - Node identity public keys as base-58 encoded strings - -#### Example: - -If the current slot is #99, query the next 10 leaders with the following request: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getSlotLeaders", "params":[100, 10]} -' -``` - -Result: - -The first leader returned is the leader for slot #100: - -```json -{ - "jsonrpc": "2.0", - "result": [ - "ChorusmmK7i1AxXeiTtQgQZhQNiXYU84ULeaYF1EH15n", - "ChorusmmK7i1AxXeiTtQgQZhQNiXYU84ULeaYF1EH15n", - "ChorusmmK7i1AxXeiTtQgQZhQNiXYU84ULeaYF1EH15n", - "ChorusmmK7i1AxXeiTtQgQZhQNiXYU84ULeaYF1EH15n", - "Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM", - "Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM", - "Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM", - "Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM", - "DWvDTSh3qfn88UoQTEKRV2JnLt5jtJAVoiCo3ivtMwXP", - "DWvDTSh3qfn88UoQTEKRV2JnLt5jtJAVoiCo3ivtMwXP" - ], - "id": 1 -} -``` - -### getStakeActivation - -Returns epoch activation information for a stake account - -#### Parameters: - -- `` - Pubkey of stake account to query, as base-58 encoded string -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `epoch: ` - epoch for which to calculate activation details. If parameter not provided, defaults to current epoch. - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -The result will be a JSON object with the following fields: - -- `state: ` - stake active during the epoch -- `inactive: ` - stake inactive during the epoch - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getStakeActivation", "params": ["CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT"]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { "active": 197717120, "inactive": 0, "state": "active" }, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getStakeActivation", - "params": [ - "CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT", - { - "epoch": 4 - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "active": 124429280, - "inactive": 73287840, - "state": "activating" - }, - "id": 1 -} -``` - -### getStakeMinimumDelegation - -Returns the stake minimum delegation, in lamports. - -#### Parameters: - -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to: - -- `` - The stake minimum delegation, in lamports - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1,"method":"getStakeMinimumDelegation"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 501 - }, - "value": 1000000000 - }, - "id": 1 -} -``` - -### getSupply - -Returns information about the current supply. - -#### Parameters: - -- `` - (optional) Configuration object containing the following optional fields: - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `excludeNonCirculatingAccountsList: ` - exclude non circulating accounts list from response - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to a JSON object containing: - -- `total: ` - Total supply in lamports -- `circulating: ` - Circulating supply in lamports -- `nonCirculating: ` - Non-circulating supply in lamports -- `nonCirculatingAccounts: ` - an array of account addresses of non-circulating accounts, as strings. If `excludeNonCirculatingAccountsList` is enabled, the returned array will be empty. - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0", "id":1, "method":"getSupply"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1114 - }, - "value": { - "circulating": 16000, - "nonCirculating": 1000000, - "nonCirculatingAccounts": [ - "FEy8pTbP5fEoqMV1GdTz83byuA8EKByqYat1PKDgVAq5", - "9huDUZfxoJ7wGMTffUE7vh1xePqef7gyrLJu9NApncqA", - "3mi1GmwEE3zo2jmfDuzvjSX9ovRXsDUKHvsntpkhuLJ9", - "BYxEJTDerkaRWBem3XgnVcdhppktBXa2HbkHPKj2Ui4Z" - ], - "total": 1016000 - } - }, - "id": 1 -} -``` - -### getTokenAccountBalance - -Returns the token balance of an SPL Token account. - -#### Parameters: - -- `` - Pubkey of Token account to query, as base-58 encoded string -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to a JSON object containing: - -- `amount: ` - the raw balance without decimals, a string representation of u64 -- `decimals: ` - number of base 10 digits to the right of the decimal place -- `uiAmount: ` - the balance, using mint-prescribed decimals **DEPRECATED** -- `uiAmountString: ` - the balance as a string, using mint-prescribed decimals - -For more details on returned data: The -[Token Balances Structure](jsonrpc-api.md#token-balances-structure) response from [getBlock](jsonrpc-api.md#getblock) follows a similar structure. - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0", "id":1, "method":"getTokenAccountBalance", "params": ["7fUAJdStEuGbc3sM84cKRL6yYaaSstyLSU4ve5oovLS7"]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1114 - }, - "value": { - "amount": "9864", - "decimals": 2, - "uiAmount": 98.64, - "uiAmountString": "98.64" - }, - "id": 1 - } -} -``` - -### getTokenAccountsByDelegate - -Returns all SPL Token accounts by approved Delegate. - -#### Parameters: - -- `` - Pubkey of account delegate to query, as base-58 encoded string -- `` - Either: - - `mint: ` - Pubkey of the specific token Mint to limit accounts to, as base-58 encoded string; or - - `programId: ` - Pubkey of the Token program that owns the accounts, as base-58 encoded string -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `encoding: ` - encoding for Account data, either "base58" (_slow_), "base64", "base64+zstd", or "jsonParsed". - "base58" is limited to Account data of less than 129 bytes. - "base64" will return base64 encoded data for Account data of any size. - "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. - "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to "base64" encoding, detectable when the `data` field is type ``. - - (optional) `dataSlice: ` - limit the returned account data using the provided `offset: ` and `length: ` fields; only available for "base58", "base64" or "base64+zstd" encodings. - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to an array of JSON objects, which will contain: - -- `pubkey: ` - the account Pubkey as base-58 encoded string -- `account: ` - a JSON object, with the following sub fields: - - `lamports: `, number of lamports assigned to this account, as a u64 - - `owner: `, base-58 encoded Pubkey of the program this account has been assigned to - - `data: `, Token state data associated with the account, either as encoded binary data or in JSON format `{: }` - - `executable: `, boolean indicating if the account contains a program \(and is strictly read-only\) - - `rentEpoch: `, the epoch at which this account will next owe rent, as u64 - -When the data is requested with the `jsonParsed` encoding a format similar to that of the [Token Balances Structure](jsonrpc-api.md#token-balances-structure) can be expected inside the structure, both for the `tokenAmount` and the `delegatedAmount`, with the latter being an optional object. - -#### Example: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getTokenAccountsByDelegate", - "params": [ - "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", - { - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "encoding": "jsonParsed" - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1114 - }, - "value": [ - { - "account": { - "data": { - "program": "spl-token", - "parsed": { - "info": { - "tokenAmount": { - "amount": "1", - "decimals": 1, - "uiAmount": 0.1, - "uiAmountString": "0.1" - }, - "delegate": "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", - "delegatedAmount": { - "amount": "1", - "decimals": 1, - "uiAmount": 0.1, - "uiAmountString": "0.1" - }, - "state": "initialized", - "isNative": false, - "mint": "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E", - "owner": "CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD" - }, - "type": "account" - }, - "space": 165 - }, - "executable": false, - "lamports": 1726080, - "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "rentEpoch": 4 - }, - "pubkey": "28YTZEwqtMHWrhWcvv34se7pjS7wctgqzCPB3gReCFKp" - } - ] - }, - "id": 1 -} -``` - -### getTokenAccountsByOwner - -Returns all SPL Token accounts by token owner. - -#### Parameters: - -- `` - Pubkey of account owner to query, as base-58 encoded string -- `` - Either: - - `mint: ` - Pubkey of the specific token Mint to limit accounts to, as base-58 encoded string; or - - `programId: ` - Pubkey of the Token program that owns the accounts, as base-58 encoded string -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `encoding: ` - encoding for Account data, either "base58" (_slow_), "base64", "base64+zstd", or "jsonParsed". - "base58" is limited to Account data of less than 129 bytes. - "base64" will return base64 encoded data for Account data of any size. - "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. - "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to "base64" encoding, detectable when the `data` field is type ``. - - (optional) `dataSlice: ` - limit the returned account data using the provided `offset: ` and `length: ` fields; only available for "base58", "base64" or "base64+zstd" encodings. - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to an array of JSON objects, which will contain: - -- `pubkey: ` - the account Pubkey as base-58 encoded string -- `account: ` - a JSON object, with the following sub fields: - - `lamports: `, number of lamports assigned to this account, as a u64 - - `owner: `, base-58 encoded Pubkey of the program this account has been assigned to - - `data: `, Token state data associated with the account, either as encoded binary data or in JSON format `{: }` - - `executable: `, boolean indicating if the account contains a program \(and is strictly read-only\) - - `rentEpoch: `, the epoch at which this account will next owe rent, as u64 - -When the data is requested with the `jsonParsed` encoding a format similar to that of the [Token Balances Structure](jsonrpc-api.md#token-balances-structure) can be expected inside the structure, both for the `tokenAmount` and the `delegatedAmount`, with the latter being an optional object. - -#### Example: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getTokenAccountsByOwner", - "params": [ - "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F", - { - "mint": "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E" - }, - { - "encoding": "jsonParsed" - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1114 - }, - "value": [ - { - "account": { - "data": { - "program": "spl-token", - "parsed": { - "accountType": "account", - "info": { - "tokenAmount": { - "amount": "1", - "decimals": 1, - "uiAmount": 0.1, - "uiAmountString": "0.1" - }, - "delegate": "4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", - "delegatedAmount": { - "amount": "1", - "decimals": 1, - "uiAmount": 0.1, - "uiAmountString": "0.1" - }, - "state": "initialized", - "isNative": false, - "mint": "3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E", - "owner": "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F" - }, - "type": "account" - }, - "space": 165 - }, - "executable": false, - "lamports": 1726080, - "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "rentEpoch": 4 - }, - "pubkey": "C2gJg6tKpQs41PRS1nC8aw3ZKNZK3HQQZGVrDFDup5nx" - } - ] - }, - "id": 1 -} -``` - -### getTokenLargestAccounts - -Returns the 20 largest accounts of a particular SPL Token type. - -#### Parameters: - -- `` - Pubkey of token Mint to query, as base-58 encoded string -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to an array of JSON objects containing: - -- `address: ` - the address of the token account -- `amount: ` - the raw token account balance without decimals, a string representation of u64 -- `decimals: ` - number of base 10 digits to the right of the decimal place -- `uiAmount: ` - the token account balance, using mint-prescribed decimals **DEPRECATED** -- `uiAmountString: ` - the token account balance as a string, using mint-prescribed decimals - -#### Example: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0", "id":1, "method":"getTokenLargestAccounts", "params": ["3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E"]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1114 - }, - "value": [ - { - "address": "FYjHNoFtSQ5uijKrZFyYAxvEr87hsKXkXcxkcmkBAf4r", - "amount": "771", - "decimals": 2, - "uiAmount": 7.71, - "uiAmountString": "7.71" - }, - { - "address": "BnsywxTcaYeNUtzrPxQUvzAWxfzZe3ZLUJ4wMMuLESnu", - "amount": "229", - "decimals": 2, - "uiAmount": 2.29, - "uiAmountString": "2.29" - } - ] - }, - "id": 1 -} -``` - -### getTokenSupply - -Returns the total supply of an SPL Token type. - -#### Parameters: - -- `` - Pubkey of token Mint to query, as base-58 encoded string -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to a JSON object containing: - -- `amount: ` - the raw total token supply without decimals, a string representation of u64 -- `decimals: ` - number of base 10 digits to the right of the decimal place -- `uiAmount: ` - the total token supply, using mint-prescribed decimals **DEPRECATED** -- `uiAmountString: ` - the total token supply as a string, using mint-prescribed decimals - -#### Example: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0", "id":1, "method":"getTokenSupply", "params": ["3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E"]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1114 - }, - "value": { - "amount": "100000", - "decimals": 2, - "uiAmount": 1000, - "uiAmountString": "1000" - } - }, - "id": 1 -} -``` - -### getTransaction - -Returns transaction details for a confirmed transaction - -#### Parameters: - -- `` - transaction signature as base-58 encoded string -- `` - (optional) Configuration object containing the following optional fields: - - (optional) `encoding: ` - encoding for each returned Transaction, either "json", "jsonParsed", "base58" (_slow_), "base64". If parameter not provided, the default encoding is "json". - "jsonParsed" encoding attempts to use program-specific instruction parsers to return more human-readable and explicit data in the `transaction.message.instructions` list. If "jsonParsed" is requested but a parser cannot be found, the instruction falls back to regular JSON encoding (`accounts`, `data`, and `programIdIndex` fields). - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". - - (optional) `maxSupportedTransactionVersion: ` - set the max transaction version to return in responses. If the requested transaction is a higher version, an error will be returned. If this parameter is omitted, only legacy transactions will be returned, and any versioned transaction will prompt the error. - -#### Results: - -- `` - if transaction is not found or not confirmed -- `` - if transaction is confirmed, an object with the following fields: - - `slot: ` - the slot this transaction was processed in - - `transaction: ` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter - - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch) of when the transaction was processed. null if not available - - `meta: ` - transaction status metadata object: - - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://docs.rs/solana-sdk/VERSION_FOR_DOCS_RS/solana_sdk/transaction/enum.TransactionError.html) - - `fee: ` - fee this transaction was charged, as u64 integer - - `preBalances: ` - array of u64 account balances from before the transaction was processed - - `postBalances: ` - array of u64 account balances after the transaction was processed - - `innerInstructions: ` - List of [inner instructions](#inner-instructions-structure) or `null` if inner instruction recording was not enabled during this transaction - - `preTokenBalances: ` - List of [token balances](#token-balances-structure) from before the transaction was processed or omitted if token balance recording was not yet enabled during this transaction - - `postTokenBalances: ` - List of [token balances](#token-balances-structure) from after the transaction was processed or omitted if token balance recording was not yet enabled during this transaction - - `logMessages: ` - array of string log messages or `null` if log message recording was not enabled during this transaction - - DEPRECATED: `status: ` - Transaction status - - `"Ok": ` - Transaction was successful - - `"Err": ` - Transaction failed with TransactionError - - `rewards: ` - present if rewards are requested; an array of JSON objects containing: - - `pubkey: ` - The public key, as base-58 encoded string, of the account that received the reward - - `lamports: `- number of reward lamports credited or debited by the account, as a i64 - - `postBalance: ` - account balance in lamports after the reward was applied - - `rewardType: ` - type of reward: currently only "rent", other types may be added in the future - - `commission: ` - vote account commission when the reward was credited, only present for voting and staking rewards - - `loadedAddresses: ` - Transaction addresses loaded from address lookup tables. Undefined if `maxSupportedTransactionVersion` is not set in request params. - - `writable: ` - Ordered list of base-58 encoded addresses for writable loaded accounts - - `readonly: ` - Ordered list of base-58 encoded addresses for readonly loaded accounts - - `version: <"legacy"|number|undefined>` - Transaction version. Undefined if `maxSupportedTransactionVersion` is not set in request params. - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getTransaction", - "params": [ - "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv", - "json" - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "meta": { - "err": null, - "fee": 5000, - "innerInstructions": [], - "postBalances": [499998932500, 26858640, 1, 1, 1], - "postTokenBalances": [], - "preBalances": [499998937500, 26858640, 1, 1, 1], - "preTokenBalances": [], - "status": { - "Ok": null - } - }, - "slot": 430, - "transaction": { - "message": { - "accountKeys": [ - "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe", - "AjozzgE83A3x1sHNUR64hfH7zaEBWeMaFuAN9kQgujrc", - "SysvarS1otHashes111111111111111111111111111", - "SysvarC1ock11111111111111111111111111111111", - "Vote111111111111111111111111111111111111111" - ], - "header": { - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 3, - "numRequiredSignatures": 1 - }, - "instructions": [ - { - "accounts": [1, 2, 3, 0], - "data": "37u9WtQpcm6ULa3WRQHmj49EPs4if7o9f1jSRVZpm2dvihR9C8jY4NqEwXUbLwx15HBSNcP1", - "programIdIndex": 4 - } - ], - "recentBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B" - }, - "signatures": [ - "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv" - ] - } - }, - "blockTime": null, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getTransaction", - "params": [ - "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv", - "base64" - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "meta": { - "err": null, - "fee": 5000, - "innerInstructions": [], - "postBalances": [499998932500, 26858640, 1, 1, 1], - "postTokenBalances": [], - "preBalances": [499998937500, 26858640, 1, 1, 1], - "preTokenBalances": [], - "status": { - "Ok": null - } - }, - "slot": 430, - "transaction": [ - "AVj7dxHlQ9IrvdYVIjuiRFs1jLaDMHixgrv+qtHBwz51L4/ImLZhszwiyEJDIp7xeBSpm/TX5B7mYzxa+fPOMw0BAAMFJMJVqLw+hJYheizSoYlLm53KzgT82cDVmazarqQKG2GQsLgiqktA+a+FDR4/7xnDX7rsusMwryYVUdixfz1B1Qan1RcZLwqvxvJl4/t3zHragsUp0L47E24tAFUgAAAABqfVFxjHdMkoVmOYaR1etoteuKObS21cc1VbIQAAAAAHYUgdNXR0u3xNdiTr072z2DVec9EQQ/wNo1OAAAAAAAtxOUhPBp2WSjUNJEgfvy70BbxI00fZyEPvFHNfxrtEAQQEAQIDADUCAAAAAQAAAAAAAACtAQAAAAAAAAdUE18R96XTJCe+YfRfUp6WP+YKCy/72ucOL8AoBFSpAA==", - "base64" - ] - }, - "id": 1 -} -``` - -### getTransactionCount - -Returns the current Transaction count from the ledger - -#### Parameters: - -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -- `` - count - -#### Example: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getTransactionCount"} -' - -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 268, "id": 1 } -``` - -### getVersion - -Returns the current solana versions running on the node - -#### Parameters: - -None - -#### Results: - -The result field will be a JSON object with the following fields: - -- `solana-core`, software version of solana-core -- `feature-set`, unique identifier of the current software's feature set - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getVersion"} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": { "solana-core": "1.11.6" }, "id": 1 } -``` - -### getVoteAccounts - -Returns the account info and associated stake for all the voting accounts in the current bank. - -#### Parameters: - -- `` - (optional) Configuration object containing the following field: - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `votePubkey: ` - Only return results for this validator vote address (base-58 encoded) - - (optional) `keepUnstakedDelinquents: ` - Do not filter out delinquent validators with no stake - - (optional) `delinquentSlotDistance: ` - Specify the number of slots behind the tip that a validator must fall to be considered delinquent. **NOTE:** For the sake of consistency between ecosystem products, _it is **not** recommended that this argument be specified._ - -#### Results: - -The result field will be a JSON object of `current` and `delinquent` accounts, -each containing an array of JSON objects with the following sub fields: - -- `votePubkey: ` - Vote account address, as base-58 encoded string -- `nodePubkey: ` - Validator identity, as base-58 encoded string -- `activatedStake: ` - the stake, in lamports, delegated to this vote account and active in this epoch -- `epochVoteAccount: ` - bool, whether the vote account is staked for this epoch -- `commission: `, percentage (0-100) of rewards payout owed to the vote account -- `lastVote: ` - Most recent slot voted on by this vote account -- `epochCredits: ` - History of how many credits earned by the end of each epoch, as an array of arrays containing: `[epoch, credits, previousCredits]` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getVoteAccounts"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "current": [ - { - "commission": 0, - "epochVoteAccount": true, - "epochCredits": [ - [1, 64, 0], - [2, 192, 64] - ], - "nodePubkey": "B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD", - "lastVote": 147, - "activatedStake": 42, - "votePubkey": "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw" - } - ], - "delinquent": [ - { - "commission": 127, - "epochVoteAccount": false, - "epochCredits": [], - "nodePubkey": "6ZPxeQaDo4bkZLRsdNrCzchNQr5LN9QMc9sipXv9Kw8f", - "lastVote": 0, - "activatedStake": 0, - "votePubkey": "CmgCk4aMS7KW1SHX3s9K5tBJ6Yng2LBaC8MFov4wx9sm" - } - ] - }, - "id": 1 -} -``` - -#### Example: Restrict results to a single validator vote account - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getVoteAccounts", - "params": [ - { - "votePubkey": "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw" - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "current": [ - { - "commission": 0, - "epochVoteAccount": true, - "epochCredits": [ - [1, 64, 0], - [2, 192, 64] - ], - "nodePubkey": "B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD", - "lastVote": 147, - "activatedStake": 42, - "votePubkey": "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw" - } - ], - "delinquent": [] - }, - "id": 1 -} -``` - -### isBlockhashValid - -**NEW: This method is only available in solana-core v1.9 or newer. Please use -[getFeeCalculatorForBlockhash](jsonrpc-api.md#getfeecalculatorforblockhash) for solana-core v1.8** - -Returns whether a blockhash is still valid or not - -#### Parameters: - -- `blockhash: ` - the blockhash of this block, as base-58 encoded string -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) (used for retrieving blockhash) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -- `` - True if the blockhash is still valid - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "id":45, - "jsonrpc":"2.0", - "method":"isBlockhashValid", - "params":[ - "J7rBdM6AecPDEZp8aPq5iPSNKVkU5Q76F3oAV4eW5wsW", - {"commitment":"processed"} - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 2483 - }, - "value": false - }, - "id": 1 -} -``` - -### minimumLedgerSlot - -Returns the lowest slot that the node has information about in its ledger. This -value may increase over time if the node is configured to purge older ledger data - -#### Parameters: - -None - -#### Results: - -- `u64` - Minimum ledger slot - -#### Example: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"minimumLedgerSlot"} -' - -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 1234, "id": 1 } -``` - -### requestAirdrop - -Requests an airdrop of lamports to a Pubkey - -#### Parameters: - -- `` - Pubkey of account to receive lamports, as base-58 encoded string -- `` - lamports, as a u64 -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) (used for retrieving blockhash and verifying airdrop success) - -#### Results: - -- `` - Transaction Signature of airdrop, as base-58 encoded string - -#### Example: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"requestAirdrop", "params":["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri", 1000000000]} -' - -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", - "id": 1 -} -``` - -### sendTransaction - -Submits a signed transaction to the cluster for processing. - -This method does not alter the transaction in any way; it relays the -transaction created by clients to the node as-is. - -If the node's rpc service receives the transaction, this method immediately -succeeds, without waiting for any confirmations. A successful response from -this method does not guarantee the transaction is processed or confirmed by the -cluster. - -While the rpc service will reasonably retry to submit it, the transaction -could be rejected if transaction's `recent_blockhash` expires before it lands. - -Use [`getSignatureStatuses`](jsonrpc-api.md#getsignaturestatuses) to ensure -a transaction is processed and confirmed. - -Before submitting, the following preflight checks are performed: - -1. The transaction signatures are verified -2. The transaction is simulated against the bank slot specified by the preflight - commitment. On failure an error will be returned. Preflight checks may be - disabled if desired. It is recommended to specify the same commitment and - preflight commitment to avoid confusing behavior. - -The returned signature is the first signature in the transaction, which -is used to identify the transaction ([transaction id](../../terminology.md#transaction-id)). -This identifier can be easily extracted from the transaction data before -submission. - -#### Parameters: - -- `` - fully-signed Transaction, as encoded string -- `` - (optional) Configuration object containing the following field: - - `skipPreflight: ` - if true, skip the preflight transaction checks (default: false) - - `preflightCommitment: ` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) level to use for preflight (default: `"finalized"`). - - `encoding: ` - (optional) Encoding used for the transaction data. Either `"base58"` (_slow_, **DEPRECATED**), or `"base64"`. (default: `"base58"`). - - `maxRetries: ` - (optional) Maximum number of times for the RPC node to retry sending the transaction to the leader. - If this parameter not provided, the RPC node will retry the transaction until it is finalized or until the blockhash expires. - - (optional) `minContextSlot: ` - set the minimum slot at which to perform preflight transaction checks. - -#### Results: - -- `` - First Transaction Signature embedded in the transaction, as base-58 encoded string ([transaction id](../../terminology.md#transaction-id)) - -#### Example: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "sendTransaction", - "params": [ - "4hXTCkRzt9WyecNzV1XPgCDfGAZzQKNxLXgynz5QDuWWPSAZBZSHptvWRL3BjCvzUXRdKvHL2b7yGrRQcWyaqsaBCncVG7BFggS8w9snUts67BSh3EqKpXLUm5UMHfD7ZBe9GhARjbNQMLJ1QD3Spr6oMTBU6EhdB4RD8CP2xUxr2u3d6fos36PD98XS6oX8TQjLpsMwncs5DAMiD4nNnR8NBfyghGCWvCVifVwvA8B8TJxE1aiyiv2L429BCWfyzAme5sZW8rDb14NeCQHhZbtNqfXhcp2tAnaAT" - ] - } -' - -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": "2id3YC2jK9G5Wo2phDx4gJVAew8DcY5NAojnVuao8rkxwPYPe8cSwE5GzhEgJA2y8fVjDEo6iR6ykBvDxrTQrtpb", - "id": 1 -} -``` - -### simulateTransaction - -Simulate sending a transaction - -#### Parameters: - -- `` - Transaction, as an encoded string. The transaction must have a valid blockhash, but is not required to be signed. -- `` - (optional) Configuration object containing the following fields: - - `sigVerify: ` - if true the transaction signatures will be verified (default: false, conflicts with `replaceRecentBlockhash`) - - `commitment: ` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) level to simulate the transaction at (default: `"finalized"`). - - `encoding: ` - (optional) Encoding used for the transaction data. Either `"base58"` (_slow_, **DEPRECATED**), or `"base64"`. (default: `"base58"`). - - `replaceRecentBlockhash: ` - (optional) if true the transaction recent blockhash will be replaced with the most recent blockhash. - (default: false, conflicts with `sigVerify`) - - `accounts: ` - (optional) Accounts configuration object containing the following fields: - - `encoding: ` - (optional) encoding for returned Account data, either "base64" (default), "base64+zstd" or "jsonParsed". - "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type ``. - - `addresses: ` - An array of accounts to return, as base-58 encoded strings - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -An RpcResponse containing a TransactionStatus object -The result will be an RpcResponse JSON object with `value` set to a JSON object with the following fields: - -- `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) -- `logs: ` - Array of log messages the transaction instructions output during execution, null if simulation failed before the transaction was able to execute (for example due to an invalid blockhash or signature verification failure) -- `accounts: ` - array of accounts with the same length as the `accounts.addresses` array in the request - - `` - if the account doesn't exist or if `err` is not null - - `` - otherwise, a JSON object containing: - - `lamports: `, number of lamports assigned to this account, as a u64 - - `owner: `, base-58 encoded Pubkey of the program this account has been assigned to - - `data: <[string, encoding]|object>`, data associated with the account, either as encoded binary data or JSON format `{: }`, depending on encoding parameter - - `executable: `, boolean indicating if the account contains a program \(and is strictly read-only\) - - `rentEpoch: `, the epoch at which this account will next owe rent, as u64 -- `unitsConsumed: `, The number of compute budget units consumed during the processing of this transaction -- `returnData: ` - the most-recent return data generated by an instruction in the transaction, with the following fields: - - `programId: `, the program that generated the return data, as base-58 encoded Pubkey - - `data: <[string, encoding]>`, the return data itself, as base-64 encoded binary data - -#### Example: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "simulateTransaction", - "params": [ - "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEDArczbMia1tLmq7zz4DinMNN0pJ1JtLdqIJPUw3YrGCzYAMHBsgN27lcgB6H2WQvFgyZuJYHa46puOQo9yQ8CVQbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCp20C7Wj2aiuk5TReAXo+VTVg8QTHjs0UjNMMKCvpzZ+ABAgEBARU=", - { - "encoding":"base64", - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 218 - }, - "value": { - "err": null, - "accounts": null, - "logs": [ - "Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri invoke [1]", - "Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri consumed 2366 of 1400000 compute units", - "Program return: 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri KgAAAAAAAAA=", - "Program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri success" - ], - "returnData": { - "data": [ - "Kg==", - "base64" - ], - "programId": "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri" - }, - "unitsConsumed": 2366 - } - }, - "id": 1 -} -``` - -## Subscription Websocket - -After connecting to the RPC PubSub websocket at `ws://
    /`: - -- Submit subscription requests to the websocket using the methods below -- Multiple subscriptions may be active at once -- Many subscriptions take the optional [`commitment` parameter](jsonrpc-api.md#configuring-state-commitment), defining how finalized a change should be to trigger a notification. For subscriptions, if commitment is unspecified, the default value is `"finalized"`. - -### accountSubscribe - -Subscribe to an account to receive notifications when the lamports or data for a given account public key changes - -#### Parameters: - -- `` - account Pubkey, as base-58 encoded string -- `` - (optional) Configuration object containing the following optional fields: - - `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - `encoding: ` - encoding for Account data, either "base58" (_slow_), "base64", "base64+zstd" or "jsonParsed". - "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type ``. - -#### Results: - -- `` - Subscription id \(needed to unsubscribe\) - -#### Example: - -Request: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "accountSubscribe", - "params": [ - "CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12", - { - "encoding": "base64", - "commitment": "finalized" - } - ] -} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "accountSubscribe", - "params": [ - "CM78CPUeXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNH12", - { - "encoding": "jsonParsed" - } - ] -} -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 23784, "id": 1 } -``` - -#### Notification Format: - -The notification format is the same as seen in the [getAccountInfo](jsonrpc-api.md#getAccountInfo) RPC HTTP method. - -Base58 encoding: - -```json -{ - "jsonrpc": "2.0", - "method": "accountNotification", - "params": { - "result": { - "context": { - "slot": 5199307 - }, - "value": { - "data": [ - "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHPXHRDEHrBesJhZyqnnq9qJeUuF7WHxiuLuL5twc38w2TXNLxnDbjmuR", - "base58" - ], - "executable": false, - "lamports": 33594, - "owner": "11111111111111111111111111111111", - "rentEpoch": 635 - } - }, - "subscription": 23784 - } -} -``` - -Parsed-JSON encoding: - -```json -{ - "jsonrpc": "2.0", - "method": "accountNotification", - "params": { - "result": { - "context": { - "slot": 5199307 - }, - "value": { - "data": { - "program": "nonce", - "parsed": { - "type": "initialized", - "info": { - "authority": "Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX", - "blockhash": "LUaQTmM7WbMRiATdMMHaRGakPtCkc2GHtH57STKXs6k", - "feeCalculator": { - "lamportsPerSignature": 5000 - } - } - } - }, - "executable": false, - "lamports": 33594, - "owner": "11111111111111111111111111111111", - "rentEpoch": 635 - } - }, - "subscription": 23784 - } -} -``` - -### accountUnsubscribe - -Unsubscribe from account change notifications - -#### Parameters: - -- `` - id of account Subscription to cancel - -#### Results: - -- `` - unsubscribe success message - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "accountUnsubscribe", "params": [0] } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": true, "id": 1 } -``` - -### blockSubscribe - Unstable, disabled by default - -**This subscription is unstable and only available if the validator was started -with the `--rpc-pubsub-enable-block-subscription` flag. The format of this -subscription may change in the future** - -Subscribe to receive notification anytime a new block is Confirmed or Finalized. - -#### Parameters: - -- `filter: |` - filter criteria for the logs to receive results by account type; currently supported: - - "all" - include all transactions in block - - `{ "mentionsAccountOrProgram": }` - return only transactions that mention the provided public key (as base-58 encoded string). If no mentions in a given block, then no notification will be sent. -- `` - (optional) Configuration object containing the following optional fields: - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `encoding: ` - encoding for Account data, either "base58" (_slow_), "base64", "base64+zstd" or "jsonParsed". - "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to base64 encoding, detectable when the `data` field is type ``. Default is "base64". - - (optional) `transactionDetails: ` - level of transaction detail to return, either "full", "signatures", or "none". If parameter not provided, the default detail level is "full". - - (optional) `showRewards: bool` - whether to populate the `rewards` array. If parameter not provided, the default includes rewards. - -#### Results: - -- `integer` - subscription id \(needed to unsubscribe\) - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": "1", "method": "blockSubscribe", "params": ["all"] } -``` - -```json -{ - "jsonrpc": "2.0", - "id": "1", - "method": "blockSubscribe", - "params": [ - { - "mentionsAccountOrProgram": "LieKvPRE8XeX3Y2xVNHjKlpAScD12lYySBVQ4HqoJ5op" - }, - { - "commitment": "confirmed", - "encoding": "base64", - "showRewards": true, - "transactionDetails": "full" - } - ] -} -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 0, "id": 1 } -``` - -#### Notification Format: - -The notification will be an object with the following fields: - --`slot: ` - The corresponding slot. - -- `err: ` - Error if something went wrong publishing the notification otherwise null. -- `block: ` - A block object as seen in the [getBlock](jsonrpc-api.md#getblock) RPC HTTP method. - -```json -{ - "jsonrpc": "2.0", - "method": "blockNotification", - "params": { - "result": { - "context": { - "slot": 112301554 - }, - "value": { - "slot": 112301554, - "block": { - "previousBlockhash": "GJp125YAN4ufCSUvZJVdCyWQJ7RPWMmwxoyUQySydZA", - "blockhash": "6ojMHjctdqfB55JDpEpqfHnP96fiaHEcvzEQ2NNcxzHP", - "parentSlot": 112301553, - "transactions": [ - { - "transaction": [ - "OpltwoUvWxYi1P2U8vbIdE/aPntjYo5Aa0VQ2JJyeJE2g9Vvxk8dDGgFMruYfDu8/IfUWb0REppTe7IpAuuLRgIBAAkWnj4KHRpEWWW7gvO1c0BHy06wZi2g7/DLqpEtkRsThAXIdBbhXCLvltw50ZnjDx2hzw74NVn49kmpYj2VZHQJoeJoYJqaKcvuxCi/2i4yywedcVNDWkM84Iuw+cEn9/ROCrXY4qBFI9dveEERQ1c4kdU46xjxj9Vi+QXkb2Kx45QFVkG4Y7HHsoS6WNUiw2m4ffnMNnOVdF9tJht7oeuEfDMuUEaO7l9JeUxppCvrGk3CP45saO51gkwVYEgKzhpKjCx3rgsYxNR81fY4hnUQXSbbc2Y55FkwgRBpVvQK7/+clR4Gjhd3L4y+OtPl7QF93Akg1LaU9wRMs5nvfDFlggqI9PqJl+IvVWrNRdBbPS8LIIhcwbRTkSbqlJQWxYg3Bo2CTVbw7rt1ZubuHWWp0mD/UJpLXGm2JprWTePNULzHu67sfqaWF99LwmwjTyYEkqkRt1T0Je5VzHgJs0N5jY4iIU9K3lMqvrKOIn/2zEMZ+ol2gdgjshx+sphIyhw65F3J/Dbzk04LLkK+CULmN571Y+hFlXF2ke0BIuUG6AUF+4214Cu7FXnqo3rkxEHDZAk0lRrAJ8X/Z+iwuwI5cgbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpDLAp8axcEkaQkLDKRoWxqp8XLNZSKial7Rk+ELAVVKWoWLRXRZ+OIggu0OzMExvVLE5VHqy71FNHq4gGitkiKYNFWSLIE4qGfdFLZXy/6hwS+wq9ewjikCpd//C9BcCL7Wl0iQdUslxNVCBZHnCoPYih9JXvGefOb9WWnjGy14sG9j70+RSVx6BlkFELWwFvIlWR/tHn3EhHAuL0inS2pwX7ZQTAU6gDVaoqbR2EiJ47cKoPycBNvHLoKxoY9AZaBjPl6q8SKQJSFyFd9n44opAgI6zMTjYF/8Ok4VpXEESp3QaoUyTI9sOJ6oFP6f4dwnvQelgXS+AEfAsHsKXxGAIUDQENAgMEBQAGBwgIDg8IBJCER3QXl1AVDBADCQoOAAQLERITDAjb7ugh3gOuTy==", - "base64" - ], - "meta": { - "err": null, - "status": { - "Ok": null - }, - "fee": 5000, - "preBalances": [ - 1758510880, 2067120, 1566000, 1461600, 2039280, 2039280, - 1900080, 1865280, 0, 3680844220, 2039280 - ], - "postBalances": [ - 1758505880, 2067120, 1566000, 1461600, 2039280, 2039280, - 1900080, 1865280, 0, 3680844220, 2039280 - ], - "innerInstructions": [ - { - "index": 0, - "instructions": [ - { - "programIdIndex": 13, - "accounts": [1, 15, 3, 4, 2, 14], - "data": "21TeLgZXNbtHXVBzCaiRmH" - }, - { - "programIdIndex": 14, - "accounts": [3, 4, 1], - "data": "6qfC8ic7Aq99" - }, - { - "programIdIndex": 13, - "accounts": [1, 15, 3, 5, 2, 14], - "data": "21TeLgZXNbsn4QEpaSEr3q" - }, - { - "programIdIndex": 14, - "accounts": [3, 5, 1], - "data": "6LC7BYyxhFRh" - } - ] - }, - { - "index": 1, - "instructions": [ - { - "programIdIndex": 14, - "accounts": [4, 3, 0], - "data": "7aUiLHFjSVdZ" - }, - { - "programIdIndex": 19, - "accounts": [17, 18, 16, 9, 11, 12, 14], - "data": "8kvZyjATKQWYxaKR1qD53V" - }, - { - "programIdIndex": 14, - "accounts": [9, 11, 18], - "data": "6qfC8ic7Aq99" - } - ] - } - ], - "logMessages": [ - "Program QMNeHCGYnLVDn1icRAfQZpjPLBNkfGbSKRB83G5d8KB invoke [1]", - "Program QMWoBmAyJLAsA1Lh9ugMTw2gciTihncciphzdNzdZYV invoke [2]" - ], - "preTokenBalances": [ - { - "accountIndex": 4, - "mint": "iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u", - "uiTokenAmount": { - "uiAmount": null, - "decimals": 6, - "amount": "0", - "uiAmountString": "0" - }, - "owner": "LieKvPRE8XeX3Y2xVNHjKlpAScD12lYySBVQ4HqoJ5op", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "accountIndex": 5, - "mint": "iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u", - "uiTokenAmount": { - "uiAmount": 11513.0679, - "decimals": 6, - "amount": "11513067900", - "uiAmountString": "11513.0679" - }, - "owner": "rXhAofQCT7NN9TUqigyEAUzV1uLL4boeD8CRkNBSkYk", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "accountIndex": 10, - "mint": "Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1", - "uiTokenAmount": { - "uiAmount": null, - "decimals": 6, - "amount": "0", - "uiAmountString": "0" - }, - "owner": "CL9wkGFT3SZRRNa9dgaovuRV7jrVVigBUZ6DjcgySsCU", - "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - }, - { - "accountIndex": 11, - "mint": "Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1", - "uiTokenAmount": { - "uiAmount": 15138.514093, - "decimals": 6, - "amount": "15138514093", - "uiAmountString": "15138.514093" - }, - "owner": "LieKvPRE8XeX3Y2xVNHjKlpAScD12lYySBVQ4HqoJ5op", - "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - } - ], - "postTokenBalances": [ - { - "accountIndex": 4, - "mint": "iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u", - "uiTokenAmount": { - "uiAmount": null, - "decimals": 6, - "amount": "0", - "uiAmountString": "0" - }, - "owner": "LieKvPRE8XeX3Y2xVNHjKlpAScD12lYySBVQ4HqoJ5op", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "accountIndex": 5, - "mint": "iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u", - "uiTokenAmount": { - "uiAmount": 11513.103028, - "decimals": 6, - "amount": "11513103028", - "uiAmountString": "11513.103028" - }, - "owner": "rXhAofQCT7NN9TUqigyEAUzV1uLL4boeD8CRkNBSkYk", - "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - }, - { - "accountIndex": 10, - "mint": "Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1", - "uiTokenAmount": { - "uiAmount": null, - "decimals": 6, - "amount": "0", - "uiAmountString": "0" - }, - "owner": "CL9wkGFT3SZRRNa9dgaovuRV7jrVVigBUZ6DjcgySsCU", - "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - }, - { - "accountIndex": 11, - "mint": "Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1", - "uiTokenAmount": { - "uiAmount": 15489.767829, - "decimals": 6, - "amount": "15489767829", - "uiAmountString": "15489.767829" - }, - "owner": "BeiHVPRE8XeX3Y2xVNrSsTpAScH94nYySBVQ4HqgN9at", - "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" - } - ], - "rewards": [] - } - } - ], - "blockTime": 1639926816, - "blockHeight": 101210751 - }, - "err": null - } - }, - "subscription": 14 - } -} -``` - -### blockUnsubscribe - -Unsubscribe from block notifications - -#### Parameters: - -- `` - subscription id to cancel - -#### Results: - -- `` - unsubscribe success message - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "blockUnsubscribe", "params": [0] } -``` - -Response: - -```json -{ "jsonrpc": "2.0", "result": true, "id": 1 } -``` - -### logsSubscribe - -Subscribe to transaction logging - -#### Parameters: - -- `filter: |` - filter criteria for the logs to receive results by account type; currently supported: - - "all" - subscribe to all transactions except for simple vote transactions - - "allWithVotes" - subscribe to all transactions including simple vote transactions - - `{ "mentions": [ ] }` - subscribe to all transactions that mention the provided Pubkey (as base-58 encoded string) -- `` - (optional) Configuration object containing the following optional fields: - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -- `` - Subscription id \(needed to unsubscribe\) - -#### Example: - -Request: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "logsSubscribe", - "params": [ - { - "mentions": [ "11111111111111111111111111111111" ] - }, - { - "commitment": "finalized" - } - ] -} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "logsSubscribe", - "params": [ "all" ] -} -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 24040, "id": 1 } -``` - -#### Notification Format: - -The notification will be an RpcResponse JSON object with value equal to: - -- `signature: ` - The transaction signature base58 encoded. -- `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) -- `logs: ` - Array of log messages the transaction instructions output during execution, null if simulation failed before the transaction was able to execute (for example due to an invalid blockhash or signature verification failure) - -Example: - -```json -{ - "jsonrpc": "2.0", - "method": "logsNotification", - "params": { - "result": { - "context": { - "slot": 5208469 - }, - "value": { - "signature": "5h6xBEauJ3PK6SWCZ1PGjBvj8vDdWG3KpwATGy1ARAXFSDwt8GFXM7W5Ncn16wmqokgpiKRLuS83KUxyZyv2sUYv", - "err": null, - "logs": [ - "BPF program 83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri success" - ] - } - }, - "subscription": 24040 - } -} -``` - -### logsUnsubscribe - -Unsubscribe from transaction logging - -#### Parameters: - -- `` - id of subscription to cancel - -#### Results: - -- `` - unsubscribe success message - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "logsUnsubscribe", "params": [0] } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": true, "id": 1 } -``` - -### programSubscribe - -Subscribe to a program to receive notifications when the lamports or data for a given account owned by the program changes - -#### Parameters: - -- `` - program_id Pubkey, as base-58 encoded string -- `` - (optional) Configuration object containing the following optional fields: - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - `encoding: ` - encoding for Account data, either "base58" (_slow_), "base64", "base64+zstd" or "jsonParsed". - "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to base64 encoding, detectable when the `data` field is type ``. - - (optional) `filters: ` - filter results using various [filter objects](jsonrpc-api.md#filters); account must meet all filter criteria to be included in results - -#### Results: - -- `` - Subscription id \(needed to unsubscribe\) - -#### Example: - -Request: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "programSubscribe", - "params": [ - "11111111111111111111111111111111", - { - "encoding": "base64", - "commitment": "finalized" - } - ] -} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "programSubscribe", - "params": [ - "11111111111111111111111111111111", - { - "encoding": "jsonParsed" - } - ] -} -{ - "jsonrpc": "2.0", - "id": 1, - "method": "programSubscribe", - "params": [ - "11111111111111111111111111111111", - { - "encoding": "base64", - "filters": [ - { - "dataSize": 80 - } - ] - } - ] -} -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 24040, "id": 1 } -``` - -#### Notification Format: - -The notification format is a single program account object as seen in the [getProgramAccounts](jsonrpc-api.md#getProgramAccounts) RPC HTTP method. - -Base58 encoding: - -```json -{ - "jsonrpc": "2.0", - "method": "programNotification", - "params": { - "result": { - "context": { - "slot": 5208469 - }, - "value": { - "pubkey": "H4vnBqifaSACnKa7acsxstsY1iV1bvJNxsCY7enrd1hq", - "account": { - "data": [ - "11116bv5nS2h3y12kD1yUKeMZvGcKLSjQgX6BeV7u1FrjeJcKfsHPXHRDEHrBesJhZyqnnq9qJeUuF7WHxiuLuL5twc38w2TXNLxnDbjmuR", - "base58" - ], - "executable": false, - "lamports": 33594, - "owner": "11111111111111111111111111111111", - "rentEpoch": 636 - } - } - }, - "subscription": 24040 - } -} -``` - -Parsed-JSON encoding: - -```json -{ - "jsonrpc": "2.0", - "method": "programNotification", - "params": { - "result": { - "context": { - "slot": 5208469 - }, - "value": { - "pubkey": "H4vnBqifaSACnKa7acsxstsY1iV1bvJNxsCY7enrd1hq", - "account": { - "data": { - "program": "nonce", - "parsed": { - "type": "initialized", - "info": { - "authority": "Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX", - "blockhash": "LUaQTmM7WbMRiATdMMHaRGakPtCkc2GHtH57STKXs6k", - "feeCalculator": { - "lamportsPerSignature": 5000 - } - } - } - }, - "executable": false, - "lamports": 33594, - "owner": "11111111111111111111111111111111", - "rentEpoch": 636 - } - } - }, - "subscription": 24040 - } -} -``` - -### programUnsubscribe - -Unsubscribe from program-owned account change notifications - -#### Parameters: - -- `` - id of account Subscription to cancel - -#### Results: - -- `` - unsubscribe success message - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "programUnsubscribe", "params": [0] } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": true, "id": 1 } -``` - -### signatureSubscribe - -Subscribe to a transaction signature to receive notification when the transaction is confirmed On `signatureNotification`, the subscription is automatically cancelled - -#### Parameters: - -- `` - Transaction Signature, as base-58 encoded string -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -- `integer` - subscription id \(needed to unsubscribe\) - -#### Example: - -Request: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "signatureSubscribe", - "params": [ - "2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b" - ] -} - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "signatureSubscribe", - "params": [ - "2EBVM6cB8vAAD93Ktr6Vd8p67XPbQzCJX47MpReuiCXJAtcjaxpvWpcg9Ege1Nr5Tk3a2GFrByT7WPBjdsTycY9b", - { - "commitment": "finalized" - } - ] -} -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 0, "id": 1 } -``` - -#### Notification Format: - -The notification will be an RpcResponse JSON object with value containing an object with: - -- `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) - -Example: - -```json -{ - "jsonrpc": "2.0", - "method": "signatureNotification", - "params": { - "result": { - "context": { - "slot": 5207624 - }, - "value": { - "err": null - } - }, - "subscription": 24006 - } -} -``` - -### signatureUnsubscribe - -Unsubscribe from signature confirmation notification - -#### Parameters: - -- `` - subscription id to cancel - -#### Results: - -- `` - unsubscribe success message - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "signatureUnsubscribe", "params": [0] } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": true, "id": 1 } -``` - -### slotSubscribe - -Subscribe to receive notification anytime a slot is processed by the validator - -#### Parameters: - -None - -#### Results: - -- `integer` - subscription id \(needed to unsubscribe\) - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "slotSubscribe" } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 0, "id": 1 } -``` - -#### Notification Format: - -The notification will be an object with the following fields: - -- `parent: ` - The parent slot -- `root: ` - The current root slot -- `slot: ` - The newly set slot value - -Example: - -```json -{ - "jsonrpc": "2.0", - "method": "slotNotification", - "params": { - "result": { - "parent": 75, - "root": 44, - "slot": 76 - }, - "subscription": 0 - } -} -``` - -### slotUnsubscribe - -Unsubscribe from slot notifications - -#### Parameters: - -- `` - subscription id to cancel - -#### Results: - -- `` - unsubscribe success message - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "slotUnsubscribe", "params": [0] } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": true, "id": 1 } -``` - -### slotsUpdatesSubscribe - Unstable - -**This subscription is unstable; the format of this subscription may change in -the future and it may not always be supported** - -Subscribe to receive a notification from the validator on a variety of updates -on every slot - -#### Parameters: - -None - -#### Results: - -- `integer` - subscription id \(needed to unsubscribe\) - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "slotsUpdatesSubscribe" } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 0, "id": 1 } -``` - -#### Notification Format: - -The notification will be an object with the following fields: - -- `parent: ` - The parent slot -- `slot: ` - The newly updated slot -- `timestamp: ` - The Unix timestamp of the update -- `type: ` - The update type, one of: - - "firstShredReceived" - - "completed" - - "createdBank" - - "frozen" - - "dead" - - "optimisticConfirmation" - - "root" - -```bash -{ - "jsonrpc": "2.0", - "method": "slotsUpdatesNotification", - "params": { - "result": { - "parent": 75, - "slot": 76, - "timestamp": 1625081266243, - "type": "optimisticConfirmation" - }, - "subscription": 0 - } -} -``` - -### slotsUpdatesUnsubscribe - -Unsubscribe from slot-update notifications - -#### Parameters: - -- `` - subscription id to cancel - -#### Results: - -- `` - unsubscribe success message - -#### Example: - -Request: - -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "slotsUpdatesUnsubscribe", - "params": [0] -} -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": true, "id": 1 } -``` - -### rootSubscribe - -Subscribe to receive notification anytime a new root is set by the validator. - -#### Parameters: - -None - -#### Results: - -- `integer` - subscription id \(needed to unsubscribe\) - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "rootSubscribe" } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 0, "id": 1 } -``` - -#### Notification Format: - -The result is the latest root slot number. - -```json -{ - "jsonrpc": "2.0", - "method": "rootNotification", - "params": { - "result": 42, - "subscription": 0 - } -} -``` - -### rootUnsubscribe - -Unsubscribe from root notifications - -#### Parameters: - -- `` - subscription id to cancel - -#### Results: - -- `` - unsubscribe success message - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "rootUnsubscribe", "params": [0] } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": true, "id": 1 } -``` - -### voteSubscribe - Unstable, disabled by default - -**This subscription is unstable and only available if the validator was started -with the `--rpc-pubsub-enable-vote-subscription` flag. The format of this -subscription may change in the future** - -Subscribe to receive notification anytime a new vote is observed in gossip. -These votes are pre-consensus therefore there is no guarantee these votes will -enter the ledger. - -#### Parameters: - -None - -#### Results: - -- `integer` - subscription id \(needed to unsubscribe\) - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "voteSubscribe" } -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 0, "id": 1 } -``` - -#### Notification Format: - -The notification will be an object with the following fields: - -- `hash: ` - The vote hash -- `slots: ` - The slots covered by the vote, as an array of u64 integers -- `timestamp: ` - The timestamp of the vote -- `signature: ` - The signature of the transaction that contained this vote - -```json -{ - "jsonrpc": "2.0", - "method": "voteNotification", - "params": { - "result": { - "hash": "8Rshv2oMkPu5E4opXTRyuyBeZBqQ4S477VG26wUTFxUM", - "slots": [1, 2], - "timestamp": null - }, - "subscription": 0 - } -} -``` - -### voteUnsubscribe - -Unsubscribe from vote notifications - -#### Parameters: - -- `` - subscription id to cancel - -#### Results: - -- `` - unsubscribe success message - -#### Example: - -Request: - -```json -{ "jsonrpc": "2.0", "id": 1, "method": "voteUnsubscribe", "params": [0] } -``` - -Response: - -```json -{ "jsonrpc": "2.0", "result": true, "id": 1 } -``` - -## JSON RPC API Deprecated Methods - -### getConfirmedBlock - -**DEPRECATED: Please use [getBlock](jsonrpc-api.md#getblock) instead** -This method is expected to be removed in solana-core v2.0 - -Returns identity and transaction information about a confirmed block in the ledger - -#### Parameters: - -- `` - slot, as u64 integer -- `` - (optional) Configuration object containing the following optional fields: - - (optional) `encoding: ` - encoding for each returned Transaction, either "json", "jsonParsed", "base58" (_slow_), "base64". If parameter not provided, the default encoding is "json". - "jsonParsed" encoding attempts to use program-specific instruction parsers to return more human-readable and explicit data in the `transaction.message.instructions` list. If "jsonParsed" is requested but a parser cannot be found, the instruction falls back to regular JSON encoding (`accounts`, `data`, and `programIdIndex` fields). - - (optional) `transactionDetails: ` - level of transaction detail to return, either "full", "signatures", or "none". If parameter not provided, the default detail level is "full". - - (optional) `rewards: bool` - whether to populate the `rewards` array. If parameter not provided, the default includes rewards. - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". - -#### Results: - -The result field will be an object with the following fields: - -- `` - if specified block is not confirmed -- `` - if block is confirmed, an object with the following fields: - - `blockhash: ` - the blockhash of this block, as base-58 encoded string - - `previousBlockhash: ` - the blockhash of this block's parent, as base-58 encoded string; if the parent block is not available due to ledger cleanup, this field will return "11111111111111111111111111111111" - - `parentSlot: ` - the slot index of this block's parent - - `transactions: ` - present if "full" transaction details are requested; an array of JSON objects containing: - - `transaction: ` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter - - `meta: ` - transaction status metadata object, containing `null` or: - - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) - - `fee: ` - fee this transaction was charged, as u64 integer - - `preBalances: ` - array of u64 account balances from before the transaction was processed - - `postBalances: ` - array of u64 account balances after the transaction was processed - - `innerInstructions: ` - List of [inner instructions](#inner-instructions-structure) or `null` if inner instruction recording was not enabled during this transaction - - `preTokenBalances: ` - List of [token balances](#token-balances-structure) from before the transaction was processed or omitted if token balance recording was not yet enabled during this transaction - - `postTokenBalances: ` - List of [token balances](#token-balances-structure) from after the transaction was processed or omitted if token balance recording was not yet enabled during this transaction - - `logMessages: ` - array of string log messages or `null` if log message recording was not enabled during this transaction - - DEPRECATED: `status: ` - Transaction status - - `"Ok": ` - Transaction was successful - - `"Err": ` - Transaction failed with TransactionError - - `signatures: ` - present if "signatures" are requested for transaction details; an array of signatures strings, corresponding to the transaction order in the block - - `rewards: ` - present if rewards are requested; an array of JSON objects containing: - - `pubkey: ` - The public key, as base-58 encoded string, of the account that received the reward - - `lamports: `- number of reward lamports credited or debited by the account, as a i64 - - `postBalance: ` - account balance in lamports after the reward was applied - - `rewardType: ` - type of reward: "fee", "rent", "voting", "staking" - - `commission: ` - vote account commission when the reward was credited, only present for voting and staking rewards - - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch). null if not available - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, {"encoding": "json","transactionDetails":"full","rewards":false}]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "blockTime": null, - "blockhash": "3Eq21vXNB5s86c62bVuUfTeaMif1N2kUqRPBmGRJhyTA", - "parentSlot": 429, - "previousBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B", - "transactions": [ - { - "meta": { - "err": null, - "fee": 5000, - "innerInstructions": [], - "logMessages": [], - "postBalances": [499998932500, 26858640, 1, 1, 1], - "postTokenBalances": [], - "preBalances": [499998937500, 26858640, 1, 1, 1], - "preTokenBalances": [], - "status": { - "Ok": null - } - }, - "transaction": { - "message": { - "accountKeys": [ - "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe", - "AjozzgE83A3x1sHNUR64hfH7zaEBWeMaFuAN9kQgujrc", - "SysvarS1otHashes111111111111111111111111111", - "SysvarC1ock11111111111111111111111111111111", - "Vote111111111111111111111111111111111111111" - ], - "header": { - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 3, - "numRequiredSignatures": 1 - }, - "instructions": [ - { - "accounts": [1, 2, 3, 0], - "data": "37u9WtQpcm6ULa3WRQHmj49EPs4if7o9f1jSRVZpm2dvihR9C8jY4NqEwXUbLwx15HBSNcP1", - "programIdIndex": 4 - } - ], - "recentBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B" - }, - "signatures": [ - "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv" - ] - } - } - ] - }, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, "base64"]} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "blockTime": null, - "blockhash": "3Eq21vXNB5s86c62bVuUfTeaMif1N2kUqRPBmGRJhyTA", - "parentSlot": 429, - "previousBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B", - "rewards": [], - "transactions": [ - { - "meta": { - "err": null, - "fee": 5000, - "innerInstructions": [], - "logMessages": [], - "postBalances": [499998932500, 26858640, 1, 1, 1], - "postTokenBalances": [], - "preBalances": [499998937500, 26858640, 1, 1, 1], - "preTokenBalances": [], - "status": { - "Ok": null - } - }, - "transaction": [ - "AVj7dxHlQ9IrvdYVIjuiRFs1jLaDMHixgrv+qtHBwz51L4/ImLZhszwiyEJDIp7xeBSpm/TX5B7mYzxa+fPOMw0BAAMFJMJVqLw+hJYheizSoYlLm53KzgT82cDVmazarqQKG2GQsLgiqktA+a+FDR4/7xnDX7rsusMwryYVUdixfz1B1Qan1RcZLwqvxvJl4/t3zHragsUp0L47E24tAFUgAAAABqfVFxjHdMkoVmOYaR1etoteuKObS21cc1VbIQAAAAAHYUgdNXR0u3xNdiTr072z2DVec9EQQ/wNo1OAAAAAAAtxOUhPBp2WSjUNJEgfvy70BbxI00fZyEPvFHNfxrtEAQQEAQIDADUCAAAAAQAAAAAAAACtAQAAAAAAAAdUE18R96XTJCe+YfRfUp6WP+YKCy/72ucOL8AoBFSpAA==", - "base64" - ] - } - ] - }, - "id": 1 -} -``` - -For more details on returned data: -[Transaction Structure](jsonrpc-api.md#transaction-structure) -[Inner Instructions Structure](jsonrpc-api.md#inner-instructions-structure) -[Token Balances Structure](jsonrpc-api.md#token-balances-structure) - -### getConfirmedBlocks - -**DEPRECATED: Please use [getBlocks](jsonrpc-api.md#getblocks) instead** -This method is expected to be removed in solana-core v2.0 - -Returns a list of confirmed blocks between two slots - -#### Parameters: - -- `` - start_slot, as u64 integer -- `` - (optional) end_slot, as u64 integer -- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". - -#### Results: - -The result field will be an array of u64 integers listing confirmed blocks -between `start_slot` and either `end_slot`, if provided, or latest confirmed block, -inclusive. Max range allowed is 500,000 slots. - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc": "2.0","id":1,"method":"getConfirmedBlocks","params":[5, 10]} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": [5, 6, 7, 8, 9, 10], "id": 1 } -``` - -### getConfirmedBlocksWithLimit - -**DEPRECATED: Please use [getBlocksWithLimit](jsonrpc-api.md#getblockswithlimit) instead** -This method is expected to be removed in solana-core v2.0 - -Returns a list of confirmed blocks starting at the given slot - -#### Parameters: - -- `` - start_slot, as u64 integer -- `` - limit, as u64 integer -- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". - -#### Results: - -The result field will be an array of u64 integers listing confirmed blocks -starting at `start_slot` for up to `limit` blocks, inclusive. - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc": "2.0","id":1,"method":"getConfirmedBlocksWithLimit","params":[5, 3]} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": [5, 6, 7], "id": 1 } -``` - -### getConfirmedSignaturesForAddress2 - -**DEPRECATED: Please use [getSignaturesForAddress](jsonrpc-api.md#getsignaturesforaddress) instead** -This method is expected to be removed in solana-core v2.0 - -Returns signatures for confirmed transactions that include the given address in -their `accountKeys` list. Returns signatures backwards in time from the -provided signature or most recent confirmed block - -#### Parameters: - -- `` - account address as base-58 encoded string -- `` - (optional) Configuration object containing the following fields: - - `limit: ` - (optional) maximum transaction signatures to return (between 1 and 1,000, default: 1,000). - - `before: ` - (optional) start searching backwards from this transaction signature. - If not provided the search starts from the top of the highest max confirmed block. - - `until: ` - (optional) search until this transaction signature, if found before limit reached. - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". - -#### Results: - -The result field will be an array of transaction signature information, ordered -from newest to oldest transaction: - -- `` - - `signature: ` - transaction signature as base-58 encoded string - - `slot: ` - The slot that contains the block with the transaction - - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/c0c60386544ec9a9ec7119229f37386d9f070523/sdk/src/transaction/error.rs#L13) - - `memo: ` - Memo associated with the transaction, null if no memo is present - - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch) of when transaction was processed. null if not available. - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getConfirmedSignaturesForAddress2", - "params": [ - "Vote111111111111111111111111111111111111111", - { - "limit": 1 - } - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": [ - { - "err": null, - "memo": null, - "signature": "5h6xBEauJ3PK6SWCZ1PGjBvj8vDdWG3KpwATGy1ARAXFSDwt8GFXM7W5Ncn16wmqokgpiKRLuS83KUxyZyv2sUYv", - "slot": 114, - "blockTime": null - } - ], - "id": 1 -} -``` - -### getConfirmedTransaction - -**DEPRECATED: Please use [getTransaction](jsonrpc-api.md#gettransaction) instead** -This method is expected to be removed in solana-core v2.0 - -Returns transaction details for a confirmed transaction - -#### Parameters: - -- `` - transaction signature as base-58 encoded string -- `` - (optional) Configuration object containing the following optional fields: - - (optional) `encoding: ` - encoding for each returned Transaction, either "json", "jsonParsed", "base58" (_slow_), "base64". If parameter not provided, the default encoding is "json". - "jsonParsed" encoding attempts to use program-specific instruction parsers to return more human-readable and explicit data in the `transaction.message.instructions` list. If "jsonParsed" is requested but a parser cannot be found, the instruction falls back to regular JSON encoding (`accounts`, `data`, and `programIdIndex` fields). - - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". - -#### Results: - -- `` - if transaction is not found or not confirmed -- `` - if transaction is confirmed, an object with the following fields: - - `slot: ` - the slot this transaction was processed in - - `transaction: ` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter - - `blockTime: ` - estimated production time, as Unix timestamp (seconds since the Unix epoch) of when the transaction was processed. null if not available - - `meta: ` - transaction status metadata object: - - `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://docs.rs/solana-sdk/VERSION_FOR_DOCS_RS/solana_sdk/transaction/enum.TransactionError.html) - - `fee: ` - fee this transaction was charged, as u64 integer - - `preBalances: ` - array of u64 account balances from before the transaction was processed - - `postBalances: ` - array of u64 account balances after the transaction was processed - - `innerInstructions: ` - List of [inner instructions](#inner-instructions-structure) or `null` if inner instruction recording was not enabled during this transaction - - `preTokenBalances: ` - List of [token balances](#token-balances-structure) from before the transaction was processed or omitted if token balance recording was not yet enabled during this transaction - - `postTokenBalances: ` - List of [token balances](#token-balances-structure) from after the transaction was processed or omitted if token balance recording was not yet enabled during this transaction - - `logMessages: ` - array of string log messages or `null` if log message recording was not enabled during this transaction - - DEPRECATED: `status: ` - Transaction status - - `"Ok": ` - Transaction was successful - - `"Err": ` - Transaction failed with TransactionError - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getConfirmedTransaction", - "params": [ - "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv", - "json" - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "meta": { - "err": null, - "fee": 5000, - "innerInstructions": [], - "postBalances": [499998932500, 26858640, 1, 1, 1], - "postTokenBalances": [], - "preBalances": [499998937500, 26858640, 1, 1, 1], - "preTokenBalances": [], - "status": { - "Ok": null - } - }, - "slot": 430, - "transaction": { - "message": { - "accountKeys": [ - "3UVYmECPPMZSCqWKfENfuoTv51fTDTWicX9xmBD2euKe", - "AjozzgE83A3x1sHNUR64hfH7zaEBWeMaFuAN9kQgujrc", - "SysvarS1otHashes111111111111111111111111111", - "SysvarC1ock11111111111111111111111111111111", - "Vote111111111111111111111111111111111111111" - ], - "header": { - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 3, - "numRequiredSignatures": 1 - }, - "instructions": [ - { - "accounts": [1, 2, 3, 0], - "data": "37u9WtQpcm6ULa3WRQHmj49EPs4if7o9f1jSRVZpm2dvihR9C8jY4NqEwXUbLwx15HBSNcP1", - "programIdIndex": 4 - } - ], - "recentBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B" - }, - "signatures": [ - "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv" - ] - } - }, - "blockTime": null, - "id": 1 -} -``` - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getConfirmedTransaction", - "params": [ - "2nBhEBYYvfaAe16UMNqRHre4YNSskvuYgx3M6E4JP1oDYvZEJHvoPzyUidNgNX5r9sTyN1J9UxtbCXy2rqYcuyuv", - "base64" - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "meta": { - "err": null, - "fee": 5000, - "innerInstructions": [], - "postBalances": [499998932500, 26858640, 1, 1, 1], - "postTokenBalances": [], - "preBalances": [499998937500, 26858640, 1, 1, 1], - "preTokenBalances": [], - "status": { - "Ok": null - } - }, - "slot": 430, - "transaction": [ - "AVj7dxHlQ9IrvdYVIjuiRFs1jLaDMHixgrv+qtHBwz51L4/ImLZhszwiyEJDIp7xeBSpm/TX5B7mYzxa+fPOMw0BAAMFJMJVqLw+hJYheizSoYlLm53KzgT82cDVmazarqQKG2GQsLgiqktA+a+FDR4/7xnDX7rsusMwryYVUdixfz1B1Qan1RcZLwqvxvJl4/t3zHragsUp0L47E24tAFUgAAAABqfVFxjHdMkoVmOYaR1etoteuKObS21cc1VbIQAAAAAHYUgdNXR0u3xNdiTr072z2DVec9EQQ/wNo1OAAAAAAAtxOUhPBp2WSjUNJEgfvy70BbxI00fZyEPvFHNfxrtEAQQEAQIDADUCAAAAAQAAAAAAAACtAQAAAAAAAAdUE18R96XTJCe+YfRfUp6WP+YKCy/72ucOL8AoBFSpAA==", - "base64" - ] - }, - "id": 1 -} -``` - -### getFeeCalculatorForBlockhash - -**DEPRECATED: Please use [isBlockhashValid](jsonrpc-api.md#isblockhashvalid) or [getFeeForMessage](jsonrpc-api.md#getfeeformessage) instead** -This method is expected to be removed in solana-core v2.0 - -Returns the fee calculator associated with the query blockhash, or `null` if the blockhash has expired - -#### Parameters: - -- `` - query blockhash as a Base58 encoded string -- `` - (optional) Configuration object containing the following fields: - - (optional) `commitment: ` - [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `minContextSlot: ` - set the minimum slot that the request can be evaluated at. - -#### Results: - -The result will be an RpcResponse JSON object with `value` equal to: - -- `` - if the query blockhash has expired -- `` - otherwise, a JSON object containing: - - `feeCalculator: `, `FeeCalculator` object describing the cluster fee rate at the queried blockhash - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - { - "jsonrpc": "2.0", - "id": 1, - "method": "getFeeCalculatorForBlockhash", - "params": [ - "GJxqhuxcgfn5Tcj6y3f8X4FeCDd2RQ6SnEMo1AAxrPRZ" - ] - } -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 221 - }, - "value": { - "feeCalculator": { - "lamportsPerSignature": 5000 - } - } - }, - "id": 1 -} -``` - -### getFeeRateGovernor - -Returns the fee rate governor information from the root bank - -#### Parameters: - -None - -#### Results: - -The `result` field will be an `object` with the following fields: - -- `burnPercent: `, Percentage of fees collected to be destroyed -- `maxLamportsPerSignature: `, Largest value `lamportsPerSignature` can attain for the next slot -- `minLamportsPerSignature: `, Smallest value `lamportsPerSignature` can attain for the next slot -- `targetLamportsPerSignature: `, Desired fee rate for the cluster -- `targetSignaturesPerSlot: `, Desired signature rate for the cluster - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getFeeRateGovernor"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 54 - }, - "value": { - "feeRateGovernor": { - "burnPercent": 50, - "maxLamportsPerSignature": 100000, - "minLamportsPerSignature": 5000, - "targetLamportsPerSignature": 10000, - "targetSignaturesPerSlot": 20000 - } - } - }, - "id": 1 -} -``` - -### getFees - -**DEPRECATED: Please use [getFeeForMessage](jsonrpc-api.md#getfeeformessage) instead** -This method is expected to be removed in solana-core v2.0 - -Returns a recent block hash from the ledger, a fee schedule that can be used to -compute the cost of submitting a transaction using it, and the last slot in -which the blockhash will be valid. - -#### Parameters: - -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -The result will be an RpcResponse JSON object with `value` set to a JSON object with the following fields: - -- `blockhash: ` - a Hash as base-58 encoded string -- `feeCalculator: ` - FeeCalculator object, the fee schedule for this block hash -- `lastValidSlot: ` - DEPRECATED - this value is inaccurate and should not be relied upon -- `lastValidBlockHeight: ` - last [block height](../../terminology.md#block-height) at which the blockhash will be valid - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getFees"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1 - }, - "value": { - "blockhash": "CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR", - "feeCalculator": { - "lamportsPerSignature": 5000 - }, - "lastValidSlot": 297, - "lastValidBlockHeight": 296 - } - }, - "id": 1 -} -``` - -### getRecentBlockhash - -**DEPRECATED: Please use [getLatestBlockhash](jsonrpc-api.md#getlatestblockhash) instead** -This method is expected to be removed in solana-core v2.0 - -Returns a recent block hash from the ledger, and a fee schedule that can be used to compute the cost of submitting a transaction using it. - -#### Parameters: - -- `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - -#### Results: - -An RpcResponse containing a JSON object consisting of a string blockhash and FeeCalculator JSON object. - -- `RpcResponse` - RpcResponse JSON object with `value` field set to a JSON object including: -- `blockhash: ` - a Hash as base-58 encoded string -- `feeCalculator: ` - FeeCalculator object, the fee schedule for this block hash - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getRecentBlockhash"} -' -``` - -Result: - -```json -{ - "jsonrpc": "2.0", - "result": { - "context": { - "slot": 1 - }, - "value": { - "blockhash": "CSymwgTNX1j3E4qhKfJAUE41nBWEwXufoYryPbkde5RR", - "feeCalculator": { - "lamportsPerSignature": 5000 - } - } - }, - "id": 1 -} -``` - -### getSnapshotSlot - -**DEPRECATED: Please use [getHighestSnapshotSlot](jsonrpc-api.md#gethighestsnapshotslot) instead** -This method is expected to be removed in solana-core v2.0 - -Returns the highest slot that the node has a snapshot for - -#### Parameters: - -None - -#### Results: - -- `` - Snapshot slot - -#### Example: - -Request: - -```bash -curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' - {"jsonrpc":"2.0","id":1, "method":"getSnapshotSlot"} -' -``` - -Result: - -```json -{ "jsonrpc": "2.0", "result": 100, "id": 1 } -``` - -Result when the node has no snapshot: - -```json -{ - "jsonrpc": "2.0", - "error": { "code": -32008, "message": "No snapshot" }, - "id": 1 -} -``` diff --git a/docs/src/developing/clients/rust-api.md b/docs/src/developing/clients/rust-api.md index 6deaf81443acdb..dd49310d00f525 100644 --- a/docs/src/developing/clients/rust-api.md +++ b/docs/src/developing/clients/rust-api.md @@ -19,7 +19,7 @@ Some important crates: that do not run on-chain will import this. - [`solana-client`] — For interacting with a Solana node via the - [JSON RPC API](jsonrpc-api). + [JSON RPC API](/api). - [`solana-cli-config`] — Loading and saving the Solana CLI configuration file. diff --git a/docs/src/developing/guides/compressed-nfts.md b/docs/src/developing/guides/compressed-nfts.md new file mode 100644 index 00000000000000..afcfbd564c9db1 --- /dev/null +++ b/docs/src/developing/guides/compressed-nfts.md @@ -0,0 +1,862 @@ +--- +title: Creating Compressed NFTs with JavaScript +description: + "Compressed NFTs use the Bubblegum program from Metaplex to cheaply and + securely store NFT metadata using State Compression on Solana." +keywords: + - compression + - merkle tree + - read api + - metaplex +--- + +Compressed NFTs on Solana use the +[Bubblegum](https://docs.metaplex.com/programs/compression/) program from +Metaplex to cheaply and securely store NFT metadata using +[State Compression](../../learn/state-compression.md). + +This developer guide will use JavaScript/TypeScript to demonstrate: + +- [how to create a tree for compressed NFTs](#create-a-tree), +- [how to mint compressed NFTs into a tree](#mint-compressed-nfts), +- [how to get compressed NFT metadata from the Read API](#reading-compressed-nfts-metadata), + and +- [how to transfer compressed NFTs](#transfer-compressed-nfts) + +## Intro to Compressed NFTs + +Compressed NFTs use [State Compression](../../learn/state-compression.md) and +[merkle trees](../../learn/state-compression.md#what-is-a-merkle-tree) to +drastically reduce the storage cost for NFTs. Instead of storing an NFT's +metadata in a typical Solana account, compressed NFTs store the metadata within +the ledger. This allows compressed NFTs to still inherit the security and speed +of the Solana blockchain, while at the same time reducing the overall storage +costs. + +Even though the on-chain data storage mechanism is different than their +uncompressed counterparts, compressed NFTs still follow the exact same +[Metadata](https://docs.metaplex.com/programs/token-metadata/accounts#metadata) +schema/structure. Allowing you to define your Collection and NFT in an identical +way. + +However, the process to mint and transfer compressed NFTs is different from +uncompressed NFTs. Aside from using a different on-chain program, compressed +NFTs are minting into a merkle tree and require verification of a "proof" to +transfer. More on this below. + +### Compressed NFTs and indexers + +Since compressed NFTs store all of their metadata in the +[ledger](../../terminology.md#ledger), instead of in traditional +[accounts](../../terminology.md#account) like uncompressed NFTs, we will need to +help of indexing services to quickly fetch our compressed NFT's metadata. + +Supporting RPC providers are using the Digital Asset Standard Read API (or "Read +API" for short) to add additional RPC methods that developers can call. These +additional, NFT oriented methods, are loaded with all the information about +particular NFTs. Including support for **BOTH** compressed NFTs **AND** +uncompressed NFTs. + +:::caution Metadata is secured by the ledger and cached by indexers + +Since validators do not keep a very long history of the recent ledger data, +these indexers effectively "cache" the compressed NFT metadata passed through +the Solana ledger. Quickly serving it back on request to improve speed and user +experience of applications. + +However, since the metadata was already secured by the ledger when minting the +compressed NFT, anyone could re-index the metadata directly from the secure +ledger. Allowing for independent verification of the data, should the need or +desire arise. + +::: + +These indexing services are already available from some of the common RPC +providers, with more rolling out support in the near future. To name a few of +the RPC providers that already support the Read API: + +- Helius +- Triton +- SimpleHash + +### How to mint compressed NFTs + +The process to create or mint compressed NFTs on Solana is similar to creating a +"traditional NFT collection", with a few differences. The mint process will +happen in 3 primary steps: + +- create an NFT collection (or use an existing one) +- create a + [concurrent merkle tree](../../learn/state-compression.md#what-is-a-concurrent-merkle-tree) + (using the `@solana/spl-account-compression` SDK) +- mint compressed NFTs into your tree (to any owner's address you want) + +### How to transfer a compressed NFT + +Once your compressed NFT exists on the Solana blockchain, the process to +transfer ownership of a compressed NFT happens in a few broad steps: + +1. get the NFT "asset" information (from the indexer) +2. get the NFT's "proof" (from the indexer) +3. get the Merkle tree account (from the Solana blockchain) +4. prepare the asset proof (by parsing and formatting it) +5. build and send the transfer instruction + +The first three steps primarily involve gathering specific pieces of information +(the `proof` and the tree's canopy depth) for the NFT to be transferred. These +pieces of information are needed to correctly parse/format the `proof` to +actually be sent within the transfer instruction itself. + +## Getting started + +For this guide, we are going to make a few assumptions about the compressed NFT +collection we are going to create: + +- we are going to use TypeScript and NodeJS for this example +- we will use a single, **new** Metaplex collection + +### Project Setup + +Before we start creating our compressed NFT collection, we need to install a few +packages: + +- [`@solana/web3.js`](https://www.npmjs.com/package/@solana/web3.js) - the base + Solana JS SDK for interacting with the blockchain, including making our RPC + connection and sending transactions +- [`@solana/spl-token`](https://www.npmjs.com/package/@solana/spl-token) - used + in creating our collection and mint on-chain +- [`@solana/spl-account-compression`](https://www.npmjs.com/package/@solana/spl-account-compression) - + used to create the on-chain tree to store our compressed NFTs +- [`@metaplex-foundation/mpl-bubblegum`](https://www.npmjs.com/package/@metaplex-foundation/mpl-bubblegum) - + used to get the types and helper functions for minting and transferring + compressed NFTs on-chain +- [`@metaplex-foundation/mpl-token-metadata`](https://www.npmjs.com/package/@metaplex-foundation/mpl-token-metadata) - +used to get the types and helper functions for our NFT's metadata + + +Using your preferred package manager (e.g. npm, yarn, pnpm, etc), install these +packages into your project: + +```sh +yarn add @solana/web3.js @solana/spl-token @solana/spl-account-compression +``` + +```sh +yarn add @metaplex-foundation/mpl-bubblegum @metaplex-foundation/mpl-token-metadata +``` + +## Create a Collection + +NFTs are normally grouped together into a +[Collection](https://docs.metaplex.com/programs/token-metadata/certified-collections#collection-nfts) +using the Metaplex standard. This is true for **BOTH** traditional NFTs **AND** +compressed NFTs. The NFT Collection will store all the broad metadata for our +NFT grouping, such as the collection image and name that will appear in wallets +and explorers. + +Under the hood, an NFT collection acts similar to any other token on Solana. +More specifically, a Collection is effectively a uncompressed NFT. So we +actually create them following the same process of creating an +[SPL token](https://spl.solana.com/token): + +- create a new token "mint" +- create a associated token account (`ata`) for our token mint +- actually mint a single single token +- store the collection's metadata in an Account on-chain + +Since NFT Collections having nothing special to do with +[State Compression](../../learn/state-compression.md) or +[compressed NFTs](./compressed-nfts.md), we will not cover creating one in this +guide. + +### Collection addresses + +Even though this guide does not cover creating one, we will need the many of the +various addresses for your Collection, including: + +- `collectionAuthority` - this may be your `payer` but it also might not be +- `collectionMint` - the collection's mint address +- `collectionMetadata` - the collection's metadata account +- `editionAccount` - for example, the `masterEditionAccount` created for your + collection + +## Create a tree + +One of the most important decisions to make when creating compressed NFTs is +[how to setup your tree](../../learn/state-compression.md#sizing-a-concurrent-merkle-tree). +Especially since the values used to size your tree will determine the overall +cost of creation, and **CANNOT** be changed after creation. + +:::caution + +A tree is **NOT** the same thing as a collection. A single collection can use +_any_ number of trees. In fact, this is usually recommended for larger +collections due to smaller trees having greater composability. + +Conversely, even though a tree **could** be used in multiple collections, it is +generally considered an anti-pattern and is not recommended. + +::: + +Using the helper functions provided by the +[`@solana/spl-account-compression`](https://www.npmjs.com/package/@solana/spl-account-compression) +SDK, we can create our tree in the following steps: + +- decide on our tree size +- generate a new Keypair and allocated space for the tree on-chain +- actually create the tree (making it owned by the Bubblegum program) + +### Size your tree + +Your tree size is set by 3 values, each serving a very specific purpose: + +1. `maxDepth` - used to determine how many NFTs we can have in the tree +2. `maxBufferSize` - used to determine how many updates to your tree are + possible in the same block +3. `canopyDepth` - used to store a portion of the proof on chain, and as such is + a large of cost and composability of your compressed NFT collection + +:::info + +Read more about the details about +[State Compression](../../learn/state-compression.md), including +[how to size a tree](../../learn/state-compression.md#sizing-a-concurrent-merkle-tree) +and potential composability concerns. + +::: + +Let's assume we are going to create a compressed NFT collection with 10k NFTs in +it. And since our collection is relatively small, we only need a single smaller +tree to store all the NFTs: + +```ts +// define the depth and buffer size of our tree to be created +const maxDepthSizePair: ValidDepthSizePair = { + // max=16,384 nodes (for a `maxDepth` of 14) + maxDepth: 14, + maxBufferSize: 64, +}; + +// define the canopy depth of our tree to be created +const canopyDepth = 10; +``` + +Setting a `maxDepth` of `14` will allow our tree to hold up to `16,384` +compressed NFTs, more than exceeding our `10k` collection size. + +Since only specific +[`ValidDepthSizePair`](https://solana-labs.github.io/solana-program-library/account-compression/sdk/docs/modules/index.html#ValidDepthSizePair) +pairs are allowed, simply set the `maxBufferSize` to the corresponding value +tied to your desired `maxDepth`. + +Next, setting `canopyDepth` of `10` tells our tree to store `10` of our "proof +node hashes" on-chain. Thus requiring us to always include `4` proof node values +(i.e. `maxDepth - canopyDepth`) in every compressed NFT transfer instruction. + +### Generate addresses for the tree + +When creating a new tree, we need to generate a new +[Keypair](../../terminology.md#keypair) address for the tree to have: + +```ts +const treeKeypair = Keypair.generate(); +``` + +Since our tree will be used for compressed NFTs, we will also need to derive an +Account with authority that is owned by the Bubblegum program (i.e. PDA): + +```ts +// derive the tree's authority (PDA), owned by Bubblegum +const [treeAuthority, _bump] = PublicKey.findProgramAddressSync( + [treeKeypair.publicKey.toBuffer()], + BUBBLEGUM_PROGRAM_ID, +); +``` + +### Build the tree creation instructions + +With our tree size values defined, and our addresses generated, we need to build +two related instructions: + +1. allocate enough space on-chain for our tree +2. actually create the tree, owned by the Bubblegum program + +Using the +[`createAllocTreeIx`](https://solana-labs.github.io/solana-program-library/account-compression/sdk/docs/modules/index.html#createAllocTreeIx) +helper function, we allocate enough space on-chain for our tree. + +```ts +// allocate the tree's account on chain with the `space` +const allocTreeIx = await createAllocTreeIx( + connection, + treeKeypair.publicKey, + payer.publicKey, + maxDepthSizePair, + canopyDepth, +); +``` + +Then using the +[`createCreateTreeInstruction`](https://metaplex-foundation.github.io/metaplex-program-library/docs/bubblegum/functions/createCreateTreeInstruction.html) +from the Bubblegum SDK, we actually create the tree on-chain. Making it owned by +the Bubblegum program. + +```ts +// create the instruction to actually create the tree +const createTreeIx = createCreateTreeInstruction( + { + payer: payer.publicKey, + treeCreator: payer.publicKey, + treeAuthority, + merkleTree: treeKeypair.publicKey, + compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, + // NOTE: this is used for some on chain logging + logWrapper: SPL_NOOP_PROGRAM_ID, + }, + { + maxBufferSize: maxDepthSizePair.maxBufferSize, + maxDepth: maxDepthSizePair.maxDepth, + public: false, + }, + BUBBLEGUM_PROGRAM_ID, +); +``` + +### Build and send the transaction + +With our two instructions built, we can add them into a transaction and send +them to the blockchain, making sure both the `payer` and generated `treeKeypair` +sign the transaction: + +```ts +// build the transaction +const tx = new Transaction().add(allocTreeIx).add(createTreeIx); +tx.feePayer = payer.publicKey; + +// send the transaction +const txSignature = await sendAndConfirmTransaction( + connection, + tx, + // ensuring the `treeKeypair` PDA and the `payer` are BOTH signers + [treeKeypair, payer], + { + commitment: "confirmed", + skipPreflight: true, + }, +); +``` + +After a few short moments, and once the transaction is confirmed, we are ready +to start minting compressed NFTs into our tree. + +## Mint compressed NFTs + +Since compressed NFTs follow the same Metaplex +[metadata standards](https://docs.metaplex.com/programs/token-metadata/accounts#metadata) +as traditional NFTs, we can define our actual NFTs data the same way. + +The primary difference is that with compressed NFTs the metadata is actually +stored in the ledger (unlike traditional NFTs that store them in accounts). The +metadata gets "hashed" and stored in our tree, and by association, secured by +the Solana ledger. + +Allowing us to cryptographically verify that our original metadata has not +changed (unless we want it to). + +:::info + +Learn more about how State Compression uses +[concurrent merkle trees](../../learn/state-compression.md#what-is-a-concurrent-merkle-tree) +to cryptographically secure off-chain data using the Solana ledger. + +::: + +### Define our NFT's metadata + +We can define the specific metadata for the single NFT we are about to mint: + +```ts +const compressedNFTMetadata: MetadataArgs = { + name: "NFT Name", + symbol: "ANY", + // specific json metadata for each NFT + uri: "https://supersweetcollection.notarealurl/token.json", + creators: null, + editionNonce: 0, + uses: null, + collection: null, + primarySaleHappened: false, + sellerFeeBasisPoints: 0, + isMutable: false, + // these values are taken from the Bubblegum package + tokenProgramVersion: TokenProgramVersion.Original, + tokenStandard: TokenStandard.NonFungible, +}; +``` + +In this demo, the key pieces of our NFT's metadata to note are: + +- `name` - this is the actual name of our NFT that will be displayed in wallets + and on explorers. +- `uri` - this is the address for your NFTs metadata JSON file. +- `creators` - for this example, we are not storing a list of creators. If you + want your NFTs to have royalties, you will need to store actual data here. You + can checkout the Metaplex docs for more info on it. + +### Derive the Bubblegum signer + +When minting new compressed NFTs, the Bubblegum program needs a PDA to perform a +[cross-program invocation](../programming-model/calling-between-programs#cross-program-invocations) +(`cpi`) to the SPL compression program. + +:::caution + +This `bubblegumSigner` PDA is derived using a hard coded seed string of +`collection_cpi` and owned by the Bubblegum program. If this hard coded value is +not provided correctly, your compressed NFT minting will fail. + +::: + +Below, we derive this PDA using the **required** hard coded seed string of +`collection_cpi`: + +```ts +// derive a PDA (owned by Bubblegum) to act as the signer of the compressed minting +const [bubblegumSigner, _bump2] = PublicKey.findProgramAddressSync( + // `collection_cpi` is a custom prefix required by the Bubblegum program + [Buffer.from("collection_cpi", "utf8")], + BUBBLEGUM_PROGRAM_ID, +); +``` + +### Create the mint instruction + +Now we should have all the information we need to actually mint our compressed +NFT. + +Using the `createMintToCollectionV1Instruction` helper function provided in the +Bubblegum SDK, we can craft the instruction to actually mint our compressed NFT +directly into our collection. + +If you have minted traditional NFTs on Solana, this will look fairly similar. We +are creating a new instruction, giving several of the account addresses you +might expect (e.g. the `payer`, `tokenMetadataProgram`, and various collection +addresses), and then some tree specific addresses. + +The addresses to pay special attention to are: + +- `leafOwner` - this will be the owner of the compressed NFT. You can either + mint it your self (i.e. the `payer`), or airdrop to any other Solana address +- `leafDelegate` - this is the delegated authority of this specific NFT we are + about to mint. If you do not want to have a delegated authority for the NFT we + are about to mint, then this value should be set to the same address of + `leafOwner`. + +```ts +const compressedMintIx = createMintToCollectionV1Instruction( + { + payer: payer.publicKey, + + merkleTree: treeAddress, + treeAuthority, + treeDelegate: payer.publicKey, + + // set the receiver of the NFT + leafOwner: receiverAddress || payer.publicKey, + // set a delegated authority over this NFT + leafDelegate: payer.publicKey, + + // collection details + collectionAuthority: payer.publicKey, + collectionAuthorityRecordPda: BUBBLEGUM_PROGRAM_ID, + collectionMint: collectionMint, + collectionMetadata: collectionMetadata, + editionAccount: collectionMasterEditionAccount, + + // other accounts + bubblegumSigner: bubblegumSigner, + compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, + logWrapper: SPL_NOOP_PROGRAM_ID, + tokenMetadataProgram: TOKEN_METADATA_PROGRAM_ID, + }, + { + metadataArgs: Object.assign(compressedNFTMetadata, { + collection: { key: collectionMint, verified: false }, + }), + }, +); +``` + +Some of the other tree specific addresses are: + +- `merkleTree` - the address of our tree we created +- `treeAuthority` - the authority of the tree +- `treeDelegate` - the delegated authority of the entire tree + +Then we also have all of our NFT collection's addresses, including the mint +address, metadata account, and edition account. These addresses are also +standard to pass in when minting uncompressed NFTs. + +#### Sign and send the transaction + +Once our compressed mint instruction has been created, we can add it to a +transaction and send it to the Solana network: + +```ts +const tx = new Transaction().add(compressedMintIx); +tx.feePayer = payer.publicKey; + +// send the transaction to the cluster +const txSignature = await sendAndConfirmTransaction(connection, tx, [payer], { + commitment: "confirmed", + skipPreflight: true, +}); +``` + +## Reading compressed NFTs metadata + +With the help of a supporting RPC provider, developers can use the Digital Asset +Standard Read API (or "Read API" for short) to fetch the metadata of NFTs. + +:::info + +The Read API supports both compressed NFTs and traditional/uncompressed NFTs. +You can use the same RPC endpoints to retrieve all the assorted information for +both types of NFTs, including auto-fetching the NFTs' JSON URI. + +::: + +### Using the Read API + +When working with the Read API and a supporting RPC provider, developers can +make `POST` requests to the RPC endpoint using your preferred method of making +such requests (e.g. `curl`, JavaScript `fetch()`, etc). + +:::warning Asset ID + +Within the Read API, digital assets (i.e. NFTs) are indexed by their `id`. This +asset `id` value differs slightly between traditional NFTs and compressed NFTs: + +- for traditional/uncompressed NFTs: this is the token's address for the actual + Account on-chain that stores the metadata for the asset. +- for compressed NFTs: this is the `id` of the compressed NFT within the tree + and is **NOT** an actual on-chain Account address. While a compressed NFT's + `assetId` resembles a traditional Solana Account address, it is not. + +::: + +### Common Read API Methods + +While the Read API supports more than these listed below, the most commonly used +methods are: + +- `getAsset` - get a specific NFT asset by its `id` +- `getAssetProof` - returns the merkle proof that is required to transfer a + compressed NFT, by its asset `id` +- `getAssetsByOwner` - get the assets owned by a specific address +- `getAssetsByGroup` - get the assets by a specific grouping (i.e. a collection) + +:::info Read API Methods, Schema, and Specification + +Explore all the additional RPC methods added by Digital Asset Standard Read API +on [Metaplex's RPC Playground](https://metaplex-read-api.surge.sh/). Here you +will also find the expected inputs and response schema for each supported RPC +method. + +::: + +### Example Read API Request + +For demonstration, below is an example request for the `getAsset` method using +the +[JavaScript Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), +which is built into modern JavaScript runtimes: + +```ts +// make a POST request to the RPC using the JavaScript `fetch` api +const response = await fetch(rpcEndpointUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: "rpd-op-123", + method: "getAsset", + params: { + id: "5q7qQ4FWYyj4vnFrivRBe6beo6p88X8HTkkyVPjPkQmF", + }, + }), +}); +``` + +### Example Read API Response + +With a successful response from the RPC, you should seem similar data to this: + +```ts +{ + interface: 'V1_NFT', + id: '5q7qQ4FWYyj4vnFrivRBe6beo6p88X8HTkkyVPjPkQmF', + content: [Object], + authorities: [Array], + compression: [Object], + grouping: [], + royalty: [Object], + creators: [], + ownership: [Object], + supply: [Object], + mutable: false +} +``` + +The response fields to pay special attention to are: + +- `id` - this is your asset's `id` +- `grouping` - can tell you the collection address that the NFT belongs to. The + collection address will be the `group_value`. +- `metadata` - contains the actual metadata for the NFT, including the auto + fetched JSON uri set when the NFT was minted +- `ownership` - gives you the NFT owner's address (and also if the NFT has + delegated authority to another address) +- `compression` - tells you if this NFT is actually using compression or not. + For compressed NFTs, this will also give you the tree address that is storing + the compressed NFT on chain. + +:::caution + +Some of the returned values may be empty if the NFT is **not** a compressed NFT, +such as many of the `compression` fields. This is expected. + +::: + +## Transfer compressed NFTs + +Transferring compressed NFTs is different from transferring uncompressed NFTs. +Aside from using a different on-chain program, compressed NFTs require the use +of a asset's "merkle proof" (or `proof` for short) to actually change ownership. + +:::info What is a merkle proof? + +An asset's "merkle proof" is a listing of all the "adjacent hashes" within the +tree that are required to validate a specific leaf within said tree. + +These proof hashes themselves, and the specific asset's leaf data, are hashed +together in a deterministic way to compute the "root hash". Therefore, allowing +for cryptographic validation of an asset within the merkle tree. + +**NOTE:** While each of these hash values resemble a Solana Account's +[address/public key](../../terminology.md#public-key-pubkey), they are not +addresses. + +::: + +Transferring ownership of a compressed NFT happens in 5 broad steps: + +1. get the NFT's "asset" data (from the indexer) +2. get the NFT's proof (from the indexer) +3. get the Merkle tree account (directly from the Solana blockchain) +4. prepare the asset proof +5. build and send the transfer instruction + +The first three steps primarily involve gathering specific pieces of information +(the `proof` and the tree's canopy depth) for the NFT to be transferred. These +pieces of information are needed to correctly parse/format the `proof` to +actually be sent within the transfer instruction itself. + +### Get the asset + +To perform the transfer of our compressed NFT, we will need to retrieve a few +pieces of information about the NFT. + +For starters, we will need to get some the asset's information in order to allow +the on-chain compression program to correctly perform validation and security +checks. + +We can use the `getAsset` RPC method to retrieve two important pieces of +information for the compressed NFT: the `data_hash` and `creator_hash`. + +#### Example response from the `getAsset` method + +Below is an example response from the `getAsset` method: + +```ts +compression: { + eligible: false, + compressed: true, + data_hash: 'D57LAefACeaJesajt6VPAxY4QFXhHjPyZbjq9efrt3jP', + creator_hash: '6Q7xtKPmmLihpHGVBA6u1ENE351YKoyqd3ssHACfmXbn', + asset_hash: 'F3oDH1mJ47Z7tNBHvrpN5UFf4VAeQSwTtxZeJmn7q3Fh', + tree: 'BBUkS4LZQ7mU8iZXYLVGNUjSxCYnB3x44UuPVHVXS9Fo', + seq: 3, + leaf_id: 0 +} +``` + +### Get the asset proof + +The next step in preparing your compressed NFT transfer instruction, is to get a +**valid** asset `proof` to perform the transfer. This proof is required by the +on-chain compression program to validate on-chain information. + +We can use the `getAssetProof` RPC method to retrieve two important pieces of +information: + +- `proof` - the "full proof" that is required to perform the transfer (more on + this below) +- `tree_id` - the on-chain address of the compressed NFTs tree + +:::info Full proof is returned + +The `getAssetProof` RPC method returns the complete listing of "proof hashes" +that are used to perform the compressed NFT transfer. Since this "full proof" is +returned from the RPC, we will need to remove the portion of the "full proof" +that is stored on-chain via the tree's `canopy`. + +::: + +#### Example response from the `getAssetProof` method + +Below is an example response from the `getAssetProof` method: + +```ts +{ + root: '7dy5bzgaRcUnNH2KMExwNXXNaCJnf7wQqxc2VrGXy9qr', + proof: [ + 'HdvzZ4hrPEdEarJfEzAavNJEZcCS1YU1fg2uBvQGwAAb', + ... + '3e2oBSLfSDVdUdS7jRGFKa8nreJUA9sFPEELrHaQyd4J' + ], + node_index: 131072, + leaf: 'F3oDH1mJ47Z7tNBHvrpN5UFf4VAeQSwTtxZeJmn7q3Fh', + tree_id: 'BBUkS4LZQ7mU8iZXYLVGNUjSxCYnB3x44UuPVHVXS9Fo' +} +``` + +### Get the Merkle tree account + +Since the `getAssetProof` will always return the "full proof", we will have to +reduce it down in order to remove the proof hashes that are stored on-chain in +the tree's canopy. But in order to remove the correct number of proof addresses, +we need to know the tree's `canopyDepth`. + +Once we have our compressed NFT's tree address (the `tree_id` value from +`getAssetProof`), we can use the +[`ConcurrentMerkleTreeAccount`](https://solana-labs.github.io/solana-program-library/account-compression/sdk/docs/classes/index.ConcurrentMerkleTreeAccount.html) +class, from the `@solana/spl-account-compression` SDK: + +```ts +// retrieve the merkle tree's account from the blockchain +const treeAccount = await ConcurrentMerkleTreeAccount.fromAccountAddress( + connection, + treeAddress, +); + +// extract the needed values for our transfer instruction +const treeAuthority = treeAccount.getAuthority(); +const canopyDepth = treeAccount.getCanopyDepth(); +``` + +For the transfer instruction, we will also need the current `treeAuthority` +address which we can also get via the `treeAccount`. + +### Prepare the asset proof + +With our "full proof" and `canopyDepth` values on hand, we can correctly format +the `proof` to be submitted within the transfer instruction itself. + +Since we will use the `createTransferInstruction` helper function from the +Bubblegum SDK to actually build our transfer instruction, we need to: + +- remove the proof values that are already stored on-chain in the + [tree's canopy](../../learn/state-compression.md#canopy-depth), and +- convert the remaining proof values into the valid `AccountMeta` structure that + the instruction builder function accepts + +```ts +// parse the list of proof addresses into a valid AccountMeta[] +const proof: AccountMeta[] = assetProof.proof + .slice(0, assetProof.proof.length - (!!canopyDepth ? canopyDepth : 0)) + .map((node: string) => ({ + pubkey: new PublicKey(node), + isSigner: false, + isWritable: false, + })); +``` + +In the TypeScript code example above, we are first taking a `slice` of our "full +proof", starting at the beginning of the array, and ensuring we only have +`proof.length - canopyDepth` number of proof values. This will remove the +portion of the proof that is already stored on-chain in the tree's canopy. + +Then we are structuring each of the remaining proof values as a valid +`AccountMeta`, since the proof is submitted on-chain in the form of "extra +accounts" within the transfer instruction. + +### Build the transfer instruction + +Finally, with all the required pieces of data about our tree and compressed +NFTs, and a correctly formatted proof, we are ready to actually create the +transfer instruction. + +Build your transfer instruction using the +[`createTransferInstruction`](https://metaplex-foundation.github.io/metaplex-program-library/docs/bubblegum/functions/createTransferInstruction.html) +helper function from the Bubblegum SDK: + +```ts +// create the NFT transfer instruction (via the Bubblegum package) +const transferIx = createTransferInstruction( + { + merkleTree: treeAddress, + treeAuthority, + leafOwner, + leafDelegate, + newLeafOwner, + logWrapper: SPL_NOOP_PROGRAM_ID, + compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, + anchorRemainingAccounts: proof, + }, + { + root: [...new PublicKey(assetProof.root.trim()).toBytes()], + dataHash: [...new PublicKey(asset.compression.data_hash.trim()).toBytes()], + creatorHash: [ + ...new PublicKey(asset.compression.creator_hash.trim()).toBytes(), + ], + nonce: asset.compression.leaf_id, + index: asset.compression.leaf_id, + }, + BUBBLEGUM_PROGRAM_ID, +); +``` + +Aside from passing in our assorted Account addresses and the asset's proof, we +are converting the string values of our `data_hash`, `creator_hash`, `root` hash +into an array of bytes that is accepted by the `createTransferInstruction` +helper function. + +Since each of these hash values resemble and are formatted similar to +PublicKeys, we can use the +[`PublicKey`](https://solana-labs.github.io/solana-web3.js/classes/PublicKey.html) +class in web3.js to convert them into a accepted byte array format. + +#### Send the transaction + +With our transfer instructions built, we can add it into a transaction and send +it to the blockchain similar to before. Making sure either the current +`leafOwner` or the `leafDelegate` signs the transaction. + +:::note + +After each successful transfer of a compressed NFT, the `leafDelegate` should +reset to an empty value. Meaning the specific asset will not have delegated +authority to an address other than its owner. + +::: + +And once confirmed by the cluster, we will have successfully transferred a +compressed NFT. + +## Example code repository + +You can find an example code repository for this developer guide on the Solana +Developers GitHub: https://github.com/solana-developers/compressed-nfts diff --git a/docs/src/developing/intro/programs.md b/docs/src/developing/intro/programs.md new file mode 100644 index 00000000000000..20c5dd9f746c3b --- /dev/null +++ b/docs/src/developing/intro/programs.md @@ -0,0 +1,56 @@ +--- +title: What are Solana Programs? +description: "A Solana Program, aka smart contract, is the executable code that interprets the instructions on the blockchain. There are two types: Native and on chain." +--- + +Solana Programs, often referred to as "_smart contracts_" on other blockchains, are the executable code that interprets the instructions sent inside of each transaction on the blockchain. They can be deployed directly into the core of the network as [Native Programs](#native-programs), or published by anyone as [On Chain Programs](#on-chain-programs). Programs are the core building blocks of the network and handle everything from sending tokens between wallets, to accepting votes of a DAOs, to tracking ownership of NFTs. + +Both types of programs run on top of the [Sealevel runtime](https://medium.com/solana-labs/sealevel-parallel-processing-thousands-of-smart-contracts-d814b378192), which is Solana's _parallel processing_ model that helps to enable the high transactions speeds of the blockchain. + +## Key points + +- Programs are essentially special type of [Accounts](../programming-model/accounts.md) that is marked as "_executable_" +- Programs can own other Accounts +- Programs can only _change the data_ or _debit_ accounts they own +- Any program can _read_ or _credit_ another account +- Programs are considered stateless since the primary data stored in a program account is the compiled BPF code +- Programs can be upgraded by their owner (see more on that below) + +## Types of programs + +The Solana blockchain has two types of programs: + +- Native programs +- On chain programs + +### On chain programs + +These user written programs, often referred to as "_smart contracts_" on other blockchains, are deployed directly to the blockchain for anyone to interact with and execute. Hence the name "on chain"! + +In effect, "on chain programs" are any program that is not baked directly into the Solana cluster's core code (like the native programs discussed below). + +And even though Solana Labs maintains a small subset of these on chain programs (collectively known as the [Solana Program Library](https://spl.solana.com/)), anyone can create or publish one. On chain programs can also be updated directly on the blockchain by the respective program's Account owner. + +### Native programs + +_Native programs_ are programs that are built directly into the core of the Solana blockchain. + +Similar to other "on chain" programs in Solana, native programs can be called by any other program/user. However, they can only be upgraded as part of the core blockchain and cluster updates. These native program upgrades are controlled via the releases to the [different clusters](../../cluster/overview.md). + +#### Examples of native programs include: + +- [System Program](../runtime-facilities/programs.md#system-program): Create new accounts, transfer tokens, and more +- [BPF Loader Program](../runtime-facilities/programs.md#bpf-loader): Deploys, upgrades, and executes programs on chain +- [Vote program](../runtime-facilities/programs.md#vote-program): Create and manage accounts that track validator voting state and rewards. + +## Executable + +When a Solana program is deployed onto the network, it is marked as "executable" by the [BPF Loader Program](../runtime-facilities/programs.md#bpf-loader). This allows the Solana runtime to efficiently and properly execute the compiled program code. + +## Upgradable + +Unlike other blockchains, Solana programs can be upgraded after they are deployed to the network. + +Native programs can only be upgraded as part of cluster updates when new software releases are made. + +On chain programs can be upgraded by the account that is marked as the "_Upgrade Authority_", which is usually the Solana account/address that deployed the program to begin with. diff --git a/docs/src/developing/intro/rent.md b/docs/src/developing/intro/rent.md new file mode 100644 index 00000000000000..544608efe4551a --- /dev/null +++ b/docs/src/developing/intro/rent.md @@ -0,0 +1,39 @@ +--- +title: What is rent? +description: "Rent: the small fee Solana accounts incur to store data on the blockchain. Accounts with >2 years of rent are rent exempt and do not pay the periodic fee." +--- + +The fee every Solana Account to store data on the blockchain is called "_rent_". This _time and space_ based fee is required to keep an account, and its therefore its data, alive on the blockchain since [clusters](../../cluster/overview.md) must actively maintain this data. + +All Solana Accounts (and therefore Programs) are required to maintain a high enough LAMPORT balance to become [rent exempt](#rent-exempt) and remain on the Solana blockchain. + +When an Account no longer has enough LAMPORTS to pay its rent, it will be removed from the network in a process known as [Garbage Collection](#garbage-collection). + +> **Note:** Rent is different from [transactions fees](../../transaction_fees.md). Rent is paid (or held in an Account) to keep data stored on the Solana blockchain. Where as transaction fees are paid to process [instructions](../developing/../programming-model/transactions.md#instructions) on the network. + +### Rent rate + +The Solana rent rate is set on a network wide basis, primarily based on the set LAMPORTS _per_ byte _per_ year. + +Currently, the rent rate is a static amount and stored in the the [Rent sysvar](../runtime-facilities/sysvars.md#rent). + +## Rent exempt + +Accounts that maintain a minimum LAMPORT balance greater than 2 years worth of rent payments are considered "_rent exempt_" and will not incur a rent collection. + +> At the time of writing this, new Accounts and Programs **are required** to be initialized with enough LAMPORTS to become rent-exempt. The RPC endpoints have the ability to calculate this [estimated rent exempt balance](../../api/http#getminimumbalanceforrentexemption) and is recommended to be used. + +Every time an account's balance is reduced, a check is performed to see if the account is still rent exempt. Transactions that would cause an account's balance to drop below the rent exempt threshold will fail. + +## Garbage collection + +Accounts that do not maintain their rent exempt status, or have a balance high enough to pay rent, are removed from the network in a process known as _garbage collection_. This process is done to help reduce the network wide storage of no longer used/maintained data. + +You can learn more about [garbage collection here](../../implemented-proposals/persistent-account-storage.md#garbage-collection) in this implemented proposal. + +## Learn more about Rent + +You can learn more about Solana Rent with the following articles and documentation: + +- [Implemented Proposals - Rent](../../implemented-proposals/rent.md) +- [Implemented Proposals - Account Storage](../../implemented-proposals/persistent-account-storage.md) diff --git a/docs/src/developing/intro/transaction_fees.md b/docs/src/developing/intro/transaction_fees.md new file mode 100644 index 00000000000000..382dae826217e8 --- /dev/null +++ b/docs/src/developing/intro/transaction_fees.md @@ -0,0 +1,95 @@ +--- +title: Transaction Fees +description: "Transaction fees are the small fees paid to process instructions on the network. These fees are based on computation and an optional prioritization fee." +keywords: + - instruction fee + - processing fee + - storage fee + - low fee blockchain + - gas + - gwei + - cheap network + - affordable blockchain +--- + +The small fees paid to process [instructions](./../../terminology.md#instruction) on the Solana blockchain are known as "_transaction fees_". + +As each transaction (which contains one or more instructions) is sent through the network, it gets processed by the current leader validation-client. Once confirmed as a global state transaction, this _transaction fee_ is paid to the network to help support the economic design of the Solana blockchain. + +> NOTE: Transactions fees are different from the blockchain's data storage fee called [rent](./rent.md) + +### Transaction Fee Calculation + +Currently, the amount of resources consumed by a transaction do not impact fees in any way. This is because the runtime imposes a small cap on the amount of resources that transaction instructions can use, not to mention that the size of transactions is limited as well. So right now, transaction fees are solely determined by the number of signatures that need to be verified in a transaction. The only limit on the number of signatures in a transaction is the max size of transaction itself. Each signature (64 bytes) in a transaction (max 1232 bytes) must reference a unique public key (32 bytes) so a single transaction could contain as many as 12 signatures (not sure why you would do that). The fee per transaction signature can be fetched with the `solana` cli: + +```bash +$ solana fees +Blockhash: 8eULQbYYp67o5tGF2gxACnBCKAE39TetbYYMGTx3iBFc +Lamports per signature: 5000 +Last valid block height: 94236543 +``` + +The `solana` cli `fees` subcommand calls the `getFees` RPC API method to retrieve the above output information, so your application can call that method directly as well: + +```bash +$ curl http://api.mainnet-beta.solana.com -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getFees"} +' + +# RESULT (lastValidSlot removed since it's inaccurate) +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 106818885 + }, + "value": { + "blockhash": "78e3YBCMXJBiPD1HpyVtVfFzZFPG6nUycnQcyNMSUQzB", + "feeCalculator": { + "lamportsPerSignature": 5000 + }, + "lastValidBlockHeight": 96137823 + } + }, + "id": 1 +} +``` + +### Fee Determinism + +It's important to keep in mind that fee rates (such as `lamports_per_signature`) are subject to change from block to block (though that hasn't happened in the full history of the `mainnet-beta` cluster). Despite the fact that fees can fluctuate, fees for a transaction can still be calculated deterministically when creating (and before signing) a transaction. This determinism comes from the fact that fees are applied using the rates from the block whose blockhash matches the `recent_blockhash` field in a transaction. Blockhashes can only be referenced by a transaction for a few minutes before they expire. + +Transactions with expired blockhashes will be ignored and dropped by the cluster, so it's important to understand how expiration actually works. Before transactions are added to a block and during block validation, [each transaction's recent blockhash is checked](https://github.com/solana-labs/solana/blob/647aa926673e3df4443d8b3d9e3f759e8ca2c44b/runtime/src/bank.rs#L3482) to ensure it hasn't expired yet. The max age of a transaction's blockhash is only 150 blocks. This means that if no slots are skipped in between, the blockhash for block 100 would be usable by transactions processed in blocks 101 to 252, inclusive (during block 101 the age of block 100 is "0" and during block 252 its age is "150"). However, it's important to remember that slots may be skipped and that age checks use "block height" _not_ "slot height". Since slots are skipped occasionally, the actual age of a blockhash can be a bit longer than 150 slots. At the time of writing, slot times are about 500ms and skip rate is about 5% so the expected lifetime of a transaction which uses the most recent blockhash is about 1min 19s. + +### Fee Collection + +Transactions are required to have at least one account which has signed the transaction and is writable. Writable signer accounts are serialized first in the list of transaction accounts and the first of these accounts is always used as the "fee payer". + +Before any transaction instructions are processed, the fee payer account balance will be deducted to pay for transaction fees. If the fee payer balance is not sufficient to cover transaction fees, the transaction will be dropped by the cluster. If the balance was sufficient, the fees will be deducted whether the transaction is processed successfully or not. In fact, if any of the transaction instructions return an error or violate runtime restrictions, all account changes _except_ the transaction fee deduction will be rolled back. + +### Fee Distribution + +Transaction fees are partially burned and the remaining fees are collected by the validator that produced the block that the corresponding transactions were included in. The transaction fee burn rate was initialized as 50% when inflation rewards were enabled at the beginning of 2021 and has not changed so far. These fees incentivize a validator to process as many transactions as possible during its slots in the leader schedule. Collected fees are deposited in the validator's account (listed in the leader schedule for the current slot) after processing all of the transactions included in a block. + +## Upcoming Changes + +### Transaction wide compute budget + +As of version 1.8 of the Solana protocol, the maximum compute budget for transactions is assessed on a per instruction basis. This has allowed for flexibility in protocol design to squeeze out more compute by splitting up operations across multiple instructions but this workaround has skewed the distribution of compute consumption across different transactions. To keep transaction fees properly priced, the maximum compute budget will instead be assessed over the entire transaction. This change is likely to be released in version 1.9 of the Solana protocol and is gated on the following feature switch: + +```bash +$ ~/Workspace/solana (master branch) > cargo run --bin solana -- feature status 5ekBxc8itEnPv4NzGJtr8BVVQLNMQuLMNQQj7pHoLNZ9 --url mainnet-beta +5ekBxc8itEnPv4NzGJtr8BVVQLNMQuLMNQQj7pHoLNZ9 | inactive | transaction wide compute cap +``` + +This adjustment could negatively impact the usability of some protocols which have relied on the compute budget being reset for each instruction in a transaction. For this reason, this compute budget change will not be enabled until a new mechanism for increasing total transaction compute budget is added. This mechanism is described below... + +### Request increased compute budget + +As protocols have gotten more complex, the [default compute budget of 200,000 compute units](https://github.com/solana-labs/solana/blob/647aa926673e3df4443d8b3d9e3f759e8ca2c44b/sdk/src/compute_budget.rs#L105) has become a common pain-point for developers. Developers have gotten creative in working around this limitation by breaking up operations across multiple instructions and/or transactions. But in order to properly address this issue, a new program instruction will be added to request additional compute units from the runtime (up to a max of 1 million compute units). To request more compute, [create a `RequestUnits` instruction which invokes the new `Compute Budget` program](https://github.com/solana-labs/solana/blob/647aa926673e3df4443d8b3d9e3f759e8ca2c44b/sdk/src/compute_budget.rs#L44) and insert it at the beginning of a transaction. This new program will be released along with the transaction-wide compute budget change described above and is gated on the same feature switch. There are currently no increased transaction fees for using this feature, however that is likely to change. + +Note that adding a `RequestUnits` compute budget instruction will take up 39 extra bytes in a serialized transaction. That breaks down into 32 bytes for the compute budget program id, 1 byte for program id index, 1 byte for empty ix account vec len, 1 byte for data vec len, and 4 bytes for the requested compute. + +### Calculate transaction fees with RPC API + +In order to simplify fee calculation for developers, a new [`getFeeForMessage`](/api/http#getfeeformessage) RPC API is planned to be released in v1.9 of the Solana protocol. This new method accepts a blockhash along with an encoded transaction message and will return the amount of fees that would be deducted if the transaction message is signed, sent, and processed by the cluster. diff --git a/docs/src/developing/lookup-tables.md b/docs/src/developing/lookup-tables.md new file mode 100644 index 00000000000000..fe49004e658bac --- /dev/null +++ b/docs/src/developing/lookup-tables.md @@ -0,0 +1,150 @@ +--- +title: Address Lookup Tables +description: "" +--- + +Address Lookup Tables, commonly referred to as "_lookup tables_" or "_ALTs_" for short, allow developers to create a collection of related addresses to efficiently load more addresses in a single transaction. + +Since each transaction on the Solana blockchain requires a listing of every address that is interacted with as part of the transaction, this listing would be effectively be capped at 32 address per transaction. With the help of [Address Lookup Tables](./lookup-tables.md), a transaction would be now be able to raise that limit to 256 addresses per transaction. + +## Compressing on chain addresses + +After all the desired address have been stored on chain in an Address Lookup Table, each address can be referenced inside a transaction by its 1-byte index within the table (instead of their full 32-byte address). This lookup method effectively "_compresses_" a 32-byte address into a 1-byte index value. + +This "_compression_" enables storing up to 256 address in a single lookup table for use inside any given transaction. + +## Versioned Transactions + +To utilize an Address Lookup Table inside a transaction, developers must use v0 transactions that were introduced with the new [Versioned Transaction format](./versioned-transactions.md). + +## How to create an address lookup table + +Creating a new lookup table with the `@solana/web3.js` library is similar to the older `legacy` transactions, but with some differences. + +Using the `@solana/web3.js` library, you can use the [`createLookupTable`](https://solana-labs.github.io/solana-web3.js/classes/AddressLookupTableProgram.html#createLookupTable) function to construct the instruction needed to create a new lookup table, as well as determine its address: + +```js +const web3 = require("@solana/web3.js"); + +// connect to a cluster and get the current `slot` +const connection = new web3.Connection(web3.clusterApiUrl("devnet")); +const slot = await connection.getSlot(); + +// Assumption: +// `payer` is a valid `Keypair` with enough SOL to pay for the execution + +const [lookupTableInst, lookupTableAddress] = + web3.AddressLookupTableProgram.createLookupTable({ + authority: payer.publicKey, + payer: payer.publicKey, + recentSlot: slot, + }); + +console.log("lookup table address:", lookupTableAddress.toBase58()); + +// To create the Address Lookup Table on chain: +// send the `lookupTableInst` instruction in a transaction +``` + +> NOTE: +> Address lookup tables can be **created** with either a `v0` transaction or a `legacy` transaction. But the Solana runtime can only retrieve and handle the additional addresses within a lookup table while using [v0 Versioned Transactions](./versioned-transactions.md#current-transaction-versions). + +## Add addresses to a lookup table + +Adding addresses to a lookup table is known as "_extending_". Using the the `@solana/web3.js` library, you can create a new _extend_ instruction using the [`extendLookupTable`](https://solana-labs.github.io/solana-web3.js/classes/AddressLookupTableProgram.html#extendLookupTable) method: + +```js +// add addresses to the `lookupTableAddress` table via an `extend` instruction +const extendInstruction = web3.AddressLookupTableProgram.extendLookupTable({ + payer: payer.publicKey, + authority: payer.publicKey, + lookupTable: lookupTableAddress, + addresses: [ + payer.publicKey, + web3.SystemProgram.programId, + // list more `publicKey` addresses here + ], +}); + +// Send this `extendInstruction` in a transaction to the cluster +// to insert the listing of `addresses` into your lookup table with address `lookupTableAddress` +``` + +> NOTE: +> Due to the same memory limits of `legacy` transactions, any transaction used to _extend_ an Address Lookup Table is also limited in how many addresses can be added at a time. Because of this, you will need to use multiple transactions to _extend_ any table with more addresses (~20) that can fit withing a single transaction's memory limits. + +Once these address have been inserted into the table, and stored on chain, you will be able to utilize the Address Lookup Table in future transactions. Enabling up to 256 address in those future transactions. + +## Fetch an Address Lookup Table + +Similar to requesting another account (or PDA) from the cluster, you can fetch a complete Address Lookup Table with the [`getAddressLookupTable`](https://solana-labs.github.io/solana-web3.js/classes/Connection.html#getAddressLookupTable) method: + +```js +// define the `PublicKey` of the lookup table to fetch +const lookupTableAddress = new web3.PublicKey(""); + +// get the table from the cluster +const lookupTableAccount = await connection + .getAddressLookupTable(lookupTableAddress) + .then((res) => res.value); + +// `lookupTableAccount` will now be a `AddressLookupTableAccount` object + +console.log("Table address from cluster:", lookupTableAccount.key.toBase58()); +``` + +Our `lookupTableAccount` variable will now be a `AddressLookupTableAccount` object which we can parse to read the listing of all the addresses stored on chain in the lookup table: + +```js +// loop through and parse all the address stored in the table +for (let i = 0; i < lookupTableAccount.state.addresses.length; i++) { + const address = lookupTableAccount.state.addresses[i]; + console.log(i, address.toBase58()); +} +``` + +## How to use an address lookup table in a transaction + +After you have created your lookup table, and stored your needed address on chain (via extending the lookup table), you can create a `v0` transaction to utilize the on chain lookup capabilities. + +Just like older `legacy` transactions, you can create all the [instructions](./../terminology.md#instruction) your transaction will execute on chain. You can then provide an array of these instructions to the [Message](./../terminology.md#message) used in the `v0 transaction. + +> NOTE: +> The instructions used inside a `v0` transaction can be constructed using the same methods and functions used to create the instructions in the past. There is no required change to the instructions used involving an Address Lookup Table. + +```js +// Assumptions: +// - `arrayOfInstructions` has been created as an `array` of `TransactionInstruction` +// - we are are using the `lookupTableAccount` obtained above + +// construct a v0 compatible transaction `Message` +const messageV0 = new web3.TransactionMessage({ + payerKey: payer.publicKey, + recentBlockhash: blockhash, + instructions: arrayOfInstructions, // note this is an array of instructions +}).compileToV0Message([lookupTableAccount]); + +// create a v0 transaction from the v0 message +const transactionV0 = new web3.VersionedTransaction(messageV0); + +// sign the v0 transaction using the file system wallet we created named `payer` +transactionV0.sign([payer]); + +// send and confirm the transaction +// (NOTE: There is NOT an array of Signers here; see the note below...) +const txid = await web3.sendAndConfirmTransaction(connection, transactionV0); + +console.log( + `Transaction: https://explorer.solana.com/tx/${txidV0}?cluster=devnet`, +); +``` + +> NOTE: +> When sending a `VersionedTransaction` to the cluster, it must be signed BEFORE calling the +> `sendAndConfirmTransaction` method. If you pass an array of `Signer` +> (like with `legacy` transactions) the method will trigger an error! + +## More Resources + +- Read the [proposal](./../proposals/transactions-v2.md) for Address Lookup Tables and Versioned transactions +- [Example Rust program using Address Lookup Tables](https://github.com/TeamRaccoons/address-lookup-table-multi-swap) diff --git a/docs/src/developing/on-chain-programs/debugging.md b/docs/src/developing/on-chain-programs/debugging.md index a9ded2bd31cb7c..d673ab23a33f64 100644 --- a/docs/src/developing/on-chain-programs/debugging.md +++ b/docs/src/developing/on-chain-programs/debugging.md @@ -1,5 +1,5 @@ --- -title: "Debugging" +title: "Debugging Programs" --- Solana programs run on-chain, so debugging them in the wild can be challenging. diff --git a/docs/src/developing/on-chain-programs/deploying.md b/docs/src/developing/on-chain-programs/deploying.md index 767d6d114bea94..852e8a195f863d 100644 --- a/docs/src/developing/on-chain-programs/deploying.md +++ b/docs/src/developing/on-chain-programs/deploying.md @@ -1,5 +1,5 @@ --- -title: "Deploying" +title: "Deploying Programs" --- ![SDK tools](/img/sdk-tools.svg) @@ -11,7 +11,7 @@ clients via a _program ID_. The program ID is an _address_ specified when deploying and is used to reference the program in subsequent transactions. Upon a successful deployment the account that holds the program is marked -executable. If the program is marked "final", its account data become permanently +executable. If the program is marked "final", its account data become permanently immutable. If any changes are required to the finalized program (features, patches, etc...) the new program must be deployed to a new program ID. diff --git a/docs/src/developing/on-chain-programs/developing-c.md b/docs/src/developing/on-chain-programs/developing-c.md index df1d551f13e117..52469efcd7e09b 100644 --- a/docs/src/developing/on-chain-programs/developing-c.md +++ b/docs/src/developing/on-chain-programs/developing-c.md @@ -33,8 +33,7 @@ for an example of a C program. First setup the environment: - Install the latest Rust stable from https://rustup.rs -- Install the latest Solana command-line tools from - https://docs.solana.com/cli/install-solana-cli-tools +- Install the latest [Solana command-line tools](../../cli/install-solana-cli-tools.md) Then build using make: diff --git a/docs/src/developing/on-chain-programs/developing-rust.md b/docs/src/developing/on-chain-programs/developing-rust.md index ee948933cc8f03..b6406902b3232f 100644 --- a/docs/src/developing/on-chain-programs/developing-rust.md +++ b/docs/src/developing/on-chain-programs/developing-rust.md @@ -59,8 +59,7 @@ For example: First setup the environment: - Install the latest Rust stable from https://rustup.rs/ -- Install the latest Solana command-line tools from - https://docs.solana.com/cli/install-solana-cli-tools +- Install the latest [Solana command-line tools](../../cli/install-solana-cli-tools.md) The normal cargo build is available for building programs against your host machine which can be used for unit testing: diff --git a/docs/src/developing/on-chain-programs/examples.md b/docs/src/developing/on-chain-programs/examples.md index d1d6adb4b03672..ef07c7b8b4cc9f 100644 --- a/docs/src/developing/on-chain-programs/examples.md +++ b/docs/src/developing/on-chain-programs/examples.md @@ -1,5 +1,5 @@ --- -title: "Examples" +title: "Program Examples" --- ## Helloworld diff --git a/docs/src/developing/plugins/geyser-plugins.md b/docs/src/developing/plugins/geyser-plugins.md index 382f7f647f58d3..30ac70a4392ed0 100644 --- a/docs/src/developing/plugins/geyser-plugins.md +++ b/docs/src/developing/plugins/geyser-plugins.md @@ -141,7 +141,7 @@ The following method is used for notifying transactions: ) -> Result<()> ``` -The `ReplicaTransactionInfoVersionsoVersions` struct +The `ReplicaTransactionInfoVersions` struct contains the information about a streamed transaction. It wraps `ReplicaTransactionInfo` ``` diff --git a/docs/src/developing/programming-model/accounts.md b/docs/src/developing/programming-model/accounts.md index 67aa96ad0105eb..90227b1b62c3c2 100644 --- a/docs/src/developing/programming-model/accounts.md +++ b/docs/src/developing/programming-model/accounts.md @@ -58,7 +58,9 @@ possible to upload a totally new program to an existing program address. To create an account, a client generates a _keypair_ and registers its public key using the `SystemProgram::CreateAccount` instruction with a fixed storage size in bytes preallocated. -The current maximum size of an account's data is 10 megabytes. +The current maximum size of an account's data is 10 MiB, which can be changed +(increased or decreased) at a rate over all accounts of 20 MiB per transaction, +and the size can be increased by 10 KiB per account and per instruction. An account address can be any arbitrary 256 bit value, and there are mechanisms for advanced users to create derived addresses @@ -146,10 +148,12 @@ that would reduce the balance to below the minimum amount will fail. Program executable accounts are required by the runtime to be rent-exempt to avoid being purged. -Note: Use the [`getMinimumBalanceForRentExemption` RPC -endpoint](developing/clients/jsonrpc-api.md#getminimumbalanceforrentexemption) to calculate the +:::info +Use the [`getMinimumBalanceForRentExemption`](../../api/http#getminimumbalanceforrentexemption) RPC +endpoint to calculate the minimum balance for a particular account size. The following calculation is illustrative only. +::: For example, a program executable with the size of 15,000 bytes requires a balance of 105,290,880 lamports (=~ 0.105 SOL) to be rent-exempt: diff --git a/docs/src/developing/programming-model/runtime.md b/docs/src/developing/programming-model/runtime.md index f0d402508808da..344cd674cb0360 100644 --- a/docs/src/developing/programming-model/runtime.md +++ b/docs/src/developing/programming-model/runtime.md @@ -35,21 +35,29 @@ The policy is as follows: - And only if the data is zero-initialized or empty. - An account not assigned to the program cannot have its balance decrease. - The balance of read-only and executable accounts may not change. -- Only the system program can change the size of the data and only if the system - program owns the account. -- Only the owner may change account data. +- Only the owner may change account size and data. - And if the account is writable. - And if the account is not executable. - Executable is one-way (false->true) and only the account owner may set it. - No one can make modifications to the rent_epoch associated with this account. +## Balancing the balances + +Before and after each instruction, the sum of all account balances must stay the same. +E.g. if one account's balance is increased, another's must be decreased by the same amount. +Because the runtime can not see changes to accounts which were not passed to it, +all accounts for which the balances were modified must be passed, +even if they are not needed in the called instruction. + ## Compute Budget To prevent abuse of computational resources, each transaction is allocated a compute budget. The budget specifies a maximum number of compute units that a transaction can consume, the costs associated with different types of operations the transaction may perform, and operational bounds the transaction must adhere -to. As the transaction is processed compute units are consumed by its +to. + +As the transaction is processed compute units are consumed by its instruction's programs performing operations such as executing BPF instructions, calling syscalls, etc... When the transaction consumes its entire budget, or exceeds a bound such as attempting a call stack that is too deep, the runtime @@ -71,11 +79,11 @@ budget, or exceeds a bound, the entire invocation chain and the top level transaction processing are halted. The current [compute -budget](https://github.com/solana-labs/solana/blob/090e11210aa7222d8295610a6ccac4acda711bb9/program-runtime/src/compute_budget.rs#L26-L87) +budget](https://github.com/solana-labs/solana/blob/090e11210aa7222d8295610a6ccac4acda711bb9/program-runtime/src/compute_budget.rs#L26-L87) can be found in the Solana Program Runtime. -can be found in the Solana Program Runtime. +#### Example Compute Budget -For example, if the current budget is: +For example, if the compute budget set in the Solana runtime is: ```rust max_units: 1,400,000, @@ -89,21 +97,23 @@ log_pubkey_units: 100, ... ``` -Then the transaction +Then any transaction: - Could execute 1,400,000 BPF instructions, if it did nothing else. - Cannot exceed 4k of stack usage. - Cannot exceed a BPF call depth of 64. - Cannot exceed 4 levels of cross-program invocations. -Since the compute budget is consumed incrementally as the transaction executes, -the total budget consumption will be a combination of the various costs of the -operations it performs. +> **NOTE:** Since the compute budget is consumed incrementally as the transaction executes, +> the total budget consumption will be a combination of the various costs of the +> operations it performs. At runtime a program may log how much of the compute budget remains. See [debugging](developing/on-chain-programs/debugging.md#monitoring-compute-budget-consumption) for more information. +### Prioritization fees + A transaction may set the maximum number of compute units it is allowed to consume and the compute unit price by including a `SetComputeUnitLimit` and a `SetComputeUnitPrice` @@ -112,20 +122,19 @@ respectively. If no `SetComputeUnitLimit` is provided the limit will be calculated as the product of the number of instructions in the transaction (excluding the [Compute -budget -instructions](https://github.com/solana-labs/solana/blob/db32549c00a1b5370fcaf128981ad3323bbd9570/sdk/src/compute_budget.rs#L22)) -and the default per-instruction units, which is currently 200k. - -Note that a transaction's prioritization fee is calculated by multiplying the -number of compute units by the compute unit price (measured in micro-lamports) -set by the transaction via compute budget instructions. So transactions should -request the minimum amount of compute units required for execution to minimize +budget instructions](https://github.com/solana-labs/solana/blob/db32549c00a1b5370fcaf128981ad3323bbd9570/sdk/src/compute_budget.rs#L22)) and the default per-instruction units, which is currently 200k. + +> **NOTE:** A transaction's [prioritization fee](./../../terminology.md#prioritization-fee) is calculated by multiplying the +> number of _compute units_ by the _compute unit price_ (measured in micro-lamports) +> set by the transaction via compute budget instructions. + +Transactions should request the minimum amount of compute units required for execution to minimize fees. Also note that fees are not adjusted when the number of requested compute units exceeds the number of compute units actually consumed by an executed transaction. Compute Budget instructions don't require any accounts and don't consume any -compute units to process. Transactions can only contain one of each type of +compute units to process. Transactions can only contain one of each type of compute budget instruction, duplicate types will result in an error. The `ComputeBudgetInstruction::set_compute_unit_limit` and diff --git a/docs/src/developing/programming-model/transactions.md b/docs/src/developing/programming-model/transactions.md index c93dd80de62755..42e6ca2d23762a 100644 --- a/docs/src/developing/programming-model/transactions.md +++ b/docs/src/developing/programming-model/transactions.md @@ -202,7 +202,7 @@ found in [Accounts](accounts.md#signers) ## Recent Blockhash -A transaction includes a recent [blockhash](terminology.md#blockhash) to prevent +A transaction includes a recent [blockhash](../../terminology.md#blockhash) to prevent duplication and to give transactions lifetimes. Any transaction that is completely identical to a previous one is rejected, so adding a newer blockhash allows multiple transactions to repeat the exact same action. Transactions also diff --git a/docs/src/developing/test-validator.md b/docs/src/developing/test-validator.md index 72dea3ea4a0878..b835692a9a4668 100644 --- a/docs/src/developing/test-validator.md +++ b/docs/src/developing/test-validator.md @@ -140,7 +140,7 @@ JSON RPC URL: http://127.0.0.1:8899 ``` - The network address of the [Gossip](/validator/gossip#gossip-overview), - [Transaction Processing Unit](/validator/tpu) and [JSON RPC](clients/jsonrpc-api#json-rpc-api-reference) + [Transaction Processing Unit](/validator/tpu) and [JSON RPC](../api/http#json-rpc-api-reference) service, respectively ``` @@ -148,7 +148,7 @@ JSON RPC URL: http://127.0.0.1:8899 ``` - Session running time, current slot of the the three block - [commitment levels](clients/jsonrpc-api#configuring-state-commitment), + [commitment levels](../api/http#configuring-state-commitment), slot height of the last snapshot, transaction count, [voting authority](/running-validator/vote-accounts#vote-authority) balance @@ -166,4 +166,4 @@ Since this may not always be desired, especially when testing programs meant for ```bash solana-test-validator --deactivate-feature --deactivate-feature -``` \ No newline at end of file +``` diff --git a/docs/src/developing/transaction_confirmation.md b/docs/src/developing/transaction_confirmation.md new file mode 100644 index 00000000000000..9fa1c6ab0b5fa3 --- /dev/null +++ b/docs/src/developing/transaction_confirmation.md @@ -0,0 +1,199 @@ +--- +title: "Transaction Confirmation" +--- + +into to transaction confirmation? + +Problems relating to [transaction confirmation](./transaction_confirmation.md) are common with many newer developers while building applications. This article aims to boost the overall understanding of the confirmation mechanism used on the Solana blockchain, including some recommended best practices. + +## Brief background on transactions + +Let’s first make sure we’re all on the same page and thinking about the same things... + +### What is a transaction? + +Transactions consist of two components: a [message](./../terminology.md#message) and a [list of signatures](./../terminology.md#signature). The transaction message is where the magic happens and at a high level it consists of three components: + +- a **list of instructions** to invoke, +- a **list of accounts** to load, and +- a **“recent blockhash.”** + +In this article, we’re going to be focusing a lot on a transaction’s [recent blockhash](./../terminology.md#blockhash) because it plays a big role in transaction confirmation. + +### Transaction lifecycle refresher + +Below is a high level view of the lifecycle of a transaction. This article will touch on everything except steps 1 and 4. + +1. Create a list of instructions along with the list of accounts that instructions need to read and write +2. Fetch a recent blockhash and use it to prepare a transaction message +3. Simulate the transaction to ensure it behaves as expected +4. Prompt user to sign the prepared transaction message with their private key +5. Send the transaction to an RPC node which attempts to forward it to the current block producer +6. Hope that a block producer validates and commits the transaction into their produced block +7. Confirm the transaction has either been included in a block or detect when it has expired + +## What is a Blockhash? + +A [“blockhash”](./../terminology.md#blockhash) refers to the last Proof of History (PoH) hash for a [“slot”](./../terminology.md#slot) (description below). Since Solana uses PoH as a trusted clock, a transaction’s recent blockhash can be thought of as a **timestamp**. + +### Proof of History refresher + +Solana’s Proof of History mechanism uses a very long chain of recursive SHA-256 hashes to build a trusted clock. The “history” part of the name comes from the fact that block producers hash transaction id’s into the stream to record which transactions were processed in their block. + +[PoH hash calculation](https://github.com/solana-labs/solana/blob/9488a73f5252ad0d7ea830a0b456d9aa4bfbb7c1/entry/src/poh.rs#L82): `next_hash = hash(prev_hash, hash(transaction_ids))` + +PoH can be used as a trusted clock because each hash must be produced sequentially. Each produced block contains a blockhash and a list of hash checkpoints called “ticks” so that validators can verify the full chain of hashes in parallel and prove that some amount of time has actually passed. The stream of hashes can be broken up into the following time units: + +# Transaction Expiration + +By default, all Solana transactions will expire if not committed to a block in a certain amount of time. The **vast majority** of transaction confirmation issues are related to how RPC nodes and validators detect and handle **expired** transactions. A solid understanding of how transaction expiration works should help you diagnose the bulk of your transaction confirmation issues. + +## How does transaction expiration work? + +Each transaction includes a “recent blockhash” which is used as a PoH clock timestamp and expires when that blockhash is no longer “recent” enough. More concretely, Solana validators look up the corresponding slot number for each transaction’s blockhash that they wish to process in a block. If the validator [can’t find a slot number for the blockhash](https://github.com/solana-labs/solana/blob/9488a73f5252ad0d7ea830a0b456d9aa4bfbb7c1/runtime/src/bank.rs#L3687) or if the looked up slot number is more than 151 slots lower than the slot number of the block being processed, the transaction will be rejected. + +Slots are configured to last about [400ms](https://github.com/solana-labs/solana/blob/47b938e617b77eb3fc171f19aae62222503098d7/sdk/program/src/clock.rs#L12) but often fluctuate between 400ms and 600ms, so a given blockhash can only be used by transactions for about 60 to 90 seconds. + +Transaction has expired pseudocode: `currentBankSlot > slotForTxRecentBlockhash + 151` + +Transaction not expired pseudocode: `currentBankSlot - slotForTxRecentBlockhash < 152` + +### Example of transaction expiration + +Let’s walk through a quick example: + +1. A validator is producing a new block for slot #1000 +2. The validator receives a transaction with recent blockhash `1234...` from a user +3. The validator checks the `1234...` blockhash against the list of recent blockhashes leading up to its new block and discovers that it was the blockhash for slot #849 +4. Since slot #849 is exactly 151 slots lower than slot #1000, the transaction hasn’t expired yet and can still be processed! +5. But wait, before actually processing the transaction, the validator finished the block for slot #1000 and starts producing the block for slot #1001 (validators get to produce blocks for 4 consecutive slots). +6. The validator checks the same transaction again and finds that it’s now too old and drops it because it’s now 152 slots lower than the current slot :( + +## Why do transactions expire? + +There’s a very good reason for this actually, it’s to help validators avoid processing the same transaction twice. + +A naive brute force approach to prevent double processing could be to check every new transaction against the blockchain’s entire transaction history. But by having transactions expire after a short amount of time, validators only need to check if a new transaction is in a relatively small set of _recently_ processed transactions. + +### Other blockchains + +Solana’s approach of prevent double processing is quite different from other blockchains. For example, Ethereum tracks a counter (nonce) for each transaction sender and will only process transactions that use the next valid nonce. + +Ethereum’s approach is simple for validators to implement, but it can be problematic for users. Many people have encountered situations when their Ethereum transactions got stuck in a _pending_ state for a long time and all the later transactions, which used higher nonce values, were blocked from processing. + +### Advantages on Solana + +There are a few advantages to Solana’s approach: + +1. A single fee payer can submit multiple transactions at the same time that are allowed to be processed in any order. This might happen if you’re using multiple applications at the same time. +2. If a transaction doesn’t get committed to a block and expires, users can try again knowing that their previous transaction won’t ever be processed. + +By not using counters, the Solana wallet experience may be easier for users to understand because they can get to success, failure, or expiration states quickly and avoid annoying pending states. + +### Disadvantages on Solana + +Of course there are some disadvantages too: + +1. Validators have to actively track a set of all processed transaction id’s to prevent double processing. +2. If the expiration time period is too short, users might not be able to submit their transaction before it expires. + +These disadvantages highlight a tradeoff in how transaction expiration is configured. If the expiration time of a transaction is increased, validators need to use more memory to track more transactions. If expiration time is decreased, users don’t have enough time to submit their transaction. + +Currently, Solana clusters require that transactions use blockhashes that are no more than [151 slots](https://github.com/solana-labs/solana/blob/9488a73f5252ad0d7ea830a0b456d9aa4bfbb7c1/sdk/program/src/clock.rs#L65) old. + +> This [Github issue](https://github.com/solana-labs/solana/issues/23582) contains some calculations that estimate that mainnet-beta validators need about 150MB of memory to track transactions. +> This could be slimmed down in the future if necessary without decreasing expiration time as I’ve detailed in that issue. + +## Transaction confirmation tips + +As mentioned before, blockhashes expire after a time period of only 151 slots which can pass as quickly as **one minute** when slots are processed within the target time of 400ms. + +One minute is not a lot of time considering that a client needs to fetch a recent blockhash, wait for the user to sign, and finally hope that the broadcasted transaction reaches a leader that is willing to accept it. Let’s go through some tips to help avoid confirmation failures due to transaction expiration! + +### Fetch blockhashes with the appropriate commitment level + +Given the short expiration time frame, it’s imperative that clients help users create transactions with blockhash that is as recent as possible. + +When fetching blockhashes, the current recommended RPC API is called [`getLatestBlockhash`](/api/http#getlatestblockhash). By default, this API uses the `"finalized"` commitment level to return the most recently finalized block’s blockhash. However, you can override this behavior by [setting the `commitment` parameter](/api/http#configuring-state-commitment) to a different commitment level. + +**Recommendation** + +The `"confirmed"` commitment level should almost always be used for RPC requests because it’s usually only a few slots behind the `"processed"` commitment and has a very low chance of belonging to a dropped [fork](./../cluster/fork-generation.md). + +But feel free to consider the other options: + +- Choosing `"processed"` will let you fetch the most recent blockhash compared to other commitment levels and therefore gives you the most time to prepare and process a transaction. But due to the prevalence of forking in the Solana protocol, roughly 5% of blocks don’t end up being finalized by the cluster so there’s a real chance that your transaction uses a blockhash that belongs to a dropped fork. Transactions that use blockhashes for abandoned blocks won’t ever be considered recent by any blocks that are in the finalized blockchain. +- Using the default commitment level `"finalized"` will eliminate any risk that the blockhash you choose will belong to a dropped fork. The tradeoff is that there is typically at least a 32 slot difference between the most recent confirmed block and the most recent finalized block. This tradeoff is pretty severe and effectively reduces the expiration of your transactions by about 13 seconds but this could be even more during unstable cluster conditions. + +### Use an appropriate preflight commitment level + +If your transaction uses a blockhash that was fetched from one RPC node then you send, or simulate, that transaction with a different RPC node, you could run into issues due to one node lagging behind the other. + +When RPC nodes receive a `sendTransaction` request, they will attempt to determine the expiration block of your transaction using the most recent finalized block or with the block selected by the `preflightCommitment` parameter. A **VERY** common issue is that a received transaction’s blockhash was produced after the block used to calculate the expiration for that transaction. If an RPC node can’t determine when your transaction expires, it will only forward your transaction **one time** and then will **drop** the transaction. + +Similarly, when RPC nodes receive a `simulateTransaction` request, they will simulate your transaction using the most recent finalized block or with the block selected by the `preflightCommitment` parameter. If the block chosen for simulation is older than the block used for your transaction’s blockhash, the simulation will fail with the dreaded “blockhash not found” error. + +**Recommendation** + +Even if you use `skipPreflight`, **ALWAYS** set the `preflightCommitment` parameter to the same commitment level used to fetch your transaction’s blockhash for both `sendTransaction` and `simulateTransaction` requests. + +### Be wary of lagging RPC nodes when sending transactions + +When your application uses an RPC pool service or when the RPC endpoint differs between creating a transaction and sending a transaction, you need to be wary of situations where one RPC node is lagging behind the other. For example, if you fetch a transaction blockhash from one RPC node then you send that transaction to a second RPC node for forwarding or simulation, the second RPC node might be lagging behind the first. + +**Recommendation** + +For `sendTransaction` requests, clients should keep resending a transaction to a RPC node on a frequent interval so that if an RPC node is slightly lagging behind the cluster, it will eventually catch up and detect your transaction’s expiration properly. + +For `simulateTransaction` requests, clients should use the [`replaceRecentBlockhash`](/api/http#simulatetransaction) parameter to tell the RPC node to replace the simulated transaction’s blockhash with a blockhash that will always be valid for simulation. + +### Avoid reusing stale blockhashes + +Even if your application has fetched a very recent blockhash, be sure that you’re not reusing that blockhash in transactions for too long. The ideal scenario is that a recent blockhash is fetched right before a user signs their transaction. + +**Recommendation for applications** + +Poll for new recent blockhashes on a frequent basis to ensure that whenever a user triggers an action that creates a transaction, your application already has a fresh blockhash that’s ready to go. + +**Recommendation for wallets** + +Poll for new recent blockhashes on a frequent basis and replace a transaction’s recent blockhash right before they sign the transaction to ensure the blockhash is as fresh as possible. + +### Use healthy RPC nodes when fetching blockhashes + +By fetching the latest blockhash with the `"confirmed"` commitment level from an RPC node, it’s going to respond with the blockhash for the latest confirmed block that it’s aware of. Solana’s block propagation protocol prioritizes sending blocks to staked nodes so RPC nodes naturally lag about a block behind the rest of the cluster. They also have to do more work to handle application requests and can lag a lot more under heavy user traffic. + +Lagging RPC nodes can therefore respond to blockhash requests with blockhashes that were confirmed by the cluster quite awhile ago. By default, a lagging RPC node detects that it is more than 150 slots behind the cluster will stop responding to requests, but just before hitting that threshold they can still return a blockhash that is just about to expire. + +**Recommendation** + +Monitor the health of your RPC nodes to ensure that they have an up-to-date view of the cluster state with one of the following methods: + +1. Fetch your RPC node’s highest processed slot by using the [`getSlot`](/api/http#getslot) RPC API with the `"processed"` commitment level and then call the [`getMaxShredInsertSlot](/api/http#getmaxshredinsertslot) RPC API to get the highest slot that your RPC node has received a “shred” of a block for. If the difference between these responses is very large, the cluster is producing blocks far ahead of what the RPC node has processed. +2. Call the `getLatestBlockhash` RPC API with the `"confirmed"` commitment level on a few different RPC API nodes and use the blockhash from the node that returns the highest slot for its [context slot](/api/http#rpcresponse-structure). + +### Wait long enough for expiration + +**Recommendation** + +When calling [`getLatestBlockhash`](/api/http#getlatestblockhash) RPC API to get a recent blockhash for your transaction, take note of the `"lastValidBlockHeight"` in the response. + +Then, poll the [`getBlockHeight`](/api/http#getblockheight) RPC API with the “confirmed” commitment level until it returns a block height greater than the previously returned last valid block height. + +### Consider using “durable” transactions + +Sometimes transaction expiration issues are really hard to avoid (e.g. offline signing, cluster instability). If the previous tips are still not sufficient for your use-case, you can switch to using durable transactions (they just require a bit of setup). + +To start using durable transactions, a user first needs to submit a transaction that [invokes instructions that create a special on-chain “nonce” account](https://docs.rs/solana-program/latest/solana_program/system_instruction/fn.create_nonce_account.html) and stores a “durable blockhash” inside of it. At any point in the future (as long as the nonce account hasn’t been used yet), the user can create a durable transaction by following these 2 rules: + +1. The instruction list must start with an [“advance nonce” system instruction](https://docs.rs/solana-program/latest/solana_program/system_instruction/fn.advance_nonce_account.html) which loads their on-chain nonce account +2. The transaction’s blockhash must be equal to the durable blockhash stored by the on-chain nonce account + +Here’s how these transactions are processed by the Solana runtime: + +1. If the transaction’s blockhash is no longer “recent”, the runtime checks if the transaction’s instruction list begins with an “advance nonce” system instruction +2. If so, it then loads the nonce account specified by the “advance nonce” instruction +3. Then it checks that the stored durable blockhash matches the transaction’s blockhash +4. Lastly it makes sure to advance the nonce account’s stored blockhash to the latest recent blockhash to ensure that the same transaction can never be processed again + +For more details about how these durable transactions work, you can read the [original proposal](./../implemented-proposals/durable-tx-nonces.md) and [check out an example](./clients/javascript-reference#nonceaccount) in the Solana docs. diff --git a/docs/src/developing/versioned-transactions.md b/docs/src/developing/versioned-transactions.md new file mode 100644 index 00000000000000..4ff509e4994a1c --- /dev/null +++ b/docs/src/developing/versioned-transactions.md @@ -0,0 +1,149 @@ +--- +title: Versioned Transactions +description: "" +--- + +[Versioned Transactions](./versioned-transactions.md) are the new transaction format that allow for additional functionality in the Solana runtime, including [Address Lookup Tables](./lookup-tables.md). + +While changes to [on chain](./on-chain-programs/overview.md) programs are **NOT** required to support the new functionality of versioned transactions (or for backwards compatibility), developers **WILL** need update their client side code to prevent [errors due to different transaction versions](#max-supported-transaction-version). + +## Current Transaction Versions + +The Solana runtime supports two transaction versions: + +- `legacy` - older transaction format with no additional benefit +- `0` - added support for [Address Lookup Tables](./lookup-tables.md) + +## Max supported transaction version + +All RPC requests that return a transaction **_should_** specify the highest version of transactions they will support in their application using the `maxSupportedTransactionVersion` option, including [`getBlock`](../api/http#getblock) and [`getTransaction`](../api/http#gettransaction). + +An RPC request will fail if a [Versioned Transaction](./versioned-transactions.md) is returned that is higher than the set `maxSupportedTransactionVersion`. (i.e. if a version `0` transaction is returned when `legacy` is selected) + +> WARNING: +> If no `maxSupportedTransactionVersion` value is set, then only `legacy` transactions will be allowed in the RPC response. Therefore, your RPC requests **WILL** fail if any version `0` transactions are returned. + +## How to set max supported version + +You can set the `maxSupportedTransactionVersion` using both the [`@solana/web3.js`](https://solana-labs.github.io/solana-web3.js/) library and JSON formatted requests directly to an RPC endpoint. + +### Using web3.js + +Using the [`@solana/web3.js`](https://solana-labs.github.io/solana-web3.js/) library, you can retrieve the most recent block or get a specific transaction: + +```js +// connect to the `devnet` cluster and get the current `slot` +const connection = new web3.Connection(web3.clusterApiUrl("devnet")); +const slot = await connection.getSlot(); + +// get the latest block (allowing for v0 transactions) +const block = await connection.getBlock(slot, { + maxSupportedTransactionVersion: 0, +}); + +// get a specific transaction (allowing for v0 transactions) +const getTx = await connection.getTransaction( + "3jpoANiFeVGisWRY5UP648xRXs3iQasCHABPWRWnoEjeA93nc79WrnGgpgazjq4K9m8g2NJoyKoWBV1Kx5VmtwHQ", + { + maxSupportedTransactionVersion: 0, + }, +); +``` + +### JSON requests to the RPC + +Using a standard JSON formatted POST request, you can set the `maxSupportedTransactionVersion` when retrieving a specific block: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d \ +'{"jsonrpc": "2.0", "id":1, "method": "getBlock", "params": [430, { + "encoding":"json", + "maxSupportedTransactionVersion":0, + "transactionDetails":"full", + "rewards":false +}]}' +``` + +## How create a Versioned Transaction + +Versioned transactions can be created similar to the older method of creating transactions. There are differences in using certain libraries that should be noted. + +Below is an example of how to create a Versioned Transaction, using the `@solana/web3.js` library, to send perform a SOL transfer between two accounts. + +#### Notes: + +- `payer` is a valid `Keypair` wallet, funded with SOL +- `toAccount` a valid `Keypair` + +Firstly, import the web3.js library and create a `connection` to your desired cluster. + +We then define the recent `blockhash` and `minRent` we will need for our transaction and the account. + +```js +const web3 = require("@solana/web3.js"); + +// connect to the cluster and get the minimum rent for rent exempt status +const connection = new web3.Connection(web3.clusterApiUrl("devnet")); +let minRent = await connection.getMinimumBalanceForRentExemption(0); +let blockhash = await connection + .getLatestBlockhash() + .then((res) => res.blockhash); +``` + +Create an `array` of all the `instructions` you desire to send in your transaction. In this example below, we are creating a simple SOL transfer instruction: + +```js +// create an array with your desires `instructions` +const instructions = [ + web3.SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: toAccount.publicKey, + lamports: minRent, + }), +]; +``` + +Next, construct a `MessageV0` formatted transaction message with your desired `instructions`: + +```js +// create v0 compatible message +const messageV0 = new web3.TransactionMessage({ + payerKey: payer.publicKey, + recentBlockhash: blockhash, + instructions, +}).compileToV0Message(); +``` + +Then, create a new `VersionedTransaction`, passing in our v0 compatible message: + +```js +const transaction = new web3.VersionedTransaction(messageV0); + +// sign your transaction with the required `Signers` +transaction.sign([payer]); +``` + +You can sign the transaction by either: + +- passing an array of `signatures` into the `VersionedTransaction` method, or +- call the `transaction.sign()` method, passing an array of the required `Signers` + +> NOTE: +> After calling the `transaction.sign()` method, all the previous transaction `signatures` will be fully replaced by new signatures created from the provided in `Signers`. + +After your `VersionedTransaction` has been signed by all required accounts, you can send it to the cluster and `await` the response. + +```js +// send our v0 transaction to the cluster +const txid = await connection.sendTransaction(transaction); +console.log(`https://explorer.solana.com/tx/${txid}?cluster=devnet`); +``` + +> NOTE: +> Unlike `legacy` transactions, sending a `VersionedTransaction` via `sendTransaction` does **NOT** support transaction signing via passing in an array of `Signers` as the second parameter. You will need to sign the transaction before calling `connection.sendTransaction()`. + +## More Resources + +- using [Versioned Transactions for Address Lookup Tables](./lookup-tables.md#how-to-create-an-address-lookup-table) +- view an [example of a v0 transaction](https://explorer.solana.com/tx/3jpoANiFeVGisWRY5UP648xRXs3iQasCHABPWRWnoEjeA93nc79WrnGgpgazjq4K9m8g2NJoyKoWBV1Kx5VmtwHQ/?cluster=devnet) on Solana Explorer +- read the [accepted proposal](./../proposals/transactions-v2.md) for Versioned Transaction and Address Lookup Tables diff --git a/docs/src/getstarted/hello-world.md b/docs/src/getstarted/hello-world.md new file mode 100644 index 00000000000000..78d0715225d77d --- /dev/null +++ b/docs/src/getstarted/hello-world.md @@ -0,0 +1,242 @@ +--- +title: "Hello World Quickstart Guide" +description: 'This "hello world" quickstart guide will demonstrate how to setup, build, and deploy your first Solana program in your browser with Solana Playground.' +keywords: + - playground + - solana pg + - on chain + - rust + - native program + - tutorial + - intro to solana development + - blockchain developer + - blockchain tutorial + - web3 developer +--- + +For this "hello world" quickstart guide, we will use [Solana Playground](https://beta.solpg.io), a browser the based IDE, to develop and deploy our Solana program. To use it, you do **NOT** have to install any software on your computer. Simply open Solana Playground in your browser of choice, and you are ready to write and deploy Solana programs. + +## What you will learn + +- How to get started with Solana Playground +- How to create a Solana wallet on Playground +- How to program a basic Solana program in Rust +- How to build and deploy a Solana Rust program +- How to interact with your on chain program using JavaScript + +## Using Solana Playground + +[Solana Playground](https://beta.solpg.io) is browser based application that will let you write, build, and deploy on chain Solana programs. All from your browser. No installation needed. + +It is a great developer resource for getting started with Solana development, especially on Windows. + +### Import our example project + +In a new tab in your browser, open our example "_Hello World_" project on Solana Playground: https://beta.solpg.io/6314a69688a7fca897ad7d1d + +Next, import the project into your local workspace by clicking the "**Import**" icon and naming your project `hello_world`. + +![Import the get started Solana program on Solana Playground](/img/quickstarts/solana-get-started-import-on-playground.png) + +> If you do **not** import the program into **your** Solana Playground, then you will **not** be able to make changes to the code. But you **will** still be able to build and deploy the code to a Solana cluster. + +### Create a Playground wallet + +Normally with [local development](./local.md), you will need to create a file system wallet for use with the Solana CLI. But with the Solana Playground, you only need to click a few buttons to create a browser based wallet. + +:::caution +Your _Playground Wallet_ will be saved in your browser's local storage. Clearing your browser cache will remove your saved wallet. When creating a new wallet, you will have the option to save a local copy of your wallet's keypair file. +::: + +Click on the red status indicator button at the bottom left of the screen, (optionally) save your wallet's keypair file to your computer for backup, then click "**Continue**". + +After your Playground Wallet is created, you will notice the bottom of the window now states your wallet's address, your SOL balance, and the Solana cluster you are connected to (Devnet is usually the default/recommended, but a "localhost" [test validator](./local.md) is also acceptable). + +## Create a Solana program + +The code for your Rust based Solana program will live in your `src/lib.rs` file. Inside `src/lib.rs` you will be able to import your Rust crates and define your logic. Open your `src/lib.rs` file within Solana Playground. + +### Import the `solana_program` crate + +At the top of `lib.rs`, we import the `solana-program` crate and bring our needed items into the local namespace: + +```rust +use solana_program::{ + account_info::AccountInfo, + entrypoint, + entrypoint::ProgramResult, + pubkey::Pubkey, + msg, +}; +``` + +### Write your program logic + +Every Solana program must define an `entrypoint` that tells the Solana runtime where to start executing your on chain code. Your program's [entrypoint](../developing/on-chain-programs/developing-rust#program-entrypoint) should provide a public function named `process_instruction`: + +```rust +// declare and export the program's entrypoint +entrypoint!(process_instruction); + +// program entrypoint's implementation +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8] +) -> ProgramResult { + // log a message to the blockchain + msg!("Hello, world!"); + + // gracefully exit the program + Ok(()) +} +``` + +Every on chain program should return the `Ok` [result enum](https://doc.rust-lang.org/std/result/) with a value of `()`. This tells the Solana runtime that your program executed successfully without errors. + +Our program above will simply [log a message](../developing/on-chain-programs/debugging#logging) of "_Hello, world!_" to the blockchain cluster, then gracefully exit with `Ok(())`. + +### Build your program + +On the left sidebar, select the "**Build & Deploy**" tab. Next, click the "Build" button. + +If you look at the Playground's terminal, you should see your Solana program begin to compile. Once complete, you will see a success message. + +![Viewing a successful build of your Rust based program](/img/quickstarts/solana-get-started-successful-build.png) + +:::caution +You may receive _warning_ when your program is compiled due to unused variables. Don't worry, these warning will not affect your build. They are due to our very simple program not using all the variables we declared in the `process_instruction` function. +::: + +### Deploy your program + +You can click the "Deploy" button to deploy your first program to the Solana blockchain. Specifically to your selected cluster (e.g. Devnet, Testnet, etc). + +After each deployment, you will see your Playground Wallet balance change. By default, Solana Playground will automatically request SOL airdrops on your behalf to ensure your wallet has enough SOL to cover the cost of deployment. + +> Note: +> If you need more SOL, you can airdrop more by typing airdrop command in the playground terminal: + +```sh +solana airdrop 2 +``` + +![Build and deploy your Solana program to the blockchain](/img/quickstarts/solana-get-started-build-and-deploy.png) + +### Find your program id + +When executing a program using [web3.js](../developing/clients/javascript-reference.md) or from [another Solana program](../developing/programming-model/calling-between-programs.md), you will need to provide the `program id` (aka public address of your program). + +Inside Solana Playground's **Build & Deploy** sidebar, you can find your `program id` under the **Program Credentials** dropdown. + +#### Congratulations! + +You have successfully setup, built, and deployed a Solana program using the Rust language directly in your browser. Next, we will demonstrate how to interact with your on chain program. + +## Interact with your on chain program + +Once you have successfully deployed a Solana program to the blockchain, you will want to be able to interact with that program. + +Like most developers creating dApps and websites, we will interact with our on chain program using JavaScript. Specifically, will use the open source [NPM package](https://www.npmjs.com/package/@solana/web3.js) `@solana/web3.js` to aid in our client application. + +:::info +This web3.js package is an abstraction layer on top of the [JSON RPC API](/api) that reduced the need for rewriting common boilerplate, helping to simplify your client side application code. +::: + +### Initialize client + +We will be using Solana Playground for the client generation. Create a client folder by running `run` command in the playground terminal: + +```bash +run +``` + +We have created `client` folder and a default `client.ts`. This is where we will work for the rest of our `hello world` program. + +### Playground globals + +In playground, there are many utilities that are globally available for us to use without installing or setting up anything. Most important ones for our `hello world` program are `web3` for `@solana/web3.js` and `pg` for Solana Playground utilities. + +:::info +You can go over all of the available globals by pressing `CTRL+SPACE` (or `CMD+SPACE` on macOS) inside the editor. +::: + +### Call the program + +To execute your on chain program, you must send a [transaction](../developing/programming-model/transactions.md) to it. Each transaction submitted to the Solana blockchain contains a listing of instructions (and the program's that instruction will interact with). + +Here we create a new transaction and add a single `instruction` to it: + +```js +// create an empty transaction +const transaction = new web3.Transaction(); + +// add a hello world program instruction to the transaction +transaction.add( + new web3.TransactionInstruction({ + keys: [], + programId: new web3.PublicKey(pg.PROGRAM_ID), + }), +); +``` + +Each `instruction` must include all the keys involved in the operation and the program ID we want to execute. In this example `keys` is empty because our program only logs `hello world` and doesn't need any accounts. + +With our transaction created, we can submit it to the cluster: + +```js +// send the transaction to the Solana cluster +console.log("Sending transaction..."); +const txHash = await web3.sendAndConfirmTransaction( + pg.connection, + transaction, + [pg.wallet.keypair], +); +console.log("Transaction sent with hash:", txHash); +``` + +:::info +The first signer in the signers array is the transaction fee payer by default. We are signing with our keypair `pg.wallet.keypair`. +::: + +### Run the application + +With the client application written, you can run the code via the same `run` command. + +Once your application completes, you will see output similar to this: + +```sh +Running client... + client.ts: + My address: GkxZRRNPfaUfL9XdYVfKF3rWjMcj5md6b6mpRoWpURwP + My balance: 5.7254472 SOL + Sending transaction... + Transaction sent with hash: 2Ra7D9JoqeNsax9HmNq6MB4qWtKPGcLwoqQ27mPYsPFh3h8wignvKB2mWZVvdzCyTnp7CEZhfg2cEpbavib9mCcq +``` + +### Get transaction logs + +We will be using `solana-cli` directly in playground to get the information about any transaction: + +```sh +solana confirm -v +``` + +Change `` with the hash you received from calling `hello world` program. + +You should see `Hello, world!` in the **Log Messages** section of the output. 🎉 + +#### Congratulations!!! + +You have now written a client application for your on chain program. You are now a Solana developer! + +PS: Try to update your program's message then re-build, re-deploy, and re-execute your program. + +## Next steps + +See the links below to learn more about writing Solana programs: + +- [Setup your local development environment](./local.md) +- [Overview of writing Solana programs](../developing/on-chain-programs/overview) +- [Learn more about developing Solana programs with Rust](../developing/on-chain-programs/developing-Rust) +- [Debugging on chain programs](../developing/on-chain-programs/debugging) diff --git a/docs/src/getstarted/local.md b/docs/src/getstarted/local.md new file mode 100644 index 00000000000000..067cc625a9c774 --- /dev/null +++ b/docs/src/getstarted/local.md @@ -0,0 +1,101 @@ +--- +title: "Local Development Quickstart" +description: "This quickstart guide will demonstrate how to quickly install and setup your local Solana development environment." +keywords: + - rust + - cargo + - toml + - program + - tutorial + - intro to solana development + - blockchain developer + - blockchain tutorial + - web3 developer +--- + +This quickstart guide will demonstrate how to quickly install and setup your local development environment, getting you ready to start developing and deploying Solana programs to the blockchain. + +## What you will learn + +- How to install the Solana CLI locally +- How to setup a localhost Solana cluster/validator +- How to create a Solana wallet for developing +- How to airdrop SOL tokens for your wallet + +## Install the Solana CLI + +To interact with the Solana clusters from your terminal, install the [Solana CLI tool suite](./../cli/install-solana-cli-tools) on your local system: + +```bash +sh -c "$(curl -sSfL https://release.solana.com/stable/install)" +``` + +## Setup a localhost blockchain cluster + +The Solana CLI comes with the [test validator](./../developing/test-validator.md) built in. This command line tool will allow you to run a full blockchain cluster on your machine. + +```bash +solana-test-validator +``` + +> **PRO TIP:** +> Run the Solana test validator in a new/separate terminal window that will remain open. The command line program must remain running for your localhost cluster to remain online and ready for action. + +Configure your Solana CLI to use your localhost validator for all your future terminal commands: + +```bash +solana config set --url localhost +``` + +At any time, you can view your current Solana CLI configuration settings: + +```bash +solana config get +``` + +## Create a file system wallet + +To deploy a program with Solana CLI, you will need a Solana wallet with SOL tokens to pay for the cost of transactions. + +Let's create a simple file system wallet for testing: + +```bash +solana-keygen new +``` + +By default, the `solana-keygen` command will create a new file system wallet located at `~/.config/solana/id.json`. You can manually specify the output file location using the `--outfile /path` option. + +> **NOTE:** +> If you already have a file system wallet saved at the default location, this command will **NOT** override it (unless you explicitly force override using the `--force` flag). + +#### Set your new wallet as default + +With your new file system wallet created, you must tell the Solana CLI to use this wallet to deploy and take ownership of your on chain program: + +```bash +solana config set -k ~/.config/solana/id.json +``` + +## Airdrop SOL tokens to your wallet + +Once your new wallet is set as the default, you can request a free airdrop of SOL tokens to it: + +```bash +solana airdrop 2 +``` + +> **NOTE:** +> The `solana airdrop` command has a limit of how many SOL tokens can be requested _per airdrop_ for each cluster (localhost, testnet, or devent). If your airdrop transaction fails, lower your airdrop request quantity and try again. + +You can check your current wallet's SOL balance any time: + +```bash +solana balance +``` + +## Next steps + +See the links below to learn more about writing Rust based Solana programs: + +- [Create and deploy a Solana Rust program](./rust.md) +- [Overview of writing Solana programs](../developing/on-chain-programs/overview) diff --git a/docs/src/getstarted/rust.md b/docs/src/getstarted/rust.md new file mode 100644 index 00000000000000..8ab59ae7811b51 --- /dev/null +++ b/docs/src/getstarted/rust.md @@ -0,0 +1,156 @@ +--- +title: "Rust Program Quickstart" +description: "This quickstart guide will demonstrate how to quickly setup, build, and deploy your first Rust based Solana program to the blockchain." +keywords: + - rust + - cargo + - toml + - program + - tutorial + - intro to solana development + - blockchain developer + - blockchain tutorial + - web3 developer +--- + +Rust is the most common programming language to write Solana programs with. This quickstart guide will demonstrate how to quickly setup, build, and deploy your first Rust based Solana program to the blockchain. + +> **NOTE: ** +> This guide uses the Solana CLI and assumes you have setup your local development environment. Checkout our [local development quickstart guide](./local.md) here to quickly get setup. + +## What you will learn + +- How to install the Rust language locally +- How to initialize a new Solana Rust program +- How to code a basic Solana program in Rust +- How to build and deploy your Rust program + +## Install Rust and Cargo + +To be able to compile Rust based Solana programs, install the Rust language and Cargo (the Rust package manager) using [Rustup](https://rustup.rs/): + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` + +## Run your localhost validator + +The Solana CLI comes with the [test validator](../developing/test-validator.md) built in. This command line tool will allow you to run a full blockchain cluster on your machine. + +```bash +solana-test-validator +``` + +> **PRO TIP:** +> Run the Solana test validator in a new/separate terminal window that will remain open. This command line program must remain running for your localhost validator to remain online and ready for action. + +Configure your Solana CLI to use your localhost validator for all your future terminal commands and Solana program deployment: + +```bash +solana config set --url localhost +``` + +## Create a new Rust library with Cargo + +Solana programs written in Rust are _libraries_ which are compiled to [BPF bytecode](../developing/on-chain-programs/overview#berkeley-packet-filter-bpf) and saved in the `.so` format. + +Initialize a new Rust library named `hello_world` via the Cargo command line: + +```bash +cargo init hello_world --lib +cd hello_world +``` + +Add the `solana-program` crate to your new Rust library: + +```bash +cargo add solana-program +``` + +Open your `Cargo.toml` file and add these required Rust library configuration settings, updating your project name as appropriate: + +```toml +[lib] +name = "hello_world" +crate-type = ["cdylib", "lib"] +``` + +## Create your first Solana program + +The code for your Rust based Solana program will live in your `src/lib.rs` file. Inside `src/lib.rs` you will be able to import your Rust crates and define your logic. Open your `src/lib.rs` file in your favorite editor. + +At the top of `lib.rs`, import the `solana-program` crate and bring our needed items into the local namespace: + +```rust +use solana_program::{ + account_info::AccountInfo, + entrypoint, + entrypoint::ProgramResult, + pubkey::Pubkey, + msg, +}; +``` + +Every Solana program must define an `entrypoint` that tells the Solana runtime where to start executing your on chain code. Your program's [entrypoint](../developing/on-chain-programs/developing-rust#program-entrypoint) should provide a public function named `process_instruction`: + +```rust +// declare and export the program's entrypoint +entrypoint!(process_instruction); + +// program entrypoint's implementation +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8] +) -> ProgramResult { + // log a message to the blockchain + msg!("Hello, world!"); + + // gracefully exit the program + Ok(()) +} +``` + +Every on chain program should return the `Ok` [result enum](https://doc.rust-lang.org/std/result/) with a value of `()`. This tells the Solana runtime that your program executed successfully without errors. + +This program above will simply [log a message](../developing/on-chain-programs/debugging#logging) of "_Hello, world!_" to the blockchain cluster, then gracefully exit with `Ok(())`. + +## Build your Rust program + +Inside a terminal window, you can build your Solana Rust program by running in the root of your project (i.e. the directory with your `Cargo.toml` file): + +```bash +cargo build-bpf +``` + +> **NOTE:** +> After each time you build your Solana program, the above command will output the build path of your compiled program's `.so` file and the default keyfile that will be used for the program's address. + +## Deploy your Solana program + +Using the Solana CLI, you can deploy your program to your currently selected cluster: + +```bash +solana program deploy ./target/deploy/hello_world.so +``` + +Once your Solana program has been deployed (and the transaction [finalized](../cluster/commitments.md)), the above command will output your program's public address (aka its "program id"). + +```bash +# example output +Program Id: EFH95fWg49vkFNbAdw9vy75tM7sWZ2hQbTTUmuACGip3 +``` + +#### Congratulations! + +You have successfully setup, built, and deployed a Solana program using the Rust language. + +> PS: Check your Solana wallet's balance again after you deployed. See how much SOL it cost to deploy your simple program? + +## Next steps + +See the links below to learn more about writing Rust based Solana programs: + +- [Overview of writing Solana programs](../developing/on-chain-programs/overview) +- [Learn more about developing Solana programs with Rust](../developing/on-chain-programs/developing-Rust) +- [Debugging on chain programs](../developing/on-chain-programs/debugging) diff --git a/docs/src/implemented-proposals/commitment.md b/docs/src/implemented-proposals/commitment.md index a404aded2fe11f..3297dd9fe74bef 100644 --- a/docs/src/implemented-proposals/commitment.md +++ b/docs/src/implemented-proposals/commitment.md @@ -4,7 +4,7 @@ title: Commitment The commitment metric aims to give clients a measure of the network confirmation and stake levels on a particular block. Clients can then use this information to -derive their own measures of commitment. +derive their own [measures of commitment](../cluster/commitments.md). # Calculation RPC @@ -40,7 +40,7 @@ This computation is performed on a votable candidate bank `b` as follows. } ``` -where `f` is some accumulation function that modifies the `Stake` entry +Where `f` is some accumulation function that modifies the `Stake` entry for slot `a` with some data derivable from vote `v` and `vote_account` (stake, lockout, etc.). Note here that the `ancestors` here only includes slots that are present in the current status cache. Signatures for banks earlier diff --git a/docs/src/implemented-proposals/ed_overview/ed_overview.md b/docs/src/implemented-proposals/ed_overview/ed_overview.md index 8d618dc22a2dfc..000ff2adbe6995 100644 --- a/docs/src/implemented-proposals/ed_overview/ed_overview.md +++ b/docs/src/implemented-proposals/ed_overview/ed_overview.md @@ -2,7 +2,7 @@ title: Cluster Economics --- -**Subject to change. Follow most recent economic discussions in the Solana forums: https://forums.solana.com** +**Subject to change.** Solana’s crypto-economic system is designed to promote a healthy, long term self-sustaining economy with participant incentives aligned to the security and decentralization of the network. The main participants in this economy are validation-clients. Their contributions to the network, state validation, and their requisite incentive mechanisms are discussed below. diff --git a/docs/src/implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_overview.md b/docs/src/implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_overview.md index c80b1fdb17b111..e0bd9bbe4e2d93 100644 --- a/docs/src/implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_overview.md +++ b/docs/src/implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_overview.md @@ -2,8 +2,8 @@ title: Validation-client Economics --- -**Subject to change. Follow most recent economic discussions in the Solana forums: https://forums.solana.com** +**Subject to change.** -Validator-clients are eligible to charge commission on inflationary rewards distributed to staked tokens. This compensation is for providing compute \(CPU+GPU\) resources to validate and vote on a given PoH state. These protocol-based rewards are determined through an algorithmic disinflationary schedule as a function of total token supply. The network is expected to launch with an annual inflation rate around 8%, set to decrease by 15% per year until a long-term stable rate of 1.5% is reached, however these parameters are yet to be finalized by the community. These issuances are to be split and distributed to participating validators, with around 95% of the issued tokens allocated for validator rewards initiall (the remaining 5% reserved for Foundation operating expenses). Because the network will be distributing a fixed amount of inflation rewards across the stake-weighted validator set, the yield observed for staked tokens will be primarily a function of the amount of staked tokens in relation to the total token supply. +Validator-clients are eligible to charge commission on inflationary rewards distributed to staked tokens. This compensation is for providing compute \(CPU+GPU\) resources to validate and vote on a given PoH state. These protocol-based rewards are determined through an algorithmic disinflationary schedule as a function of total token supply. The network is expected to launch with an annual inflation rate around 8%, set to decrease by 15% per year until a long-term stable rate of 1.5% is reached, however these parameters are yet to be finalized by the community. These issuances are to be split and distributed to participating validators, with around 95% of the issued tokens allocated for validator rewards initial (the remaining 5% reserved for Foundation operating expenses). Because the network will be distributing a fixed amount of inflation rewards across the stake-weighted validator set, the yield observed for staked tokens will be primarily a function of the amount of staked tokens in relation to the total token supply. Additionally, validator clients may earn revenue through fees via state-validation transactions. For clarity, we separately describe the design and motivation of these revenue distributions for validation-clients below: state-validation protocol-based rewards and state-validation transaction fees and rent. diff --git a/docs/src/implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards.md b/docs/src/implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards.md index 8f3fb5c03a3ac0..e5c3d10380b87a 100644 --- a/docs/src/implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards.md +++ b/docs/src/implemented-proposals/ed_overview/ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards.md @@ -2,7 +2,7 @@ title: Inflation Schedule --- -**Subject to change. Follow most recent economic discussions in the Solana forums: https://forums.solana.com** +**Subject to change.** Validator-clients have two functional roles in the Solana network: @@ -13,7 +13,7 @@ Validator-client rewards for these services are to be distributed at the end of The effective protocol-based annual staking yield \(%\) per epoch received by validation-clients is to be a function of: -- the current global inflation rate, derived from the pre-determined dis-inflationary issuance schedule \(see [Validation-client Economics](ed_vce_overview.md)\) +- the current global inflation rate, derived from the pre-determined disinflationary issuance schedule \(see [Validation-client Economics](ed_vce_overview.md)\) - the fraction of staked SOLs out of the current total circulating supply, - the commission charged by the validation service, - the up-time/participation \[% of available slots that validator had opportunity to vote on\] of a given validator over the previous epoch. @@ -25,7 +25,7 @@ As a first step to understanding the impact of the _Inflation Schedule_ on the S Specifically: - _Initial Inflation Rate_: 7-9% -- _Dis-inflation Rate_: -14-16% +- _Disinflation Rate_: -14-16% - _Long-term Inflation Rate_: 1-2% Using these ranges to simulate a number of possible Inflation Schedules, we can explore inflation over time: @@ -46,7 +46,7 @@ $$ In this case, because _% of Staked SOL_ is a parameter that must be estimated (unlike the _Inflation Schedule_ parameters), it is easier to use specific _Inflation Schedule_ parameters and explore a range of _% of Staked SOL_. For the below example, we’ve chosen the middle of the parameter ranges explored above: - _Initial Inflation Rate_: 8% -- _Dis-inflation Rate_: -15% +- _Disinflation Rate_: -15% - _Long-term Inflation Rate_: 1.5% The values of _% of Staked SOL_ range from 60% - 90%, which we feel covers the likely range we expect to observe, based on feedback from the investor and validator communities as well as what is observed on comparable Proof-of-Stake protocols. diff --git a/docs/src/implemented-proposals/implemented-proposals.md b/docs/src/implemented-proposals/implemented-proposals.md index 52ecd71ee37473..5dd1bc16fe03d6 100644 --- a/docs/src/implemented-proposals/implemented-proposals.md +++ b/docs/src/implemented-proposals/implemented-proposals.md @@ -1,9 +1,15 @@ --- title: Implemented Design Proposals +sidebar_position: 1 +sidebar_label: Overview --- -The following architectural proposals have been accepted and implemented -by the Solana team. Any designs that may be subject to future change are noted -in the specific proposal page. -Design proposals that have been accepted but not yet implemented are found in -[Accepted Proposals](../proposals/accepted-design-proposals.md). +These architectural proposals have been accepted and implemented by the Solana maintainers. Any designs that may be subject to future change are noted in the specific proposal page. + +## Not Yet Implemented + +Design proposals that have been accepted but not yet implemented are found in [Accepted Proposals](../proposals/accepted-design-proposals.md). + +## Submit a New Proposal + +To submit a new design proposal, consult this guide on [how to submit a design proposal](../proposals.md#submit-a-design-proposal). diff --git a/docs/src/implemented-proposals/rpc-transaction-history.md b/docs/src/implemented-proposals/rpc-transaction-history.md index 4853d19e6f2290..c8eb878eae45ce 100644 --- a/docs/src/implemented-proposals/rpc-transaction-history.md +++ b/docs/src/implemented-proposals/rpc-transaction-history.md @@ -10,13 +10,13 @@ fall back to the external data store. The affected RPC endpoints are: -- [getFirstAvailableBlock](developing/clients/jsonrpc-api.md#getfirstavailableblock) -- [getConfirmedBlock](developing/clients/jsonrpc-api.md#getconfirmedblock) -- [getConfirmedBlocks](developing/clients/jsonrpc-api.md#getconfirmedblocks) -- [getConfirmedSignaturesForAddress](developing/clients/jsonrpc-api.md#getconfirmedsignaturesforaddress) -- [getConfirmedTransaction](developing/clients/jsonrpc-api.md#getconfirmedtransaction) -- [getSignatureStatuses](developing/clients/jsonrpc-api.md#getsignaturestatuses) -- [getBlockTime](developing/clients/jsonrpc-api.md#getblocktime) +- [getFirstAvailableBlock](../api/http#getfirstavailableblock) +- [getConfirmedBlock](../api/http#getconfirmedblock) +- [getConfirmedBlocks](../api/http#getconfirmedblocks) +- [getConfirmedSignaturesForAddress](../api/http#getconfirmedsignaturesforaddress) +- [getConfirmedTransaction](../api/http#getconfirmedtransaction) +- [getSignatureStatuses](../api/http#getsignaturestatuses) +- [getBlockTime](../api/http#getblocktime) Some system design constraints: diff --git a/docs/src/implemented-proposals/transaction-fees.md b/docs/src/implemented-proposals/transaction-fees.md index 02245647b594c0..042db21a50af76 100644 --- a/docs/src/implemented-proposals/transaction-fees.md +++ b/docs/src/implemented-proposals/transaction-fees.md @@ -6,14 +6,14 @@ title: Deterministic Transaction Fees Before sending a transaction to the cluster, a client may query the network to determine what the transaction's fee will be via the rpc request -[getFeeForMessage](/developing/clients/jsonrpc-api#getfeeformessage). +[getFeeForMessage](../api/http#getfeeformessage). ## Fee Parameters The fee is based on the number of signatures in the transaction, the more signatures a transaction contains, the higher the fee. In addition, a transaction can specify an additional fee that determines how the transaction is -relatively prioritized against others. A transaction's prioritization fee is +relatively prioritized against others. A transaction's prioritization fee is calculated by multiplying the number of compute units by the compute unit price (measured in micro-lamports) set by the transaction via compute budget instructions. diff --git a/docs/src/index.mdx b/docs/src/index.mdx new file mode 100644 index 00000000000000..a0e58819704881 --- /dev/null +++ b/docs/src/index.mdx @@ -0,0 +1,56 @@ +--- +slug: / +id: home +title: Home +sidebar_label: Home +description: + "Solana is a high performance network that is utilized for a range of use cases, \ + including finance, NFTs, payments, and gaming." +# displayed_sidebar: introductionSidebar +--- + +# Solana Documentaion + +Solana is a blockchain built for mass adoption. It's a high performance network that is utilized for +a range of use cases, including finance, NFTs, payments, and gaming. Solana operates as a single +global state machine, and is open, interoperable and decentralized. + +## Getting started + +Dive right into Solana to start building or setup your tooling. + +- [Setup local environment](/cli) - Install the Solana CLI to get your local development environment setup +- [Hello World in your browser](getstarted/hello-world) - Build and deploy your first on-chain Solana program, + directly in your browser using Solana Playground + +## Start learning + +Build a strong understanding of the core concepts that make Solana different from other blockchains. + +- [Transactions](./developing/programming-model/transactions) - Collection of instructions for the blockchain to execute +- [Accounts](./developing/programming-model/accounts) - Data and state storage mechanism for Solana +- [Programs](./developing/intro/programs) - The executable code used to perform actions on the blockchain +- [Cross-Program Invocation](./developing/programming-model/calling-between-programs) - Core of the "composability" + of Solana, this is how programs can "call" each other. + +## Understanding the architecture + +Get to know the underlying architecture of how the proof-of-stake blockchain works. + +- [Validators](./validator/anatomy) - the individual nodes that are the backbone of the network +- [Clusters](./cluster/overview) - a collection of validators that work together for consensus + +## Running a validator + +Explore what it takes to operate a Solana validator and help secure the network. + +- [System requirements](./running-validator/validator-reqs) - Recommended hardware requirements and expected SOL + needed to operate a validator +- [Quick start guide](./running-validator/validator-start) - Setup a validator and get connected to a cluster + for the first time + +## Learn more + +import HomeCtaLinks from "../components/HomeCtaLinks"; + + diff --git a/docs/src/inflation/inflation_schedule.md b/docs/src/inflation/inflation_schedule.md index 1b6777ebdb989b..0b83a75f4e843a 100644 --- a/docs/src/inflation/inflation_schedule.md +++ b/docs/src/inflation/inflation_schedule.md @@ -2,19 +2,19 @@ title: Solana's Proposed Inflation Schedule --- -As mentioned above, the network's _Inflation Schedule_ is uniquely described by three parameters: _Initial Inflation Rate_, _Dis-inflation Rate_ and _Long-term Inflation Rate_. When considering these numbers, there are many factors to take into account: +As mentioned above, the network's _Inflation Schedule_ is uniquely described by three parameters: _Initial Inflation Rate_, _Disinflation Rate_ and _Long-term Inflation Rate_. When considering these numbers, there are many factors to take into account: - A large portion of the SOL issued via inflation will be distributed to stake-holders in proportion to the SOL they have staked. We want to ensure that the _Inflation Schedule_ design results in reasonable _Staking Yields_ for token holders who delegate SOL and for validation service providers (via commissions taken from _Staking Yields_). - The primary driver of _Staked Yield_ is the amount of SOL staked divided by the total amount of SOL (% of total SOL staked). Therefore the distribution and delegation of tokens across validators are important factors to understand when determining initial inflation parameters. -- [Yield throttling](https://forums.solana.com/t/validator-yield-throttling-proposal-discussion/855/5) is a current area of research that would impact _staking-yields_. This is not taken into consideration in the discussion here or the modeling below. +- Yield throttling is a current area of research that would impact _staking-yields_. This is not taken into consideration in the discussion here or the modeling below. - Overall token issuance - i.e. what do we expect the Current Total Supply to be in 10 years, or 20 years? - Long-term, steady-state inflation is an important consideration not only for sustainable support for the validator ecosystem and the Solana Foundation grant programs, but also should be tuned in consideration with expected token losses and burning over time. -- The rate at which we expect network usage to grow, as a consideration to the dis-inflationary rate. Over time, we plan for inflation to drop and expect that usage will grow. +- The rate at which we expect network usage to grow, as a consideration to the disinflationary rate. Over time, we plan for inflation to drop and expect that usage will grow. -Based on these considerations and the community discussions following the initial [design](https://forums.solana.com/t/solana-inflation-design-overview/920), the Solana Foundation proposes the following Inflation Schedule parameters: +Based on these considerations and the community discussions following the initial design, the Solana Foundation proposes the following Inflation Schedule parameters: - Initial Inflation Rate: $8\%$ -- Dis-inflation Rate: $-15\%$ +- Disinflation Rate: $-15\%$ - Long-term Inflation Rate: $1.5\%$ These parameters define the proposed _Inflation Schedule_. Below we show implications of these parameters. These plots only show the impact of inflation issuances given the Inflation Schedule as parameterized above. They _do not account_ for other factors that may impact the Total Supply such as fee/rent burning, slashing or other unforeseen future token destruction events. Therefore, what is presented here is an **upper limit** on the amount of SOL issued via inflation. diff --git a/docs/src/inflation/terminology.md b/docs/src/inflation/terminology.md index df7ae3122c4e08..e9de2b8656ea74 100644 --- a/docs/src/inflation/terminology.md +++ b/docs/src/inflation/terminology.md @@ -14,10 +14,10 @@ The Solana protocol will automatically create new tokens on a predetermined infl ### Inflation Schedule -A deterministic description of token issuance over time. The Solana Foundation is proposing a dis-inflationary _Inflation Schedule_. I.e. Inflation starts at its highest value, the rate reduces over time until stabilizing at a predetermined long-term inflation rate (see discussion below). This schedule is completely and uniquely parameterized by three numbers: +A deterministic description of token issuance over time. The Solana Foundation is proposing a disinflationary _Inflation Schedule_. I.e. Inflation starts at its highest value, the rate reduces over time until stabilizing at a predetermined long-term inflation rate (see discussion below). This schedule is completely and uniquely parameterized by three numbers: - **Initial Inflation Rate [%]**: The starting _Inflation Rate_ for when inflation is first enabled. Token issuance rate can only decrease from this point. -- **Dis-inflation Rate [%]**: The rate at which the _Inflation Rate_ is reduced. +- **Disinflation Rate [%]**: The rate at which the _Inflation Rate_ is reduced. - **Long-term Inflation Rate [%]**: The stable, long-term _Inflation Rate_ to be expected. ### Effective Inflation Rate [%] diff --git a/docs/src/integrations/exchange.md b/docs/src/integrations/exchange.md index c308d001fbe1ad..02306f4332d6d4 100644 --- a/docs/src/integrations/exchange.md +++ b/docs/src/integrations/exchange.md @@ -156,11 +156,17 @@ We recommend using a unique deposit account for each of your users. Solana accounts must be made rent-exempt by containing 2-years worth of [rent](developing/programming-model/accounts.md#rent) in SOL. In order to find the minimum rent-exempt balance for your deposit accounts, query the -[`getMinimumBalanceForRentExemption` endpoint](developing/clients/jsonrpc-api.md#getminimumbalanceforrentexemption): +[`getMinimumBalanceForRentExemption` endpoint](../api/http#getminimumbalanceforrentexemption): ```bash -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getMinimumBalanceForRentExemption","params":[0]}' localhost:8899 +curl localhost:8899 -X POST -H "Content-Type: application/json" -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "getMinimumBalanceForRentExemption", + "params":[0] +}' +# Result {"jsonrpc":"2.0","result":890880,"id":1} ``` @@ -175,83 +181,151 @@ greater security. If so, you will need to move SOL to hot accounts using our When a user wants to deposit SOL into your exchange, instruct them to send a transfer to the appropriate deposit address. +### Versioned Transaction Migration + +When the Mainnet Beta network starts processing versioned transactions, exchanges +**MUST** make changes. If no changes are made, deposit detection will no longer +work properly because fetching a versioned transaction or a block containing +versioned transactions will return an error. + +- `{"maxSupportedTransactionVersion": 0}` + + The `maxSupportedTransactionVersion` parameter must be added to `getBlock` and + `getTransaction` requests to avoid disruption to deposit detection. The latest + transaction version is `0` and should be specified as the max supported + transaction version value. + +It's important to understand that versioned transactions allow users to create +transactions that use another set of account keys loaded from on-chain address +lookup tables. + +- `{"encoding": "jsonParsed"}` + + When fetching blocks and transactions, it's now recommended to use the + `"jsonParsed"` encoding because it includes all transaction account keys + (including those from lookup tables) in the message `"accountKeys"` list. + This makes it straightforward to resolve balance changes detailed in + `preBalances` / `postBalances` and `preTokenBalances` / `postTokenBalances`. + + If the `"json"` encoding is used instead, entries in `preBalances` / + `postBalances` and `preTokenBalances` / `postTokenBalances` may refer to + account keys that are **NOT** in the `"accountKeys"` list and need to be + resolved using `"loadedAddresses"` entries in the transaction metadata. + ### Poll for Blocks To track all the deposit accounts for your exchange, poll for each confirmed block and inspect for addresses of interest, using the JSON-RPC service of your Solana API node. -- To identify which blocks are available, send a [`getBlocks` request](developing/clients/jsonrpc-api.md#getblocks), +- To identify which blocks are available, send a [`getBlocks`](../api/http#getblocks) request, passing the last block you have already processed as the start-slot parameter: ```bash -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getBlocks","params":[5]}' localhost:8899 +curl https://api.devnet.solana.com -X POST -H "Content-Type: application/json" -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "getBlocks", + "params": [160017005, 160017015] +}' -{"jsonrpc":"2.0","result":[5,6,8,9,11],"id":1} +# Result +{"jsonrpc":"2.0","result":[160017005,160017006,160017007,160017012,160017013,160017014,160017015],"id":1} ``` Not every slot produces a block, so there may be gaps in the sequence of integers. -- For each block, request its contents with a [`getBlock` request](developing/clients/jsonrpc-api.md#getblock): +- For each block, request its contents with a [`getBlock`](../api/http#getblock) request: + +### Block Fetching Tips + +- `{"rewards": false}` + +By default, fetched blocks will return information about validator fees on each +block and staking rewards on epoch boundaries. If you don't need this +information, disable it with the "rewards" parameter. + +- `{"transactionDetails": "accounts"}` + +By default, fetched blocks will return a lot of transaction info and metadata +that isn't necessary for tracking account balances. Set the "transactionDetails" +parameter to speed up block fetching. ```bash -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getBlock","params":[5, "json"]}' localhost:8899 +curl https://api.devnet.solana.com -X POST -H 'Content-Type: application/json' -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "getBlock", + "params": [ + 166974442, + { + "encoding": "jsonParsed", + "maxSupportedTransactionVersion": 0, + "transactionDetails": "accounts", + "rewards": false + } + ] +}' +# Result { "jsonrpc": "2.0", "result": { - "blockhash": "2WcrsKSVANoe6xQHKtCcqNdUpCQPQ3vb6QTgi1dcE2oL", - "parentSlot": 4, - "previousBlockhash": "7ZDoGW83nXgP14vnn9XhGSaGjbuLdLWkQAoUQ7pg6qDZ", - "rewards": [], + "blockHeight": 157201607, + "blockTime": 1665070281, + "blockhash": "HKhao674uvFc4wMK1Cm3UyuuGbKExdgPFjXQ5xtvsG3o", + "parentSlot": 166974441, + "previousBlockhash": "98CNLU4rsYa2HDUyp7PubU4DhwYJJhSX9v6pvE7SWsAo", "transactions": [ + ... (omit) { "meta": { "err": null, "fee": 5000, "postBalances": [ - 2033973061360, - 218099990000, - 42000000003 + 1110663066, + 1, + 1040000000 ], + "postTokenBalances": [], "preBalances": [ - 2044973066360, - 207099990000, - 42000000003 + 1120668066, + 1, + 1030000000 ], + "preTokenBalances": [], "status": { "Ok": null } }, "transaction": { - "message": { - "accountKeys": [ - "Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX", - "47Sbuv6jL7CViK9F2NMW51aQGhfdpUu7WNvKyH645Rfi", - "11111111111111111111111111111111" - ], - "header": { - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 1, - "numRequiredSignatures": 1 + "accountKeys": [ + { + "pubkey": "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde", + "signer": true, + "source": "transaction", + "writable": true }, - "instructions": [ - { - "accounts": [ - 0, - 1 - ], - "data": "3Bxs3zyH82bhpB8j", - "programIdIndex": 2 - } - ], - "recentBlockhash": "7GytRgrWXncJWKhzovVoP9kjfLwoiuDb3cWjpXGnmxWh" - }, + { + "pubkey": "11111111111111111111111111111111", + "signer": false, + "source": "transaction", + "writable": false + }, + { + "pubkey": "G1wZ113tiUHdSpQEBcid8n1x8BAvcWZoZgxPKxgE5B7o", + "signer": false, + "source": "lookupTable", + "writable": true + } + ], "signatures": [ - "dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6" + "2CxNRsyRT7y88GBwvAB3hRg8wijMSZh3VNYXAdUesGSyvbRJbRR2q9G1KSEpQENmXHmmMLHiXumw4dp8CvzQMjrM" ] - } - } + }, + "version": 0 + }, + ... (omit) ] }, "id": 1 @@ -262,9 +336,9 @@ The `preBalances` and `postBalances` fields allow you to track the balance changes in every account without having to parse the entire transaction. They list the starting and ending balances of each account in [lamports](../terminology.md#lamport), indexed to the `accountKeys` list. For -example, if the deposit address if interest is -`47Sbuv6jL7CViK9F2NMW51aQGhfdpUu7WNvKyH645Rfi`, this transaction represents a -transfer of 218099990000 - 207099990000 = 11000000000 lamports = 11 SOL +example, if the deposit address of interest is +`G1wZ113tiUHdSpQEBcid8n1x8BAvcWZoZgxPKxgE5B7o`, this transaction represents a +transfer of 1040000000 - 1030000000 = 10,000,000 lamports = 0.01 SOL If you need more information about the transaction type or other specifics, you can request the block from RPC in binary format, and parse it using either our @@ -278,32 +352,49 @@ generally _not_ a viable method for tracking all your deposit addresses over all slots, but may be useful for examining a few accounts for a specific period of time. -- Send a [`getSignaturesForAddress`](developing/clients/jsonrpc-api.md#getsignaturesforaddress) +- Send a [`getSignaturesForAddress`](../api/http#getsignaturesforaddress) request to the api node: ```bash -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getSignaturesForAddress","params":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC", {"limit": 3}]}' localhost:8899 +curl localhost:8899 -X POST -H "Content-Type: application/json" -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "getSignaturesForAddress", + "params": [ + "3M2b3tLji7rvscqrLAHMukYxDK2nB96Q9hwfV6QkdzBN", + { + "limit": 3 + } + ] +}' +# Result { "jsonrpc": "2.0", "result": [ { + "blockTime": 1662064640, + "confirmationStatus": "finalized", "err": null, "memo": null, - "signature": "35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby", - "slot": 114 + "signature": "3EDRvnD5TbbMS2mCusop6oyHLD8CgnjncaYQd5RXpgnjYUXRCYwiNPmXb6ZG5KdTK4zAaygEhfdLoP7TDzwKBVQp", + "slot": 148697216 }, { + "blockTime": 1662064434, + "confirmationStatus": "finalized", "err": null, "memo": null, - "signature": "4bJdGN8Tt2kLWZ3Fa1dpwPSEkXWWTSszPSf1rRVsCwNjxbbUdwTeiWtmi8soA26YmwnKD4aAxNp8ci1Gjpdv4gsr", - "slot": 112 + "signature": "4rPQ5wthgSP1kLdLqcRgQnkYkPAZqjv5vm59LijrQDSKuL2HLmZHoHjdSLDXXWFwWdaKXUuryRBGwEvSxn3TQckY", + "slot": 148696843 }, { + "blockTime": 1662064341, + "confirmationStatus": "finalized", "err": null, "memo": null, - "signature": "dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6", - "slot": 108 + "signature": "36Q383JMiqiobuPV9qBqy41xjMsVnQBm9rdZSdpbrLTGhSQDTGZJnocM4TQTVfUGfV2vEX9ZB3sex6wUBUWzjEvs", + "slot": 148696677 } ], "id": 1 @@ -311,61 +402,105 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"m ``` - For each signature returned, get the transaction details by sending a - [`getTransaction`](developing/clients/jsonrpc-api.md#gettransaction) request: + [`getTransaction`](../api/http#gettransaction) request: ```bash -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getTransaction","params":["dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6", "json"]}' localhost:8899 +curl https://api.devnet.solana.com -X POST -H 'Content-Type: application/json' -d '{ + "jsonrpc":"2.0", + "id":1, + "method":"getTransaction", + "params":[ + "2CxNRsyRT7y88GBwvAB3hRg8wijMSZh3VNYXAdUesGSyvbRJbRR2q9G1KSEpQENmXHmmMLHiXumw4dp8CvzQMjrM", + { + "encoding":"jsonParsed", + "maxSupportedTransactionVersion":0 + } + ] +}' -// Result +# Result { "jsonrpc": "2.0", "result": { - "slot": 5, + "blockTime": 1665070281, + "meta": { + "err": null, + "fee": 5000, + "innerInstructions": [], + "logMessages": [ + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success" + ], + "postBalances": [ + 1110663066, + 1, + 1040000000 + ], + "postTokenBalances": [], + "preBalances": [ + 1120668066, + 1, + 1030000000 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "slot": 166974442, "transaction": { "message": { "accountKeys": [ - "Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX", - "47Sbuv6jL7CViK9F2NMW51aQGhfdpUu7WNvKyH645Rfi", - "11111111111111111111111111111111" + { + "pubkey": "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde", + "signer": true, + "source": "transaction", + "writable": true + }, + { + "pubkey": "11111111111111111111111111111111", + "signer": false, + "source": "transaction", + "writable": false + }, + { + "pubkey": "G1wZ113tiUHdSpQEBcid8n1x8BAvcWZoZgxPKxgE5B7o", + "signer": false, + "source": "lookupTable", + "writable": true + } + ], + "addressTableLookups": [ + { + "accountKey": "4syr5pBaboZy4cZyF6sys82uGD7jEvoAP2ZMaoich4fZ", + "readonlyIndexes": [], + "writableIndexes": [ + 3 + ] + } ], - "header": { - "numReadonlySignedAccounts": 0, - "numReadonlyUnsignedAccounts": 1, - "numRequiredSignatures": 1 - }, "instructions": [ { - "accounts": [ - 0, - 1 - ], - "data": "3Bxs3zyH82bhpB8j", - "programIdIndex": 2 + "parsed": { + "info": { + "destination": "G1wZ113tiUHdSpQEBcid8n1x8BAvcWZoZgxPKxgE5B7o", + "lamports": 10000000, + "source": "9aE476sH92Vz7DMPyq5WLPkrKWivxeuTKEFKd2sZZcde" + }, + "type": "transfer" + }, + "program": "system", + "programId": "11111111111111111111111111111111" } ], - "recentBlockhash": "7GytRgrWXncJWKhzovVoP9kjfLwoiuDb3cWjpXGnmxWh" + "recentBlockhash": "BhhivDNgoy4L5tLtHb1s3TP19uUXqKiy4FfUR34d93eT" }, "signatures": [ - "dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6" + "2CxNRsyRT7y88GBwvAB3hRg8wijMSZh3VNYXAdUesGSyvbRJbRR2q9G1KSEpQENmXHmmMLHiXumw4dp8CvzQMjrM" ] }, - "meta": { - "err": null, - "fee": 5000, - "postBalances": [ - 2033973061360, - 218099990000, - 42000000003 - ], - "preBalances": [ - 2044973066360, - 207099990000, - 42000000003 - ], - "status": { - "Ok": null - } - } + "version": 0 }, "id": 1 } @@ -409,8 +544,7 @@ before retrying a withdrawal transfer that does not appear to have been confirmed or finalized by the cluster. Otherwise, you risk a double spend. See more on [blockhash expiration](#blockhash-expiration) below. -First, get a recent blockhash using the [`getFees` endpoint](developing/clients/jsonrpc-api.md#getfees) -or the CLI command: +First, get a recent blockhash using the [`getFees`](../api/http#getfees) endpoint or the CLI command: ```bash solana fees --url http://localhost:8899 @@ -424,19 +558,30 @@ solana transfer --no-wait --allow-unfunded-recipient --b ``` You can also build, sign, and serialize the transaction manually, and fire it off to -the cluster using the JSON-RPC [`sendTransaction` endpoint](developing/clients/jsonrpc-api.md#sendtransaction). +the cluster using the JSON-RPC [`sendTransaction`](../api/http#sendtransaction) endpoint. #### Transaction Confirmations & Finality Get the status of a batch of transactions using the -[`getSignatureStatuses` JSON-RPC endpoint](developing/clients/jsonrpc-api.md#getsignaturestatuses). +[`getSignatureStatuses`](../api/http#getsignaturestatuses) JSON-RPC endpoint. The `confirmations` field reports how many [confirmed blocks](../terminology.md#confirmed-block) have elapsed since the transaction was processed. If `confirmations: null`, it is [finalized](../terminology.md#finality). ```bash -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatuses", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"]]}' http://localhost:8899 +curl localhost:8899 -X POST -H "Content-Type: application/json" -d '{ + "jsonrpc":"2.0", + "id":1, + "method":"getSignatureStatuses", + "params":[ + [ + "5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", + "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7" + ] + ] +}' +# Result { "jsonrpc": "2.0", "result": { @@ -469,7 +614,7 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, " #### Blockhash Expiration You can check whether a particular blockhash is still valid by sending a -[`getFeeCalculatorForBlockhash`](developing/clients/jsonrpc-api.md#getfeecalculatorforblockhash) +[`getFeeCalculatorForBlockhash`](../api/http#getfeecalculatorforblockhash) request with the blockhash as a parameter. If the response value is `null`, the blockhash is expired, and the withdrawal transaction using that blockhash should never succeed. @@ -480,7 +625,7 @@ As withdrawals are irreversible, it may be a good practice to validate a user-supplied account address before authorizing a withdrawal in order to prevent accidental loss of user funds. -#### Basic verfication +#### Basic verification Solana addresses a 32-byte array, encoded with the bitcoin base58 alphabet. This results in an ASCII text string matching the following regular expression: @@ -575,8 +720,14 @@ holding no data), currently: 0.000890880 SOL Similarly, every deposit account must contain at least this balance. ```bash -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getMinimumBalanceForRentExemption","params":[0]}' localhost:8899 +curl localhost:8899 -X POST -H "Content-Type: application/json" -d '{ + "jsonrpc": "2.0", + "id": 1, + "method": "getMinimumBalanceForRentExemption", + "params": [0] +}' +# Result {"jsonrpc":"2.0","result":890880,"id":1} ``` @@ -722,7 +873,7 @@ identify the token mint and account owner (main wallet address) of the affected account. Note that if a receiving account is created during the transaction, it will have no -`preTokenBalance` entry as there is no existing account state. In this +`preTokenBalance` entry as there is no existing account state. In this case, the initial balance can be assumed to be zero. ### Withdrawing @@ -734,7 +885,7 @@ the exchange should check the address as [described above](#validating-user-supplied-account-addresses-for-withdrawals). Additionally this address must be owned by the System Program and have no account data. If the address has no SOL balance, user confirmation should be -obtained before proceeding with the withdrawal. All other withdrawal addresses +obtained before proceeding with the withdrawal. All other withdrawal addresses must be rejected. From the withdrawal address, the [Associated Token Account](https://spl.solana.com/associated-token-account) diff --git a/docs/src/integrations/retrying-transactions.md b/docs/src/integrations/retrying-transactions.md index c8152ab1a0ee29..c2d7ff24be2292 100644 --- a/docs/src/integrations/retrying-transactions.md +++ b/docs/src/integrations/retrying-transactions.md @@ -7,7 +7,7 @@ title: Retrying Transactions On some occasions, a seemingly valid transaction may be dropped before it is included in a block. This most often occurs during periods of network congestion, when an RPC node fails to rebroadcast the transaction to the -[leader](https://docs.solana.com/terminology#leader). To an end-user, it may +[leader](../terminology#leader). To an end-user, it may appear as if their transaction disappears entirely. While RPC nodes are equipped with a generic rebroadcasting algorithm, application developers are also capable of developing their own custom rebroadcasting logic. @@ -25,6 +25,7 @@ Fact Sheet are submitted - Before re-signing any transaction, it is **very important** to ensure that the initial transaction’s blockhash has expired + ::: ## The Journey of a Transaction @@ -37,7 +38,7 @@ so that they can be processed into a block. There are two main ways in which a transaction can be sent to leaders: 1. By proxy via an RPC server and the - [sendTransaction](https://docs.solana.com/developing/clients/jsonrpc-api#sendtransaction) + [sendTransaction](../api/http#sendtransaction) JSON-RPC method 2. Directly to leaders via a [TPU Client](https://docs.rs/solana-client/1.7.3/solana_client/tpu_client/index.html) @@ -50,9 +51,9 @@ outside of what the client and the relaying RPC nodes are aware of. In the case of a TPU client, rebroadcast and leader forwarding is handled entirely by the client software. -![Transaction Journey](/img/rt-tx-journey.png) +![Transaction Journey](../../static/img/rt-tx-journey.png) - + ### How RPC Nodes Broadcast Transactions @@ -64,7 +65,7 @@ communicate with one another, but does not provide any guarantees regarding transaction delivery. Because Solana’s leader schedule is known in advance of every -[epoch](https://docs.solana.com/terminology#epoch) (~2 days), an RPC node will +[epoch](../terminology#epoch) (~2 days), an RPC node will broadcast its transaction directly to the current and next leaders. This is in contrast to other gossip protocols such as Ethereum that propagate transactions randomly and broadly across the entire network. By default, RPC nodes will try @@ -89,7 +90,7 @@ The TPU processes transactions in five distinct phases: - [Proof of History Service](https://github.com/solana-labs/solana/blob/cd6f931223181d5a1d47cba64e857785a175a760/poh/src/poh_service.rs) - [Broadcast Stage](https://github.com/solana-labs/solana/blob/cd6f931223181d5a1d47cba64e857785a175a760/core/src/tpu.rs#L136) -![TPU Overview](/img/rt-tpu-jito-labs.png) +![TPU Overview](../../static/img/rt-tpu-jito-labs.png) Of these five phases, the Fetch Stage is responsible for receiving transactions. Within the Fetch Stage, validators will categorize incoming transactions @@ -132,15 +133,15 @@ it is processed. The first scenario involves transactions that are submitted via an RPC pool. Occasionally, part of the RPC pool can be sufficiently ahead of the rest of the pool. This can cause issues when nodes within the pool are required to work together. In this example, the transaction’s -[recentBlockhash](https://docs.solana.com/developing/programming-model/transactions#recent-blockhash) +[recentBlockhash](../developing/programming-model/transactions#recent-blockhash) is queried from the advanced part of the pool (Backend A). When the transaction is submitted to the lagging part of the pool (Backend B), the nodes will not recognize the advanced blockhash and will drop the transaction. This can be detected upon transaction submission if developers enable -[preflight checks](https://docs.solana.com/developing/clients/jsonrpc-api#sendtransaction) +[preflight checks](../api/http#sendtransaction) on `sendTransaction`. -![Dropped via RPC Pool](/img/rt-dropped-via-rpc-pool.png) +![Dropped via RPC Pool](../../static/img/rt-dropped-via-rpc-pool.png) Temporarily network forks can also result in dropped transactions. If a validator is slow to replay its blocks within the Banking Stage, it may end up @@ -150,7 +151,7 @@ minority fork. After the transaction is submitted, the cluster can then switch away from its minority fork before the transaction is processed. In this scenario, the transaction is dropped due to the blockhash not being found. -![Dropped due to Minority Fork (Before Processed)](/img/rt-dropped-minority-fork-pre-process.png) +![Dropped due to Minority Fork (Before Processed)](../../static/img/rt-dropped-minority-fork-pre-process.png) ### After a transaction is processed and before it is finalized @@ -162,7 +163,7 @@ would fail to reach consensus with the majority of validators that do not recognize the minority fork. At this time, the transaction would be dropped before it could be finalized. -![Dropped due to Minority Fork (After Processed)](/img/rt-dropped-minority-fork-post-process.png) +![Dropped due to Minority Fork (After Processed)](../../static/img/rt-dropped-minority-fork-post-process.png) ## Handling Dropped Transactions @@ -189,7 +190,7 @@ the transaction will be processed or finalized by the cluster. - `skipPreflight`: `boolean` - if true, skip the preflight transaction checks (default: false) - (optional) `preflightCommitment`: `string` - - [Commitment](https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment) + [Commitment](../api/http#configuring-state-commitment) level to use for preflight simulations against the bank slot (default: "finalized"). - (optional) `encoding`: `string` - Encoding used for the transaction data. @@ -203,8 +204,9 @@ Response - `transaction id`: `string` - First transaction signature embedded in the transaction, as base-58 encoded string. This transaction id can be used with - [getSignatureStatuses](https://docs.solana.com/developing/clients/jsonrpc-api#getsignaturestatuses) + [`getSignatureStatuses`](../api/http#getsignaturestatuses) to poll for status updates. + ::: ## Customizing Rebroadcast Logic @@ -217,9 +219,9 @@ developers to manually control the retry process A common pattern for manually retrying transactions involves temporarily storing the `lastValidBlockHeight` that comes from -[getLatestBlockhash](https://docs.solana.com/developing/clients/jsonrpc-api#getlatestblockhash). +[getLatestBlockhash](../api/http#getlatestblockhash). Once stashed, an application can then -[poll the cluster’s blockheight](https://docs.solana.com/developing/clients/jsonrpc-api#getblockheight) +[poll the cluster’s blockheight](../api/http#getblockheight) and manually retry the transaction at an appropriate interval. In times of network congestion, it’s advantageous to set `maxRetries` to 0 and manually rebroadcast via a custom algorithm. While some applications may employ an @@ -253,7 +255,7 @@ const sleep = async (ms: number) => { LAMPORTS_PER_SOL, ); - await connection.confirmTransaction(airdropSignature); + await connection.confirmTransaction({ signature: airdropSignature }); const blockhashResponse = await connection.getLatestBlockhashAndContext(); const lastValidBlockHeight = blockhashResponse.context.slot + 150; @@ -287,7 +289,7 @@ const sleep = async (ms: number) => { When polling via `getLatestBlockhash`, applications should specify their intended -[commitment](https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment) +[commitment](../api/http#configuring-state-commitment) level. By setting its commitment to `confirmed` (voted on) or `finalized` (~30 blocks after `confirmed`), an application can avoid polling a blockhash from a minority fork. @@ -329,6 +331,6 @@ In Solana, a dropped transaction can be safely discarded once the blockhash it references is older than the `lastValidBlockHeight` received from `getLatestBlockhash`. Developers should keep track of this `lastValidBlockHeight` by querying -[`getEpochInfo`](https://docs.solana.com/developing/clients/jsonrpc-api#getepochinfo) +[`getEpochInfo`](../api/http#getepochinfo) and comparing with `blockHeight` in the response. Once a blockhash is invalidated, clients may re-sign with a newly-queried blockhash. diff --git a/docs/src/learn/state-compression.md b/docs/src/learn/state-compression.md new file mode 100644 index 00000000000000..993544944b3d06 --- /dev/null +++ b/docs/src/learn/state-compression.md @@ -0,0 +1,334 @@ +--- +title: State Compression +description: + 'State Compression is the method of cheaply and securely storing + "fingerprints" of off-chain data in the Solana leger, instead of expensive + accounts.' +--- + +On Solana, [State Compression](./state-compression.md) is the method of creating +a "fingerprint" (or hash) of off-chain data and storing this fingerprint +on-chain for secure verification. Effectively using the security of the Solana +ledger to securely validate off-chain data, verifying it has not been tampered +with. + +This method of "compression" allows Solana programs and dApps to use cheap +blockchain [ledger](./../terminology.md#ledger) space, instead of the more +expensive [account](./../terminology.md#account) space, to securely store data. + +This is accomplished by using a special binary tree structure, known as a +[concurrent merkle tree](#what-is-a-concurrent-merkle-tree), to create a hash of +each piece of data (called a `leaf`), hashing those together, and only storing +this final hash on-chain. + +## What is State Compression? + +In simple terms, state compression uses "**_tree_**" structures to +cryptographically hash off-chain data together, in a deterministic way, to +compute a single final hash that gets stored on-chain. + +These _trees_ are created in this "_deterministic_" process by: + +- taking any piece of data +- creating a hash of this data +- storing this hash as a `leaf` the bottom of the tree +- each `leaf` pair is then hash together, creating a `branch` +- each `branch` is then hash together +- continually climbing the tree and hashing adjacent branches together +- once at the top of the tree, a final `root hash` is produced + +This `root hash` is then stored on chain, as a verifiable **_proof_** of all of +the data within every leaf. Allowing anyone to cryptographically verify all the +off-chain data within the tree, while only actually storing a **minimal** amount +of data on-chain. Therefore, significantly reducing the cost to store/prove +large amounts of data due to this "state compression". + +## Merkle trees and concurrent merkle trees + +Solana's state compression used a special type of +[merkle tree](#what-is-a-merkle-tree) that allows for multiple changes to any +given tree to happen, while still maintaining the integrity and validity of the +tree. + +This special tree, known as a +"[concurrent merkle tree](#what-is-a-concurrent-merkle-tree)", effectively +retains a "changelog" of the tree on-chain. Allowing for multiple rapid changes +to the same tree (i.e. all in the same block), before a proof is invalidated. + +### What is a merkle tree? + +A [merkle tree](https://en.wikipedia.org/wiki/merkle_tree), sometimes called a +"hash tree", is a hash based binary tree structure where each `leaf` node is +represented as a cryptographic hash of its inner data. And every node that is +**not** a leaf, called a `branch`, is represented as a hash of its child leaf +hashes. + +Each branch is then also hashed together, climbing the tree, until eventually +only a single hash remains. This final hash, called the `root hash` or "root", +can then be used in combination with a "proof path" to verify any piece of data +stored within a leaf node. + +Once a final `root hash` has been computed, any piece of data stored within a +`leaf` node can be verified by rehashing the specific leaf's data and the hash +label of each adjacent branch climbing the tree (known as the `proof` or "proof +path"). Comparing this "rehash" to the `root hash` is the verification of the +underlying leaf data. If they match, the data is verified accurate. If they do +not match, the leaf data was changed. + +Whenever desired, the original leaf data can be changed by simply hashing the +**new leaf** data and recomputing the root hash in the same manner of the +original root. This **new root hash** is then used to verify any of the data, +and effectively invalidates the previous root hash and previous proof. +Therefore, each change to these _traditional merkle trees_ are required to be +performed in series. + +:::info + +This process of changing leaf data, and computing a new root hash can be a +**very common** thing when using merkle trees! While it is one of the design +points of the tree, it can result in one of the most notable drawbacks: rapid +changes. + +::: + +### What is a Concurrent merkle tree? + +In high throughput applications, like within the +[Solana runtime](/src/validator/runtime.md), requests to change an on-chain +_traditional merkle tree_ could be received by validators in relatively rapid +succession (e.g. within the same slot). Each leaf data change would still be +required to performed in series. Resulting in each subsequent request for change +to fail, due to the root hash and proof being invalidated by the previous change +request in the slot. + +Enter, Concurrent merkle trees. + +A **Concurrent merkle tree** stores a **secure changelog** of the most recent +changes, their root hash, and the proof to derive it. This changelog "buffer" is +stored on-chain in an account specific to each tree, with a maximum number of +changelog "records" (aka `maxBufferSize`). + +When multiple leaf data change requests are received by validators in the same +slot, the on-chain _concurrent merkle tree_ can use this "changelog buffer" as a +source of truth for more acceptable proofs. Effectively allowing for up to +`maxBufferSize` changes to the same tree in the same slot. Significantly +boosting throughput. + +## Sizing a concurrent merkle tree + +When creating one of these on-chain trees, there are 3 values that will +determine the size of your tree, the cost to create your tree, and the number of +concurrent changes to your tree: + +1. max depth +2. max buffer size +3. canopy depth + +### Max depth + +The "max depth" of a tree is the **maximum number** of hops to get from any data +`leaf` to the `root` of the tree. + +Since merkle trees are binary trees, every leaf is connected to **only one** +other leaf; existing as a `leaf pair`. + +Therefore, the `maxDepth` of a tree is used to determine the maximum number of +nodes (aka pieces of data or `leafs`) to store within the tree using a simple +calculation: + +``` +nodes_count = 2 ^ maxDepth +``` + +Since a trees depth must be set at tree creation, you must decide how many +pieces of data you want your tree to store. Then using the simple calculation +above, you can determine the lowest `maxDepth` to store your data. + +#### Example 1: minting 100 nfts + +If you wanted to create a tree to store 100 compressed nfts, we will need a +minimum of "100 leafs" or "100 nodes". + +``` +// maxDepth=6 -> 64 nodes +2^6 = 64 + +// maxDepth=7 -> 128 nodes +2^7 = 128 +``` + +We must use a `maxDepth` of `7` to ensure we can store all of our data. + +#### Example 2: minting 15000 nfts + +If you wanted to create a tree to store 15000 compressed nfts, we will need a +minimum of "15000 leafs" or "15000 nodes". + +``` +// maxDepth=13 -> 8192 nodes +2^13 = 8192 + +// maxDepth=14 -> 16384 nodes +2^14 = 16384 +``` + +We must use a `maxDepth` of `14` to ensure we can store all of our data. + +#### The higher the max depth, the higher the cost + +The `maxDepth` value will be one of the primary drivers of cost when creating a +tree since you will pay this cost upfront at tree creation. The higher the max +tree depth depth, the more data fingerprints (aka hashes) you can store, the +higher the cost. + +### Max buffer size + +The "max buffer size" is effectively the maximum number of changes that can +occur on a tree, with the `root hash` still being valid. + +Due to the root hash effectively being a single hash of all leaf data, changing +any single leaf would invalidate the proof needed for all subsequent attempts to +change any leaf of a regular tree. + +But with a [concurrent tree](#what-is-a-concurrent-merkle-tree), there is +effectively a changelog of updates for these proofs. This changelog buffer is +sized and set at tree creation via this `maxBufferSize` value. + +### Canopy depth + +The "canopy depth", sometimes called the canopy size, is the number of proof +nodes that are cached/stored on-chain for any given proof path. + +When performing an update action on a `leaf`, like transferring ownership (e.g. +selling a compressed NFT), the **complete** proof path must be used to verify +original ownership of the leaf and therefore allow for the update action. This +verification is performed using the **complete** proof path to correctly compute +the current `root hash` (or any cached `root hash` via the on-chain "concurrent +buffer"). + +The larger a tree's max depth is, the more proof nodes are required to perform +this verification. For example, if your max depth is `14`, there are `14` total +proof nodes required to be used to verify. As a tree gets larger, the complete +proof path gets larger. + +Normally, each of these proof nodes would be required to be included within each +tree update transaction. Since each proof node value takes up `32 bytes` in a +transaction (similar to providing a Public Key), larger trees would very quickly +exceed the maximum transaction size limit. + +Enter the canopy. The canopy enables storing a set number of proof nodes on +chain (for any given proof path). Allowing for less proof nodes to be included +within each update transactions, therefore keeping the overall transaction size +below the limit. + +For example, a tree with a max depth of `14` would require `14` total proof +nodes. With a canopy of `10`, only `4` proof nodes are required to be submitted +per update transaction. + +#### The larger the canopy depth value, the higher the cost + +The `canopyDepth` value is also a primary factor of cost when creating a tree +since you will pay this cost upfront at tree creation. The higher the canopy +depth, the more data proof nodes are stored on chain, the higher the cost. + +#### Smaller canopy limits composability + +While a tree's creation costs are higher with a higher canopy, having a lower +`canopyDepth` will require more proof nodes to be included within each update +transaction. The more nodes required to be submitted, the larger the transaction +size, and therefore the easier it is to exceed the transaction size limits. + +This will also be the case for any other Solana program or dApp that attempts to +interact with your tree/leafs. If your tree requires too many proof nodes +(because of a low canopy depth), then any other additional actions another +on-chain program **could** offer will be **limited** by their specific +instruction size plus your proof node list size. Limiting composability, and +potential additional utility for your specific tree. + +For example, if your tree is being used for compressed NFTs and has a very low +canopy depth, an NFT marketplace may only be able to support simple NFTs +transfers. And not be able to support an on-chain bidding system. + +## Cost of creating a tree + +The cost of creating a concurrent merkle tree is based on the tree's size +parameters: `maxDepth`, `maxBufferSize`, and `canopyDepth`. These values are all +used to calculate the on-chain storage (in bytes) required for a tree to exist +on chain. + +Once the required space (in bytes) has been calculated, and using the +[`getMinimumBalanceForRentExemption`](/api/http#getminimumbalanceforrentexemption) +RPC method, request the cost (in lamports) to allocate this amount of bytes +on-chain. + +### Calculate tree cost in JavaScript + +Within the +[`@solana/spl-account-compression`](https://www.npmjs.com/package/@solana/spl-account-compression) +package, developers can use the +[`getConcurrentMerkleTreeAccountSize`](https://solana-labs.github.io/solana-program-library/account-compression/sdk/docs/modules/index.html#getConcurrentMerkleTreeAccountSize) +function to calculate the required space for a given tree size parameters. + +Then using the +[`getMinimumBalanceForRentExemption`](https://solana-labs.github.io/solana-web3.js/classes/Connection.html#getMinimumBalanceForRentExemption) +function to get the final cost (in lamports) to allocate the required space for +the tree on-chain. + +Then determine the cost in lamports to make an account of this size rent exempt, +similar to any other account creation. + +```ts +// calculate the space required for the tree +const requiredSpace = getConcurrentMerkleTreeAccountSize( + maxDepth, + maxBufferSize, + canopyDepth, +); + +// get the cost (in lamports) to store the tree on-chain +const storageCost = await connection.getMinimumBalanceForRentExemption( + requiredSpace, +); +``` + +### Example costs + +Listed below are several example costs, for different tree sizes, including how +many leaf nodes are possible for each: + +**Example #1: 16,384 nodes costing 0.222 SOL** + +- max depth of `14` and max buffer size of `64` +- maximum number of leaf nodes: `16,384` +- canopy depth of `0` costs approximately `0.222 SOL` to create + +**Example #2: 16,384 nodes costing 1.134 SOL** + +- max depth of `14` and max buffer size of `64` +- maximum number of leaf nodes: `16,384` +- canopy depth of `11` costs approximately `1.134 SOL` to create + +**Example #3: 1,048,576 nodes costing 1.673 SOL** + +- max depth of `20` and max buffer size of `256` +- maximum number of leaf nodes: `1,048,576` +- canopy depth of `10` costs approximately `1.673 SOL` to create + +**Example #4: 1,048,576 nodes costing 15.814 SOL** + +- max depth of `20` and max buffer size of `256` +- maximum number of leaf nodes: `1,048,576` +- canopy depth of `15` costs approximately `15.814 SOL` to create + +## Compressed NFTs + +Compressed NFTs are one of the most popular use cases for State Compression on +Solana. With compression, a one million NFT collection could be minted for +`~50 SOL`, vice `~12,000 SOL` for its uncompressed equivalent collection. + +:::info Developer Guide + +Read our developer guide for +[minting and transferring compressed NFTs](./../developing/guides/compressed-nfts). + +::: diff --git a/docs/src/pages/CodeDocBlock.module.css b/docs/src/pages/CodeDocBlock.module.css new file mode 100644 index 00000000000000..7cffc5625bd7a0 --- /dev/null +++ b/docs/src/pages/CodeDocBlock.module.css @@ -0,0 +1,80 @@ +/* stylelint-disable docusaurus/copyright-header */ + +.DocBlock { + border-top: 1px solid #414141; + padding-top: 3rem; + /* display: flex; */ + /* justify-content: space-between; */ + margin-top: 5rem; + /* width: 100%; */ + /* align-items: center; */ +} + +.DocSideBySide { + margin-top: 2rem; +} + +.CodeParams { + display: block; + width: 100%; +} + +.CodeSnippets { + display: block; + width: 100%; +} + +@media screen and (min-width: 768px) { + .DocSideBySide { + display: flex; + width: 100%; + } + .CodeParams { + margin-right: 3rem; + width: 50%; + } + .CodeSnippets { + width: 50%; + } +} + +.Parameter { + padding: 1em 0em; + margin-bottom: 1em; + border-top: 1px solid #414141; + /* // border-bottom: 1px solid #414141; */ +} + +.ParameterName { + font-weight: 700; +} + +.ParameterHeader { + font-family: mono; + padding: 0.1em 0em; +} + +.Field { + /* // padding: 1em 0em; */ + margin: 1em 0em 1em 1em; + /* // border-top: 1px solid #414141; */ + /* // border-bottom: 1px solid #414141; */ +} +.Field section { + padding: 0em 1em; +} + +.FlagItem { + margin: 0 0.5rem; + color: #767676; + font-weight: 600; +} + +.Heading { + font-size: 1.24rem; + font-weight: 700; +} +.SubHeading { + /* font-size: 1.24rem; */ + font-weight: 600; +} diff --git a/docs/src/pages/api.js b/docs/src/pages/api.js new file mode 100644 index 00000000000000..6252d21df5027e --- /dev/null +++ b/docs/src/pages/api.js @@ -0,0 +1,82 @@ +import React from "react"; +import Link from "@docusaurus/Link"; +import styles from "./styles.module.css"; +import Card from "../../components/Card"; +import CardLayout from "../../layouts/CardLayout"; + +function APIPage() { + return ( + +
    +
    +
    +

    JSON RPC API

    + +
    +

    + Interact with Solana nodes directly with the JSON RPC API via + the HTTP and Websocket methods. +

    + + + Explore the API + +
    +
    + +
    +

    Explore the JSON RPC Methods

    + +
    + + + + + +
    +
    +
    +
    +
    + ); +} + +export default APIPage; diff --git a/docs/src/pages/developers.js b/docs/src/pages/developers.js new file mode 100644 index 00000000000000..fb612bb0d0b5dd --- /dev/null +++ b/docs/src/pages/developers.js @@ -0,0 +1,184 @@ +import React from "react"; +import Link from "@docusaurus/Link"; +import styles from "./styles.module.css"; +import Card from "../../components/Card"; +import CardLayout from "../../layouts/CardLayout"; + +function Developers() { + return ( + +
    +
    +
    +

    Learn Solana Development

    + +
    +

    + Build and deploy your first on chain Solana program directly in + your browser. +

    + + + Get Started + +
    +
    + +
    +

    Learn core concepts

    + +
    + + + + + +
    +
    + +
    +

    Learn through coding

    + +
    + + + + + +
    +
    + +
    +

    Setup your local development

    + +
    + + + + {/* future card to replace the RPC API card */} + {/* */} + + +
    +
    +
    +
    +
    + ); +} + +export default Developers; diff --git a/docs/src/pages/getstarted.jsx b/docs/src/pages/getstarted.jsx new file mode 100644 index 00000000000000..954d80f43b7d3d --- /dev/null +++ b/docs/src/pages/getstarted.jsx @@ -0,0 +1,126 @@ +import React from "react"; +import Link from "@docusaurus/Link"; +import styles from "./styles.module.css"; +import Card from "../../components/Card"; +import CardLayout from "../../layouts/CardLayout"; + +function GetStartedPage() { + return ( + +
    +
    +
    +

    Get started with Solana development

    + +
    +

    + Build and deploy your first on chain Solana program directly in + your browser. +

    + + + Get Started + +
    +
    + +
    +
    + + + + + +
    +
    + +
    +

    Community Resources

    + +
    + + + + + +
    +
    +
    +
    +
    + ); +} + +export default GetStartedPage; diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js deleted file mode 100644 index ebf7b60a7a4ca5..00000000000000 --- a/docs/src/pages/index.js +++ /dev/null @@ -1,160 +0,0 @@ -import React from "react"; -import clsx from "clsx"; -import Layout from "@theme/Layout"; -import Link from "@docusaurus/Link"; -import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; -// import useBaseUrl from "@docusaurus/useBaseUrl"; -import styles from "./styles.module.css"; -import Translate from "@docusaurus/Translate"; -// import { translate } from "@docusaurus/Translate"; - -function Home() { - const context = useDocusaurusContext(); - const { siteConfig = {} } = context; - return ( - -
    -
    -
    -
    -
    - -
    -
    -

    - - ⛏ Start Building - -

    -
    -
    -

    - - Get started building your decentralized app or - marketplace. - -

    -
    -
    - -
    -
    - -
    -
    -

    - - 🎛 Run a Validator Node - -

    -
    -
    -

    - - Validate transactions, secure the network, and earn - rewards. - -

    -
    -
    - -
    -
    - -
    -
    -

    - - 🏛 Create an SPL Token - -

    -
    -
    -

    - - Launch your own SPL Token, Solana's equivalent of - ERC-20. - -

    -
    -
    - -
    -
    - -
    -
    -

    - - 🏦 Integrate an Exchange - -

    -
    -
    -

    - - Follow our extensive integration guide to ensure a - seamless user experience. - -

    -
    -
    - -
    -
    - -
    -
    -

    - - 📲 Manage a Wallet - -

    -
    -
    -

    - - Create a wallet, check your balance, and learn about - wallet options. - -

    -
    -
    - -
    -
    - -
    -
    -

    - - 🤯 Learn How Solana Works - -

    -
    -
    -

    - - Get a high-level understanding of Solana's - architecture. - -

    -
    -
    - -
    -
    -
    -
    -
    -
    - ); -} - -export default Home; diff --git a/docs/src/pages/styles.module.css b/docs/src/pages/styles.module.css index c9d1c5aea88454..677dc9b068636f 100644 --- a/docs/src/pages/styles.module.css +++ b/docs/src/pages/styles.module.css @@ -30,7 +30,7 @@ .features { display: flex; - align-items: center; + align-items: top; padding: 2rem 0; width: 100%; } @@ -39,3 +39,7 @@ height: 200px; width: 200px; } + +.iconExternalIcon { + margin-left: 0.5em; +} diff --git a/docs/src/proposals.md b/docs/src/proposals.md new file mode 100644 index 00000000000000..d2a6e9a3255ce4 --- /dev/null +++ b/docs/src/proposals.md @@ -0,0 +1,49 @@ +--- +title: System Design Proposals +--- + +Changes to the Solana architecture are performed through a public proposal process (via pull requests) on the [Solana GitHub repository](https://github.com/solana-labs/solana). New proposals should be submitted with the "[Submit a Design Proposal](#submit-a-design-proposal)" guide below. + +There are currently two different states of these design proposals: + +1. [Accepted Proposals](./proposals/accepted-design-proposals.md) +2. [Implemented Proposals](./implemented-proposals/implemented-proposals.md) + +## Accepted Proposals + +These architectural proposals have been accepted by the Solana team, but are not yet fully implemented. + +Each proposal may be implemented as described, implemented differently as issues in the designs become evident, or not implemented at all. If implemented, the proposal will be moved to [Implemented Proposals](./implemented-proposals/implemented-proposals.md) and the details will be added to relevant sections of the docs. + +## Implemented Proposals + +These architectural proposals have been accepted and implemented by the Solana team. + +Any designs that may be subject to future change are noted in their specific proposal page. + +## Submit a Design Proposal + +To submit a new design proposal for Solana: + +1. Propose a design by creating a PR that adds a markdown document to the `docs/src/proposals` directory. +2. Add any relevant Solana maintainers to the PR review. +3. Publish the PR for community review and feedback. + +> **NOTE:** All people submitting PRs to the Solana repo should consult the [CONTRIBUTING](https://github.com/solana-labs/solana/blob/master/CONTRIBUTING.md) doc in the repo. + +### After Accepted + +Once a design proposal has been accepted, the PR will be merged into the `master` branch of the Solana repo. This also signifies the maintainers support your plan of attack. + +> **NOTE:** The merging of the PR will **automatically** create a link in the "Accepted Proposals" table of contents sidebar. +> Once approved, continue to submit PRs that implement the proposal. When the implementation reveals the need for tweaks to the proposal, be sure to update the "accepted proposal" document and have these change reviewed by the same approving maintainers. + +### After Implemented + +After a proposal has been fully implemented into the Solana architecture, a PR should be created to perform the following: + +1. Move the newly implemented proposal file from `docs/src/proposals` to `docs/src/implemented-proposals` +2. Create a new redirect in the `publish-docs.sh` to redirect the old `accepted` proposal page to the new `implemented-proposal` page +3. Publish the PR + +> **NOTE:** Moving the proposal document into the `implemented-proposals` directory will **automatically** move the link in the "Accepted Proposals" table of contents sidebar to the "Implemented Proposals" sidebar. diff --git a/docs/src/proposals/accepted-design-proposals.md b/docs/src/proposals/accepted-design-proposals.md index 98f197da4eddf4..e2145d26b656b1 100644 --- a/docs/src/proposals/accepted-design-proposals.md +++ b/docs/src/proposals/accepted-design-proposals.md @@ -1,11 +1,15 @@ --- title: Accepted Design Proposals +sidebar_position: 1 +sidebar_label: Overview --- -The following architectural proposals have been accepted by the Solana team, -but are not yet fully implemented. -The proposals may be implemented as described, implemented differently as -issues in the designs become evident, or not implemented at all. -If implemented, the proposal will be moved to -[Implemented Proposals](../implemented-proposals/implemented-proposals.md) -and the details will be added to relevant sections of the docs. +These architectural proposals have been accepted by the Solana maintainers, but are not yet fully implemented. These proposals may be implemented as described, implemented differently as issues in the designs become evident, or not implemented at all. + +## After Implemented + +Once a proposal has been implemented, it will be moved to [Implemented Proposals](../implemented-proposals/implemented-proposals.md) and the details will be added to relevant sections of the docs. + +## Submit a New Proposal + +To submit a new design proposal, consult this guide on [how to submit a design proposal](../proposals.md#submit-a-design-proposal). diff --git a/docs/src/proposals/embedding-move.md b/docs/src/proposals/embedding-move.md index 9826220f5c7c94..2216feac6310f5 100644 --- a/docs/src/proposals/embedding-move.md +++ b/docs/src/proposals/embedding-move.md @@ -2,6 +2,8 @@ title: Embedding the Move Language --- +### This document is outdated and a new approach to support 'move' is in the works. + ## Problem Solana enables developers to write on-chain programs in general purpose programming languages such as C or Rust, but those programs contain Solana-specific mechanisms. For example, there isn't another chain that asks developers to create a Rust module with a `process_instruction(KeyedAccounts)` function. Whenever practical, Solana should offer application developers more portable options. diff --git a/docs/src/proposals/timely-vote-credits.md b/docs/src/proposals/timely-vote-credits.md new file mode 100644 index 00000000000000..7875b815c6b11b --- /dev/null +++ b/docs/src/proposals/timely-vote-credits.md @@ -0,0 +1,190 @@ +--- +title: Timely Vote Credits +--- + +## Timely Vote Credits + +This design describes a modification to the method that is used to calculate +vote credits earned by validator votes. + +Vote credits are the accounting method used to determine what percentage of +inflation rewards a validator earns on behalf of its stakers. Currently, when +a slot that a validator has previously voted on is "rooted", it earns 1 vote +credit. A "rooted" slot is one which has received full committment by the +validator (i.e. has been finalized). + +One problem with this simple accounting method is that it awards one credit +regardless of how "old" the slot that was voted on at the time that it was +voted on. This means that a validator can delay its voting for many slots in +order to survey forks and wait to make votes that are less likely to be +expired, and without incurring any penalty for doing so. This is not just a +theoretical concern: there are known and documented instances of validators +using this technique to significantly delay their voting while earning more +credits as a result. + + +### Proposed Change + +The proposal is to award a variable number of vote credits per voted on slot, +with more credits being given for votes that have "less latency" than votes +that have "more latency". + +In this context, "latency" is the number of slots in between the slot that is +being voted on and the slot in which the vote has landed. Because a slot +cannot be voted on until after it has been completed, the minimum possible +latency is 1, which would occur when a validator voted as quickly as possible, +transmitting its vote on that slot in time for it to be included in the very +next slot. + +Credits awarded would become a function of this latency, with lower latencies +awarding more credits. This will discourage intentional "lagging", because +delaying a vote for any slots decreases the number of credits that vote will +earn, because it will necessarily land in a later slot if it is delayed, and +then earn a lower number of credits than it would have earned had it been +transmitted immediately and landed in an earlier slot. + +### Grace Period + +If landing a vote with 1 slot latency awarded more credit than landing that +same vote in 2 slots latency, then validators who could land votes +consistently wihthin 1 slot would have a credits earning advantage over those +who could not. Part of the latency when transmitting votes is unavoidable as +it's a function of geographical distance between the sender and receiver of +the vote. The Solana network is spread around the world but it is not evenly +distributed over the whole planet; there are some locations which are, on +average, more distant from the network than others are. + +It would likely be harmful to the network to encourage tight geographical +concentration - if, for example, the only way to achieve 1 slot latency was to +be within a specific country - then a very strict credit rewards schedule +would encourage all validators to move to the same country in order to +maximize their credit earnings. + +For this reason, the credits reward schedule should have a built-in "grace +period" that gives all validators a "reasonable" amount of time to land their +votes. This will reduce the credits earning disadvantage that comes from +being more distant from the network. A balance needs to be struck between the +strictest rewards schedule, which most strongly discourages intentional +lagging, and more lenient rewards schedules, which improves credit earnings +for distant validators who are not artificially lagging. + +Historical voting data has been analyzed over many epochs and the data +suggests that the smallest grace period that allows for very minimal impact on +well behaved distant validators is 3 slots, which means that all slots voted +on within 3 slots will award maximum vote credits to the voting validator. +This gives validators nearly 2 seconds to land their votes without penalty. +The maximum latency between two points on Earth is about 100 ms, so allowing a +full 1,500 ms to 2,000 ms latency without penalty should not have adverse +impact on distant validators. + +### Maximum Vote Credits + +Another factor to consider is what the maximum vote credits to award for a +vote should be. Assuming linear reduction in vote credits awarded (where 1 +slot of additional lag reduces earned vote credits by 1), the maximum vote +credits value determines how much "penalty" there is for each additional slot +of latency. For example, a value of 10 would mean that after the grace period +slots, every additional slot of latency would result in a 10% reduction in +vote credits earned as each subsequent slot earns 1 credit less out of a +maximum possible 10 credits. + +Again, historical voting data was analyzed over many epochs and the conclusion +drawn was that a maximum credits of 10 is the largest value that can be used +and still have a noticeable effect on known laggers. Values higher than that +result in such a small penalty for each slot of lagging that intentional +lagging is still too profitable. Lower values are even more punishing to +intentional lagging; but an attempt has been made to conservatively choose the +highest value that produces noticeable results. + +The selection of these values is partially documented here: + +https://www.shinobi-systems.com/timely_voting_proposal + +The above document is somewhat out of date with more recent analysis, which +occurred in this github issue: + +https://github.com/solana-labs/solana/issues/19002 + +To summarize the findings of these documents: analysis over many epochs showed +that almost all validators from all regions have an average vote latency of 1 +slot or less. The validators with higher average latency are either known +laggers, or are not representative of their region since many other validators +in the same region achieve low latency voting. With a maximum vote credit of +10, there is almost no change in vote credits earned relative to the highest vote +earner by the majority of validators, aside from a general uplift of about 0.4%. +Additionally, data centers were analyzed to ensure that there aren't regions of +the world that would be adversely affected, and none were found. + + +### Method of Implementation + +When a Vote or VoteStateUpdate instruction is received by a validator, it will +use the Clock sysvar to identify the slot in which that instruction has +landed. For any newly voted on slot within that Vote or VoteStateUpdate +transaction, the validator will record the vote latency of that slot as +(voted_in_slot - voted_on_slot). + +These vote latencies will be stored a new vector of u8 latency values appended +to the end of the VoteState. VoteState currently has ~200 bytes of free space +at the end that is unused, so this new vector of u8 values should easily fit +within this available space. Because VoteState is an ABI frozen structure, +utilizing the mechanisms for updating frozen ABI will be required, which will +complicate the change. Furthermore, because VoteState is embedded in the +Tower data structure and it is frozen ABI as well, updates to the frozen ABI +mechanisms for Tower will be needed also. These are almost entirely +mechanical changes though, that involve ensuring that older versions of these +data structures can be updated to the new version as they are read in, and the +new version written out when the data structure is next persisted. + +The credits to award for a rooted slot will be calculated using the latency +value stored in latency vector for the slot, and a formula that awards +latencies of 1 - 3 slots ten credits, with a 1 credit reduction for each vote +latency after 3. Rooted slots will always be awarded a minimum credit of 1 +(never 0) so that very old votes, possibly necessary in times of network +stress, are not discouraged. + +To summarize the above: latency is recorded in a new Vector at the end of +VoteState when a vote first lands, but the credits for that slot are not +awarded until the slot becomes rooted, at which point the latency that was +recorded is used to compute the credits to award for that newly rooted slot. + +When a Vote instruction is processed, the changes are fairly easy to implement +as Vote can only add new slots to the end of Lockouts and pop existing slots +off of the back (which become rooted), so the logic merely has to compute +rewards for the new roots, and new latencies for the newly added slots, both +of which can be processed in the fairly simple existing logic for Vote +processing. + +When a VoteStateUpdate instruction is processed: + +1. For each slot that was in the previous VoteState but are not in the new +VoteState because they have been rooted in the transition from the old +VoteState to the new VoteState, credits to award are calculated based on the +latency that was recorded for them and still available in the old VoteState. + +2. For each slot that was in both the previous VoteState and the new +VoteState, the latency that was previously recorded for that slot is copied +from the old VoteState to the new VoteState. + +3. For each slot that is in the new VoteState but wasn't in the old VoteState, +the latency value is calculated for this new slot according to what slot the +vote is for and what slot is in the Clock (i.e. the slot this VoteStateUpdate +tx landed in) and this latency is stored in VoteState for that slot. + +The code to handle this is more complex, because VoteStateUpdate may include +removal of slots that expired as performed by the voting validator, in +addition to slots that have been rooted and new slots added. However, the +assumptions that are needed to handle VoteStateUpdate with timely vote credits +are already guaranteed by existing VoteStateUpdate correctness checking code: + +The existing VoteStateUpdate processing code already ensures that (1) only +roots slots that could actually have been rooted in the transition from the +old VoteState to the new VoteState, so there is no danger of over-counting +credits (i.e. imagine that a 'cheating' validator "pretended" that slots were +rooted by dropping them off of the back of the new VoteState before they have +actually achieved 32 confirmations; the existing logic prevents this). + +The existing VoteStateUpdate processing code already ensures that (2) new +slots included in the new VoteState are only slots after slots that have +already been voted on in the old VoteState (i.e. can't inject new slots in the +middle of slots already voted on). diff --git a/docs/src/running-validator/restart-cluster.md b/docs/src/running-validator/restart-cluster.md index 6d1fec88b792d9..2353709e85beee 100644 --- a/docs/src/running-validator/restart-cluster.md +++ b/docs/src/running-validator/restart-cluster.md @@ -1,14 +1,26 @@ ## Restarting a cluster -### Step 1. Identify the slot that the cluster will be restarted at +### Step 1. Identify the latest optimistically confirmed slot for the cluster -The highest optimistically confirmed slot is the best slot to start from, which -can be found by looking for +In Solana 1.14 or greater, run the following command to output the latest +optimistically confirmed slot your validator observed: +```bash +solana-ledger-tool -l ledger latest-optimistic-slots +``` + +In Solana 1.13 or less, the latest optimistically confirmed can be found by looking for the more recent occurence of [this](https://github.com/solana-labs/solana/blob/0264147d42d506fb888f5c4c021a998e231a3e74/core/src/optimistic_confirmation_verifier.rs#L71) -metrics datapoint. Otherwise use the last root. +metrics datapoint. Call this slot `SLOT_X` +Note that it's possible that some validators observed an optimistically +confirmed slot that's greater than others before the outage. Survey the other +validators on the cluster to ensure that a greater optimistically confirmed slot +does not exist before proceeding. If a greater slot value is found use it +instead. + + ### Step 2. Stop the validator(s) ### Step 3. Optionally install the new solana version diff --git a/docs/src/running-validator/validator-stake.md b/docs/src/running-validator/validator-stake.md index de1034fd9a4999..ae430ae9675c8f 100644 --- a/docs/src/running-validator/validator-stake.md +++ b/docs/src/running-validator/validator-stake.md @@ -71,7 +71,7 @@ account. This is a normal transaction so the standard transaction fee will apply. The transaction fee range is defined by the genesis block. The actual fee will fluctuate based on transaction load. You can determine the current fee via the -[RPC API “getRecentBlockhash”](developing/clients/jsonrpc-api.md#getrecentblockhash) +[RPC API “getRecentBlockhash”](../api/http#getrecentblockhash) before submitting a transaction. Learn more about [transaction fees here](../implemented-proposals/transaction-fees.md). diff --git a/docs/src/running-validator/validator-start.md b/docs/src/running-validator/validator-start.md index 6089c218319afd..caaf1ecae4e205 100644 --- a/docs/src/running-validator/validator-start.md +++ b/docs/src/running-validator/validator-start.md @@ -81,6 +81,7 @@ sudo sysctl -p /etc/sysctl.d/21-solana-validator.conf ``` ##### **Increase systemd and session file limits** + Add ``` @@ -226,14 +227,14 @@ Read more about the [difference between SOL and lamports here](../introduction.m ## Create Authorized Withdrawer Account If you haven't already done so, create an authorized-withdrawer keypair to be used -as the ultimate authority over your validator. This keypair will have the +as the ultimate authority over your validator. This keypair will have the authority to withdraw from your vote account, and will have the additional -authority to change all other aspects of your vote account. Needless to say, +authority to change all other aspects of your vote account. Needless to say, this is a very important keypair as anyone who possesses it can make any changes to your vote account, including taking ownership of it permanently. So it is very important to keep your authorized-withdrawer keypair in a safe -location. It does not need to be stored on your validator, and should not be -stored anywhere from where it could be accessed by unauthorized parties. To +location. It does not need to be stored on your validator, and should not be +stored anywhere from where it could be accessed by unauthorized parties. To create your authorized-withdrawer keypair: ```bash @@ -363,7 +364,7 @@ WantedBy=multi-user.target Now create `/home/sol/bin/validator.sh` to include the desired `solana-validator` command-line. Ensure that the 'exec' command is used to -start the validator process (i.e. "exec solana-validator ..."). This is +start the validator process (i.e. "exec solana-validator ..."). This is important because without it, logrotate will end up killing the validator every time the logs are rotated. @@ -393,7 +394,7 @@ to be reverted and the issue reproduced before help can be provided. The validator log file, as specified by `--log ~/solana-validator.log`, can get very large over time and it's recommended that log rotation be configured. -The validator will re-open its when it receives the `USR1` signal, which is the +The validator will re-open its log file when it receives the `USR1` signal, which is the basic primitive that enables log rotation. If the validator is being started by a wrapper shell script, it is important to @@ -430,12 +431,6 @@ solana-validator ..."); otherwise, when logrotate sends its signal to the validator, the enclosing script will die and take the validator process with it. -### Disable port checks to speed up restarts - -Once your validator is operating normally, you can reduce the time it takes to -restart your validator by adding the `--no-port-check` flag to your -`solana-validator` command-line. - ### Using a ramdisk with spill-over into swap for the accounts database to reduce SSD wear If your machine has plenty of RAM, a tmpfs ramdisk @@ -474,13 +469,13 @@ command-line arguments and restart the validator. As the number of populated accounts on the cluster grows, account-data RPC requests that scan the entire account set -- like -[`getProgramAccounts`](developing/clients/jsonrpc-api.md#getprogramaccounts) and -[SPL-token-specific requests](developing/clients/jsonrpc-api.md#gettokenaccountsbydelegate) -- +[`getProgramAccounts`](../api/http#getprogramaccounts) and +[SPL-token-specific requests](../api/http#gettokenaccountsbydelegate) -- may perform poorly. If your validator needs to support any of these requests, you can use the `--account-index` parameter to activate one or more in-memory account indexes that significantly improve RPC performance by indexing accounts by the key field. Currently supports the following parameter values: -- `program-id`: each account indexed by its owning program; used by [`getProgramAccounts`](developing/clients/jsonrpc-api.md#getprogramaccounts) -- `spl-token-mint`: each SPL token account indexed by its token Mint; used by [getTokenAccountsByDelegate](developing/clients/jsonrpc-api.md#gettokenaccountsbydelegate), and [getTokenLargestAccounts](developing/clients/jsonrpc-api.md#gettokenlargestaccounts) -- `spl-token-owner`: each SPL token account indexed by the token-owner address; used by [getTokenAccountsByOwner](developing/clients/jsonrpc-api.md#gettokenaccountsbyowner), and [`getProgramAccounts`](developing/clients/jsonrpc-api.md#getprogramaccounts) requests that include an spl-token-owner filter. +- `program-id`: each account indexed by its owning program; used by [getProgramAccounts](../api/http#getprogramaccounts) +- `spl-token-mint`: each SPL token account indexed by its token Mint; used by [getTokenAccountsByDelegate](../api/http#gettokenaccountsbydelegate), and [getTokenLargestAccounts](../api/http#gettokenlargestaccounts) +- `spl-token-owner`: each SPL token account indexed by the token-owner address; used by [getTokenAccountsByOwner](../api/http#gettokenaccountsbyowner), and [getProgramAccounts](../api/http#getprogramaccounts) requests that include an spl-token-owner filter. diff --git a/docs/src/running-validator/validator-troubleshoot.md b/docs/src/running-validator/validator-troubleshoot.md index 4fa35fceddc25e..533c66c53308b5 100644 --- a/docs/src/running-validator/validator-troubleshoot.md +++ b/docs/src/running-validator/validator-troubleshoot.md @@ -3,7 +3,7 @@ title: Troubleshooting --- There is a **\#validator-support** Discord channel available to reach other -testnet participants, [https://discord.gg/pquxPsq](https://discord.gg/pquxPsq). +testnet participants, [https://solana.com/discord](https://solana.com/discord) ## Useful Links & Discussion @@ -14,8 +14,6 @@ testnet participants, [https://discord.gg/pquxPsq](https://discord.gg/pquxPsq). - [\#testnet-announcements](https://discord.gg/Q5TxEC) The single source of truth for critical information relating Testnet - [Core software repo](https://github.com/solana-labs/solana) -Can't find what you're looking for? Send an email to ryan@solana.com or reach out to @rshea\#2622 on Discord. - ## Blockstore The validator blockstore rocksdb database can be inspected using the `ldb` tool. diff --git a/docs/src/staking.md b/docs/src/staking.md index 46146e36e9ee3d..312f44fd99d347 100644 --- a/docs/src/staking.md +++ b/docs/src/staking.md @@ -74,11 +74,6 @@ Follow the wallet's instructions for selecting a validator. You can get information about potentially performant validators from the links below. The Solana Foundation does not recommend any particular validator. -The Mainnet Beta validators introduce themselves and their services on this -Solana Forum thread: - -- https://forums.solana.com/t/validator-information-thread - The site solanabeach.io is built and maintained by one of our validators, Staking Facilities. It provides a some high-level graphical information about the network as a whole, as well as a list of each validator and some recent diff --git a/docs/src/staking/stake-accounts.md b/docs/src/staking/stake-accounts.md index c78224d88dd5f9..0890bfb2abb1d5 100644 --- a/docs/src/staking/stake-accounts.md +++ b/docs/src/staking/stake-accounts.md @@ -118,10 +118,10 @@ Details on the warmup and cooldown timing can be found #### Lockups Stake accounts can have a lockup which prevents the tokens they hold from being -withdrawn before a particular date or epoch has been -reached. While locked up, the stake account can still be delegated, un-delegated, -or split, and its stake and withdraw authorities can be changed as normal. Only -withdrawal into a wallet address is not allowed. +withdrawn before a particular date or epoch has been reached. While locked up, +the stake account can still be delegated, un-delegated, or split, and its stake +authority can be changed as normal. Only withdrawal into another wallet or +updating the withdraw authority is not allowed. A lockup can only be added when a stake account is first created, but it can be modified later, by the _lockup authority_ or _custodian_, the address of which diff --git a/docs/src/staking/stake-programming.md b/docs/src/staking/stake-programming.md index d8de644e28476b..99dace2cd9619c 100644 --- a/docs/src/staking/stake-programming.md +++ b/docs/src/staking/stake-programming.md @@ -11,8 +11,7 @@ stakes easier to manage. This off-chain program manages a large population of validators staked by a central authority. The Solana Foundation uses an auto-delegation bot to regularly delegate its -stake to "non-delinquent" validators that meet specified performance requirements. More information can be found on the -[official announcement](https://forums.solana.com/t/stake-o-matic-delegation-matching-program/790). +stake to "non-delinquent" validators that meet specified performance requirements. #### Stake Pools diff --git a/docs/src/storage_rent_economics.md b/docs/src/storage_rent_economics.md index 8405c8906f3a29..4b65b250e140a7 100644 --- a/docs/src/storage_rent_economics.md +++ b/docs/src/storage_rent_economics.md @@ -2,16 +2,38 @@ title: Storage Rent Economics --- -Each transaction that is submitted to the Solana ledger imposes costs. Transaction fees paid by the submitter, and collected by a validator, in theory, account for the acute, transactional, costs of validating and adding that data to the ledger. Unaccounted in this process is the mid-term storage of active ledger state, necessarily maintained by the rotating validator set. This type of storage imposes costs not only to validators but also to the broader network as active state grows so does data transmission and validation overhead. To account for these costs, we describe here our preliminary design and implementation of storage rent. +Each transaction that is submitted to the Solana ledger imposes costs. +Transaction fees paid by the submitter, and collected by a validator, in +theory, account for the acute, transactional, costs of validating and adding +that data to the ledger. Unaccounted in this process is the mid-term storage of +active ledger state, necessarily maintained by the rotating validator set. This +type of storage imposes costs not only to validators but also to the broader +network as active state grows so does data transmission and validation +overhead. To account for these costs, we describe here our preliminary design +and implementation of storage rent. Storage rent can be paid via one of two methods: Method 1: Set it and forget it -With this approach, accounts with two-years worth of rent deposits secured are exempt from network rent charges. By maintaining this minimum-balance, the broader network benefits from reduced liquidity and the account holder can rest assured that their `Account::data` will be retained for continual access/usage. +With this approach, accounts with two-years worth of rent deposits secured are +exempt from network rent charges. By maintaining this minimum-balance, the +broader network benefits from reduced liquidity and the account holder can rest +assured that their `Account::data` will be retained for continual access/usage. Method 2: Pay per byte -If an account has less than two-years worth of deposited rent the network charges rent on a per-epoch basis, in credit for the next epoch. This rent is deducted at a rate specified in genesis, in lamports per kilobyte-year. +If an account has less than two-years worth of deposited rent the network +charges rent on a per-epoch basis, in credit for the next epoch. This rent is +deducted at a rate specified in genesis, in lamports per kilobyte-year. -For information on the technical implementation details of this design, see the [Rent](implemented-proposals/rent.md) section. +For information on the technical implementation details of this design, see the +[Rent](implemented-proposals/rent.md) section. + +**Note:** New accounts now **are required** to be initialized with enough +lamports to be rent exempt. Additionally, transactions that leave an account's +balance below the rent exempt minimum (and non-zero) will **fail**. This +essentially renders all accounts rent exempt. Rent-paying accounts that were +created before this requirement will continue paying rent until either (1) +their balance falls to zero, or (2) a transaction increases the account's +balance to be rent exempt. diff --git a/docs/src/terminology.md b/docs/src/terminology.md index 2c22efb2bbdfa3..4b86a921a86c28 100644 --- a/docs/src/terminology.md +++ b/docs/src/terminology.md @@ -1,8 +1,15 @@ --- title: Terminology +description: "Learn the essential terminology used throughout the Solana blockchain and development models." +keywords: + - terms + - dictionary + - definitions + - define + - programming models --- -The following terms are used throughout the documentation. +The following terms are used throughout the Solana documentation and development ecosystem. ## account @@ -12,9 +19,9 @@ Like an account at a traditional bank, a Solana account may hold funds called [l The key may be one of: -* an ed25519 public key -* a program-derived account address (32byte value forced off the ed25519 curve) -* a hash of an ed25519 public key with a 32 character string +- an ed25519 public key +- a program-derived account address (32byte value forced off the ed25519 curve) +- a hash of an ed25519 public key with a 32 character string ## account owner @@ -34,7 +41,7 @@ A contiguous set of [entries](#entry) on the ledger covered by a [vote](#ledger- ## blockhash -A unique value ([hash](#hash)) that identifies a record (block). Solana computes a blockhash from the last [entry id](#entry-id) of the block. +A unique value ([hash](#hash)) that identifies a record (block). Solana computes a blockhash from the last [entry id](#entry-id) of the block. ## block height @@ -52,17 +59,29 @@ The Solana program that owns and loads [BPF](developing/on-chain-programs/overvi A computer program that accesses the Solana server network [cluster](#cluster). +## commitment + +A measure of the network confirmation for the [block](#block). + ## cluster A set of [validators](#validator) maintaining a single [ledger](#ledger). +## compute budget + +The maximum number of [compute units](#compute-units) consumed per transaction. + +## compute units + +The smallest unit of measure for consumption of computational resources of the blockchain. + ## confirmation time The wallclock duration between a [leader](#leader) creating a [tick entry](#tick) and creating a [confirmed block](#confirmed-block). ## confirmed block -A [block](#block) that has received a [supermajority](#supermajority) of [ledger votes](#ledger-vote). +A [block](#block) that has received a [super majority](#supermajority) of [ledger votes](#ledger-vote). ## control plane @@ -90,7 +109,7 @@ An off-chain service that acts as a custodian for a user's private key. It typic ## entry -An entry on the [ledger](#ledger) either a [tick](#tick) or a [transactions entry](#transactions-entry). +An entry on the [ledger](#ledger) either a [tick](#tick) or a [transaction's entry](#transactions-entry). ## entry id @@ -179,6 +198,12 @@ A [program](#program) with the ability to interpret the binary encoding of other The duration of time for which a [validator](#validator) is unable to [vote](#ledger-vote) on another [fork](#fork). +## message + +The structured contents of a [transaction](#transaction). Generally containing a header, array of account addresses, recent [blockhash](#blockhash), and an array of [instructions](#instruction). + +Learn more about the [message formatting inside of transactions](./developing/programming-model/transactions.md#message-format) here. + ## native token The [token](#token) used to track work done by [nodes](#node) in a cluster. @@ -205,7 +230,7 @@ The private key of a [keypair](#keypair). ## program -The code that interprets [instructions](#instruction). +The executable code that interprets the [instructions](#instruction) sent inside of each [transaction](#transaction) on the Solana. These programs are often referred to as "[_smart contracts_](./developing//intro/programs.md)" on other blockchains. ## program derived account (PDA) @@ -221,7 +246,7 @@ A stack of proofs, each of which proves that some data existed before the proof ## prioritization fee -An additional fee user can specify in compute budget [instruction](#instruction) to prioritize their [transactions](#transaction). +An additional fee user can specify in the compute budget [instruction](#instruction) to prioritize their [transactions](#transaction). The prioritization fee is calculated by multiplying the requested maximum compute units by the compute-unit price (specified in increments of 0.000001 lamports per compute unit) rounded up to the nearest lamport. @@ -231,6 +256,16 @@ Transactions should request the minimum amount of compute units required for exe The public key of a [keypair](#keypair). +## rent + +Fee paid by [Accounts](#account) and [Programs](#program) to store data on the blockchain. When accounts do not have enough balance to pay rent, they may be Garbage Collected. + +See also [rent exempt](#rent-exempt) below. Learn more about rent here: [What is rent?](../src/developing/intro/rent.md). + +## rent exempt + +Accounts that maintain more than 2 years with of rent payments in their account are considered "*rent exempt*" and will not incur the [collection of rent](../src/developing/intro/rent.md#collecting-rent). + ## root A [block](#block) or [slot](#slot) that has reached maximum [lockout](#lockout) on a [validator](#validator). The root is the highest block that is an ancestor of all active forks on a validator. All ancestor blocks of a root are also transitively a root. Blocks that are not an ancestor and not a descendant of the root are excluded from consideration for consensus and can be discarded. @@ -287,7 +322,7 @@ Tokens forfeit to the [cluster](#cluster) if malicious [validator](#validator) b ## sysvar -A system [account](#account). [Sysvars](developing/runtime-facilities/sysvars.md) provide cluster state information such as current tick height, rewards [points](#point) values, etc. Programs can access Sysvars via a Sysvar account (pubkey) or by querying via a syscall. +A system [account](#account). [Sysvars](developing/runtime-facilities/sysvars.md) provide cluster state information such as current tick height, rewards [points](#point) values, etc. Programs can access Sysvars via a Sysvar account (pubkey) or by querying via a syscall. ## thin client @@ -327,7 +362,7 @@ A set of [transactions](#transaction) that may be executed in parallel. ## validator -A full participant in a Solana network [cluster](#cluster) that produces new [blocks](#block). A validator validates the transactions added to the [ledger](#ledger) +A full participant in a Solana network [cluster](#cluster) that produces new [blocks](#block). A validator validates the transactions added to the [ledger](#ledger) ## VDF diff --git a/docs/src/transaction_fees.md b/docs/src/transaction_fees.md index c28cb32543457e..a3c684adceedb2 100644 --- a/docs/src/transaction_fees.md +++ b/docs/src/transaction_fees.md @@ -1,21 +1,78 @@ --- title: Transaction Fees +description: "Transaction fees are the small fees paid to process instructions on the network. These fees are based on computation and an optional prioritization fee." +keywords: + - instruction fee + - processing fee + - storage fee + - low fee blockchain + - gas + - gwei + - cheap network + - affordable blockchain --- -**Subject to change.** +The small fees paid to process [instructions](./terminology.md#instruction) on the Solana blockchain are known as "_transaction fees_". -Each transaction sent through the network, to be processed by the current leader validation-client and confirmed as a global state transaction, contains a transaction fee. Transaction fees offer many benefits in the Solana economic design, for example they: +As each transaction (which contains one or more instructions) is sent through the network, it gets processed by the current leader validation-client. Once confirmed as a global state transaction, this _transaction fee_ is paid to the network to help support the [economic design](#economic-design) of the Solana blockchain. -- provide unit compensation to the validator network for the CPU/GPU resources necessary to process the state transaction, +> **NOTE:** Transaction fees are different from [account rent](./terminology.md#rent)! +> While transaction fees are paid to process instructions on the Solana network, rent is paid to store data on the blockchain. + + + +## Why pay transaction fees? + +Transaction fees offer many benefits in the Solana [economic design](#basic-economic-design) described below. Mainly: + +- they provide compensation to the validator network for the CPU/GPU resources necessary to process transactions, - reduce network spam by introducing real cost to transactions, -- and provide potential long-term economic stability of the network through a protocol-captured minimum fee amount per transaction, as described below. +- and provide potential long-term economic stability of the network through a protocol-captured minimum fee amount per transaction + +> **NOTE:** Network consensus votes are sent as normal system transfers, which means that validators pay transaction fees to participate in consensus. + +## Basic economic design + +Many current blockchain economies \(e.g. Bitcoin, Ethereum\), rely on _protocol-based rewards_ to support the economy in the short term. And when the protocol derived rewards expire, predict that the revenue generated through _transaction fees_ will support the economy in the long term. + +In an attempt to create a sustainable economy on Solana through _protocol-based rewards_ and _transaction fees_: + +- a fixed portion (initially 50%) of each transaction fee is _burned_ (aka destroyed), +- with the remaining fee going to the current [leader](./terminology.md#leader) processing the transaction. + +A scheduled global inflation rate provides a source for [rewards](./implemented-proposals/staking-rewards.md) distributed to [Solana Validators](../src/running-validator.md). + +### Why burn some fees? + +As mentioned above, a fixed proportion of each transaction fee is _burned_ (aka destroyed). The intent of this design is to retain leader incentive to include as many transactions as possible within the leader-slot time. While still providing an inflation limiting mechanism that protects against "tax evasion" attacks \(i.e. side-channel fee payments\). + +Burnt fees can also help prevent malicious validators from censoring transactions by being considered in [fork](./terminology.md#fork) selection. + +#### Example of an attack: + +In the case of a [Proof of History (PoH)](./terminology.md#proof-of-history-poh) fork with a malicious, censoring leader: + +- due to the fees lost from censoring, we would expect the total fees destroyed to be **_less than_** a comparable honest fork +- if the censoring leader is to compensate for these lost protocol fees, they would have to replace the burnt fees on their fork themselves +- thus potentially reducing the incentive to censor in the first place + +## Calculating transaction fees + +Transactions fees are calculated based on two main parts: + +- a statically set base fee per signature, and +- the computational resources used during the transaction, measured in "[_compute units_](./terminology.md#compute-units)" + +Since each transaction may require a different amount of computational resources, they are alloted a maximum number of _compute units_ per transaction known as the "[_compute budget_](./terminology.md#compute-budget)". + +The execution of each instruction within a transactions consumes a different number of _compute units_. After the maximum number of _computer units_ has been consumed (aka compute budget exhaustion), the runtime will halt the transaction and return an error. Resulting in a failed transaction. -Network consensus votes are sent as normal system transfers, which means that validators pay transaction fees to participate in consensus. +> **Learn more:** compute units and the [Compute Budget](./developing/programming-model/runtime#compute-budget) in the Runtime and [requesting a fee estimate](../api/http#getfeeformessage) from the RPC. -Many current blockchain economies \(e.g. Bitcoin, Ethereum\), rely on protocol-based rewards to support the economy in the short term, with the assumption that the revenue generated through transaction fees will support the economy in the long term, when the protocol derived rewards expire. In an attempt to create a sustainable economy through protocol-based rewards and transaction fees, a fixed portion (initially 50%) of each transaction fee is destroyed, with the remaining fee going to the current leader processing the transaction. A scheduled global inflation rate provides a source for rewards distributed to validation-clients, through the process described above. +## Prioritization fee -Transaction fees are set by the network cluster based on recent historical throughput, see [Congestion Driven Fees](implemented-proposals/transaction-fees.md#congestion-driven-fees). This minimum portion of each transaction fee can be dynamically adjusted depending on historical _signatures-per-slot_. In this way, the protocol can use the minimum fee to target a desired hardware utilization. By monitoring a protocol specified _signatures-per-slot_ with respect to a desired, target usage amount, the minimum fee can be raised/lowered which should, in turn, lower/raise the actual _signature-per-slot_ per block until it reaches the target amount. This adjustment process can be thought of as similar to the difficulty adjustment algorithm in the Bitcoin protocol, however in this case it is adjusting the minimum transaction fee to guide the transaction processing hardware usage to a desired level. +Recently, Solana has introduced an optional fee called the "_[prioritization fee](./terminology.md#prioritization-fee)_". This additional fee can be paid to help boost how a transaction is prioritized against others, resulting in faster transaction execution times. -As mentioned, a fixed-proportion of each transaction fee is to be destroyed. The intent of this design is to retain leader incentive to include as many transactions as possible within the leader-slot time, while providing an inflation limiting mechanism that protects against "tax evasion" attacks \(i.e. side-channel fee payments\). +The prioritization fee is calculated by multiplying the requested maximum _compute units_ by the compute-unit price (specified in increments of 0.000001 lamports per compute unit) rounded up to the nearest lamport. -Additionally, the burnt fees can be a consideration in fork selection. In the case of a PoH fork with a malicious, censoring leader, we would expect the total fees destroyed to be less than a comparable honest fork, due to the fees lost from censoring. If the censoring leader is to compensate for these lost protocol fees, they would have to replace the burnt fees on their fork themselves, thus potentially reducing the incentive to censor in the first place. +You can read more about the [compute budget instruction](./developing/programming-model/runtime.md#compute-budget) here. diff --git a/docs/static/img/logo-horizontal-dark.svg b/docs/static/img/logo-horizontal-dark.svg index 4a2dcf3be0eaab..4eb5ab152c005c 100644 --- a/docs/static/img/logo-horizontal-dark.svg +++ b/docs/static/img/logo-horizontal-dark.svg @@ -1,4 +1,4 @@ - + @@ -8,22 +8,4 @@ - - - - - - - - - - - - - - - - - - diff --git a/docs/static/img/logo-horizontal.svg b/docs/static/img/logo-horizontal.svg index 9de8e80eab5e7d..d906fe507706d3 100644 --- a/docs/static/img/logo-horizontal.svg +++ b/docs/static/img/logo-horizontal.svg @@ -1,4 +1,4 @@ - + @@ -8,22 +8,4 @@ - - - - - - - - - - - - - - - - - - diff --git a/docs/static/img/quickstarts/solana-get-started-build-and-deploy.png b/docs/static/img/quickstarts/solana-get-started-build-and-deploy.png new file mode 100644 index 00000000000000..59bb3ef527fd8a Binary files /dev/null and b/docs/static/img/quickstarts/solana-get-started-build-and-deploy.png differ diff --git a/docs/static/img/quickstarts/solana-get-started-import-on-playground.png b/docs/static/img/quickstarts/solana-get-started-import-on-playground.png new file mode 100644 index 00000000000000..cd90b00cb210ba Binary files /dev/null and b/docs/static/img/quickstarts/solana-get-started-import-on-playground.png differ diff --git a/docs/static/img/quickstarts/solana-get-started-successful-build.png b/docs/static/img/quickstarts/solana-get-started-successful-build.png new file mode 100644 index 00000000000000..82b0a5df054930 Binary files /dev/null and b/docs/static/img/quickstarts/solana-get-started-successful-build.png differ diff --git a/docs/yarn.lock b/docs/yarn.lock index 7969630912d805..5f52e1820560cc 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -2,142 +2,148 @@ # yarn lockfile v1 -"@algolia/autocomplete-core@1.0.0-alpha.44": - "integrity" "sha512-2iMXthldMIDXtlbg9omRKLgg1bLo2ZzINAEqwhNjUeyj1ceEyL1ck6FY0VnJpf2LsjmNthHCz2BuFk+nYUeDNA==" - "resolved" "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.0.0-alpha.44.tgz" - "version" "1.0.0-alpha.44" - dependencies: - "@algolia/autocomplete-shared" "1.0.0-alpha.44" +"@algolia/autocomplete-core@1.7.1": + "integrity" "sha512-eiZw+fxMzNQn01S8dA/hcCpoWCOCwcIIEUtHHdzN5TGB3IpzLbuhqFeTfh2OUhhgkE8Uo17+wH+QJ/wYyQmmzg==" + "resolved" "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.7.1.tgz" + "version" "1.7.1" + dependencies: + "@algolia/autocomplete-shared" "1.7.1" -"@algolia/autocomplete-preset-algolia@1.0.0-alpha.44": - "integrity" "sha512-DCHwo5ovzg9k2ejUolGNTLFnIA7GpsrkbNJTy1sFbMnYfBmeK8egZPZnEl7lBTr27OaZu7IkWpTepLVSztZyng==" - "resolved" "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.0.0-alpha.44.tgz" - "version" "1.0.0-alpha.44" - dependencies: - "@algolia/autocomplete-shared" "1.0.0-alpha.44" +"@algolia/autocomplete-preset-algolia@1.7.1": + "integrity" "sha512-pJwmIxeJCymU1M6cGujnaIYcY3QPOVYZOXhFkWVM7IxKzy272BwCvMFMyc5NpG/QmiObBxjo7myd060OeTNJXg==" + "resolved" "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.1.tgz" + "version" "1.7.1" + dependencies: + "@algolia/autocomplete-shared" "1.7.1" -"@algolia/autocomplete-shared@1.0.0-alpha.44": - "integrity" "sha512-2oQZPERYV+yNx/yoVWYjZZdOqsitJ5dfxXJjL18yczOXH6ujnsq+DTczSrX+RjzjQdVeJ1UAG053EJQF/FOiMg==" - "resolved" "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.0.0-alpha.44.tgz" - "version" "1.0.0-alpha.44" +"@algolia/autocomplete-shared@1.7.1": + "integrity" "sha512-eTmGVqY3GeyBTT8IWiB2K5EuURAqhnumfktAEoHxfDY2o7vg2rSnO16ZtIG0fMgt3py28Vwgq42/bVEuaQV7pg==" + "resolved" "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.1.tgz" + "version" "1.7.1" -"@algolia/cache-browser-local-storage@4.9.1": - "integrity" "sha512-bAUU9vKCy45uTTlzJw0LYu1IjoZsmzL6lgjaVFaW1crhX/4P+JD5ReQv3n/wpiXSFaHq1WEO3WyH2g3ymzeipQ==" - "resolved" "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/cache-common" "4.9.1" - -"@algolia/cache-common@4.9.1": - "integrity" "sha512-tcvw4mOfFy44V4ZxDEy9wNGr6vFROZKRpXKTEBgdw/WBn6mX51H1ar4RWtceDEcDU4H5fIv5tsY3ip2hU+fTPg==" - "resolved" "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.9.1.tgz" - "version" "4.9.1" - -"@algolia/cache-in-memory@4.9.1": - "integrity" "sha512-IEJrHonvdymW2CnRfJtsTVWyfAH05xPEFkGXGCw00+6JNCj8Dln3TeaRLiaaY1srlyGedkemekQm1/Xb46CGOQ==" - "resolved" "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/cache-common" "4.9.1" - -"@algolia/client-account@4.9.1": - "integrity" "sha512-Shpjeuwb7i2LR5QuWREb6UbEQLGB+Pl/J5+wPgILJDP/uWp7jpl0ase9mYNQGKj7TjztpSpQCPZ3dSHPnzZPfw==" - "resolved" "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/client-common" "4.9.1" - "@algolia/client-search" "4.9.1" - "@algolia/transporter" "4.9.1" - -"@algolia/client-analytics@4.9.1": - "integrity" "sha512-/g6OkOSIA+A0t/tjvbL6iG/zV4El4LPFgv/tcAYHTH27BmlNtnEXw+iFpGjeUlQoPily9WVB3QNLMJkaNwL3HA==" - "resolved" "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/client-common" "4.9.1" - "@algolia/client-search" "4.9.1" - "@algolia/requester-common" "4.9.1" - "@algolia/transporter" "4.9.1" - -"@algolia/client-common@4.9.1": - "integrity" "sha512-UziRTZ8km3qwoVPIyEre8TV6V+MX7UtbfVqPmSafZ0xu41UUZ+sL56YoKjOXkbKuybeIC9prXMGy/ID5bXkTqg==" - "resolved" "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/requester-common" "4.9.1" - "@algolia/transporter" "4.9.1" - -"@algolia/client-recommendation@4.9.1": - "integrity" "sha512-Drtvvm1PNIOpYf4HFlkPFstFQ3IsN+TRmxur2F7y6Faplb5ybISa8ithu1tmlTdyTf3A78hQUQjgJet6qD2XZw==" - "resolved" "https://registry.npmjs.org/@algolia/client-recommendation/-/client-recommendation-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/client-common" "4.9.1" - "@algolia/requester-common" "4.9.1" - "@algolia/transporter" "4.9.1" - -"@algolia/client-search@^4.5.1", "@algolia/client-search@4.9.1": - "integrity" "sha512-r9Cw2r8kJr45iYncFDht6EshARghU265wuY8Q8oHrpFHjAziEYdsUOdNmQKbsSH5J3gLjDPx1EI5DzVd6ivn3w==" - "resolved" "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/client-common" "4.9.1" - "@algolia/requester-common" "4.9.1" - "@algolia/transporter" "4.9.1" - -"@algolia/logger-common@4.9.1": - "integrity" "sha512-9mPrbFlFyPT7or/7PXTiJjyOewWB9QRkZKVXkt5zHAUiUzGxmmdpJIGpPv3YQnDur8lXrXaRI0MHXUuIDMY1ng==" - "resolved" "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.9.1.tgz" - "version" "4.9.1" - -"@algolia/logger-console@4.9.1": - "integrity" "sha512-74VUwjtFjFpjZpi3QoHIPv0kcr3vWUSHX/Vs8PJW3lPsD4CgyhFenQbG9v+ZnyH0JrJwiYTtzfmrVh7IMWZGrQ==" - "resolved" "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/logger-common" "4.9.1" - -"@algolia/requester-browser-xhr@4.9.1": - "integrity" "sha512-zc46tk5o0ikOAz3uYiRAMxC2iVKAMFKT7nNZnLB5IzT0uqAh7pz/+D/UvIxP4bKmsllpBSnPcpfQF+OI4Ag/BA==" - "resolved" "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/requester-common" "4.9.1" - -"@algolia/requester-common@4.9.1": - "integrity" "sha512-9hPgXnlCSbqJqF69M5x5WN3h51Dc+mk/iWNeJSVxExHGvCDfBBZd0v6S15i8q2a9cD1I2RnhMpbnX5BmGtabVA==" - "resolved" "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.9.1.tgz" - "version" "4.9.1" - -"@algolia/requester-node-http@4.9.1": - "integrity" "sha512-vYNVbSCuyrCSCjHBQJk+tLZtWCjvvDf5tSbRJjyJYMqpnXuIuP7gZm24iHil4NPYBhbBj5NU2ZDAhc/gTn75Ag==" - "resolved" "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.9.1.tgz" - "version" "4.9.1" +"@algolia/cache-browser-local-storage@4.14.2": + "integrity" "sha512-FRweBkK/ywO+GKYfAWbrepewQsPTIEirhi1BdykX9mxvBPtGNKccYAxvGdDCumU1jL4r3cayio4psfzKMejBlA==" + "resolved" "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.2.tgz" + "version" "4.14.2" dependencies: - "@algolia/requester-common" "4.9.1" - -"@algolia/transporter@4.9.1": - "integrity" "sha512-AbjFfGzX+cAuj7Qyc536OxIQzjFOA5FU2ANGStx8LBH+AKXScwfkx67C05riuaRR5adSCLMSEbVvUscH0nF+6A==" - "resolved" "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/cache-common" "4.9.1" - "@algolia/logger-common" "4.9.1" - "@algolia/requester-common" "4.9.1" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.5.5": - "integrity" "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==" - "resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz" - "version" "7.12.13" + "@algolia/cache-common" "4.14.2" + +"@algolia/cache-common@4.14.2": + "integrity" "sha512-SbvAlG9VqNanCErr44q6lEKD2qoK4XtFNx9Qn8FK26ePCI8I9yU7pYB+eM/cZdS9SzQCRJBbHUumVr4bsQ4uxg==" + "resolved" "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.14.2.tgz" + "version" "4.14.2" + +"@algolia/cache-in-memory@4.14.2": + "integrity" "sha512-HrOukWoop9XB/VFojPv1R5SVXowgI56T9pmezd/djh2JnVN/vXswhXV51RKy4nCpqxyHt/aGFSq2qkDvj6KiuQ==" + "resolved" "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.14.2.tgz" + "version" "4.14.2" dependencies: - "@babel/highlight" "^7.12.13" + "@algolia/cache-common" "4.14.2" -"@babel/code-frame@7.10.4": - "integrity" "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==" - "resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz" - "version" "7.10.4" +"@algolia/client-account@4.14.2": + "integrity" "sha512-WHtriQqGyibbb/Rx71YY43T0cXqyelEU0lB2QMBRXvD2X0iyeGl4qMxocgEIcbHyK7uqE7hKgjT8aBrHqhgc1w==" + "resolved" "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.14.2.tgz" + "version" "4.14.2" dependencies: - "@babel/highlight" "^7.10.4" + "@algolia/client-common" "4.14.2" + "@algolia/client-search" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/client-analytics@4.14.2": + "integrity" "sha512-yBvBv2mw+HX5a+aeR0dkvUbFZsiC4FKSnfqk9rrfX+QrlNOKEhCG0tJzjiOggRW4EcNqRmaTULIYvIzQVL2KYQ==" + "resolved" "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.14.2.tgz" + "version" "4.14.2" + dependencies: + "@algolia/client-common" "4.14.2" + "@algolia/client-search" "4.14.2" + "@algolia/requester-common" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/client-common@4.14.2": + "integrity" "sha512-43o4fslNLcktgtDMVaT5XwlzsDPzlqvqesRi4MjQz2x4/Sxm7zYg5LRYFol1BIhG6EwxKvSUq8HcC/KxJu3J0Q==" + "resolved" "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.14.2.tgz" + "version" "4.14.2" + dependencies: + "@algolia/requester-common" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/client-personalization@4.14.2": + "integrity" "sha512-ACCoLi0cL8CBZ1W/2juehSltrw2iqsQBnfiu/Rbl9W2yE6o2ZUb97+sqN/jBqYNQBS+o0ekTMKNkQjHHAcEXNw==" + "resolved" "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.14.2.tgz" + "version" "4.14.2" + dependencies: + "@algolia/client-common" "4.14.2" + "@algolia/requester-common" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/client-search@^4.9.1", "@algolia/client-search@4.14.2": + "integrity" "sha512-L5zScdOmcZ6NGiVbLKTvP02UbxZ0njd5Vq9nJAmPFtjffUSOGEp11BmD2oMJ5QvARgx2XbX4KzTTNS5ECYIMWw==" + "resolved" "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.14.2.tgz" + "version" "4.14.2" + dependencies: + "@algolia/client-common" "4.14.2" + "@algolia/requester-common" "4.14.2" + "@algolia/transporter" "4.14.2" + +"@algolia/events@^4.0.1": + "integrity" "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + "resolved" "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz" + "version" "4.0.1" + +"@algolia/logger-common@4.14.2": + "integrity" "sha512-/JGlYvdV++IcMHBnVFsqEisTiOeEr6cUJtpjz8zc0A9c31JrtLm318Njc72p14Pnkw3A/5lHHh+QxpJ6WFTmsA==" + "resolved" "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.14.2.tgz" + "version" "4.14.2" + +"@algolia/logger-console@4.14.2": + "integrity" "sha512-8S2PlpdshbkwlLCSAB5f8c91xyc84VM9Ar9EdfE9UmX+NrKNYnWR1maXXVDQQoto07G1Ol/tYFnFVhUZq0xV/g==" + "resolved" "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.14.2.tgz" + "version" "4.14.2" + dependencies: + "@algolia/logger-common" "4.14.2" + +"@algolia/requester-browser-xhr@4.14.2": + "integrity" "sha512-CEh//xYz/WfxHFh7pcMjQNWgpl4wFB85lUMRyVwaDPibNzQRVcV33YS+63fShFWc2+42YEipFGH2iPzlpszmDw==" + "resolved" "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.2.tgz" + "version" "4.14.2" + dependencies: + "@algolia/requester-common" "4.14.2" + +"@algolia/requester-common@4.14.2": + "integrity" "sha512-73YQsBOKa5fvVV3My7iZHu1sUqmjjfs9TteFWwPwDmnad7T0VTCopttcsM3OjLxZFtBnX61Xxl2T2gmG2O4ehg==" + "resolved" "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.14.2.tgz" + "version" "4.14.2" + +"@algolia/requester-node-http@4.14.2": + "integrity" "sha512-oDbb02kd1o5GTEld4pETlPZLY0e+gOSWjWMJHWTgDXbv9rm/o2cF7japO6Vj1ENnrqWvLBmW1OzV9g6FUFhFXg==" + "resolved" "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.14.2.tgz" + "version" "4.14.2" + dependencies: + "@algolia/requester-common" "4.14.2" + +"@algolia/transporter@4.14.2": + "integrity" "sha512-t89dfQb2T9MFQHidjHcfhh6iGMNwvuKUvojAj+JsrHAGbuSy7yE4BylhLX6R0Q1xYRoC4Vvv+O5qIw/LdnQfsQ==" + "resolved" "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.14.2.tgz" + "version" "4.14.2" + dependencies: + "@algolia/cache-common" "4.14.2" + "@algolia/logger-common" "4.14.2" + "@algolia/requester-common" "4.14.2" + +"@ampproject/remapping@^2.1.0": + "integrity" "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==" + "resolved" "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.8.3": + "integrity" "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==" + "resolved" "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/highlight" "^7.18.6" "@babel/code-frame@7.12.11": "integrity" "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==" @@ -146,31 +152,31 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.14.0": - "integrity" "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==" - "resolved" "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz" - "version" "7.14.0" - -"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.12.16", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.4.0-0": - "integrity" "sha512-OgC1mON+l4U4B4wiohJlQNUU3H73mpTyYY3j/c8U9dr9UagGGSm+WFpzjy/YLdoyjiG++c1kIDgxCo/mLwQJeQ==" - "resolved" "https://registry.npmjs.org/@babel/core/-/core-7.14.2.tgz" - "version" "7.14.2" - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.14.2" - "@babel/helper-compilation-targets" "^7.13.16" - "@babel/helper-module-transforms" "^7.14.2" - "@babel/helpers" "^7.14.0" - "@babel/parser" "^7.14.2" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.14.2" - "@babel/types" "^7.14.2" +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.19.3", "@babel/compat-data@^7.19.4": + "integrity" "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==" + "resolved" "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.4.tgz" + "version" "7.19.4" + +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.18.6", "@babel/core@^7.19.6", "@babel/core@^7.4.0-0": + "integrity" "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==" + "resolved" "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz" + "version" "7.19.6" + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.19.6" + "@babel/helper-compilation-targets" "^7.19.3" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helpers" "^7.19.4" + "@babel/parser" "^7.19.6" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.6" + "@babel/types" "^7.19.4" "convert-source-map" "^1.7.0" "debug" "^4.1.0" "gensync" "^1.0.0-beta.2" - "json5" "^2.1.2" + "json5" "^2.2.1" "semver" "^6.3.0" - "source-map" "^0.5.0" "@babel/core@7.12.9": "integrity" "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==" @@ -194,325 +200,336 @@ "semver" "^5.4.1" "source-map" "^0.5.0" -"@babel/generator@^7.12.15", "@babel/generator@^7.12.5", "@babel/generator@^7.14.2": - "integrity" "sha512-OnADYbKrffDVai5qcpkMxQ7caomHOoEwjkouqnN2QhydAjowFAZcsdecFIRUBdb+ZcruwYE4ythYmF1UBZU5xQ==" - "resolved" "https://registry.npmjs.org/@babel/generator/-/generator-7.14.2.tgz" - "version" "7.14.2" +"@babel/generator@^7.12.5", "@babel/generator@^7.18.7", "@babel/generator@^7.19.6": + "integrity" "sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==" + "resolved" "https://registry.npmjs.org/@babel/generator/-/generator-7.19.6.tgz" + "version" "7.19.6" dependencies: - "@babel/types" "^7.14.2" + "@babel/types" "^7.19.4" + "@jridgewell/gen-mapping" "^0.3.2" "jsesc" "^2.5.1" - "source-map" "^0.5.0" -"@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13": - "integrity" "sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==" - "resolved" "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz" - "version" "7.12.13" +"@babel/helper-annotate-as-pure@^7.18.6": + "integrity" "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==" + "resolved" "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/types" "^7.12.13" + "@babel/types" "^7.18.6" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.12.13": - "integrity" "sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==" - "resolved" "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz" - "version" "7.12.13" +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + "integrity" "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==" + "resolved" "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/helper-explode-assignable-expression" "^7.12.13" - "@babel/types" "^7.12.13" + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.16": - "integrity" "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==" - "resolved" "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz" - "version" "7.13.16" +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.19.0", "@babel/helper-compilation-targets@^7.19.3": + "integrity" "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==" + "resolved" "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz" + "version" "7.19.3" dependencies: - "@babel/compat-data" "^7.13.15" - "@babel/helper-validator-option" "^7.12.17" - "browserslist" "^4.14.5" + "@babel/compat-data" "^7.19.3" + "@babel/helper-validator-option" "^7.18.6" + "browserslist" "^4.21.3" "semver" "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.14.0": - "integrity" "sha512-6YctwVsmlkchxfGUogvVrrhzyD3grFJyluj5JgDlQrwfMLJSt5tdAzFZfPf4H2Xoi5YLcQ6BxfJlaOBHuctyIw==" - "resolved" "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.2.tgz" - "version" "7.14.2" - dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - "@babel/helper-function-name" "^7.14.2" - "@babel/helper-member-expression-to-functions" "^7.13.12" - "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/helper-replace-supers" "^7.13.12" - "@babel/helper-split-export-declaration" "^7.12.13" - -"@babel/helper-create-regexp-features-plugin@^7.12.13": - "integrity" "sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg==" - "resolved" "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz" - "version" "7.12.17" - dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - "regexpu-core" "^4.7.1" - -"@babel/helper-define-polyfill-provider@^0.2.0": - "integrity" "sha512-JT8tHuFjKBo8NnaUbblz7mIu1nnvUDiHVjXXkulZULyidvo/7P6TY7+YqpV37IfF+KUFxmlK04elKtGKXaiVgw==" - "resolved" "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.0.tgz" - "version" "0.2.0" - dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.19.0": + "integrity" "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==" + "resolved" "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz" + "version" "7.19.0" + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": + "integrity" "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==" + "resolved" "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz" + "version" "7.19.0" + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "regexpu-core" "^5.1.0" + +"@babel/helper-define-polyfill-provider@^0.3.3": + "integrity" "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==" + "resolved" "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz" + "version" "0.3.3" + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" "debug" "^4.1.1" "lodash.debounce" "^4.0.8" "resolve" "^1.14.2" "semver" "^6.1.2" -"@babel/helper-explode-assignable-expression@^7.12.13": - "integrity" "sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==" - "resolved" "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz" - "version" "7.13.0" - dependencies: - "@babel/types" "^7.13.0" +"@babel/helper-environment-visitor@^7.18.9": + "integrity" "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + "resolved" "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" + "version" "7.18.9" -"@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.14.2": - "integrity" "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==" - "resolved" "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz" - "version" "7.14.2" +"@babel/helper-explode-assignable-expression@^7.18.6": + "integrity" "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==" + "resolved" "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-get-function-arity" "^7.12.13" - "@babel/template" "^7.12.13" - "@babel/types" "^7.14.2" + "@babel/types" "^7.18.6" -"@babel/helper-get-function-arity@^7.12.13": - "integrity" "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==" - "resolved" "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz" - "version" "7.12.13" +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": + "integrity" "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==" + "resolved" "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz" + "version" "7.19.0" dependencies: - "@babel/types" "^7.12.13" + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" -"@babel/helper-hoist-variables@^7.13.0": - "integrity" "sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==" - "resolved" "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz" - "version" "7.13.16" +"@babel/helper-hoist-variables@^7.18.6": + "integrity" "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==" + "resolved" "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/traverse" "^7.13.15" - "@babel/types" "^7.13.16" + "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.13.12": - "integrity" "sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==" - "resolved" "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz" - "version" "7.13.12" +"@babel/helper-member-expression-to-functions@^7.18.9": + "integrity" "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==" + "resolved" "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/types" "^7.13.12" + "@babel/types" "^7.18.9" -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.13.12": - "integrity" "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==" - "resolved" "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz" - "version" "7.13.12" +"@babel/helper-module-imports@^7.18.6": + "integrity" "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==" + "resolved" "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/types" "^7.13.12" + "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.14.0", "@babel/helper-module-transforms@^7.14.2": - "integrity" "sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==" - "resolved" "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz" - "version" "7.14.2" +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.6": + "integrity" "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==" + "resolved" "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz" + "version" "7.19.6" dependencies: - "@babel/helper-module-imports" "^7.13.12" - "@babel/helper-replace-supers" "^7.13.12" - "@babel/helper-simple-access" "^7.13.12" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/helper-validator-identifier" "^7.14.0" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.14.2" - "@babel/types" "^7.14.2" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.19.4" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.6" + "@babel/types" "^7.19.4" -"@babel/helper-optimise-call-expression@^7.12.13": - "integrity" "sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==" - "resolved" "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz" - "version" "7.12.13" +"@babel/helper-optimise-call-expression@^7.18.6": + "integrity" "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==" + "resolved" "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/types" "^7.12.13" + "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - "integrity" "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==" - "resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz" - "version" "7.13.0" +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + "integrity" "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==" + "resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz" + "version" "7.20.2" "@babel/helper-plugin-utils@7.10.4": "integrity" "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" "resolved" "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz" "version" "7.10.4" -"@babel/helper-remap-async-to-generator@^7.13.0": - "integrity" "sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==" - "resolved" "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz" - "version" "7.13.0" - dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - "@babel/helper-wrap-function" "^7.13.0" - "@babel/types" "^7.13.0" - -"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.12": - "integrity" "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==" - "resolved" "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz" - "version" "7.13.12" - dependencies: - "@babel/helper-member-expression-to-functions" "^7.13.12" - "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.12" - -"@babel/helper-simple-access@^7.13.12": - "integrity" "sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==" - "resolved" "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz" - "version" "7.13.12" - dependencies: - "@babel/types" "^7.13.12" - -"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": - "integrity" "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==" - "resolved" "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz" - "version" "7.12.1" - dependencies: - "@babel/types" "^7.12.1" - -"@babel/helper-split-export-declaration@^7.12.13": - "integrity" "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==" - "resolved" "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz" - "version" "7.12.13" - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.0": - "integrity" "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" - "resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz" - "version" "7.14.0" - -"@babel/helper-validator-option@^7.12.17": - "integrity" "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==" - "resolved" "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz" - "version" "7.12.17" - -"@babel/helper-wrap-function@^7.13.0": - "integrity" "sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==" - "resolved" "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz" - "version" "7.13.0" - dependencies: - "@babel/helper-function-name" "^7.12.13" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - -"@babel/helpers@^7.12.5", "@babel/helpers@^7.14.0": - "integrity" "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==" - "resolved" "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz" - "version" "7.14.0" - dependencies: - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.14.0" - -"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": - "integrity" "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==" - "resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz" - "version" "7.14.0" - dependencies: - "@babel/helper-validator-identifier" "^7.14.0" +"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": + "integrity" "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==" + "resolved" "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz" + "version" "7.18.9" + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": + "integrity" "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==" + "resolved" "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz" + "version" "7.19.1" + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.19.1" + "@babel/types" "^7.19.0" + +"@babel/helper-simple-access@^7.19.4": + "integrity" "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==" + "resolved" "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz" + "version" "7.19.4" + dependencies: + "@babel/types" "^7.19.4" + +"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": + "integrity" "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==" + "resolved" "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz" + "version" "7.18.9" + dependencies: + "@babel/types" "^7.18.9" + +"@babel/helper-split-export-declaration@^7.18.6": + "integrity" "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==" + "resolved" "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.19.4": + "integrity" "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" + "resolved" "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" + "version" "7.19.4" + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + "integrity" "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + "resolved" "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" + "version" "7.19.1" + +"@babel/helper-validator-option@^7.18.6": + "integrity" "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" + "resolved" "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz" + "version" "7.18.6" + +"@babel/helper-wrap-function@^7.18.9": + "integrity" "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==" + "resolved" "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz" + "version" "7.19.0" + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.0" + "@babel/types" "^7.19.0" + +"@babel/helpers@^7.12.5", "@babel/helpers@^7.19.4": + "integrity" "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==" + "resolved" "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.4.tgz" + "version" "7.19.4" + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.19.4" + "@babel/types" "^7.19.4" + +"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": + "integrity" "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==" + "resolved" "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" "chalk" "^2.0.0" "js-tokens" "^4.0.0" -"@babel/parser@^7.12.13", "@babel/parser@^7.12.16", "@babel/parser@^7.12.7", "@babel/parser@^7.14.2", "@babel/parser@^7.7.0": - "integrity" "sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ==" - "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.14.2.tgz" - "version" "7.14.2" +"@babel/parser@^7.12.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.8", "@babel/parser@^7.19.6", "@babel/parser@^7.7.0": + "integrity" "sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==" + "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.19.6.tgz" + "version" "7.19.6" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": - "integrity" "sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz" - "version" "7.13.12" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + "integrity" "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" - "@babel/plugin-proposal-optional-chaining" "^7.13.12" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-async-generator-functions@^7.14.2": - "integrity" "sha512-b1AM4F6fwck4N8ItZ/AtC4FP/cqZqmKRQ4FaTDutwSYyjuhtvsGEMLK4N/ztV/ImP40BjIDyMgBQAeAMsQYVFQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": + "integrity" "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-remap-async-to-generator" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + +"@babel/plugin-proposal-async-generator-functions@^7.19.1": + "integrity" "sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz" + "version" "7.19.1" + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.13.0": - "integrity" "sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz" - "version" "7.13.0" +"@babel/plugin-proposal-class-properties@^7.18.6": + "integrity" "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-create-class-features-plugin" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-class-static-block@^7.13.11": - "integrity" "sha512-fJTdFI4bfnMjvxJyNuaf8i9mVcZ0UhetaGEUHaHV9KEnibLugJkZAtXikR8KcYj+NYmI4DZMS8yQAyg+hvfSqg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.13.11.tgz" - "version" "7.13.11" +"@babel/plugin-proposal-class-static-block@^7.18.6": + "integrity" "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-class-static-block" "^7.12.13" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-dynamic-import@^7.14.2": - "integrity" "sha512-oxVQZIWFh91vuNEMKltqNsKLFWkOIyJc95k2Gv9lWVyDfPUQGSSlbDEgWuJUU1afGE9WwlzpucMZ3yDRHIItkA==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-proposal-dynamic-import@^7.18.6": + "integrity" "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-proposal-export-namespace-from@^7.14.2": - "integrity" "sha512-sRxW3z3Zp3pFfLAgVEvzTFutTXax837oOatUIvSG9o5gRj9mKwm3br1Se5f4QalTQs9x4AzlA/HrCWbQIHASUQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + "integrity" "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.14.2": - "integrity" "sha512-w2DtsfXBBJddJacXMBhElGEYqCZQqN99Se1qeYn8DVLB33owlrlLftIbMzn5nz1OITfDVknXF433tBrLEAOEjA==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-proposal-json-strings@^7.18.6": + "integrity" "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-logical-assignment-operators@^7.14.2": - "integrity" "sha512-1JAZtUrqYyGsS7IDmFeaem+/LJqujfLZ2weLR9ugB0ufUPjzf8cguyVT1g5im7f7RXxuLq1xUxEzvm68uYRtGg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": + "integrity" "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.14.2": - "integrity" "sha512-ebR0zU9OvI2N4qiAC38KIAK75KItpIPTpAtd2r4OZmMFeKbKJpUFLYP2EuDut82+BmYi8sz42B+TfTptJ9iG5Q==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + "integrity" "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-proposal-numeric-separator@^7.14.2": - "integrity" "sha512-DcTQY9syxu9BpU3Uo94fjCB3LN9/hgPS8oUL7KrSW3bA2ePrKZZPJcc5y0hoJAM9dft3pGfErtEUvxXQcfLxUg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-proposal-numeric-separator@^7.18.6": + "integrity" "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.14.2": - "integrity" "sha512-hBIQFxwZi8GIp934+nj5uV31mqclC1aYDhctDu5khTi9PCCUOczyy0b34W0oE9U/eJXiqQaKyVsmjeagOaSlbw==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-proposal-object-rest-spread@^7.19.4": + "integrity" "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz" + "version" "7.19.4" dependencies: - "@babel/compat-data" "^7.14.0" - "@babel/helper-compilation-targets" "^7.13.16" - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/compat-data" "^7.19.4" + "@babel/helper-compilation-targets" "^7.19.3" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.14.2" + "@babel/plugin-transform-parameters" "^7.18.8" "@babel/plugin-proposal-object-rest-spread@7.12.1": "integrity" "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==" @@ -523,48 +540,48 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-transform-parameters" "^7.12.1" -"@babel/plugin-proposal-optional-catch-binding@^7.14.2": - "integrity" "sha512-XtkJsmJtBaUbOxZsNk0Fvrv8eiqgneug0A6aqLFZ4TSkar2L5dSXWcnUKHgmjJt49pyB/6ZHvkr3dPgl9MOWRQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + "integrity" "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.14.2": - "integrity" "sha512-qQByMRPwMZJainfig10BoaDldx/+VDtNcrA7qdNaEOAj6VXud+gfrkA8j4CRAU5HjnWREXqIpSpH30qZX1xivA==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-proposal-optional-chaining@^7.18.9": + "integrity" "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-private-methods@^7.13.0": - "integrity" "sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz" - "version" "7.13.0" +"@babel/plugin-proposal-private-methods@^7.18.6": + "integrity" "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-create-class-features-plugin" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-proposal-private-property-in-object@^7.14.0": - "integrity" "sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz" - "version" "7.14.0" +"@babel/plugin-proposal-private-property-in-object@^7.18.6": + "integrity" "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - "@babel/helper-create-class-features-plugin" "^7.14.0" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-private-property-in-object" "^7.14.0" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.12.13", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - "integrity" "sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + "integrity" "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==" + "resolved" "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-async-generators@^7.8.4": "integrity" "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==" @@ -580,12 +597,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-class-static-block@^7.12.13": - "integrity" "sha512-ZmKQ0ZXR0nYpHZIIuj9zE7oIqCx2hw9TKi+lIo73NNrMPAZGHfS92/VRV0ZmPj6H2ffBgyFHXvJ5NYsNeEaP2A==" - "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-syntax-class-static-block@^7.14.5": + "integrity" "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" + "version" "7.14.5" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-dynamic-import@^7.8.3": "integrity" "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==" @@ -601,6 +618,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-syntax-import-assertions@^7.18.6": + "integrity" "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings@^7.8.3": "integrity" "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==" "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" @@ -608,12 +632,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.12.13": - "integrity" "sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==" - "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-syntax-jsx@^7.18.6": + "integrity" "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-jsx@7.12.1": "integrity" "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==" @@ -664,366 +688,370 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-private-property-in-object@^7.14.0": - "integrity" "sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w==" - "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz" - "version" "7.14.0" +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + "integrity" "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" + "version" "7.14.5" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-top-level-await@^7.12.13": - "integrity" "sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-syntax-top-level-await@^7.14.5": + "integrity" "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" + "version" "7.14.5" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.12.13": - "integrity" "sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w==" - "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-syntax-typescript@^7.18.6": + "integrity" "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.13.0": - "integrity" "sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz" - "version" "7.13.0" +"@babel/plugin-transform-arrow-functions@^7.18.6": + "integrity" "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-async-to-generator@^7.13.0": - "integrity" "sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz" - "version" "7.13.0" +"@babel/plugin-transform-async-to-generator@^7.18.6": + "integrity" "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-remap-async-to-generator" "^7.13.0" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" -"@babel/plugin-transform-block-scoped-functions@^7.12.13": - "integrity" "sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + "integrity" "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-block-scoping@^7.14.2": - "integrity" "sha512-neZZcP19NugZZqNwMTH+KoBjx5WyvESPSIOQb4JHpfd+zPfqcH65RMu5xJju5+6q/Y2VzYrleQTr+b6METyyxg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-transform-block-scoping@^7.19.4": + "integrity" "sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz" + "version" "7.19.4" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-transform-classes@^7.14.2": - "integrity" "sha512-7oafAVcucHquA/VZCsXv/gmuiHeYd64UJyyTYU+MPfNu0KeNlxw06IeENBO8bJjXVbolu+j1MM5aKQtH1OMCNg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-transform-classes@^7.19.0": + "integrity" "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz" + "version" "7.19.0" dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - "@babel/helper-function-name" "^7.14.2" - "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-replace-supers" "^7.13.12" - "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.19.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" "globals" "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.13.0": - "integrity" "sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz" - "version" "7.13.0" +"@babel/plugin-transform-computed-properties@^7.18.9": + "integrity" "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-destructuring@^7.13.17": - "integrity" "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz" - "version" "7.13.17" +"@babel/plugin-transform-destructuring@^7.19.4": + "integrity" "sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz" + "version" "7.19.4" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-transform-dotall-regex@^7.12.13", "@babel/plugin-transform-dotall-regex@^7.4.4": - "integrity" "sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": + "integrity" "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-duplicate-keys@^7.12.13": - "integrity" "sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-duplicate-keys@^7.18.9": + "integrity" "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-exponentiation-operator@^7.12.13": - "integrity" "sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + "integrity" "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-for-of@^7.13.0": - "integrity" "sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz" - "version" "7.13.0" +"@babel/plugin-transform-for-of@^7.18.8": + "integrity" "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz" + "version" "7.18.8" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-function-name@^7.12.13": - "integrity" "sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-function-name@^7.18.9": + "integrity" "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/helper-function-name" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-literals@^7.12.13": - "integrity" "sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-literals@^7.18.9": + "integrity" "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz" + "version" "7.18.9" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/plugin-transform-member-expression-literals@^7.12.13": - "integrity" "sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-member-expression-literals@^7.18.6": + "integrity" "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-modules-amd@^7.14.2": - "integrity" "sha512-hPC6XBswt8P3G2D1tSV2HzdKvkqOpmbyoy+g73JG0qlF/qx2y3KaMmXb1fLrpmWGLZYA0ojCvaHdzFWjlmV+Pw==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-transform-modules-amd@^7.18.6": + "integrity" "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz" + "version" "7.19.6" dependencies: - "@babel/helper-module-transforms" "^7.14.2" - "@babel/helper-plugin-utils" "^7.13.0" - "babel-plugin-dynamic-import-node" "^2.3.3" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-transform-modules-commonjs@^7.14.0": - "integrity" "sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz" - "version" "7.14.0" +"@babel/plugin-transform-modules-commonjs@^7.18.6": + "integrity" "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz" + "version" "7.19.6" dependencies: - "@babel/helper-module-transforms" "^7.14.0" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-simple-access" "^7.13.12" - "babel-plugin-dynamic-import-node" "^2.3.3" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-simple-access" "^7.19.4" -"@babel/plugin-transform-modules-systemjs@^7.13.8": - "integrity" "sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz" - "version" "7.13.8" +"@babel/plugin-transform-modules-systemjs@^7.19.0": + "integrity" "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz" + "version" "7.19.6" dependencies: - "@babel/helper-hoist-variables" "^7.13.0" - "@babel/helper-module-transforms" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-validator-identifier" "^7.12.11" - "babel-plugin-dynamic-import-node" "^2.3.3" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-validator-identifier" "^7.19.1" -"@babel/plugin-transform-modules-umd@^7.14.0": - "integrity" "sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz" - "version" "7.14.0" +"@babel/plugin-transform-modules-umd@^7.18.6": + "integrity" "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-module-transforms" "^7.14.0" - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-named-capturing-groups-regex@^7.12.13": - "integrity" "sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": + "integrity" "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz" + "version" "7.19.1" dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" + "@babel/helper-create-regexp-features-plugin" "^7.19.0" + "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-transform-new-target@^7.12.13": - "integrity" "sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-new-target@^7.18.6": + "integrity" "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-object-super@^7.12.13": - "integrity" "sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-object-super@^7.18.6": + "integrity" "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - "@babel/helper-replace-supers" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.14.2": - "integrity" "sha512-NxoVmA3APNCC1JdMXkdYXuQS+EMdqy0vIwyDHeKHiJKRxmp1qGSdb0JLEIoPRhkx6H/8Qi3RJ3uqOCYw8giy9A==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.18.8": + "integrity" "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz" + "version" "7.18.8" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-property-literals@^7.12.13": - "integrity" "sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-property-literals@^7.18.6": + "integrity" "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-react-constant-elements@^7.12.1": - "integrity" "sha512-SNJU53VM/SjQL0bZhyU+f4kJQz7bQQajnrZRSaU21hruG/NWY41AEM9AWXeXX90pYr/C2yAmTgI6yW3LlLrAUQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.13.13.tgz" - "version" "7.13.13" +"@babel/plugin-transform-react-constant-elements@^7.18.12": + "integrity" "sha512-KS/G8YI8uwMGKErLFOHS/ekhqdHhpEloxs43NecQHVgo2QuQSyJhGIY1fL8UGl9wy5ItVwwoUL4YxVqsplGq2g==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.20.2.tgz" + "version" "7.20.2" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.20.2" -"@babel/plugin-transform-react-display-name@^7.12.13": - "integrity" "sha512-zCubvP+jjahpnFJvPaHPiGVfuVUjXHhFvJKQdNnsmSsiU9kR/rCZ41jHc++tERD2zV+p7Hr6is+t5b6iWTCqSw==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-transform-react-display-name@^7.18.6": + "integrity" "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.13.0" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-react-jsx-development@^7.12.17": - "integrity" "sha512-BPjYV86SVuOaudFhsJR1zjgxxOhJDt6JHNoD48DxWEIxUCAMjV1ys6DYw4SDYZh0b1QsS2vfIA9t/ZsQGsDOUQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.17.tgz" - "version" "7.12.17" +"@babel/plugin-transform-react-jsx-development@^7.18.6": + "integrity" "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/plugin-transform-react-jsx" "^7.12.17" + "@babel/plugin-transform-react-jsx" "^7.18.6" -"@babel/plugin-transform-react-jsx@^7.12.17", "@babel/plugin-transform-react-jsx@^7.13.12": - "integrity" "sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz" - "version" "7.13.12" +"@babel/plugin-transform-react-jsx@^7.18.6": + "integrity" "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz" + "version" "7.19.0" dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - "@babel/helper-module-imports" "^7.13.12" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-jsx" "^7.12.13" - "@babel/types" "^7.13.12" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.19.0" -"@babel/plugin-transform-react-pure-annotations@^7.12.1": - "integrity" "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz" - "version" "7.12.1" - dependencies: - "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-react-pure-annotations@^7.18.6": + "integrity" "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-regenerator@^7.13.15": - "integrity" "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz" - "version" "7.13.15" +"@babel/plugin-transform-regenerator@^7.18.6": + "integrity" "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz" + "version" "7.18.6" dependencies: - "regenerator-transform" "^0.14.2" + "@babel/helper-plugin-utils" "^7.18.6" + "regenerator-transform" "^0.15.0" -"@babel/plugin-transform-reserved-words@^7.12.13": - "integrity" "sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz" - "version" "7.12.13" +"@babel/plugin-transform-reserved-words@^7.18.6": + "integrity" "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz" + "version" "7.18.6" dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-runtime@^7.12.15": - "integrity" "sha512-LyA2AiPkaYzI7G5e2YI4NCasTfFe7mZvlupNprDOB7CdNUHb2DQC4uV6oeZ0396gOcicUzUCh0MShL6wiUgk+Q==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.2.tgz" - "version" "7.14.2" +"@babel/plugin-transform-runtime@^7.18.6": + "integrity" "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz" + "version" "7.19.6" dependencies: - "@babel/helper-module-imports" "^7.13.12" - "@babel/helper-plugin-utils" "^7.13.0" - "babel-plugin-polyfill-corejs2" "^0.2.0" - "babel-plugin-polyfill-corejs3" "^0.2.0" - "babel-plugin-polyfill-regenerator" "^0.2.0" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + "babel-plugin-polyfill-corejs2" "^0.3.3" + "babel-plugin-polyfill-corejs3" "^0.6.0" + "babel-plugin-polyfill-regenerator" "^0.4.1" "semver" "^6.3.0" -"@babel/plugin-transform-shorthand-properties@^7.12.13": - "integrity" "sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz" - "version" "7.12.13" - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-spread@^7.13.0": - "integrity" "sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz" - "version" "7.13.0" - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" - -"@babel/plugin-transform-sticky-regex@^7.12.13": - "integrity" "sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz" - "version" "7.12.13" - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-template-literals@^7.13.0": - "integrity" "sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz" - "version" "7.13.0" - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-transform-typeof-symbol@^7.12.13": - "integrity" "sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz" - "version" "7.12.13" - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-typescript@^7.13.0": - "integrity" "sha512-elQEwluzaU8R8dbVuW2Q2Y8Nznf7hnjM7+DSCd14Lo5fF63C9qNLbwZYbmZrtV9/ySpSUpkRpQXvJb6xyu4hCQ==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.13.0.tgz" - "version" "7.13.0" - dependencies: - "@babel/helper-create-class-features-plugin" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-typescript" "^7.12.13" - -"@babel/plugin-transform-unicode-escapes@^7.12.13": - "integrity" "sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz" - "version" "7.12.13" - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-unicode-regex@^7.12.13": - "integrity" "sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==" - "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz" - "version" "7.12.13" - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/preset-env@^7.12.1", "@babel/preset-env@^7.12.16": - "integrity" "sha512-7dD7lVT8GMrE73v4lvDEb85cgcQhdES91BSD7jS/xjC6QY8PnRhux35ac+GCpbiRhp8crexBvZZqnaL6VrY8TQ==" - "resolved" "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.2.tgz" - "version" "7.14.2" - dependencies: - "@babel/compat-data" "^7.14.0" - "@babel/helper-compilation-targets" "^7.13.16" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-validator-option" "^7.12.17" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12" - "@babel/plugin-proposal-async-generator-functions" "^7.14.2" - "@babel/plugin-proposal-class-properties" "^7.13.0" - "@babel/plugin-proposal-class-static-block" "^7.13.11" - "@babel/plugin-proposal-dynamic-import" "^7.14.2" - "@babel/plugin-proposal-export-namespace-from" "^7.14.2" - "@babel/plugin-proposal-json-strings" "^7.14.2" - "@babel/plugin-proposal-logical-assignment-operators" "^7.14.2" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.2" - "@babel/plugin-proposal-numeric-separator" "^7.14.2" - "@babel/plugin-proposal-object-rest-spread" "^7.14.2" - "@babel/plugin-proposal-optional-catch-binding" "^7.14.2" - "@babel/plugin-proposal-optional-chaining" "^7.14.2" - "@babel/plugin-proposal-private-methods" "^7.13.0" - "@babel/plugin-proposal-private-property-in-object" "^7.14.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.12.13" +"@babel/plugin-transform-shorthand-properties@^7.18.6": + "integrity" "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-spread@^7.19.0": + "integrity" "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz" + "version" "7.19.0" + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + +"@babel/plugin-transform-sticky-regex@^7.18.6": + "integrity" "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-template-literals@^7.18.9": + "integrity" "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz" + "version" "7.18.9" + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typeof-symbol@^7.18.9": + "integrity" "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz" + "version" "7.18.9" + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typescript@^7.18.6": + "integrity" "sha512-z6fnuK9ve9u/0X0rRvI9MY0xg+DOUaABDYOe+/SQTxtlptaBB/V9JIUxJn6xp3lMBeb9qe8xSFmHU35oZDXD+w==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.19.3.tgz" + "version" "7.19.3" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.19.0" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-syntax-typescript" "^7.18.6" + +"@babel/plugin-transform-unicode-escapes@^7.18.10": + "integrity" "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz" + "version" "7.18.10" + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-unicode-regex@^7.18.6": + "integrity" "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==" + "resolved" "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/preset-env@^7.18.6", "@babel/preset-env@^7.19.4": + "integrity" "sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==" + "resolved" "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.19.4.tgz" + "version" "7.19.4" + dependencies: + "@babel/compat-data" "^7.19.4" + "@babel/helper-compilation-targets" "^7.19.3" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-async-generator-functions" "^7.19.1" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.19.4" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.18.6" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -1031,52 +1059,52 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.0" - "@babel/plugin-syntax-top-level-await" "^7.12.13" - "@babel/plugin-transform-arrow-functions" "^7.13.0" - "@babel/plugin-transform-async-to-generator" "^7.13.0" - "@babel/plugin-transform-block-scoped-functions" "^7.12.13" - "@babel/plugin-transform-block-scoping" "^7.14.2" - "@babel/plugin-transform-classes" "^7.14.2" - "@babel/plugin-transform-computed-properties" "^7.13.0" - "@babel/plugin-transform-destructuring" "^7.13.17" - "@babel/plugin-transform-dotall-regex" "^7.12.13" - "@babel/plugin-transform-duplicate-keys" "^7.12.13" - "@babel/plugin-transform-exponentiation-operator" "^7.12.13" - "@babel/plugin-transform-for-of" "^7.13.0" - "@babel/plugin-transform-function-name" "^7.12.13" - "@babel/plugin-transform-literals" "^7.12.13" - "@babel/plugin-transform-member-expression-literals" "^7.12.13" - "@babel/plugin-transform-modules-amd" "^7.14.2" - "@babel/plugin-transform-modules-commonjs" "^7.14.0" - "@babel/plugin-transform-modules-systemjs" "^7.13.8" - "@babel/plugin-transform-modules-umd" "^7.14.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.13" - "@babel/plugin-transform-new-target" "^7.12.13" - "@babel/plugin-transform-object-super" "^7.12.13" - "@babel/plugin-transform-parameters" "^7.14.2" - "@babel/plugin-transform-property-literals" "^7.12.13" - "@babel/plugin-transform-regenerator" "^7.13.15" - "@babel/plugin-transform-reserved-words" "^7.12.13" - "@babel/plugin-transform-shorthand-properties" "^7.12.13" - "@babel/plugin-transform-spread" "^7.13.0" - "@babel/plugin-transform-sticky-regex" "^7.12.13" - "@babel/plugin-transform-template-literals" "^7.13.0" - "@babel/plugin-transform-typeof-symbol" "^7.12.13" - "@babel/plugin-transform-unicode-escapes" "^7.12.13" - "@babel/plugin-transform-unicode-regex" "^7.12.13" - "@babel/preset-modules" "^0.1.4" - "@babel/types" "^7.14.2" - "babel-plugin-polyfill-corejs2" "^0.2.0" - "babel-plugin-polyfill-corejs3" "^0.2.0" - "babel-plugin-polyfill-regenerator" "^0.2.0" - "core-js-compat" "^3.9.0" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.18.6" + "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.19.4" + "@babel/plugin-transform-classes" "^7.19.0" + "@babel/plugin-transform-computed-properties" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.19.4" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.18.6" + "@babel/plugin-transform-modules-commonjs" "^7.18.6" + "@babel/plugin-transform-modules-systemjs" "^7.19.0" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.19.0" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.19.4" + "babel-plugin-polyfill-corejs2" "^0.3.3" + "babel-plugin-polyfill-corejs3" "^0.6.0" + "babel-plugin-polyfill-regenerator" "^0.4.1" + "core-js-compat" "^3.25.1" "semver" "^6.3.0" -"@babel/preset-modules@^0.1.4": - "integrity" "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==" - "resolved" "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz" - "version" "0.1.4" +"@babel/preset-modules@^0.1.5": + "integrity" "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==" + "resolved" "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz" + "version" "0.1.5" dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" @@ -1084,448 +1112,510 @@ "@babel/types" "^7.4.4" "esutils" "^2.0.2" -"@babel/preset-react@^7.12.13", "@babel/preset-react@^7.12.5": - "integrity" "sha512-gx+tDLIE06sRjKJkVtpZ/t3mzCDOnPG+ggHZG9lffUbX8+wC739x20YQc9V35Do6ZAxaUc/HhVHIiOzz5MvDmA==" - "resolved" "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.13.13.tgz" - "version" "7.13.13" - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-validator-option" "^7.12.17" - "@babel/plugin-transform-react-display-name" "^7.12.13" - "@babel/plugin-transform-react-jsx" "^7.13.12" - "@babel/plugin-transform-react-jsx-development" "^7.12.17" - "@babel/plugin-transform-react-pure-annotations" "^7.12.1" - -"@babel/preset-typescript@^7.12.16": - "integrity" "sha512-LXJwxrHy0N3f6gIJlYbLta1D9BDtHpQeqwzM0LIfjDlr6UE/D5Mc7W4iDiQzaE+ks0sTjT26ArcHWnJVt0QiHw==" - "resolved" "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.13.0.tgz" - "version" "7.13.0" - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-validator-option" "^7.12.17" - "@babel/plugin-transform-typescript" "^7.13.0" - -"@babel/runtime-corejs3@^7.12.13": - "integrity" "sha512-0R0HTZWHLk6G8jIk0FtoX+AatCtKnswS98VhXwGImFc759PJRp4Tru0PQYZofyijTFUr+gT8Mu7sgXVJLQ0ceg==" - "resolved" "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz" - "version" "7.14.0" - dependencies: - "core-js-pure" "^3.0.0" +"@babel/preset-react@^7.18.6": + "integrity" "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==" + "resolved" "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-react-display-name" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-pure-annotations" "^7.18.6" + +"@babel/preset-typescript@^7.18.6": + "integrity" "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==" + "resolved" "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz" + "version" "7.18.6" + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-typescript" "^7.18.6" + +"@babel/runtime-corejs3@^7.18.6": + "integrity" "sha512-oWNn1ZlGde7b4i/3tnixpH9qI0bOAACiUs+KEES4UUCnsPjVWFlWdLV/iwJuPC2qp3EowbAqsm+0XqNwnwYhxA==" + "resolved" "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.19.6.tgz" + "version" "7.19.6" + dependencies: + "core-js-pure" "^3.25.1" "regenerator-runtime" "^0.13.4" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4": - "integrity" "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==" - "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz" - "version" "7.14.0" +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.6", "@babel/runtime@^7.8.4": + "integrity" "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==" + "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz" + "version" "7.19.4" dependencies: "regenerator-runtime" "^0.13.4" -"@babel/template@^7.12.13", "@babel/template@^7.12.7": - "integrity" "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==" - "resolved" "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz" - "version" "7.12.13" - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/parser" "^7.12.13" - "@babel/types" "^7.12.13" - -"@babel/traverse@^7.12.13", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.15", "@babel/traverse@^7.14.0", "@babel/traverse@^7.14.2", "@babel/traverse@^7.7.0": - "integrity" "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==" - "resolved" "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz" - "version" "7.14.2" - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.14.2" - "@babel/helper-function-name" "^7.14.2" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.14.2" - "@babel/types" "^7.14.2" +"@babel/template@^7.12.7", "@babel/template@^7.18.10": + "integrity" "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==" + "resolved" "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz" + "version" "7.18.10" + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + +"@babel/traverse@^7.12.9", "@babel/traverse@^7.18.8", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.19.4", "@babel/traverse@^7.19.6", "@babel/traverse@^7.7.0": + "integrity" "sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==" + "resolved" "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.6.tgz" + "version" "7.19.6" + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.19.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.19.6" + "@babel/types" "^7.19.4" "debug" "^4.1.0" "globals" "^11.1.0" -"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.6", "@babel/types@^7.12.7", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.16", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.4.4", "@babel/types@^7.7.0": - "integrity" "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==" - "resolved" "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz" - "version" "7.14.2" +"@babel/types@^7.12.7", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.19.4", "@babel/types@^7.20.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0": + "integrity" "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==" + "resolved" "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz" + "version" "7.20.7" dependencies: - "@babel/helper-validator-identifier" "^7.14.0" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" "to-fast-properties" "^2.0.0" +"@colors/colors@1.5.0": + "integrity" "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" + "resolved" "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz" + "version" "1.5.0" + "@crowdin/cli@^3.6.1": - "integrity" "sha512-RUKFrPCX3R1MJPyRpBSqWFwmjbDlVWLHtXAzx2FPeyjnyMXrXLPJ8YEl4t8YU+96q/0t46qTdmMLeQmYyDEvGQ==" - "resolved" "https://registry.npmjs.org/@crowdin/cli/-/cli-3.6.1.tgz" - "version" "3.6.1" + "integrity" "sha512-4wQjqJZmU/mg3VYfRL6IYXw/pPAL9vdfW3QVSBovYA+bYaEt43ZuGsSrqeBGOhLehasWwRqklXWsl96gxQlLdw==" + "resolved" "https://registry.npmjs.org/@crowdin/cli/-/cli-3.9.0.tgz" + "version" "3.9.0" dependencies: + "njre" "^0.2.0" "shelljs" "^0.8.4" -"@docsearch/css@3.0.0-alpha.36": - "integrity" "sha512-zSN2SXuZPDqQaSFzYa1kOwToukqzhLHG7c66iO+/PlmWb6/RZ5cjTkG6VCJynlohRWea7AqZKWS/ptm8kM2Dmg==" - "resolved" "https://registry.npmjs.org/@docsearch/css/-/css-3.0.0-alpha.36.tgz" - "version" "3.0.0-alpha.36" +"@docsearch/css@3.2.2": + "integrity" "sha512-VB0Evx4ikS1ZlW1YVUw+vI9b3H/UXMCo4W/ZWy+n56Sho4KOqyCHcINVays91TJt7HTV/CKO3FCbm2VJg5Wipw==" + "resolved" "https://registry.npmjs.org/@docsearch/css/-/css-3.2.2.tgz" + "version" "3.2.2" -"@docsearch/react@^3.0.0-alpha.33": - "integrity" "sha512-synYZDHalvMzesFiy7kK+uoz4oTdWSTbe2cU+iiUjwFMyQ+WWjWwGVnvcvk+cjj9pRCVaZo5y5WpqNXq1j8k9Q==" - "resolved" "https://registry.npmjs.org/@docsearch/react/-/react-3.0.0-alpha.36.tgz" - "version" "3.0.0-alpha.36" +"@docsearch/react@^3.1.1": + "integrity" "sha512-1Hn2SNQUFVPrzqvaj+vxXZfsfn3rnW8CoyGAJ1LqXMY9py8GbxK8VfmJ5Z6z4LwG9849tGru/N6dp0cQO6r9Ag==" + "resolved" "https://registry.npmjs.org/@docsearch/react/-/react-3.2.2.tgz" + "version" "3.2.2" dependencies: - "@algolia/autocomplete-core" "1.0.0-alpha.44" - "@algolia/autocomplete-preset-algolia" "1.0.0-alpha.44" - "@docsearch/css" "3.0.0-alpha.36" + "@algolia/autocomplete-core" "1.7.1" + "@algolia/autocomplete-preset-algolia" "1.7.1" + "@docsearch/css" "3.2.2" "algoliasearch" "^4.0.0" -"@docusaurus/core@^2.0.0-beta.0", "@docusaurus/core@2.0.0-beta.0": - "integrity" "sha512-xWwpuEwFRKJmZvNGOpr/dyRDnx/psckLPsozQTg2hu3u81Wqu9gigWgYK/C2fPlEjxMcVw0/2WH+zwpbyWmF2Q==" - "resolved" "https://registry.npmjs.org/@docusaurus/core/-/core-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" +"@docusaurus/core@^2.2.0", "@docusaurus/core@2.2.0": + "integrity" "sha512-Vd6XOluKQqzG12fEs9prJgDtyn6DPok9vmUWDR2E6/nV5Fl9SVkhEQOBxwObjk3kQh7OY7vguFaLh0jqdApWsA==" + "resolved" "https://registry.npmjs.org/@docusaurus/core/-/core-2.2.0.tgz" + "version" "2.2.0" dependencies: - "@babel/core" "^7.12.16" - "@babel/generator" "^7.12.15" + "@babel/core" "^7.18.6" + "@babel/generator" "^7.18.7" "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-transform-runtime" "^7.12.15" - "@babel/preset-env" "^7.12.16" - "@babel/preset-react" "^7.12.13" - "@babel/preset-typescript" "^7.12.16" - "@babel/runtime" "^7.12.5" - "@babel/runtime-corejs3" "^7.12.13" - "@babel/traverse" "^7.12.13" - "@docusaurus/cssnano-preset" "2.0.0-beta.0" - "@docusaurus/react-loadable" "5.5.0" - "@docusaurus/types" "2.0.0-beta.0" - "@docusaurus/utils" "2.0.0-beta.0" - "@docusaurus/utils-validation" "2.0.0-beta.0" - "@endiliey/static-site-generator-webpack-plugin" "^4.0.0" - "@svgr/webpack" "^5.5.0" - "autoprefixer" "^10.2.5" - "babel-loader" "^8.2.2" - "babel-plugin-dynamic-import-node" "2.3.0" - "boxen" "^5.0.0" - "chalk" "^4.1.0" - "chokidar" "^3.5.1" - "clean-css" "^5.1.1" + "@babel/plugin-transform-runtime" "^7.18.6" + "@babel/preset-env" "^7.18.6" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@babel/runtime" "^7.18.6" + "@babel/runtime-corejs3" "^7.18.6" + "@babel/traverse" "^7.18.8" + "@docusaurus/cssnano-preset" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/react-loadable" "5.5.2" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-common" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "@slorber/static-site-generator-webpack-plugin" "^4.0.7" + "@svgr/webpack" "^6.2.1" + "autoprefixer" "^10.4.7" + "babel-loader" "^8.2.5" + "babel-plugin-dynamic-import-node" "^2.3.3" + "boxen" "^6.2.1" + "chalk" "^4.1.2" + "chokidar" "^3.5.3" + "clean-css" "^5.3.0" + "cli-table3" "^0.6.2" + "combine-promises" "^1.1.0" "commander" "^5.1.0" - "copy-webpack-plugin" "^8.1.0" - "core-js" "^3.9.1" - "css-loader" "^5.1.1" - "css-minimizer-webpack-plugin" "^2.0.0" - "cssnano" "^5.0.1" - "del" "^6.0.0" + "copy-webpack-plugin" "^11.0.0" + "core-js" "^3.23.3" + "css-loader" "^6.7.1" + "css-minimizer-webpack-plugin" "^4.0.0" + "cssnano" "^5.1.12" + "del" "^6.1.1" "detect-port" "^1.3.0" - "eta" "^1.12.1" - "express" "^4.17.1" + "escape-html" "^1.0.3" + "eta" "^1.12.3" "file-loader" "^6.2.0" - "fs-extra" "^9.1.0" - "github-slugger" "^1.3.0" - "globby" "^11.0.2" - "html-minifier-terser" "^5.1.1" - "html-tags" "^3.1.0" - "html-webpack-plugin" "^5.2.0" + "fs-extra" "^10.1.0" + "html-minifier-terser" "^6.1.0" + "html-tags" "^3.2.0" + "html-webpack-plugin" "^5.5.0" "import-fresh" "^3.3.0" - "is-root" "^2.1.0" "leven" "^3.1.0" - "lodash" "^4.17.20" - "mini-css-extract-plugin" "^1.4.0" - "module-alias" "^2.2.2" - "nprogress" "^0.2.0" - "postcss" "^8.2.10" - "postcss-loader" "^5.2.0" - "prompts" "^2.4.0" - "react-dev-utils" "^11.0.1" - "react-error-overlay" "^6.0.9" - "react-helmet" "^6.1.0" - "react-loadable" "^5.5.0" + "lodash" "^4.17.21" + "mini-css-extract-plugin" "^2.6.1" + "postcss" "^8.4.14" + "postcss-loader" "^7.0.0" + "prompts" "^2.4.2" + "react-dev-utils" "^12.0.1" + "react-helmet-async" "^1.3.0" + "react-loadable" "npm:@docusaurus/react-loadable@5.5.2" "react-loadable-ssr-addon-v5-slorber" "^1.0.1" - "react-router" "^5.2.0" + "react-router" "^5.3.3" "react-router-config" "^5.1.1" - "react-router-dom" "^5.2.0" - "resolve-pathname" "^3.0.0" - "rtl-detect" "^1.0.2" - "semver" "^7.3.4" + "react-router-dom" "^5.3.3" + "rtl-detect" "^1.0.4" + "semver" "^7.3.7" "serve-handler" "^6.1.3" - "shelljs" "^0.8.4" - "std-env" "^2.2.1" - "strip-ansi" "^6.0.0" - "terser-webpack-plugin" "^5.1.1" - "tslib" "^2.1.0" + "shelljs" "^0.8.5" + "terser-webpack-plugin" "^5.3.3" + "tslib" "^2.4.0" "update-notifier" "^5.1.0" "url-loader" "^4.1.1" - "wait-on" "^5.2.1" - "webpack" "^5.28.0" - "webpack-bundle-analyzer" "^4.4.0" - "webpack-dev-server" "^3.11.2" - "webpack-merge" "^5.7.3" - "webpackbar" "^5.0.0-3" - -"@docusaurus/cssnano-preset@2.0.0-beta.0": - "integrity" "sha512-gqQHeQCDHZDd5NaiKZwDiyg75sBCqDyAsvmFukkDAty8xE7u9IhzbOQKvCAtwseuvzu2BNN41gnJ8bz7vZzQiw==" - "resolved" "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" - dependencies: - "cssnano-preset-advanced" "^5.0.0" - "postcss" "^8.2.10" - "postcss-sort-media-queries" "^3.8.9" - -"@docusaurus/mdx-loader@2.0.0-beta.0": - "integrity" "sha512-oQLS2ZeUnqw79CV37glglZpaYgFfA5Az5lT83m5tJfMUZjoK4ehG1XWBeUzWy8QQNI452yAID8jz8jihEQeCcw==" - "resolved" "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" - dependencies: - "@babel/parser" "^7.12.16" - "@babel/traverse" "^7.12.13" - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/utils" "2.0.0-beta.0" - "@mdx-js/mdx" "^1.6.21" - "@mdx-js/react" "^1.6.21" + "wait-on" "^6.0.1" + "webpack" "^5.73.0" + "webpack-bundle-analyzer" "^4.5.0" + "webpack-dev-server" "^4.9.3" + "webpack-merge" "^5.8.0" + "webpackbar" "^5.0.2" + +"@docusaurus/cssnano-preset@2.2.0": + "integrity" "sha512-mAAwCo4n66TMWBH1kXnHVZsakW9VAXJzTO4yZukuL3ro4F+JtkMwKfh42EG75K/J/YIFQG5I/Bzy0UH/hFxaTg==" + "resolved" "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "cssnano-preset-advanced" "^5.3.8" + "postcss" "^8.4.14" + "postcss-sort-media-queries" "^4.2.1" + "tslib" "^2.4.0" + +"@docusaurus/logger@2.2.0": + "integrity" "sha512-DF3j1cA5y2nNsu/vk8AG7xwpZu6f5MKkPPMaaIbgXLnWGfm6+wkOeW7kNrxnM95YOhKUkJUophX69nGUnLsm0A==" + "resolved" "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "chalk" "^4.1.2" + "tslib" "^2.4.0" + +"@docusaurus/mdx-loader@2.2.0": + "integrity" "sha512-X2bzo3T0jW0VhUU+XdQofcEeozXOTmKQMvc8tUnWRdTnCvj4XEcBVdC3g+/jftceluiwSTNRAX4VBOJdNt18jA==" + "resolved" "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@babel/parser" "^7.18.8" + "@babel/traverse" "^7.18.8" + "@docusaurus/logger" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@mdx-js/mdx" "^1.6.22" "escape-html" "^1.0.3" "file-loader" "^6.2.0" - "fs-extra" "^9.1.0" - "github-slugger" "^1.3.0" - "gray-matter" "^4.0.2" + "fs-extra" "^10.1.0" + "image-size" "^1.0.1" "mdast-util-to-string" "^2.0.0" - "remark-emoji" "^2.1.0" + "remark-emoji" "^2.2.0" "stringify-object" "^3.3.0" - "unist-util-visit" "^2.0.2" + "tslib" "^2.4.0" + "unified" "^9.2.2" + "unist-util-visit" "^2.0.3" "url-loader" "^4.1.1" - "webpack" "^5.28.0" - -"@docusaurus/plugin-content-blog@2.0.0-beta.0": - "integrity" "sha512-lz63i5k/23RJ3Rk/2fIsYAoD8Wua3b5b0AbH2JoOhQu1iAIQiV8m91Z3XALBSzA3nBtAOIweNI7yzWL+JFSTvw==" - "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" - dependencies: - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/mdx-loader" "2.0.0-beta.0" - "@docusaurus/types" "2.0.0-beta.0" - "@docusaurus/utils" "2.0.0-beta.0" - "@docusaurus/utils-validation" "2.0.0-beta.0" - "chalk" "^4.1.0" + "webpack" "^5.73.0" + +"@docusaurus/module-type-aliases@2.2.0": + "integrity" "sha512-wDGW4IHKoOr9YuJgy7uYuKWrDrSpsUSDHLZnWQYM9fN7D5EpSmYHjFruUpKWVyxLpD/Wh0rW8hYZwdjJIQUQCQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@docusaurus/react-loadable" "5.5.2" + "@docusaurus/types" "2.2.0" + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router-config" "*" + "@types/react-router-dom" "*" + "react-helmet-async" "*" + "react-loadable" "npm:@docusaurus/react-loadable@5.5.2" + +"@docusaurus/plugin-content-blog@2.2.0": + "integrity" "sha512-0mWBinEh0a5J2+8ZJXJXbrCk1tSTNf7Nm4tYAl5h2/xx+PvH/Bnu0V+7mMljYm/1QlDYALNIIaT/JcoZQFUN3w==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-common" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "cheerio" "^1.0.0-rc.12" "feed" "^4.2.2" - "fs-extra" "^9.1.0" - "globby" "^11.0.2" - "loader-utils" "^2.0.0" - "lodash" "^4.17.20" - "reading-time" "^1.3.0" - "remark-admonitions" "^1.2.1" - "tslib" "^2.1.0" - "webpack" "^5.28.0" - -"@docusaurus/plugin-content-docs@2.0.0-beta.0": - "integrity" "sha512-WdDQUh2rRCbfJswVc0vY9EaAspxgziqpVEZja8+BmQR/TZh7HuLplT6GJbiFbE4RvwM3+PwG/jHMPglYDK60kw==" - "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" - dependencies: - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/mdx-loader" "2.0.0-beta.0" - "@docusaurus/types" "2.0.0-beta.0" - "@docusaurus/utils" "2.0.0-beta.0" - "@docusaurus/utils-validation" "2.0.0-beta.0" - "chalk" "^4.1.0" - "combine-promises" "^1.1.0" - "execa" "^5.0.0" - "fs-extra" "^9.1.0" - "globby" "^11.0.2" - "import-fresh" "^3.2.2" - "js-yaml" "^4.0.0" - "loader-utils" "^1.2.3" - "lodash" "^4.17.20" - "remark-admonitions" "^1.2.1" - "shelljs" "^0.8.4" - "tslib" "^2.1.0" + "fs-extra" "^10.1.0" + "lodash" "^4.17.21" + "reading-time" "^1.5.0" + "tslib" "^2.4.0" + "unist-util-visit" "^2.0.3" "utility-types" "^3.10.0" - "webpack" "^5.28.0" - -"@docusaurus/plugin-content-pages@2.0.0-beta.0": - "integrity" "sha512-mk5LVVSvn+HJPKBaAs/Pceq/hTGxF2LVBvJEquuQz0NMAW3QdBWaYRRpOrL9CO8v+ygn5RuLslXsyZBsDNuhww==" - "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" - dependencies: - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/mdx-loader" "2.0.0-beta.0" - "@docusaurus/types" "2.0.0-beta.0" - "@docusaurus/utils" "2.0.0-beta.0" - "@docusaurus/utils-validation" "2.0.0-beta.0" - "globby" "^11.0.2" - "lodash" "^4.17.20" - "minimatch" "^3.0.4" - "remark-admonitions" "^1.2.1" - "slash" "^3.0.0" - "tslib" "^2.1.0" - "webpack" "^5.28.0" + "webpack" "^5.73.0" -"@docusaurus/plugin-debug@2.0.0-beta.0": - "integrity" "sha512-m75sZdF8Yccxfih3qfdQg9DucMTrYBnmeTA8GNmdVaK701Ip8t50d1pDJchtu0FSEh6vzVB9C6D2YD5YgVFp8A==" - "resolved" "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" +"@docusaurus/plugin-content-docs@2.2.0": + "integrity" "sha512-BOazBR0XjzsHE+2K1wpNxz5QZmrJgmm3+0Re0EVPYFGW8qndCWGNtXW/0lGKhecVPML8yyFeAmnUCIs7xM2wPw==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.2.0.tgz" + "version" "2.2.0" dependencies: - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/types" "2.0.0-beta.0" - "@docusaurus/utils" "2.0.0-beta.0" - "react-json-view" "^1.21.1" - "tslib" "^2.1.0" + "@docusaurus/core" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/module-type-aliases" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "@types/react-router-config" "^5.0.6" + "combine-promises" "^1.1.0" + "fs-extra" "^10.1.0" + "import-fresh" "^3.3.0" + "js-yaml" "^4.1.0" + "lodash" "^4.17.21" + "tslib" "^2.4.0" + "utility-types" "^3.10.0" + "webpack" "^5.73.0" -"@docusaurus/plugin-google-analytics@2.0.0-beta.0": - "integrity" "sha512-7lHrg1L+adc8VbiaLexa15i4fdq4MRPUTLMxRPAWz+QskhisW89Ryi2/gDmfMNqLblX84Qg2RASa+2gqO4wepw==" - "resolved" "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" +"@docusaurus/plugin-content-pages@2.2.0": + "integrity" "sha512-+OTK3FQHk5WMvdelz8v19PbEbx+CNT6VSpx7nVOvMNs5yJCKvmqBJBQ2ZSxROxhVDYn+CZOlmyrC56NSXzHf6g==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.2.0.tgz" + "version" "2.2.0" dependencies: - "@docusaurus/core" "2.0.0-beta.0" + "@docusaurus/core" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "fs-extra" "^10.1.0" + "tslib" "^2.4.0" + "webpack" "^5.73.0" + +"@docusaurus/plugin-debug@2.2.0": + "integrity" "sha512-p9vOep8+7OVl6r/NREEYxf4HMAjV8JMYJ7Bos5fCFO0Wyi9AZEo0sCTliRd7R8+dlJXZEgcngSdxAUo/Q+CJow==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "fs-extra" "^10.1.0" + "react-json-view" "^1.21.3" + "tslib" "^2.4.0" -"@docusaurus/plugin-google-gtag@2.0.0-beta.0": - "integrity" "sha512-V7zaYbhAMv0jexm5H/5sAnoM1GHibcn9QQk5UWC++x1kE0KRuLDZHV+9OyvW5wr0wWFajod/b88SpUpSMF5u+g==" - "resolved" "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" +"@docusaurus/plugin-google-analytics@2.2.0": + "integrity" "sha512-+eZVVxVeEnV5nVQJdey9ZsfyEVMls6VyWTIj8SmX0k5EbqGvnIfET+J2pYEuKQnDIHxy+syRMoRM6AHXdHYGIg==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.2.0.tgz" + "version" "2.2.0" dependencies: - "@docusaurus/core" "2.0.0-beta.0" + "@docusaurus/core" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "tslib" "^2.4.0" -"@docusaurus/plugin-sitemap@2.0.0-beta.0": - "integrity" "sha512-dvmk8Sr+6pBkiKDb7Rjdp0GeFDWPUlayoJWK3fN3g0Fno6uxFfYhNZyXJ+ObyCA7HoW5rzeBMiO+uAja19JXTg==" - "resolved" "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" +"@docusaurus/plugin-google-gtag@2.2.0": + "integrity" "sha512-6SOgczP/dYdkqUMGTRqgxAS1eTp6MnJDAQMy8VCF1QKbWZmlkx4agHDexihqmYyCujTYHqDAhm1hV26EET54NQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.2.0.tgz" + "version" "2.2.0" dependencies: - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/types" "2.0.0-beta.0" - "@docusaurus/utils" "2.0.0-beta.0" - "@docusaurus/utils-validation" "2.0.0-beta.0" - "fs-extra" "^9.1.0" - "sitemap" "^6.3.6" - "tslib" "^2.1.0" + "@docusaurus/core" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "tslib" "^2.4.0" -"@docusaurus/preset-classic@^2.0.0-beta.0": - "integrity" "sha512-cFpR0UaAeUt5qVx1bpidhlar6tiRNITIQlxP4bOVsjbxVTZhZ/cNuIz7C+2zFPCuKIflGXdTIQOrucPmd7z51Q==" - "resolved" "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" - dependencies: - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/plugin-content-blog" "2.0.0-beta.0" - "@docusaurus/plugin-content-docs" "2.0.0-beta.0" - "@docusaurus/plugin-content-pages" "2.0.0-beta.0" - "@docusaurus/plugin-debug" "2.0.0-beta.0" - "@docusaurus/plugin-google-analytics" "2.0.0-beta.0" - "@docusaurus/plugin-google-gtag" "2.0.0-beta.0" - "@docusaurus/plugin-sitemap" "2.0.0-beta.0" - "@docusaurus/theme-classic" "2.0.0-beta.0" - "@docusaurus/theme-search-algolia" "2.0.0-beta.0" - -"@docusaurus/react-loadable@5.5.0": - "integrity" "sha512-Ld/kwUE6yATIOTLq3JCsWiTa/drisajwKqBQ2Rw6IcT+sFsKfYek8F2jSH8f68AT73xX97UehduZeCSlnuCBIg==" - "resolved" "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.0.tgz" - "version" "5.5.0" +"@docusaurus/plugin-sitemap@2.2.0": + "integrity" "sha512-0jAmyRDN/aI265CbWZNZuQpFqiZuo+5otk2MylU9iVrz/4J7gSc+ZJ9cy4EHrEsW7PV8s1w18hIEsmcA1YgkKg==" + "resolved" "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-common" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "fs-extra" "^10.1.0" + "sitemap" "^7.1.1" + "tslib" "^2.4.0" + +"@docusaurus/preset-classic@^2.2.0": + "integrity" "sha512-yKIWPGNx7BT8v2wjFIWvYrS+nvN04W+UameSFf8lEiJk6pss0kL6SG2MRvyULiI3BDxH+tj6qe02ncpSPGwumg==" + "resolved" "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.2.0.tgz" + "version" "2.2.0" dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/plugin-content-blog" "2.2.0" + "@docusaurus/plugin-content-docs" "2.2.0" + "@docusaurus/plugin-content-pages" "2.2.0" + "@docusaurus/plugin-debug" "2.2.0" + "@docusaurus/plugin-google-analytics" "2.2.0" + "@docusaurus/plugin-google-gtag" "2.2.0" + "@docusaurus/plugin-sitemap" "2.2.0" + "@docusaurus/theme-classic" "2.2.0" + "@docusaurus/theme-common" "2.2.0" + "@docusaurus/theme-search-algolia" "2.2.0" + "@docusaurus/types" "2.2.0" + +"@docusaurus/react-loadable@5.5.2": + "integrity" "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz" + "version" "5.5.2" + dependencies: + "@types/react" "*" "prop-types" "^15.6.2" -"@docusaurus/theme-classic@2.0.0-beta.0": - "integrity" "sha512-cBNtwAyg3be7Gk41FazMtgyibAcfuYaGHhGHIDRsXfc/qp3RhbiGiei7tyh200QT0NgKZxiVQy/r4d0mtjC++Q==" - "resolved" "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" - dependencies: - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/plugin-content-blog" "2.0.0-beta.0" - "@docusaurus/plugin-content-docs" "2.0.0-beta.0" - "@docusaurus/plugin-content-pages" "2.0.0-beta.0" - "@docusaurus/theme-common" "2.0.0-beta.0" - "@docusaurus/types" "2.0.0-beta.0" - "@docusaurus/utils" "2.0.0-beta.0" - "@docusaurus/utils-validation" "2.0.0-beta.0" - "@mdx-js/mdx" "^1.6.21" - "@mdx-js/react" "^1.6.21" - "chalk" "^4.1.0" - "clsx" "^1.1.1" - "copy-text-to-clipboard" "^3.0.0" - "fs-extra" "^9.1.0" - "globby" "^11.0.2" - "infima" "0.2.0-alpha.23" - "lodash" "^4.17.20" - "parse-numeric-range" "^1.2.0" - "postcss" "^8.2.10" - "prism-react-renderer" "^1.1.1" - "prismjs" "^1.23.0" - "prop-types" "^15.7.2" - "react-router-dom" "^5.2.0" - "rtlcss" "^3.1.2" - -"@docusaurus/theme-common@2.0.0-beta.0": - "integrity" "sha512-2rcVmQpvbdAgnzTWuM7Bfpu+2TQm928bhlvxn226jQy7IYz8ySRlIode63HhCtpx03hpdMCkrK6HxhfEcvHjQg==" - "resolved" "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" - dependencies: - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/plugin-content-blog" "2.0.0-beta.0" - "@docusaurus/plugin-content-docs" "2.0.0-beta.0" - "@docusaurus/plugin-content-pages" "2.0.0-beta.0" - "@docusaurus/types" "2.0.0-beta.0" - "tslib" "^2.1.0" +"@docusaurus/theme-classic@2.2.0": + "integrity" "sha512-kjbg/qJPwZ6H1CU/i9d4l/LcFgnuzeiGgMQlt6yPqKo0SOJIBMPuz7Rnu3r/WWbZFPi//o8acclacOzmXdUUEg==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@docusaurus/core" "2.2.0" + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/module-type-aliases" "2.2.0" + "@docusaurus/plugin-content-blog" "2.2.0" + "@docusaurus/plugin-content-docs" "2.2.0" + "@docusaurus/plugin-content-pages" "2.2.0" + "@docusaurus/theme-common" "2.2.0" + "@docusaurus/theme-translations" "2.2.0" + "@docusaurus/types" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-common" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "@mdx-js/react" "^1.6.22" + "clsx" "^1.2.1" + "copy-text-to-clipboard" "^3.0.1" + "infima" "0.2.0-alpha.42" + "lodash" "^4.17.21" + "nprogress" "^0.2.0" + "postcss" "^8.4.14" + "prism-react-renderer" "^1.3.5" + "prismjs" "^1.28.0" + "react-router-dom" "^5.3.3" + "rtlcss" "^3.5.0" + "tslib" "^2.4.0" + "utility-types" "^3.10.0" -"@docusaurus/theme-search-algolia@^2.0.0-beta.0", "@docusaurus/theme-search-algolia@2.0.0-beta.0": - "integrity" "sha512-/GhgAm4yuwqTXWTsWnqpFYxpjTv+t45Wk8q/LmTVINa+A7b6jkMkch2lygagIt69/ufDm2Uw6eYhgrmF4DJqfQ==" - "resolved" "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" - dependencies: - "@docsearch/react" "^3.0.0-alpha.33" - "@docusaurus/core" "2.0.0-beta.0" - "@docusaurus/theme-common" "2.0.0-beta.0" - "@docusaurus/utils" "2.0.0-beta.0" - "@docusaurus/utils-validation" "2.0.0-beta.0" - "algoliasearch" "^4.8.4" - "algoliasearch-helper" "^3.3.4" - "clsx" "^1.1.1" - "eta" "^1.12.1" - "lodash" "^4.17.20" +"@docusaurus/theme-common@2.2.0": + "integrity" "sha512-R8BnDjYoN90DCL75gP7qYQfSjyitXuP9TdzgsKDmSFPNyrdE3twtPNa2dIN+h+p/pr+PagfxwWbd6dn722A1Dw==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@docusaurus/mdx-loader" "2.2.0" + "@docusaurus/module-type-aliases" "2.2.0" + "@docusaurus/plugin-content-blog" "2.2.0" + "@docusaurus/plugin-content-docs" "2.2.0" + "@docusaurus/plugin-content-pages" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router-config" "*" + "clsx" "^1.2.1" + "parse-numeric-range" "^1.3.0" + "prism-react-renderer" "^1.3.5" + "tslib" "^2.4.0" + "utility-types" "^3.10.0" + +"@docusaurus/theme-search-algolia@^2.2.0", "@docusaurus/theme-search-algolia@2.2.0": + "integrity" "sha512-2h38B0tqlxgR2FZ9LpAkGrpDWVdXZ7vltfmTdX+4RsDs3A7khiNsmZB+x/x6sA4+G2V2CvrsPMlsYBy5X+cY1w==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@docsearch/react" "^3.1.1" + "@docusaurus/core" "2.2.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/plugin-content-docs" "2.2.0" + "@docusaurus/theme-common" "2.2.0" + "@docusaurus/theme-translations" "2.2.0" + "@docusaurus/utils" "2.2.0" + "@docusaurus/utils-validation" "2.2.0" + "algoliasearch" "^4.13.1" + "algoliasearch-helper" "^3.10.0" + "clsx" "^1.2.1" + "eta" "^1.12.3" + "fs-extra" "^10.1.0" + "lodash" "^4.17.21" + "tslib" "^2.4.0" + "utility-types" "^3.10.0" -"@docusaurus/types@2.0.0-beta.0": - "integrity" "sha512-z9PI+GbtYwqTXnkX4/a/A6psDX2p8N2uWlN2f4ifrm8WY4WhR9yiTOh0uo0pIqqaUQQvkEq3o5hOXuXLECEs+w==" - "resolved" "https://registry.npmjs.org/@docusaurus/types/-/types-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" +"@docusaurus/theme-translations@2.2.0": + "integrity" "sha512-3T140AG11OjJrtKlY4pMZ5BzbGRDjNs2co5hJ6uYJG1bVWlhcaFGqkaZ5lCgKflaNHD7UHBHU9Ec5f69jTdd6w==" + "resolved" "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.2.0.tgz" + "version" "2.2.0" dependencies: + "fs-extra" "^10.1.0" + "tslib" "^2.4.0" + +"@docusaurus/types@*", "@docusaurus/types@2.2.0": + "integrity" "sha512-b6xxyoexfbRNRI8gjblzVOnLr4peCJhGbYGPpJ3LFqpi5nsFfoK4mmDLvWdeah0B7gmJeXabN7nQkFoqeSdmOw==" + "resolved" "https://registry.npmjs.org/@docusaurus/types/-/types-2.2.0.tgz" + "version" "2.2.0" + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" "commander" "^5.1.0" - "joi" "^17.4.0" - "querystring" "0.2.0" - "webpack" "^5.28.0" - "webpack-merge" "^5.7.3" + "joi" "^17.6.0" + "react-helmet-async" "^1.3.0" + "utility-types" "^3.10.0" + "webpack" "^5.73.0" + "webpack-merge" "^5.8.0" -"@docusaurus/utils-validation@2.0.0-beta.0": - "integrity" "sha512-ELl/FVJ6xBz35TisZ1NmJhjbiVXDeU++K531PEFPCPmwnQPh7S6hZXdPnR71/Kc3BmuN9X2ZkwGOqNKVfys2Bg==" - "resolved" "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" +"@docusaurus/utils-common@2.2.0": + "integrity" "sha512-qebnerHp+cyovdUseDQyYFvMW1n1nv61zGe5JJfoNQUnjKuApch3IVsz+/lZ9a38pId8kqehC1Ao2bW/s0ntDA==" + "resolved" "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.2.0.tgz" + "version" "2.2.0" dependencies: - "@docusaurus/utils" "2.0.0-beta.0" - "chalk" "^4.1.0" - "joi" "^17.4.0" - "tslib" "^2.1.0" + "tslib" "^2.4.0" -"@docusaurus/utils@2.0.0-beta.0": - "integrity" "sha512-bvrT1EQu0maavr0Hb/lke9jmpzgVL/9tn5VQtbyahf472eJFY0bQDExllDrHK+l784SUvucqX0iaQeg0q6ySUw==" - "resolved" "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.0.0-beta.0.tgz" - "version" "2.0.0-beta.0" +"@docusaurus/utils-validation@2.2.0": + "integrity" "sha512-I1hcsG3yoCkasOL5qQAYAfnmVoLei7apugT6m4crQjmDGxq+UkiRrq55UqmDDyZlac/6ax/JC0p+usZ6W4nVyg==" + "resolved" "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.2.0.tgz" + "version" "2.2.0" dependencies: - "@docusaurus/types" "2.0.0-beta.0" - "@types/github-slugger" "^1.3.0" - "chalk" "^4.1.0" - "escape-string-regexp" "^4.0.0" - "fs-extra" "^9.1.0" - "gray-matter" "^4.0.2" - "lodash" "^4.17.20" - "resolve-pathname" "^3.0.0" - "tslib" "^2.1.0" + "@docusaurus/logger" "2.2.0" + "@docusaurus/utils" "2.2.0" + "joi" "^17.6.0" + "js-yaml" "^4.1.0" + "tslib" "^2.4.0" -"@endiliey/static-site-generator-webpack-plugin@^4.0.0": - "integrity" "sha512-3MBqYCs30qk1OBRC697NqhGouYbs71D1B8hrk/AFJC6GwF2QaJOQZtA1JYAaGSe650sZ8r5ppRTtCRXepDWlng==" - "resolved" "https://registry.npmjs.org/@endiliey/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.0.tgz" - "version" "4.0.0" +"@docusaurus/utils@2.2.0": + "integrity" "sha512-oNk3cjvx7Tt1Lgh/aeZAmFpGV2pDr5nHKrBVx6hTkzGhrnMuQqLt6UPlQjdYQ3QHXwyF/ZtZMO1D5Pfi0lu7SA==" + "resolved" "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.2.0.tgz" + "version" "2.2.0" dependencies: - "bluebird" "^3.7.1" - "cheerio" "^0.22.0" - "eval" "^0.1.4" - "url" "^0.11.0" - "webpack-sources" "^1.4.3" + "@docusaurus/logger" "2.2.0" + "@svgr/webpack" "^6.2.1" + "file-loader" "^6.2.0" + "fs-extra" "^10.1.0" + "github-slugger" "^1.4.0" + "globby" "^11.1.0" + "gray-matter" "^4.0.3" + "js-yaml" "^4.1.0" + "lodash" "^4.17.21" + "micromatch" "^4.0.5" + "resolve-pathname" "^3.0.0" + "shelljs" "^0.8.5" + "tslib" "^2.4.0" + "url-loader" "^4.1.1" + "webpack" "^5.73.0" -"@eslint/eslintrc@^0.4.1": - "integrity" "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==" - "resolved" "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz" - "version" "0.4.1" +"@eslint/eslintrc@^0.4.3": + "integrity" "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==" + "resolved" "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz" + "version" "0.4.3" dependencies: "ajv" "^6.12.4" "debug" "^4.1.1" "espree" "^7.3.0" - "globals" "^12.1.0" + "globals" "^13.9.0" "ignore" "^4.0.6" "import-fresh" "^3.2.1" "js-yaml" "^3.13.1" @@ -1533,18 +1623,113 @@ "strip-json-comments" "^3.1.1" "@hapi/hoek@^9.0.0": - "integrity" "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==" - "resolved" "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz" - "version" "9.2.0" + "integrity" "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + "resolved" "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz" + "version" "9.3.0" "@hapi/topo@^5.0.0": - "integrity" "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==" - "resolved" "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz" - "version" "5.0.0" + "integrity" "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==" + "resolved" "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz" + "version" "5.1.0" dependencies: "@hapi/hoek" "^9.0.0" -"@mdx-js/mdx@^1.6.21": +"@humanwhocodes/config-array@^0.5.0": + "integrity" "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==" + "resolved" "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz" + "version" "0.5.0" + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + "debug" "^4.1.1" + "minimatch" "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.0": + "integrity" "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + "resolved" "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + "version" "1.2.1" + +"@jest/schemas@^29.0.0": + "integrity" "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==" + "resolved" "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz" + "version" "29.0.0" + dependencies: + "@sinclair/typebox" "^0.24.1" + +"@jest/types@^29.3.1": + "integrity" "sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==" + "resolved" "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz" + "version" "29.3.1" + dependencies: + "@jest/schemas" "^29.0.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + "chalk" "^4.0.0" + +"@jridgewell/gen-mapping@^0.1.0": + "integrity" "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==" + "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" + "version" "0.1.1" + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.0": + "integrity" "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==" + "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" + "version" "0.3.2" + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/gen-mapping@^0.3.2": + "integrity" "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==" + "resolved" "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" + "version" "0.3.2" + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + "integrity" "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + "resolved" "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" + "version" "3.1.0" + +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + "integrity" "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + "resolved" "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + "version" "1.1.2" + +"@jridgewell/source-map@^0.3.2": + "integrity" "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==" + "resolved" "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz" + "version" "0.3.2" + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@1.4.14": + "integrity" "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + "resolved" "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + "version" "1.4.14" + +"@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": + "integrity" "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==" + "resolved" "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" + "version" "0.3.17" + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@leichtgewicht/ip-codec@^2.0.1": + "integrity" "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + "resolved" "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz" + "version" "2.0.4" + +"@mdx-js/mdx@^1.6.22": "integrity" "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==" "resolved" "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz" "version" "1.6.22" @@ -1569,7 +1754,7 @@ "unist-builder" "2.0.3" "unist-util-visit" "2.0.3" -"@mdx-js/react@^1.6.21": +"@mdx-js/react@^1.6.22": "integrity" "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==" "resolved" "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz" "version" "1.6.22" @@ -1579,36 +1764,36 @@ "resolved" "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz" "version" "1.6.22" -"@nodelib/fs.scandir@2.1.4": - "integrity" "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==" - "resolved" "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz" - "version" "2.1.4" +"@nodelib/fs.scandir@2.1.5": + "integrity" "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + "version" "2.1.5" dependencies: - "@nodelib/fs.stat" "2.0.4" + "@nodelib/fs.stat" "2.0.5" "run-parallel" "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.4": - "integrity" "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==" - "resolved" "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz" - "version" "2.0.4" +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + "integrity" "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + "version" "2.0.5" "@nodelib/fs.walk@^1.2.3": - "integrity" "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==" - "resolved" "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz" - "version" "1.2.6" + "integrity" "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==" + "resolved" "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + "version" "1.2.8" dependencies: - "@nodelib/fs.scandir" "2.1.4" + "@nodelib/fs.scandir" "2.1.5" "fastq" "^1.6.0" -"@polka/url@^1.0.0-next.9": - "integrity" "sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ==" - "resolved" "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.12.tgz" - "version" "1.0.0-next.12" +"@polka/url@^1.0.0-next.20": + "integrity" "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==" + "resolved" "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz" + "version" "1.0.0-next.21" -"@sideway/address@^4.1.0": - "integrity" "sha512-idTz8ibqWFrPU8kMirL0CoPH/A29XOzzAzpyN3zQ4kAWnzmNfFmRaoMNN6VI8ske5M73HZyhIaW4OuSFIdM4oA==" - "resolved" "https://registry.npmjs.org/@sideway/address/-/address-4.1.2.tgz" - "version" "4.1.2" +"@sideway/address@^4.1.3": + "integrity" "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==" + "resolved" "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz" + "version" "4.1.4" dependencies: "@hapi/hoek" "^9.0.0" @@ -1622,113 +1807,130 @@ "resolved" "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz" "version" "2.0.0" +"@sinclair/typebox@^0.24.1": + "integrity" "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "resolved" "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz" + "version" "0.24.51" + "@sindresorhus/is@^0.14.0": "integrity" "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" "resolved" "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz" "version" "0.14.0" -"@svgr/babel-plugin-add-jsx-attribute@^5.4.0": - "integrity" "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" - "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz" - "version" "5.4.0" - -"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": - "integrity" "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" - "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz" - "version" "5.4.0" - -"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": - "integrity" "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" - "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz" - "version" "5.0.1" - -"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": - "integrity" "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" - "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz" - "version" "5.0.1" +"@slorber/static-site-generator-webpack-plugin@^4.0.7": + "integrity" "sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA==" + "resolved" "https://registry.npmjs.org/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz" + "version" "4.0.7" + dependencies: + "eval" "^0.1.8" + "p-map" "^4.0.0" + "webpack-sources" "^3.2.2" -"@svgr/babel-plugin-svg-dynamic-title@^5.4.0": - "integrity" "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" - "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz" - "version" "5.4.0" +"@svgr/babel-plugin-add-jsx-attribute@^6.5.1": + "integrity" "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz" + "version" "6.5.1" -"@svgr/babel-plugin-svg-em-dimensions@^5.4.0": - "integrity" "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" - "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz" - "version" "5.4.0" - -"@svgr/babel-plugin-transform-react-native-svg@^5.4.0": - "integrity" "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" - "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz" - "version" "5.4.0" +"@svgr/babel-plugin-remove-jsx-attribute@*": + "integrity" "sha512-8zYdkym7qNyfXpWvu4yq46k41pyNM9SOstoWhKlm+IfdCE1DdnRKeMUPsWIEO/DEkaWxJ8T9esNdG3QwQ93jBA==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.5.0.tgz" + "version" "6.5.0" -"@svgr/babel-plugin-transform-svg-component@^5.5.0": - "integrity" "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" - "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz" - "version" "5.5.0" +"@svgr/babel-plugin-remove-jsx-empty-expression@*": + "integrity" "sha512-NFdxMq3xA42Kb1UbzCVxplUc0iqSyM9X8kopImvFnB+uSDdzIHOdbs1op8ofAvVRtbg4oZiyRl3fTYeKcOe9Iw==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.5.0.tgz" + "version" "6.5.0" -"@svgr/babel-preset@^5.5.0": - "integrity" "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==" - "resolved" "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz" - "version" "5.5.0" - dependencies: - "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0" - "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0" - "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1" - "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1" - "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0" - "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0" - "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" - "@svgr/babel-plugin-transform-svg-component" "^5.5.0" - -"@svgr/core@^5.5.0": - "integrity" "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==" - "resolved" "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz" - "version" "5.5.0" - dependencies: - "@svgr/plugin-jsx" "^5.5.0" +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.5.1": + "integrity" "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz" + "version" "6.5.1" + +"@svgr/babel-plugin-svg-dynamic-title@^6.5.1": + "integrity" "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz" + "version" "6.5.1" + +"@svgr/babel-plugin-svg-em-dimensions@^6.5.1": + "integrity" "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz" + "version" "6.5.1" + +"@svgr/babel-plugin-transform-react-native-svg@^6.5.1": + "integrity" "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz" + "version" "6.5.1" + +"@svgr/babel-plugin-transform-svg-component@^6.5.1": + "integrity" "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==" + "resolved" "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz" + "version" "6.5.1" + +"@svgr/babel-preset@^6.5.1": + "integrity" "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==" + "resolved" "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz" + "version" "6.5.1" + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.5.1" + "@svgr/babel-plugin-remove-jsx-attribute" "*" + "@svgr/babel-plugin-remove-jsx-empty-expression" "*" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.5.1" + "@svgr/babel-plugin-svg-dynamic-title" "^6.5.1" + "@svgr/babel-plugin-svg-em-dimensions" "^6.5.1" + "@svgr/babel-plugin-transform-react-native-svg" "^6.5.1" + "@svgr/babel-plugin-transform-svg-component" "^6.5.1" + +"@svgr/core@*", "@svgr/core@^6.0.0", "@svgr/core@^6.5.1": + "integrity" "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==" + "resolved" "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz" + "version" "6.5.1" + dependencies: + "@babel/core" "^7.19.6" + "@svgr/babel-preset" "^6.5.1" + "@svgr/plugin-jsx" "^6.5.1" "camelcase" "^6.2.0" - "cosmiconfig" "^7.0.0" + "cosmiconfig" "^7.0.1" -"@svgr/hast-util-to-babel-ast@^5.5.0": - "integrity" "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==" - "resolved" "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz" - "version" "5.5.0" +"@svgr/hast-util-to-babel-ast@^6.5.1": + "integrity" "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==" + "resolved" "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz" + "version" "6.5.1" dependencies: - "@babel/types" "^7.12.6" + "@babel/types" "^7.20.0" + "entities" "^4.4.0" -"@svgr/plugin-jsx@^5.5.0": - "integrity" "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==" - "resolved" "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz" - "version" "5.5.0" +"@svgr/plugin-jsx@^6.5.1": + "integrity" "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==" + "resolved" "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz" + "version" "6.5.1" dependencies: - "@babel/core" "^7.12.3" - "@svgr/babel-preset" "^5.5.0" - "@svgr/hast-util-to-babel-ast" "^5.5.0" - "svg-parser" "^2.0.2" + "@babel/core" "^7.19.6" + "@svgr/babel-preset" "^6.5.1" + "@svgr/hast-util-to-babel-ast" "^6.5.1" + "svg-parser" "^2.0.4" -"@svgr/plugin-svgo@^5.5.0": - "integrity" "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==" - "resolved" "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz" - "version" "5.5.0" +"@svgr/plugin-svgo@^6.5.1": + "integrity" "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==" + "resolved" "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz" + "version" "6.5.1" dependencies: - "cosmiconfig" "^7.0.0" + "cosmiconfig" "^7.0.1" "deepmerge" "^4.2.2" - "svgo" "^1.2.2" - -"@svgr/webpack@^5.5.0": - "integrity" "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==" - "resolved" "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz" - "version" "5.5.0" - dependencies: - "@babel/core" "^7.12.3" - "@babel/plugin-transform-react-constant-elements" "^7.12.1" - "@babel/preset-env" "^7.12.1" - "@babel/preset-react" "^7.12.5" - "@svgr/core" "^5.5.0" - "@svgr/plugin-jsx" "^5.5.0" - "@svgr/plugin-svgo" "^5.5.0" - "loader-utils" "^2.0.0" + "svgo" "^2.8.0" + +"@svgr/webpack@^6.2.1": + "integrity" "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==" + "resolved" "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz" + "version" "6.5.1" + dependencies: + "@babel/core" "^7.19.6" + "@babel/plugin-transform-react-constant-elements" "^7.18.12" + "@babel/preset-env" "^7.19.4" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@svgr/core" "^6.5.1" + "@svgr/plugin-jsx" "^6.5.1" + "@svgr/plugin-svgo" "^6.5.1" "@szmarczak/http-timer@^1.1.2": "integrity" "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==" @@ -1737,88 +1939,155 @@ dependencies: "defer-to-connect" "^1.0.1" -"@trysound/sax@0.1.1": - "integrity" "sha512-Z6DoceYb/1xSg5+e+ZlPZ9v0N16ZvZ+wYMraFue4HYrE4ttONKtsvruIRf6t9TBR0YvSOfi1hUU0fJfBLCDYow==" - "resolved" "https://registry.npmjs.org/@trysound/sax/-/sax-0.1.1.tgz" - "version" "0.1.1" +"@trysound/sax@0.2.0": + "integrity" "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + "resolved" "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz" + "version" "0.2.0" + +"@types/body-parser@*": + "integrity" "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==" + "resolved" "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" + "version" "1.19.2" + dependencies: + "@types/connect" "*" + "@types/node" "*" -"@types/eslint-scope@^3.7.0": - "integrity" "sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==" - "resolved" "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.0.tgz" - "version" "3.7.0" +"@types/bonjour@^3.5.9": + "integrity" "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==" + "resolved" "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz" + "version" "3.5.10" + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + "integrity" "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==" + "resolved" "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz" + "version" "1.3.5" + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + "integrity" "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==" + "resolved" "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" + "version" "3.4.35" + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + "integrity" "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==" + "resolved" "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz" + "version" "3.7.4" dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": - "integrity" "sha512-kUEPnMKrqbtpCq/KTaGFFKAcz6Ethm2EjCoKIDaCmfRBWLbFuTcOJfTlorwbnboXBzahqWLgUp1BQeKHiJzPUQ==" - "resolved" "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.10.tgz" - "version" "7.2.10" + "integrity" "sha512-ehM7cCt2RSFs42mb+lcmhFT9ouIlV92PuaeRGn8N8c98oMjG4Z5pJHA9b1QiCcuqnbPSHcyfiD3mlhqMaHsQIw==" + "resolved" "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.7.tgz" + "version" "8.4.7" dependencies: "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^0.0.47": - "integrity" "sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==" - "resolved" "https://registry.npmjs.org/@types/estree/-/estree-0.0.47.tgz" - "version" "0.0.47" - -"@types/github-slugger@^1.3.0": - "integrity" "sha512-J/rMZa7RqiH/rT29TEVZO4nBoDP9XJOjnbbIofg7GQKs4JIduEO3WLpte+6WeUz/TcrXKlY+bM7FYrp8yFB+3g==" - "resolved" "https://registry.npmjs.org/@types/github-slugger/-/github-slugger-1.3.0.tgz" - "version" "1.3.0" +"@types/estree@*", "@types/estree@^0.0.51": + "integrity" "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + "resolved" "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz" + "version" "0.0.51" -"@types/glob@^7.1.1": - "integrity" "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==" - "resolved" "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz" - "version" "7.1.3" +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.31": + "integrity" "sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==" + "resolved" "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz" + "version" "4.17.32" dependencies: - "@types/minimatch" "*" "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.13": + "integrity" "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==" + "resolved" "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz" + "version" "4.17.15" + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.31" + "@types/qs" "*" + "@types/serve-static" "*" "@types/hast@^2.0.0": - "integrity" "sha512-viwwrB+6xGzw+G1eWpF9geV3fnsDgXqHG+cqgiHrvQfDUW5hzhCyV7Sy3UJxhfRFBsgky2SSW33qi/YrIkjX5Q==" - "resolved" "https://registry.npmjs.org/@types/hast/-/hast-2.3.1.tgz" - "version" "2.3.1" + "integrity" "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==" + "resolved" "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz" + "version" "2.3.4" dependencies: "@types/unist" "*" -"@types/html-minifier-terser@^5.0.0": - "integrity" "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==" - "resolved" "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz" - "version" "5.1.1" +"@types/history@^4.7.11": + "integrity" "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" + "resolved" "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz" + "version" "4.7.11" + +"@types/html-minifier-terser@^6.0.0": + "integrity" "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + "resolved" "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + "version" "6.1.0" + +"@types/http-proxy@^1.17.8": + "integrity" "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==" + "resolved" "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz" + "version" "1.17.9" + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + "integrity" "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + "resolved" "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" + "version" "2.0.4" + +"@types/istanbul-lib-report@*": + "integrity" "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==" + "resolved" "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + "version" "3.0.0" + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + "integrity" "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==" + "resolved" "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "@types/istanbul-lib-report" "*" -"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": - "integrity" "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==" - "resolved" "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz" - "version" "7.0.7" +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + "integrity" "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" + "resolved" "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" + "version" "7.0.11" "@types/katex@^0.11.0": - "integrity" "sha512-27BfE8zASRLYfSBNMk5/+KIjr2CBBrH0i5lhsO04fca4TGirIIMay73v3zNkzqmsaeIa/Mi5kejWDcxPLAmkvA==" - "resolved" "https://registry.npmjs.org/@types/katex/-/katex-0.11.0.tgz" - "version" "0.11.0" + "integrity" "sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg==" + "resolved" "https://registry.npmjs.org/@types/katex/-/katex-0.11.1.tgz" + "version" "0.11.1" "@types/mdast@^3.0.0": - "integrity" "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==" - "resolved" "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz" - "version" "3.0.3" + "integrity" "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==" + "resolved" "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz" + "version" "3.0.10" dependencies: "@types/unist" "*" -"@types/minimatch@*": - "integrity" "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==" - "resolved" "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz" - "version" "3.0.4" +"@types/mime@*": + "integrity" "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + "resolved" "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz" + "version" "3.0.1" "@types/node@*": - "integrity" "sha512-/WbxFeBU+0F79z9RdEOXH4CsDga+ibi5M8uEYr91u3CkT/pdWcV8MCook+4wDPnZBexRdwWS+PiVZ2xJviAzcQ==" - "resolved" "https://registry.npmjs.org/@types/node/-/node-15.0.3.tgz" - "version" "15.0.3" + "integrity" "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz" + "version" "18.11.3" -"@types/node@^14.14.28": - "integrity" "sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==" - "resolved" "https://registry.npmjs.org/@types/node/-/node-14.14.45.tgz" - "version" "14.14.45" +"@types/node@^17.0.5": + "integrity" "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz" + "version" "17.0.45" "@types/parse-json@^4.0.0": "integrity" "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" @@ -1831,160 +2100,237 @@ "version" "5.0.3" "@types/prop-types@*": - "integrity" "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" - "resolved" "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz" - "version" "15.7.3" + "integrity" "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "resolved" "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz" + "version" "15.7.5" + +"@types/qs@*": + "integrity" "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "resolved" "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" + "version" "6.9.7" + +"@types/range-parser@*": + "integrity" "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "resolved" "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" + "version" "1.2.4" + +"@types/react-router-config@*", "@types/react-router-config@^5.0.6": + "integrity" "sha512-db1mx37a1EJDf1XeX8jJN7R3PZABmJQXR8r28yUjVMFSjkmnQo6X6pOEEmNl+Tp2gYQOGPdYbFIipBtdElZ3Yg==" + "resolved" "https://registry.npmjs.org/@types/react-router-config/-/react-router-config-5.0.6.tgz" + "version" "5.0.6" + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router-dom@*": + "integrity" "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==" + "resolved" "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz" + "version" "5.3.3" + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "*" -"@types/q@^1.5.1": - "integrity" "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==" - "resolved" "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz" - "version" "1.5.4" +"@types/react-router@*": + "integrity" "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==" + "resolved" "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz" + "version" "5.1.20" + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" -"@types/react@>= 16.8.0 < 18.0.0": - "integrity" "sha512-bj4biDB9ZJmGAYTWSKJly6bMr4BLUiBrx9ujiJEoP9XIDY9CTaPGxE5QWN/1WjpPLzYF7/jRNnV2nNxNe970sw==" - "resolved" "https://registry.npmjs.org/@types/react/-/react-17.0.5.tgz" - "version" "17.0.5" +"@types/react@*", "@types/react@>= 16.8.0 < 19.0.0": + "integrity" "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==" + "resolved" "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz" + "version" "18.0.26" dependencies: "@types/prop-types" "*" "@types/scheduler" "*" "csstype" "^3.0.2" +"@types/retry@0.12.0": + "integrity" "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + "resolved" "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz" + "version" "0.12.0" + "@types/sax@^1.2.1": - "integrity" "sha512-dqYdvN7Sbw8QT/0Ci5rhjE4/iCMJEM0Y9rHpCu+gGXD9Lwbz28t6HI2yegsB6BoV1sShRMU6lAmAcgRjmFy7LA==" - "resolved" "https://registry.npmjs.org/@types/sax/-/sax-1.2.1.tgz" - "version" "1.2.1" + "integrity" "sha512-pSAff4IAxJjfAXUG6tFkO7dsSbTmf8CtUpfhhZ5VhkRpC4628tJhh3+V6H1E+/Gs9piSzYKT5yzHO5M4GG9jkw==" + "resolved" "https://registry.npmjs.org/@types/sax/-/sax-1.2.4.tgz" + "version" "1.2.4" dependencies: "@types/node" "*" "@types/scheduler@*": - "integrity" "sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==" - "resolved" "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz" - "version" "0.16.1" - -"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": - "integrity" "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==" - "resolved" "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz" - "version" "2.0.3" + "integrity" "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + "resolved" "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz" + "version" "0.16.2" -"@webassemblyjs/ast@1.11.0": - "integrity" "sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.0.tgz" - "version" "1.11.0" +"@types/serve-index@^1.9.1": + "integrity" "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==" + "resolved" "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz" + "version" "1.9.1" dependencies: - "@webassemblyjs/helper-numbers" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" + "@types/express" "*" -"@webassemblyjs/floating-point-hex-parser@1.11.0": - "integrity" "sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz" - "version" "1.11.0" +"@types/serve-static@*", "@types/serve-static@^1.13.10": + "integrity" "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==" + "resolved" "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz" + "version" "1.15.0" + dependencies: + "@types/mime" "*" + "@types/node" "*" -"@webassemblyjs/helper-api-error@1.11.0": - "integrity" "sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz" - "version" "1.11.0" +"@types/sockjs@^0.3.33": + "integrity" "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==" + "resolved" "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz" + "version" "0.3.33" + dependencies: + "@types/node" "*" -"@webassemblyjs/helper-buffer@1.11.0": - "integrity" "sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz" - "version" "1.11.0" +"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": + "integrity" "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" + "resolved" "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz" + "version" "2.0.6" -"@webassemblyjs/helper-numbers@1.11.0": - "integrity" "sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz" - "version" "1.11.0" +"@types/ws@^8.5.1": + "integrity" "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==" + "resolved" "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz" + "version" "8.5.4" dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.0" - "@webassemblyjs/helper-api-error" "1.11.0" - "@xtuc/long" "4.2.2" + "@types/node" "*" -"@webassemblyjs/helper-wasm-bytecode@1.11.0": - "integrity" "sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz" - "version" "1.11.0" +"@types/yargs-parser@*": + "integrity" "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + "resolved" "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz" + "version" "21.0.0" -"@webassemblyjs/helper-wasm-section@1.11.0": - "integrity" "sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz" - "version" "1.11.0" +"@types/yargs@^17.0.8": + "integrity" "sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==" + "resolved" "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.19.tgz" + "version" "17.0.19" dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-buffer" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" - "@webassemblyjs/wasm-gen" "1.11.0" + "@types/yargs-parser" "*" -"@webassemblyjs/ieee754@1.11.0": - "integrity" "sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz" - "version" "1.11.0" +"@webassemblyjs/ast@1.11.1": + "integrity" "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz" + "version" "1.11.1" dependencies: - "@xtuc/ieee754" "^1.2.0" + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" -"@webassemblyjs/leb128@1.11.0": - "integrity" "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz" - "version" "1.11.0" +"@webassemblyjs/floating-point-hex-parser@1.11.1": + "integrity" "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz" + "version" "1.11.1" + +"@webassemblyjs/helper-api-error@1.11.1": + "integrity" "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz" + "version" "1.11.1" + +"@webassemblyjs/helper-buffer@1.11.1": + "integrity" "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz" + "version" "1.11.1" + +"@webassemblyjs/helper-numbers@1.11.1": + "integrity" "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz" + "version" "1.11.1" dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.11.0": - "integrity" "sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.0.tgz" - "version" "1.11.0" +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + "integrity" "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz" + "version" "1.11.1" -"@webassemblyjs/wasm-edit@1.11.0": - "integrity" "sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz" - "version" "1.11.0" - dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-buffer" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" - "@webassemblyjs/helper-wasm-section" "1.11.0" - "@webassemblyjs/wasm-gen" "1.11.0" - "@webassemblyjs/wasm-opt" "1.11.0" - "@webassemblyjs/wasm-parser" "1.11.0" - "@webassemblyjs/wast-printer" "1.11.0" - -"@webassemblyjs/wasm-gen@1.11.0": - "integrity" "sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz" - "version" "1.11.0" +"@webassemblyjs/helper-wasm-section@1.11.1": + "integrity" "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz" + "version" "1.11.1" dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" - "@webassemblyjs/ieee754" "1.11.0" - "@webassemblyjs/leb128" "1.11.0" - "@webassemblyjs/utf8" "1.11.0" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" -"@webassemblyjs/wasm-opt@1.11.0": - "integrity" "sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz" - "version" "1.11.0" +"@webassemblyjs/ieee754@1.11.1": + "integrity" "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz" + "version" "1.11.1" dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-buffer" "1.11.0" - "@webassemblyjs/wasm-gen" "1.11.0" - "@webassemblyjs/wasm-parser" "1.11.0" + "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/wasm-parser@1.11.0": - "integrity" "sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz" - "version" "1.11.0" +"@webassemblyjs/leb128@1.11.1": + "integrity" "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz" + "version" "1.11.1" dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-api-error" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" - "@webassemblyjs/ieee754" "1.11.0" - "@webassemblyjs/leb128" "1.11.0" - "@webassemblyjs/utf8" "1.11.0" + "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.11.0": - "integrity" "sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==" - "resolved" "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz" - "version" "1.11.0" - dependencies: - "@webassemblyjs/ast" "1.11.0" +"@webassemblyjs/utf8@1.11.1": + "integrity" "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz" + "version" "1.11.1" + +"@webassemblyjs/wasm-edit@1.11.1": + "integrity" "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + "integrity" "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + "integrity" "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + "integrity" "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + "integrity" "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==" + "resolved" "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz" + "version" "1.11.1" + dependencies: + "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -1997,23 +2343,28 @@ "resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" "version" "4.2.2" -"accepts@~1.3.4", "accepts@~1.3.5", "accepts@~1.3.7": - "integrity" "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==" - "resolved" "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" - "version" "1.3.7" +"accepts@~1.3.4", "accepts@~1.3.5", "accepts@~1.3.8": + "integrity" "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==" + "resolved" "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" + "version" "1.3.8" dependencies: - "mime-types" "~2.1.24" - "negotiator" "0.6.2" + "mime-types" "~2.1.34" + "negotiator" "0.6.3" + +"acorn-import-assertions@^1.7.6": + "integrity" "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" + "resolved" "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz" + "version" "1.8.0" "acorn-jsx@^5.3.1": - "integrity" "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==" - "resolved" "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz" - "version" "5.3.1" + "integrity" "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" + "resolved" "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + "version" "5.3.2" "acorn-walk@^8.0.0": - "integrity" "sha512-mjmzmv12YIG/G8JQdQuz2MUDShEJ6teYpT5bmWA4q7iwoGen8xtt3twF3OvzIUl+Q06aWIjvnwQUKvQ6TtMRjg==" - "resolved" "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.0.tgz" - "version" "8.1.0" + "integrity" "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + "resolved" "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" + "version" "8.2.0" "acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^7.4.0": "integrity" "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" @@ -2021,19 +2372,24 @@ "version" "7.4.1" "acorn@^8.0.4": - "integrity" "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==" - "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz" - "version" "8.2.4" - -"acorn@^8.2.1": - "integrity" "sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==" - "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.2.4.tgz" - "version" "8.2.4" - -"address@^1.0.1", "address@1.1.2": - "integrity" "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==" - "resolved" "https://registry.npmjs.org/address/-/address-1.1.2.tgz" - "version" "1.1.2" + "integrity" "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz" + "version" "8.8.0" + +"acorn@^8.5.0": + "integrity" "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz" + "version" "8.8.1" + +"acorn@^8", "acorn@^8.7.1": + "integrity" "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==" + "resolved" "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz" + "version" "8.8.0" + +"address@^1.0.1", "address@^1.1.2": + "integrity" "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==" + "resolved" "https://registry.npmjs.org/address/-/address-1.2.1.tgz" + "version" "1.2.1" "aggregate-error@^3.0.0": "integrity" "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==" @@ -2043,17 +2399,26 @@ "clean-stack" "^2.0.0" "indent-string" "^4.0.0" -"ajv-errors@^1.0.0": - "integrity" "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" - "resolved" "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz" - "version" "1.0.1" +"ajv-formats@^2.1.1": + "integrity" "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==" + "resolved" "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" + "version" "2.1.1" + dependencies: + "ajv" "^8.0.0" -"ajv-keywords@^3.1.0", "ajv-keywords@^3.5.2": +"ajv-keywords@^3.4.1", "ajv-keywords@^3.5.2": "integrity" "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" "resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" "version" "3.5.2" -"ajv@^6.1.0", "ajv@^6.10.0", "ajv@^6.12.4", "ajv@^6.12.5", "ajv@^6.9.1", "ajv@>=5.0.0": +"ajv-keywords@^5.0.0": + "integrity" "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==" + "resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz" + "version" "5.1.0" + dependencies: + "fast-deep-equal" "^3.1.3" + +"ajv@^6.10.0", "ajv@^6.12.2", "ajv@^6.12.4", "ajv@^6.12.5", "ajv@^6.9.1": "integrity" "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==" "resolved" "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" "version" "6.12.6" @@ -2063,98 +2428,89 @@ "json-schema-traverse" "^0.4.1" "uri-js" "^4.2.2" -"ajv@^8.0.1": - "integrity" "sha512-RYE7B5An83d7eWnDR8kbdaIFqmKCNsP16ay1hDbJEU+sa0e3H9SebskCt0Uufem6cfAVu7Col6ubcn/W+Sm8/Q==" - "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.3.0.tgz" - "version" "8.3.0" +"ajv@^8.0.0": + "integrity" "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + "version" "8.12.0" dependencies: "fast-deep-equal" "^3.1.1" "json-schema-traverse" "^1.0.0" "require-from-string" "^2.0.2" "uri-js" "^4.2.2" -"algoliasearch-helper@^3.3.4": - "integrity" "sha512-OjyVLjykaYKCMxxRMZNiwLp8CS310E0qAeIY2NaublcmLAh8/SL19+zYHp7XCLtMem2ZXwl3ywMiA32O9jszuw==" - "resolved" "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.4.4.tgz" - "version" "3.4.4" - dependencies: - "events" "^1.1.1" - -"algoliasearch@^4.0.0", "algoliasearch@^4.5.1", "algoliasearch@^4.8.4", "algoliasearch@>= 3.1 < 5": - "integrity" "sha512-EeJUYXzBEhZSsL6tXc3hseLBCtlNLa1MZ4mlMK6EeX38yRjY5vgnFcNNml6uUhlOjvheKxgkKRpPWkxgL8Cqkg==" - "resolved" "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.9.1.tgz" - "version" "4.9.1" - dependencies: - "@algolia/cache-browser-local-storage" "4.9.1" - "@algolia/cache-common" "4.9.1" - "@algolia/cache-in-memory" "4.9.1" - "@algolia/client-account" "4.9.1" - "@algolia/client-analytics" "4.9.1" - "@algolia/client-common" "4.9.1" - "@algolia/client-recommendation" "4.9.1" - "@algolia/client-search" "4.9.1" - "@algolia/logger-common" "4.9.1" - "@algolia/logger-console" "4.9.1" - "@algolia/requester-browser-xhr" "4.9.1" - "@algolia/requester-common" "4.9.1" - "@algolia/requester-node-http" "4.9.1" - "@algolia/transporter" "4.9.1" - -"alphanum-sort@^1.0.2": - "integrity" "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" - "resolved" "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz" - "version" "1.0.2" - -"ansi-align@^3.0.0": - "integrity" "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==" - "resolved" "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz" - "version" "3.0.0" +"ajv@^8.0.1": + "integrity" "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz" + "version" "8.11.0" dependencies: - "string-width" "^3.0.0" - -"ansi-colors@^3.0.0": - "integrity" "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" - "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz" - "version" "3.2.4" - -"ansi-colors@^4.1.1": - "integrity" "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" - "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" - "version" "4.1.1" + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" -"ansi-escapes@^4.3.1": - "integrity" "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==" - "resolved" "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" - "version" "4.3.2" +"ajv@^8.8.0", "ajv@^8.8.2": + "integrity" "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==" + "resolved" "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + "version" "8.12.0" dependencies: - "type-fest" "^0.21.3" - -"ansi-html@0.0.7": - "integrity" "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" - "resolved" "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz" - "version" "0.0.7" + "fast-deep-equal" "^3.1.1" + "json-schema-traverse" "^1.0.0" + "require-from-string" "^2.0.2" + "uri-js" "^4.2.2" -"ansi-regex@^2.0.0": - "integrity" "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" - "version" "2.1.1" +"algoliasearch-helper@^3.10.0": + "integrity" "sha512-mvsPN3eK4E0bZG0/WlWJjeqe/bUD2KOEVOl0GyL/TGXn6wcpZU8NOuztGHCUKXkyg5gq6YzUakVTmnmSSO5Yiw==" + "resolved" "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.1.tgz" + "version" "3.11.1" + dependencies: + "@algolia/events" "^4.0.1" -"ansi-regex@^4.1.0": - "integrity" "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz" - "version" "4.1.0" +"algoliasearch@^4.0.0", "algoliasearch@^4.13.1", "algoliasearch@^4.9.1", "algoliasearch@>= 3.1 < 6": + "integrity" "sha512-ngbEQonGEmf8dyEh5f+uOIihv4176dgbuOZspiuhmTTBRBuzWu3KCGHre6uHj5YyuC7pNvQGzB6ZNJyZi0z+Sg==" + "resolved" "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.14.2.tgz" + "version" "4.14.2" + dependencies: + "@algolia/cache-browser-local-storage" "4.14.2" + "@algolia/cache-common" "4.14.2" + "@algolia/cache-in-memory" "4.14.2" + "@algolia/client-account" "4.14.2" + "@algolia/client-analytics" "4.14.2" + "@algolia/client-common" "4.14.2" + "@algolia/client-personalization" "4.14.2" + "@algolia/client-search" "4.14.2" + "@algolia/logger-common" "4.14.2" + "@algolia/logger-console" "4.14.2" + "@algolia/requester-browser-xhr" "4.14.2" + "@algolia/requester-common" "4.14.2" + "@algolia/requester-node-http" "4.14.2" + "@algolia/transporter" "4.14.2" + +"ansi-align@^3.0.0", "ansi-align@^3.0.1": + "integrity" "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==" + "resolved" "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "string-width" "^4.1.0" -"ansi-regex@^5.0.0": - "integrity" "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz" - "version" "5.0.0" +"ansi-colors@^4.1.1": + "integrity" "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==" + "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz" + "version" "4.1.3" + +"ansi-html-community@^0.0.8": + "integrity" "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + "resolved" "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" + "version" "0.0.8" + +"ansi-regex@^5.0.1": + "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + "version" "5.0.1" -"ansi-styles@^3.2.0": - "integrity" "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" - "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" - "version" "3.2.1" - dependencies: - "color-convert" "^1.9.0" +"ansi-regex@^6.0.1": + "integrity" "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + "version" "6.0.1" "ansi-styles@^3.2.1": "integrity" "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" @@ -2170,15 +2526,12 @@ dependencies: "color-convert" "^2.0.1" -"anymatch@^2.0.0": - "integrity" "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==" - "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz" - "version" "2.0.0" - dependencies: - "micromatch" "^3.1.4" - "normalize-path" "^2.1.1" +"ansi-styles@^6.1.0": + "integrity" "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + "version" "6.2.1" -"anymatch@~3.1.1": +"anymatch@~3.1.2": "integrity" "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==" "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" "version" "3.1.2" @@ -2187,9 +2540,9 @@ "picomatch" "^2.0.4" "arg@^5.0.0": - "integrity" "sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ==" - "resolved" "https://registry.npmjs.org/arg/-/arg-5.0.0.tgz" - "version" "5.0.0" + "integrity" "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + "resolved" "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" + "version" "5.0.2" "argparse@^1.0.7": "integrity" "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==" @@ -2203,134 +2556,75 @@ "resolved" "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" "version" "2.0.1" -"arr-diff@^4.0.0": - "integrity" "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - "resolved" "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" - "version" "4.0.0" - -"arr-flatten@^1.1.0": - "integrity" "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - "resolved" "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" - "version" "1.1.0" - -"arr-union@^3.1.0": - "integrity" "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - "resolved" "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" - "version" "3.1.0" - -"array-flatten@^2.1.0": +"array-flatten@^2.1.2": "integrity" "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" "resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz" "version" "2.1.2" "array-flatten@1.1.1": - "integrity" "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity" "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" "resolved" "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" "version" "1.1.1" -"array-includes@^3.1.2", "array-includes@^3.1.3": - "integrity" "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==" - "resolved" "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz" - "version" "3.1.3" +"array-includes@^3.1.5": + "integrity" "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==" + "resolved" "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz" + "version" "3.1.5" dependencies: "call-bind" "^1.0.2" - "define-properties" "^1.1.3" - "es-abstract" "^1.18.0-next.2" + "define-properties" "^1.1.4" + "es-abstract" "^1.19.5" "get-intrinsic" "^1.1.1" - "is-string" "^1.0.5" - -"array-union@^1.0.1": - "integrity" "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=" - "resolved" "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "array-uniq" "^1.0.1" + "is-string" "^1.0.7" "array-union@^2.1.0": "integrity" "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" "resolved" "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" "version" "2.1.0" -"array-uniq@^1.0.1": - "integrity" "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - "resolved" "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz" - "version" "1.0.3" - -"array-unique@^0.3.2": - "integrity" "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - "resolved" "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz" - "version" "0.3.2" - -"array.prototype.flatmap@^1.2.4": - "integrity" "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==" - "resolved" "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz" - "version" "1.2.4" +"array.prototype.flatmap@^1.3.0": + "integrity" "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==" + "resolved" "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz" + "version" "1.3.0" dependencies: - "call-bind" "^1.0.0" + "call-bind" "^1.0.2" "define-properties" "^1.1.3" - "es-abstract" "^1.18.0-next.1" - "function-bind" "^1.1.1" + "es-abstract" "^1.19.2" + "es-shim-unscopables" "^1.0.0" "asap@~2.0.3": - "integrity" "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "integrity" "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" "resolved" "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" "version" "2.0.6" -"assign-symbols@^1.0.0": - "integrity" "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - "resolved" "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" - "version" "1.0.0" - "astral-regex@^2.0.0": "integrity" "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" "resolved" "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" "version" "2.0.0" -"async-each@^1.0.1": - "integrity" "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" - "resolved" "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz" - "version" "1.0.3" - -"async-limiter@~1.0.0": - "integrity" "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - "resolved" "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" - "version" "1.0.1" - -"async@^2.6.2": - "integrity" "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==" - "resolved" "https://registry.npmjs.org/async/-/async-2.6.3.tgz" - "version" "2.6.3" - dependencies: - "lodash" "^4.17.14" - "at-least-node@^1.0.0": "integrity" "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" "resolved" "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" "version" "1.0.0" -"atob@^2.1.2": - "integrity" "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - "resolved" "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" - "version" "2.1.2" - -"autoprefixer@^10.0.2", "autoprefixer@^10.2.5": - "integrity" "sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==" - "resolved" "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.5.tgz" - "version" "10.2.5" +"autoprefixer@^10.4.12", "autoprefixer@^10.4.7": + "integrity" "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==" + "resolved" "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz" + "version" "10.4.12" dependencies: - "browserslist" "^4.16.3" - "caniuse-lite" "^1.0.30001196" - "colorette" "^1.2.2" - "fraction.js" "^4.0.13" + "browserslist" "^4.21.4" + "caniuse-lite" "^1.0.30001407" + "fraction.js" "^4.2.0" "normalize-range" "^0.1.2" - "postcss-value-parser" "^4.1.0" + "picocolors" "^1.0.0" + "postcss-value-parser" "^4.2.0" -"axios@^0.21.1": - "integrity" "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==" - "resolved" "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz" - "version" "0.21.1" +"axios@^0.25.0": + "integrity" "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==" + "resolved" "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz" + "version" "0.25.0" dependencies: - "follow-redirects" "^1.10.0" + "follow-redirects" "^1.14.7" "babel-eslint@^10.1.0": "integrity" "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==" @@ -2344,13 +2638,13 @@ "eslint-visitor-keys" "^1.0.0" "resolve" "^1.12.0" -"babel-loader@^8.2.2": - "integrity" "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==" - "resolved" "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz" - "version" "8.2.2" +"babel-loader@^8.2.5": + "integrity" "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==" + "resolved" "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz" + "version" "8.2.5" dependencies: "find-cache-dir" "^3.3.1" - "loader-utils" "^1.4.0" + "loader-utils" "^2.0.0" "make-dir" "^3.1.0" "schema-utils" "^2.6.5" @@ -2369,13 +2663,6 @@ dependencies: "object.assign" "^4.1.0" -"babel-plugin-dynamic-import-node@2.3.0": - "integrity" "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==" - "resolved" "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz" - "version" "2.3.0" - dependencies: - "object.assign" "^4.1.0" - "babel-plugin-extract-import-names@1.6.22": "integrity" "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==" "resolved" "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz" @@ -2383,29 +2670,29 @@ dependencies: "@babel/helper-plugin-utils" "7.10.4" -"babel-plugin-polyfill-corejs2@^0.2.0": - "integrity" "sha512-9bNwiR0dS881c5SHnzCmmGlMkJLl0OUZvxrxHo9w/iNoRuqaPjqlvBf4HrovXtQs/au5yKkpcdgfT1cC5PAZwg==" - "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.0.tgz" - "version" "0.2.0" +"babel-plugin-polyfill-corejs2@^0.3.3": + "integrity" "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz" + "version" "0.3.3" dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.2.0" + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" "semver" "^6.1.1" -"babel-plugin-polyfill-corejs3@^0.2.0": - "integrity" "sha512-zZyi7p3BCUyzNxLx8KV61zTINkkV65zVkDAFNZmrTCRVhjo1jAS+YLvDJ9Jgd/w2tsAviCwFHReYfxO3Iql8Yg==" - "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.0.tgz" - "version" "0.2.0" +"babel-plugin-polyfill-corejs3@^0.6.0": + "integrity" "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz" + "version" "0.6.0" dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.0" - "core-js-compat" "^3.9.1" + "@babel/helper-define-polyfill-provider" "^0.3.3" + "core-js-compat" "^3.25.1" -"babel-plugin-polyfill-regenerator@^0.2.0": - "integrity" "sha512-J7vKbCuD2Xi/eEHxquHN14bXAW9CXtecwuLrOIDJtcZzTaPzV1VdEfoUf9AzcRBMolKUQKM9/GVojeh0hFiqMg==" - "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.0.tgz" - "version" "0.2.0" +"babel-plugin-polyfill-regenerator@^0.4.1": + "integrity" "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==" + "resolved" "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz" + "version" "0.4.1" dependencies: - "@babel/helper-define-polyfill-provider" "^0.2.0" + "@babel/helper-define-polyfill-provider" "^0.3.3" "bail@^1.0.0": "integrity" "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==" @@ -2417,26 +2704,13 @@ "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" "version" "1.0.2" -"base@^0.11.1": - "integrity" "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==" - "resolved" "https://registry.npmjs.org/base/-/base-0.11.2.tgz" - "version" "0.11.2" - dependencies: - "cache-base" "^1.0.1" - "class-utils" "^0.3.5" - "component-emitter" "^1.2.1" - "define-property" "^1.0.0" - "isobject" "^3.0.1" - "mixin-deep" "^1.2.0" - "pascalcase" "^0.1.1" - "base16@^1.0.0": - "integrity" "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" + "integrity" "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" "resolved" "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz" "version" "1.0.0" "batch@0.6.1": - "integrity" "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + "integrity" "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" "resolved" "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" "version" "0.6.1" @@ -2445,75 +2719,72 @@ "resolved" "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" "version" "5.2.2" -"binary-extensions@^1.0.0": - "integrity" "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" - "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz" - "version" "1.13.1" - "binary-extensions@^2.0.0": "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" "version" "2.2.0" -"bindings@^1.5.0": - "integrity" "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==" - "resolved" "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" - "version" "1.5.0" - dependencies: - "file-uri-to-path" "1.0.0" - -"bluebird@^3.7.1": - "integrity" "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - "resolved" "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" - "version" "3.7.2" - -"body-parser@1.19.0": - "integrity" "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==" - "resolved" "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz" - "version" "1.19.0" +"body-parser@1.20.1": + "integrity" "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==" + "resolved" "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz" + "version" "1.20.1" dependencies: - "bytes" "3.1.0" + "bytes" "3.1.2" "content-type" "~1.0.4" "debug" "2.6.9" - "depd" "~1.1.2" - "http-errors" "1.7.2" + "depd" "2.0.0" + "destroy" "1.2.0" + "http-errors" "2.0.0" "iconv-lite" "0.4.24" - "on-finished" "~2.3.0" - "qs" "6.7.0" - "raw-body" "2.4.0" - "type-is" "~1.6.17" - -"bonjour@^3.5.0": - "integrity" "sha1-jokKGD2O6aI5OzhExpGkK897yfU=" - "resolved" "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz" - "version" "3.5.0" + "on-finished" "2.4.1" + "qs" "6.11.0" + "raw-body" "2.5.1" + "type-is" "~1.6.18" + "unpipe" "1.0.0" + +"bonjour-service@^1.0.11": + "integrity" "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==" + "resolved" "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz" + "version" "1.0.14" dependencies: - "array-flatten" "^2.1.0" - "deep-equal" "^1.0.1" + "array-flatten" "^2.1.2" "dns-equal" "^1.0.0" - "dns-txt" "^2.0.2" - "multicast-dns" "^6.0.1" - "multicast-dns-service-types" "^1.1.0" + "fast-deep-equal" "^3.1.3" + "multicast-dns" "^7.2.5" -"boolbase@^1.0.0", "boolbase@~1.0.0": - "integrity" "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" +"boolbase@^1.0.0": + "integrity" "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" "resolved" "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" "version" "1.0.0" "boxen@^5.0.0": - "integrity" "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==" - "resolved" "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz" - "version" "5.0.1" + "integrity" "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==" + "resolved" "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz" + "version" "5.1.2" dependencies: "ansi-align" "^3.0.0" "camelcase" "^6.2.0" "chalk" "^4.1.0" "cli-boxes" "^2.2.1" - "string-width" "^4.2.0" + "string-width" "^4.2.2" "type-fest" "^0.20.2" "widest-line" "^3.1.0" "wrap-ansi" "^7.0.0" +"boxen@^6.2.1": + "integrity" "sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==" + "resolved" "https://registry.npmjs.org/boxen/-/boxen-6.2.1.tgz" + "version" "6.2.1" + dependencies: + "ansi-align" "^3.0.1" + "camelcase" "^6.2.0" + "chalk" "^4.1.2" + "cli-boxes" "^3.0.0" + "string-width" "^5.0.1" + "type-fest" "^2.5.0" + "widest-line" "^4.0.1" + "wrap-ansi" "^8.0.1" + "brace-expansion@^1.1.7": "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" @@ -2522,84 +2793,42 @@ "balanced-match" "^1.0.0" "concat-map" "0.0.1" -"braces@^2.3.1", "braces@^2.3.2": - "integrity" "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==" - "resolved" "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz" - "version" "2.3.2" - dependencies: - "arr-flatten" "^1.1.0" - "array-unique" "^0.3.2" - "extend-shallow" "^2.0.1" - "fill-range" "^4.0.0" - "isobject" "^3.0.1" - "repeat-element" "^1.1.2" - "snapdragon" "^0.8.1" - "snapdragon-node" "^2.0.1" - "split-string" "^3.0.2" - "to-regex" "^3.0.1" - -"braces@^3.0.1", "braces@~3.0.2": +"braces@^3.0.2", "braces@~3.0.2": "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" "version" "3.0.2" dependencies: "fill-range" "^7.0.1" -"browserslist@^4.0.0", "browserslist@^4.14.5", "browserslist@^4.16.0", "browserslist@^4.16.3", "browserslist@^4.16.6": - "integrity" "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==" - "resolved" "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz" - "version" "4.16.6" +"browserslist@^4.0.0", "browserslist@^4.14.5", "browserslist@^4.16.6", "browserslist@^4.18.1", "browserslist@^4.21.3", "browserslist@^4.21.4", "browserslist@>= 4.21.0": + "integrity" "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==" + "resolved" "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz" + "version" "4.21.4" dependencies: - "caniuse-lite" "^1.0.30001219" - "colorette" "^1.2.2" - "electron-to-chromium" "^1.3.723" - "escalade" "^3.1.1" - "node-releases" "^1.1.71" + "caniuse-lite" "^1.0.30001400" + "electron-to-chromium" "^1.4.251" + "node-releases" "^2.0.6" + "update-browserslist-db" "^1.0.9" -"browserslist@4.14.2": - "integrity" "sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==" - "resolved" "https://registry.npmjs.org/browserslist/-/browserslist-4.14.2.tgz" - "version" "4.14.2" - dependencies: - "caniuse-lite" "^1.0.30001125" - "electron-to-chromium" "^1.3.564" - "escalade" "^3.0.2" - "node-releases" "^1.1.61" +"buffer-crc32@~0.2.3": + "integrity" "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + "resolved" "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" + "version" "0.2.13" "buffer-from@^1.0.0": - "integrity" "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - "resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" - "version" "1.1.1" - -"buffer-indexof@^1.0.0": - "integrity" "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" - "resolved" "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz" - "version" "1.1.1" + "integrity" "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + "version" "1.1.2" "bytes@3.0.0": - "integrity" "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + "integrity" "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" "version" "3.0.0" -"bytes@3.1.0": - "integrity" "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz" - "version" "3.1.0" - -"cache-base@^1.0.1": - "integrity" "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==" - "resolved" "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" - "version" "1.0.1" - dependencies: - "collection-visit" "^1.0.0" - "component-emitter" "^1.2.1" - "get-value" "^2.0.6" - "has-value" "^1.0.0" - "isobject" "^3.0.1" - "set-value" "^2.0.0" - "to-object-path" "^0.3.0" - "union-value" "^1.0.0" - "unset-value" "^1.0.0" +"bytes@3.1.2": + "integrity" "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "resolved" "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + "version" "3.1.2" "cacheable-request@^6.0.0": "integrity" "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==" @@ -2627,7 +2856,7 @@ "resolved" "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" "version" "3.1.0" -"camel-case@^4.1.1": +"camel-case@^4.1.2": "integrity" "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==" "resolved" "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" "version" "4.1.2" @@ -2640,15 +2869,10 @@ "resolved" "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" "version" "2.0.1" -"camelcase@^5.0.0": - "integrity" "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" - "version" "5.3.1" - "camelcase@^6.2.0": - "integrity" "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==" - "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz" - "version" "6.2.0" + "integrity" "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + "version" "6.3.0" "caniuse-api@^3.0.0": "integrity" "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==" @@ -2660,12 +2884,12 @@ "lodash.memoize" "^4.1.2" "lodash.uniq" "^4.5.0" -"caniuse-lite@^1.0.0", "caniuse-lite@^1.0.30001125", "caniuse-lite@^1.0.30001196", "caniuse-lite@^1.0.30001219": - "integrity" "sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==" - "resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz" - "version" "1.0.30001341" +"caniuse-lite@^1.0.0", "caniuse-lite@^1.0.30001400", "caniuse-lite@^1.0.30001407": + "integrity" "sha512-hSesn02u1QacQHhaxl/kNMZwqVG35Sz/8DgvmgedxSH8z9UUpcDYSPYgsj3x5dQNRcNp6BwpSfQfVzYUTm+fog==" + "resolved" "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001422.tgz" + "version" "1.0.30001422" -"ccount@^1.0.0", "ccount@^1.0.3": +"ccount@^1.0.0": "integrity" "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==" "resolved" "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz" "version" "1.1.0" @@ -2679,32 +2903,14 @@ "escape-string-regexp" "^1.0.5" "supports-color" "^5.3.0" -"chalk@^2.4.1": - "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==" - "resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - "version" "2.4.2" - dependencies: - "ansi-styles" "^3.2.1" - "escape-string-regexp" "^1.0.5" - "supports-color" "^5.3.0" - -"chalk@^4.0.0", "chalk@^4.1.0": - "integrity" "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==" - "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz" - "version" "4.1.1" +"chalk@^4.0.0", "chalk@^4.1.0", "chalk@^4.1.2": + "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + "version" "4.1.2" dependencies: "ansi-styles" "^4.1.0" "supports-color" "^7.1.0" -"chalk@2.4.2": - "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==" - "resolved" "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - "version" "2.4.2" - dependencies: - "ansi-styles" "^3.2.1" - "escape-string-regexp" "^1.0.5" - "supports-color" "^5.3.0" - "character-entities-legacy@^1.0.0": "integrity" "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" "resolved" "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz" @@ -2720,61 +2926,50 @@ "resolved" "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz" "version" "1.1.4" -"cheerio@^0.22.0": - "integrity" "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=" - "resolved" "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz" - "version" "0.22.0" - dependencies: - "css-select" "~1.2.0" - "dom-serializer" "~0.1.0" - "entities" "~1.1.1" - "htmlparser2" "^3.9.1" - "lodash.assignin" "^4.0.9" - "lodash.bind" "^4.1.4" - "lodash.defaults" "^4.0.1" - "lodash.filter" "^4.4.0" - "lodash.flatten" "^4.2.0" - "lodash.foreach" "^4.3.0" - "lodash.map" "^4.4.0" - "lodash.merge" "^4.4.0" - "lodash.pick" "^4.2.1" - "lodash.reduce" "^4.4.0" - "lodash.reject" "^4.4.0" - "lodash.some" "^4.4.0" - -"chokidar@^2.1.8": - "integrity" "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==" - "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz" - "version" "2.1.8" - dependencies: - "anymatch" "^2.0.0" - "async-each" "^1.0.1" - "braces" "^2.3.2" - "glob-parent" "^3.1.0" - "inherits" "^2.0.3" - "is-binary-path" "^1.0.0" - "is-glob" "^4.0.0" - "normalize-path" "^3.0.0" - "path-is-absolute" "^1.0.0" - "readdirp" "^2.2.1" - "upath" "^1.1.1" - optionalDependencies: - "fsevents" "^1.2.7" - -"chokidar@^3.5.1": - "integrity" "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==" - "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz" - "version" "3.5.1" +"cheerio-select@^2.1.0": + "integrity" "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==" + "resolved" "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz" + "version" "2.1.0" dependencies: - "anymatch" "~3.1.1" + "boolbase" "^1.0.0" + "css-select" "^5.1.0" + "css-what" "^6.1.0" + "domelementtype" "^2.3.0" + "domhandler" "^5.0.3" + "domutils" "^3.0.1" + +"cheerio@^1.0.0-rc.12": + "integrity" "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==" + "resolved" "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz" + "version" "1.0.0-rc.12" + dependencies: + "cheerio-select" "^2.1.0" + "dom-serializer" "^2.0.0" + "domhandler" "^5.0.3" + "domutils" "^3.0.1" + "htmlparser2" "^8.0.1" + "parse5" "^7.0.0" + "parse5-htmlparser2-tree-adapter" "^7.0.0" + +"chokidar@^3.4.2", "chokidar@^3.5.3": + "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" + "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + "version" "3.5.3" + dependencies: + "anymatch" "~3.1.2" "braces" "~3.0.2" - "glob-parent" "~5.1.0" + "glob-parent" "~5.1.2" "is-binary-path" "~2.1.0" "is-glob" "~4.0.1" "normalize-path" "~3.0.0" - "readdirp" "~3.5.0" + "readdirp" "~3.6.0" optionalDependencies: - "fsevents" "~2.3.1" + "fsevents" "~2.3.2" + +"chownr@^1.1.4": + "integrity" "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + "resolved" "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz" + "version" "1.1.4" "chrome-trace-event@^1.0.2": "integrity" "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" @@ -2786,32 +2981,15 @@ "resolved" "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz" "version" "2.0.0" -"ci-info@^3.0.0": - "integrity" "sha512-kdRWLBIJwdsYJWYJFtAFFYxybguqeF91qpZaggjG5Nf8QKdizFG2hjqvaTXbxFIcYbSaD74KpAXv6BSm17DHEQ==" - "resolved" "https://registry.npmjs.org/ci-info/-/ci-info-3.1.1.tgz" - "version" "3.1.1" - -"class-utils@^0.3.5": - "integrity" "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==" - "resolved" "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" - "version" "0.3.6" - dependencies: - "arr-union" "^3.1.0" - "define-property" "^0.2.5" - "isobject" "^3.0.0" - "static-extend" "^0.1.1" - -"clean-css@^4.2.3": - "integrity" "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==" - "resolved" "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz" - "version" "4.2.3" - dependencies: - "source-map" "~0.6.0" +"ci-info@^3.2.0": + "integrity" "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==" + "resolved" "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz" + "version" "3.7.1" -"clean-css@^5.1.1": - "integrity" "sha512-QcaGg9OuMo+0Ds933yLOY+gHPWbxhxqF0HDexmToPf8pczvmvZGYzd+QqWp9/mkucAOKViI+dSFOqoZIvXbeBw==" - "resolved" "https://registry.npmjs.org/clean-css/-/clean-css-5.1.2.tgz" - "version" "5.1.2" +"clean-css@^5.2.2", "clean-css@^5.3.0": + "integrity" "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==" + "resolved" "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz" + "version" "5.3.1" dependencies: "source-map" "~0.6.0" @@ -2825,23 +3003,19 @@ "resolved" "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" "version" "2.2.1" -"clipboard@^2.0.0": - "integrity" "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==" - "resolved" "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz" - "version" "2.0.8" - dependencies: - "good-listener" "^1.2.2" - "select" "^1.1.2" - "tiny-emitter" "^2.0.0" +"cli-boxes@^3.0.0": + "integrity" "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==" + "resolved" "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz" + "version" "3.0.0" -"cliui@^5.0.0": - "integrity" "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==" - "resolved" "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz" - "version" "5.0.0" +"cli-table3@^0.6.2": + "integrity" "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==" + "resolved" "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz" + "version" "0.6.3" dependencies: - "string-width" "^3.1.0" - "strip-ansi" "^5.2.0" - "wrap-ansi" "^5.1.0" + "string-width" "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" "clone-deep@^4.0.1": "integrity" "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==" @@ -2853,47 +3027,23 @@ "shallow-clone" "^3.0.0" "clone-response@^1.0.2": - "integrity" "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=" - "resolved" "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" - "version" "1.0.2" + "integrity" "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==" + "resolved" "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz" + "version" "1.0.3" dependencies: "mimic-response" "^1.0.0" -"clsx@^1.1.1": - "integrity" "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" - "resolved" "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz" - "version" "1.1.1" - -"coa@^2.0.2": - "integrity" "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==" - "resolved" "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz" - "version" "2.0.2" - dependencies: - "@types/q" "^1.5.1" - "chalk" "^2.4.1" - "q" "^1.1.2" - -"collapse-white-space@^1.0.2": - "integrity" "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==" - "resolved" "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz" - "version" "1.0.6" - -"collection-visit@^1.0.0": - "integrity" "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=" - "resolved" "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "map-visit" "^1.0.0" - "object-visit" "^1.0.0" +"clsx@^1.2.1": + "integrity" "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + "resolved" "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" + "version" "1.2.1" -"color-convert@^1.9.0": - "integrity" "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==" - "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" - "version" "1.9.3" - dependencies: - "color-name" "1.1.3" +"collapse-white-space@^1.0.2": + "integrity" "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==" + "resolved" "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz" + "version" "1.0.6" -"color-convert@^1.9.1": +"color-convert@^1.9.0": "integrity" "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==" "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" "version" "1.9.3" @@ -2907,36 +3057,25 @@ dependencies: "color-name" "~1.1.4" -"color-name@^1.0.0", "color-name@~1.1.4": +"color-name@~1.1.4": "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" "version" "1.1.4" "color-name@1.1.3": - "integrity" "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity" "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" "version" "1.1.3" -"color-string@^1.5.4": - "integrity" "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==" - "resolved" "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz" - "version" "1.5.5" - dependencies: - "color-name" "^1.0.0" - "simple-swizzle" "^0.2.2" - -"color@^3.1.1": - "integrity" "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==" - "resolved" "https://registry.npmjs.org/color/-/color-3.1.3.tgz" - "version" "3.1.3" - dependencies: - "color-convert" "^1.9.1" - "color-string" "^1.5.4" +"colord@^2.9.1": + "integrity" "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + "resolved" "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" + "version" "2.9.3" -"colorette@^1.2.2": - "integrity" "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" - "resolved" "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz" - "version" "1.2.2" +"colorette@^2.0.10": + "integrity" "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + "resolved" "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" + "version" "2.0.19" "combine-promises@^1.1.0": "integrity" "sha512-ZI9jvcLDxqwaXEixOhArm3r7ReIivsXkpbyEWyeOhzz1QS0iSgBPnWvEqvIQtYyamGCYA88gFhmUrs9hrrQ0pg==" @@ -2948,6 +3087,11 @@ "resolved" "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz" "version" "1.0.8" +"command-exists-promise@^2.0.2": + "integrity" "sha512-T6PB6vdFrwnHXg/I0kivM3DqaCGZLjjYSOe0a5WgFKcz1sOnmOeIjnhQPXVXX3QjVbLyTJ85lJkX6lUpukTzaA==" + "resolved" "https://registry.npmjs.org/command-exists-promise/-/command-exists-promise-2.0.2.tgz" + "version" "2.0.2" + "commander@^2.19.0": "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" @@ -2958,36 +3102,26 @@ "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" "version" "2.20.3" -"commander@^4.1.1": - "integrity" "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" - "resolved" "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" - "version" "4.1.1" - "commander@^5.1.0": "integrity" "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" "resolved" "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz" "version" "5.1.0" -"commander@^6.2.0": - "integrity" "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" - "resolved" "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz" - "version" "6.2.1" - -"commander@^7.1.0": +"commander@^7.2.0": "integrity" "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" "resolved" "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" "version" "7.2.0" +"commander@^8.3.0": + "integrity" "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + "resolved" "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" + "version" "8.3.0" + "commondir@^1.0.1": - "integrity" "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + "integrity" "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" "resolved" "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" "version" "1.0.1" -"component-emitter@^1.2.1": - "integrity" "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - "resolved" "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" - "version" "1.3.0" - "compressible@~2.0.16": "integrity" "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==" "resolved" "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" @@ -3009,7 +3143,7 @@ "vary" "~1.1.2" "concat-map@0.0.1": - "integrity" "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity" "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" "version" "0.0.1" @@ -3025,27 +3159,27 @@ "write-file-atomic" "^3.0.0" "xdg-basedir" "^4.0.0" -"connect-history-api-fallback@^1.6.0": - "integrity" "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" - "resolved" "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz" - "version" "1.6.0" +"connect-history-api-fallback@^2.0.0": + "integrity" "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" + "resolved" "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz" + "version" "2.0.0" -"consola@^2.15.0": +"consola@^2.15.3": "integrity" "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" "resolved" "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz" "version" "2.15.3" "content-disposition@0.5.2": - "integrity" "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + "integrity" "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==" "resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz" "version" "0.5.2" -"content-disposition@0.5.3": - "integrity" "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==" - "resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz" - "version" "0.5.3" +"content-disposition@0.5.4": + "integrity" "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==" + "resolved" "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + "version" "0.5.4" dependencies: - "safe-buffer" "5.1.2" + "safe-buffer" "5.2.1" "content-type@~1.0.4": "integrity" "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" @@ -3053,72 +3187,74 @@ "version" "1.0.4" "convert-source-map@^1.7.0": - "integrity" "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==" - "resolved" "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz" - "version" "1.7.0" - dependencies: - "safe-buffer" "~5.1.1" + "integrity" "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "resolved" "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + "version" "1.9.0" "cookie-signature@1.0.6": - "integrity" "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity" "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" "resolved" "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" "version" "1.0.6" -"cookie@0.4.0": - "integrity" "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - "resolved" "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz" - "version" "0.4.0" - -"copy-descriptor@^0.1.0": - "integrity" "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - "resolved" "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" - "version" "0.1.1" +"cookie@0.5.0": + "integrity" "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "resolved" "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" + "version" "0.5.0" -"copy-text-to-clipboard@^3.0.0": +"copy-text-to-clipboard@^3.0.1": "integrity" "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==" "resolved" "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz" "version" "3.0.1" -"copy-webpack-plugin@^8.1.0": - "integrity" "sha512-rYM2uzRxrLRpcyPqGceRBDpxxUV8vcDqIKxAUKfcnFpcrPxT5+XvhTxv7XLjo5AvEJFPdAE3zCogG2JVahqgSQ==" - "resolved" "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-8.1.1.tgz" - "version" "8.1.1" +"copy-webpack-plugin@^11.0.0": + "integrity" "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==" + "resolved" "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz" + "version" "11.0.0" dependencies: - "fast-glob" "^3.2.5" - "glob-parent" "^5.1.1" - "globby" "^11.0.3" + "fast-glob" "^3.2.11" + "glob-parent" "^6.0.1" + "globby" "^13.1.1" "normalize-path" "^3.0.0" - "p-limit" "^3.1.0" - "schema-utils" "^3.0.0" - "serialize-javascript" "^5.0.1" + "schema-utils" "^4.0.0" + "serialize-javascript" "^6.0.0" -"core-js-compat@^3.9.0", "core-js-compat@^3.9.1": - "integrity" "sha512-i6h5qODpw6EsHAoIdQhKoZdWn+dGBF3dSS8m5tif36RlWvW3A6+yu2S16QHUo3CrkzrnEskMAt9f8FxmY9fhWQ==" - "resolved" "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.12.1.tgz" - "version" "3.12.1" +"core-js-compat@^3.25.1": + "integrity" "sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==" + "resolved" "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.5.tgz" + "version" "3.25.5" dependencies: - "browserslist" "^4.16.6" - "semver" "7.0.0" + "browserslist" "^4.21.4" -"core-js-pure@^3.0.0": - "integrity" "sha512-1cch+qads4JnDSWsvc7d6nzlKAippwjUlf6vykkTLW53VSV+NkE6muGBToAjEA8pG90cSfcud3JgVmW2ds5TaQ==" - "resolved" "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.12.1.tgz" - "version" "3.12.1" +"core-js-pure@^3.25.1": + "integrity" "sha512-oml3M22pHM+igfWHDfdLVq2ShWmjM2V4L+dQEBs0DWVIqEm9WHCwGAlZ6BmyBQGy5sFrJmcx+856D9lVKyGWYg==" + "resolved" "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.25.5.tgz" + "version" "3.25.5" -"core-js@^3.9.1": - "integrity" "sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw==" - "resolved" "https://registry.npmjs.org/core-js/-/core-js-3.12.1.tgz" - "version" "3.12.1" +"core-js@^3.23.3": + "integrity" "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw==" + "resolved" "https://registry.npmjs.org/core-js/-/core-js-3.25.5.tgz" + "version" "3.25.5" "core-util-is@~1.0.0": - "integrity" "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - "resolved" "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - "version" "1.0.2" + "integrity" "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "resolved" "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + "version" "1.0.3" -"cosmiconfig@^7.0.0": - "integrity" "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==" - "resolved" "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz" - "version" "7.0.0" +"cosmiconfig@^6.0.0": + "integrity" "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==" + "resolved" "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "@types/parse-json" "^4.0.0" + "import-fresh" "^3.1.0" + "parse-json" "^5.0.0" + "path-type" "^4.0.0" + "yaml" "^1.7.2" + +"cosmiconfig@^7.0.0", "cosmiconfig@^7.0.1": + "integrity" "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==" + "resolved" "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz" + "version" "7.0.1" dependencies: "@types/parse-json" "^4.0.0" "import-fresh" "^3.2.1" @@ -3126,25 +3262,14 @@ "path-type" "^4.0.0" "yaml" "^1.10.0" -"cross-fetch@^3.0.4": +"cross-fetch@^3.1.5": "integrity" "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==" "resolved" "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz" "version" "3.1.5" dependencies: "node-fetch" "2.6.7" -"cross-spawn@^6.0.0": - "integrity" "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==" - "resolved" "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" - "version" "6.0.5" - dependencies: - "nice-try" "^1.0.4" - "path-key" "^2.0.1" - "semver" "^5.5.0" - "shebang-command" "^1.2.0" - "which" "^1.2.9" - -"cross-spawn@^7.0.2", "cross-spawn@^7.0.3", "cross-spawn@7.0.3": +"cross-spawn@^7.0.2", "cross-spawn@^7.0.3": "integrity" "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==" "resolved" "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" "version" "7.0.3" @@ -3158,100 +3283,60 @@ "resolved" "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" "version" "2.0.0" -"css-color-names@^0.0.4": - "integrity" "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" - "resolved" "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz" - "version" "0.0.4" - -"css-color-names@^1.0.1": - "integrity" "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==" - "resolved" "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz" - "version" "1.0.1" - -"css-declaration-sorter@6.0.0": - "integrity" "sha512-S0TE4E0ha5+tBHdLWPc5n+S8E4dFBS5xScPvgHkLNZwWvX4ISoFGhGeerLC9uS1cKA/sC+K2wHq6qEbcagT/fg==" - "resolved" "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.0.0.tgz" - "version" "6.0.0" - dependencies: - "timsort" "^0.3.0" +"css-declaration-sorter@^6.3.1": + "integrity" "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==" + "resolved" "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz" + "version" "6.3.1" -"css-loader@^5.1.1": - "integrity" "sha512-OFYGyINCKkdQsTrSYxzGSFnGS4gNjcXkKkQgWxK138jgnPt+lepxdjSZNc8sHAl5vP3DhsJUxufWIjOwI8PMMw==" - "resolved" "https://registry.npmjs.org/css-loader/-/css-loader-5.2.4.tgz" - "version" "5.2.4" +"css-loader@^6.7.1": + "integrity" "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==" + "resolved" "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz" + "version" "6.7.3" dependencies: - "camelcase" "^6.2.0" "icss-utils" "^5.1.0" - "loader-utils" "^2.0.0" - "postcss" "^8.2.10" + "postcss" "^8.4.19" "postcss-modules-extract-imports" "^3.0.0" "postcss-modules-local-by-default" "^4.0.0" "postcss-modules-scope" "^3.0.0" "postcss-modules-values" "^4.0.0" - "postcss-value-parser" "^4.1.0" - "schema-utils" "^3.0.0" - "semver" "^7.3.5" + "postcss-value-parser" "^4.2.0" + "semver" "^7.3.8" -"css-minimizer-webpack-plugin@^2.0.0": - "integrity" "sha512-cG/uc94727tx5pBNtb1Sd7gvUPzwmcQi1lkpfqTpdkuNq75hJCw7bIVsCNijLm4dhDcr1atvuysl2rZqOG8Txw==" - "resolved" "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-2.0.0.tgz" - "version" "2.0.0" +"css-minimizer-webpack-plugin@^4.0.0": + "integrity" "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==" + "resolved" "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz" + "version" "4.2.2" dependencies: - "cssnano" "^5.0.0" - "jest-worker" "^26.3.0" - "p-limit" "^3.0.2" - "postcss" "^8.2.9" - "schema-utils" "^3.0.0" - "serialize-javascript" "^5.0.1" + "cssnano" "^5.1.8" + "jest-worker" "^29.1.2" + "postcss" "^8.4.17" + "schema-utils" "^4.0.0" + "serialize-javascript" "^6.0.0" "source-map" "^0.6.1" -"css-select-base-adapter@^0.1.1": - "integrity" "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" - "resolved" "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz" - "version" "0.1.1" - -"css-select@^2.0.0": - "integrity" "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==" - "resolved" "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz" - "version" "2.1.0" - dependencies: - "boolbase" "^1.0.0" - "css-what" "^3.2.1" - "domutils" "^1.7.0" - "nth-check" "^1.0.2" - -"css-select@^2.0.2": - "integrity" "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==" - "resolved" "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz" - "version" "2.1.0" +"css-select@^4.1.3": + "integrity" "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==" + "resolved" "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz" + "version" "4.3.0" dependencies: "boolbase" "^1.0.0" - "css-what" "^3.2.1" - "domutils" "^1.7.0" - "nth-check" "^1.0.2" - -"css-select@^3.1.2": - "integrity" "sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==" - "resolved" "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz" - "version" "3.1.2" + "css-what" "^6.0.1" + "domhandler" "^4.3.1" + "domutils" "^2.8.0" + "nth-check" "^2.0.1" + +"css-select@^5.1.0": + "integrity" "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==" + "resolved" "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz" + "version" "5.1.0" dependencies: "boolbase" "^1.0.0" - "css-what" "^4.0.0" - "domhandler" "^4.0.0" - "domutils" "^2.4.3" - "nth-check" "^2.0.0" - -"css-select@~1.2.0": - "integrity" "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=" - "resolved" "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz" - "version" "1.2.0" - dependencies: - "boolbase" "~1.0.0" - "css-what" "2.1" - "domutils" "1.5.1" - "nth-check" "~1.0.1" + "css-what" "^6.1.0" + "domhandler" "^5.0.2" + "domutils" "^3.0.1" + "nth-check" "^2.0.1" -"css-tree@^1.1.2": +"css-tree@^1.1.2", "css-tree@^1.1.3": "integrity" "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==" "resolved" "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" "version" "1.1.3" @@ -3259,96 +3344,78 @@ "mdn-data" "2.0.14" "source-map" "^0.6.1" -"css-tree@1.0.0-alpha.37": - "integrity" "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==" - "resolved" "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" - "version" "1.0.0-alpha.37" - dependencies: - "mdn-data" "2.0.4" - "source-map" "^0.6.1" - -"css-what@^3.2.1": - "integrity" "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" - "resolved" "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz" - "version" "3.4.2" - -"css-what@^4.0.0": - "integrity" "sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==" - "resolved" "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz" - "version" "4.0.0" - -"css-what@2.1": - "integrity" "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" - "resolved" "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz" - "version" "2.1.3" +"css-what@^6.0.1", "css-what@^6.1.0": + "integrity" "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + "resolved" "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" + "version" "6.1.0" "cssesc@^3.0.0": "integrity" "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" "resolved" "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" "version" "3.0.0" -"cssnano-preset-advanced@^5.0.0": - "integrity" "sha512-g+LB6GcihLXcBEdDh+mzk1qX9jgtBkVpzAg1OlgrH6C+qKIQYRHwAPyaoXy95Ci83sYYXlwJ0OrqLYTIUEBLZQ==" - "resolved" "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.0.1.tgz" - "version" "5.0.1" - dependencies: - "autoprefixer" "^10.0.2" - "cssnano-preset-default" "^5.0.1" - "postcss-discard-unused" "^5.0.0" - "postcss-merge-idents" "^5.0.0" - "postcss-reduce-idents" "^5.0.0" - "postcss-zindex" "^5.0.0" - -"cssnano-preset-default@^5.0.1": - "integrity" "sha512-cfmfThYODGqhpQKDq9H0MTAqkMvZ3dGbOUTBKw0xWZiIycMqHid22LsJXJl4r1qX4qzDeKxcSyQ/Xb5Mu3Z//Q==" - "resolved" "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.0.1.tgz" - "version" "5.0.1" - dependencies: - "css-declaration-sorter" "6.0.0" - "cssnano-utils" "^2.0.0" - "postcss-calc" "^8.0.0" - "postcss-colormin" "^5.0.0" - "postcss-convert-values" "^5.0.0" - "postcss-discard-comments" "^5.0.0" - "postcss-discard-duplicates" "^5.0.0" - "postcss-discard-empty" "^5.0.0" - "postcss-discard-overridden" "^5.0.0" - "postcss-merge-longhand" "^5.0.1" - "postcss-merge-rules" "^5.0.0" - "postcss-minify-font-values" "^5.0.0" - "postcss-minify-gradients" "^5.0.0" - "postcss-minify-params" "^5.0.0" - "postcss-minify-selectors" "^5.0.0" - "postcss-normalize-charset" "^5.0.0" - "postcss-normalize-display-values" "^5.0.0" - "postcss-normalize-positions" "^5.0.0" - "postcss-normalize-repeat-style" "^5.0.0" - "postcss-normalize-string" "^5.0.0" - "postcss-normalize-timing-functions" "^5.0.0" - "postcss-normalize-unicode" "^5.0.0" - "postcss-normalize-url" "^5.0.0" - "postcss-normalize-whitespace" "^5.0.0" - "postcss-ordered-values" "^5.0.0" - "postcss-reduce-initial" "^5.0.0" - "postcss-reduce-transforms" "^5.0.0" - "postcss-svgo" "^5.0.0" - "postcss-unique-selectors" "^5.0.0" - -"cssnano-utils@^2.0.0": - "integrity" "sha512-xvxmTszdrvSyTACdPe8VU5J6p4sm3egpgw54dILvNqt5eBUv6TFjACLhSxtRuEsxYrgy8uDy269YjScO5aKbGA==" - "resolved" "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.0.tgz" - "version" "2.0.0" +"cssnano-preset-advanced@^5.3.8": + "integrity" "sha512-njnh4pp1xCsibJcEHnWZb4EEzni0ePMqPuPNyuWT4Z+YeXmsgqNuTPIljXFEXhxGsWs9183JkXgHxc1TcsahIg==" + "resolved" "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.9.tgz" + "version" "5.3.9" + dependencies: + "autoprefixer" "^10.4.12" + "cssnano-preset-default" "^5.2.13" + "postcss-discard-unused" "^5.1.0" + "postcss-merge-idents" "^5.1.1" + "postcss-reduce-idents" "^5.2.0" + "postcss-zindex" "^5.1.0" + +"cssnano-preset-default@^5.2.12", "cssnano-preset-default@^5.2.13": + "integrity" "sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==" + "resolved" "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz" + "version" "5.2.13" + dependencies: + "css-declaration-sorter" "^6.3.1" + "cssnano-utils" "^3.1.0" + "postcss-calc" "^8.2.3" + "postcss-colormin" "^5.3.0" + "postcss-convert-values" "^5.1.3" + "postcss-discard-comments" "^5.1.2" + "postcss-discard-duplicates" "^5.1.0" + "postcss-discard-empty" "^5.1.1" + "postcss-discard-overridden" "^5.1.0" + "postcss-merge-longhand" "^5.1.7" + "postcss-merge-rules" "^5.1.3" + "postcss-minify-font-values" "^5.1.0" + "postcss-minify-gradients" "^5.1.1" + "postcss-minify-params" "^5.1.4" + "postcss-minify-selectors" "^5.2.1" + "postcss-normalize-charset" "^5.1.0" + "postcss-normalize-display-values" "^5.1.0" + "postcss-normalize-positions" "^5.1.1" + "postcss-normalize-repeat-style" "^5.1.1" + "postcss-normalize-string" "^5.1.0" + "postcss-normalize-timing-functions" "^5.1.0" + "postcss-normalize-unicode" "^5.1.1" + "postcss-normalize-url" "^5.1.0" + "postcss-normalize-whitespace" "^5.1.1" + "postcss-ordered-values" "^5.1.3" + "postcss-reduce-initial" "^5.1.1" + "postcss-reduce-transforms" "^5.1.0" + "postcss-svgo" "^5.1.0" + "postcss-unique-selectors" "^5.1.1" + +"cssnano-utils@^3.1.0": + "integrity" "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==" + "resolved" "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz" + "version" "3.1.0" -"cssnano@^5.0.0", "cssnano@^5.0.1": - "integrity" "sha512-8JK3EnPsjQsULme9/e5M2hF564f/480hwsdcHvQ7ZtAIMfQ1O3SCfs+b8Mjf5KJxhYApyRshR2QSovEJi2K72Q==" - "resolved" "https://registry.npmjs.org/cssnano/-/cssnano-5.0.2.tgz" - "version" "5.0.2" +"cssnano@^5.1.12", "cssnano@^5.1.8": + "integrity" "sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ==" + "resolved" "https://registry.npmjs.org/cssnano/-/cssnano-5.1.13.tgz" + "version" "5.1.13" dependencies: - "cosmiconfig" "^7.0.0" - "cssnano-preset-default" "^5.0.1" - "is-resolvable" "^1.1.0" + "cssnano-preset-default" "^5.2.12" + "lilconfig" "^2.0.3" + "yaml" "^1.10.2" -"csso@^4.0.2", "csso@^4.2.0": +"csso@^4.2.0": "integrity" "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==" "resolved" "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz" "version" "4.2.0" @@ -3356,23 +3423,9 @@ "css-tree" "^1.1.2" "csstype@^3.0.2": - "integrity" "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" - "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz" - "version" "3.0.8" - -"debug@^2.2.0": - "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" - "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - "version" "2.6.9" - dependencies: - "ms" "2.0.0" - -"debug@^2.3.3": - "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" - "resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - "version" "2.6.9" - dependencies: - "ms" "2.0.0" + "integrity" "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz" + "version" "3.1.1" "debug@^2.6.0": "integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==" @@ -3381,24 +3434,10 @@ dependencies: "ms" "2.0.0" -"debug@^3.1.1": - "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" - "resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - "version" "3.2.7" - dependencies: - "ms" "^2.1.1" - -"debug@^3.2.6": - "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" - "resolved" "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - "version" "3.2.7" - dependencies: - "ms" "^2.1.1" - -"debug@^4.0.1", "debug@^4.1.0", "debug@^4.1.1": - "integrity" "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==" - "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" - "version" "4.3.1" +"debug@^4.0.1", "debug@^4.1.0", "debug@^4.1.1", "debug@4": + "integrity" "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + "version" "4.3.4" dependencies: "ms" "2.1.2" @@ -3409,109 +3448,57 @@ dependencies: "ms" "2.0.0" -"decamelize@^1.2.0": - "integrity" "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - "resolved" "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" - "version" "1.2.0" - -"decode-uri-component@^0.2.0": - "integrity" "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - "resolved" "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz" - "version" "0.2.0" - "decompress-response@^3.3.0": - "integrity" "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=" + "integrity" "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==" "resolved" "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" "version" "3.3.0" dependencies: "mimic-response" "^1.0.0" -"deep-equal@^1.0.1": - "integrity" "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==" - "resolved" "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz" - "version" "1.1.1" - dependencies: - "is-arguments" "^1.0.4" - "is-date-object" "^1.0.1" - "is-regex" "^1.0.4" - "object-is" "^1.0.1" - "object-keys" "^1.1.1" - "regexp.prototype.flags" "^1.2.0" - "deep-extend@^0.6.0": "integrity" "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" "resolved" "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" "version" "0.6.0" "deep-is@^0.1.3": - "integrity" "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - "resolved" "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" - "version" "0.1.3" + "integrity" "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + "resolved" "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + "version" "0.1.4" "deepmerge@^4.2.2": "integrity" "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" "resolved" "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz" "version" "4.2.2" -"default-gateway@^4.2.0": - "integrity" "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==" - "resolved" "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz" - "version" "4.2.0" +"default-gateway@^6.0.3": + "integrity" "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==" + "resolved" "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" + "version" "6.0.3" dependencies: - "execa" "^1.0.0" - "ip-regex" "^2.1.0" + "execa" "^5.0.0" "defer-to-connect@^1.0.1": "integrity" "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" "resolved" "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz" "version" "1.1.3" -"define-properties@^1.1.3": - "integrity" "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==" - "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" - "version" "1.1.3" - dependencies: - "object-keys" "^1.0.12" - -"define-property@^0.2.5": - "integrity" "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=" - "resolved" "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz" - "version" "0.2.5" - dependencies: - "is-descriptor" "^0.1.0" - -"define-property@^1.0.0": - "integrity" "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=" - "resolved" "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "is-descriptor" "^1.0.0" +"define-lazy-prop@^2.0.0": + "integrity" "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + "resolved" "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" + "version" "2.0.0" -"define-property@^2.0.2": - "integrity" "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==" - "resolved" "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz" - "version" "2.0.2" +"define-properties@^1.1.3", "define-properties@^1.1.4": + "integrity" "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==" + "resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz" + "version" "1.1.4" dependencies: - "is-descriptor" "^1.0.2" - "isobject" "^3.0.1" + "has-property-descriptors" "^1.0.0" + "object-keys" "^1.1.1" -"del@^4.1.1": - "integrity" "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==" - "resolved" "https://registry.npmjs.org/del/-/del-4.1.1.tgz" - "version" "4.1.1" - dependencies: - "@types/glob" "^7.1.1" - "globby" "^6.1.0" - "is-path-cwd" "^2.0.0" - "is-path-in-cwd" "^2.0.0" - "p-map" "^2.0.0" - "pify" "^4.0.1" - "rimraf" "^2.6.3" - -"del@^6.0.0": - "integrity" "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==" - "resolved" "https://registry.npmjs.org/del/-/del-6.0.0.tgz" - "version" "6.0.0" +"del@^6.1.1": + "integrity" "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==" + "resolved" "https://registry.npmjs.org/del/-/del-6.1.1.tgz" + "version" "6.1.1" dependencies: "globby" "^11.0.1" "graceful-fs" "^4.2.4" @@ -3522,20 +3509,20 @@ "rimraf" "^3.0.2" "slash" "^3.0.0" -"delegate@^3.1.2": - "integrity" "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" - "resolved" "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz" - "version" "3.2.0" - "depd@~1.1.2": - "integrity" "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "integrity" "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" "resolved" "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" "version" "1.1.2" -"destroy@~1.0.4": - "integrity" "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - "resolved" "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" - "version" "1.0.4" +"depd@2.0.0": + "integrity" "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + "resolved" "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + "version" "2.0.0" + +"destroy@1.2.0": + "integrity" "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + "resolved" "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" + "version" "1.2.0" "detab@2.0.4": "integrity" "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==" @@ -3545,11 +3532,11 @@ "repeat-string" "^1.5.4" "detect-node@^2.0.4": - "integrity" "sha512-qi86tE6hRcFHy8jI1m2VG+LaPUR1LhqDa5G8tVjuUXmOrpuAgqsA1pN0+ldgr3aKUH+QLI9hCY/OcRYisERejw==" - "resolved" "https://registry.npmjs.org/detect-node/-/detect-node-2.0.5.tgz" - "version" "2.0.5" + "integrity" "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + "resolved" "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" + "version" "2.1.0" -"detect-port-alt@1.1.6": +"detect-port-alt@^1.1.6": "integrity" "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==" "resolved" "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz" "version" "1.1.6" @@ -3558,12 +3545,12 @@ "debug" "^2.6.0" "detect-port@^1.3.0": - "integrity" "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==" - "resolved" "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz" - "version" "1.3.0" + "integrity" "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==" + "resolved" "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz" + "version" "1.5.1" dependencies: "address" "^1.0.1" - "debug" "^2.6.0" + "debug" "4" "dir-glob@^3.0.1": "integrity" "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==" @@ -3573,24 +3560,16 @@ "path-type" "^4.0.0" "dns-equal@^1.0.0": - "integrity" "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + "integrity" "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" "resolved" "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz" "version" "1.0.0" -"dns-packet@^1.3.1": - "integrity" "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==" - "resolved" "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz" - "version" "1.3.4" - dependencies: - "ip" "^1.1.0" - "safe-buffer" "^5.0.1" - -"dns-txt@^2.0.2": - "integrity" "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=" - "resolved" "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz" - "version" "2.0.2" +"dns-packet@^5.2.2": + "integrity" "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==" + "resolved" "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz" + "version" "5.4.0" dependencies: - "buffer-indexof" "^1.0.0" + "@leichtgewicht/ip-codec" "^2.0.1" "doctrine@^2.1.0": "integrity" "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==" @@ -3606,7 +3585,7 @@ dependencies: "esutils" "^2.0.2" -"dom-converter@^0.2": +"dom-converter@^0.2.0": "integrity" "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==" "resolved" "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz" "version" "0.2.0" @@ -3614,71 +3593,60 @@ "utila" "~0.4" "dom-serializer@^1.0.1": - "integrity" "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==" - "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz" - "version" "1.3.1" + "integrity" "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" + "version" "1.4.1" dependencies: "domelementtype" "^2.0.1" - "domhandler" "^4.0.0" + "domhandler" "^4.2.0" "entities" "^2.0.0" -"dom-serializer@~0.1.0", "dom-serializer@0": - "integrity" "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==" - "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz" - "version" "0.1.1" +"dom-serializer@^2.0.0": + "integrity" "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==" + "resolved" "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz" + "version" "2.0.0" dependencies: - "domelementtype" "^1.3.0" - "entities" "^1.1.1" - -"domelementtype@^1.3.0", "domelementtype@^1.3.1", "domelementtype@1": - "integrity" "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" - "version" "1.3.1" - -"domelementtype@^2.0.1", "domelementtype@^2.2.0": - "integrity" "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" - "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz" - "version" "2.2.0" + "domelementtype" "^2.3.0" + "domhandler" "^5.0.2" + "entities" "^4.2.0" -"domhandler@^2.3.0": - "integrity" "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==" - "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz" - "version" "2.4.2" - dependencies: - "domelementtype" "1" +"domelementtype@^2.0.1", "domelementtype@^2.2.0", "domelementtype@^2.3.0": + "integrity" "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + "resolved" "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" + "version" "2.3.0" -"domhandler@^4.0.0", "domhandler@^4.2.0": - "integrity" "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==" - "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz" - "version" "4.2.0" +"domhandler@^4.0.0", "domhandler@^4.2.0", "domhandler@^4.3.1": + "integrity" "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==" + "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz" + "version" "4.3.1" dependencies: "domelementtype" "^2.2.0" -"domutils@^1.5.1", "domutils@1.5.1": - "integrity" "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=" - "resolved" "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz" - "version" "1.5.1" - dependencies: - "dom-serializer" "0" - "domelementtype" "1" - -"domutils@^1.7.0": - "integrity" "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==" - "resolved" "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" - "version" "1.7.0" +"domhandler@^5.0.1", "domhandler@^5.0.2", "domhandler@^5.0.3": + "integrity" "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==" + "resolved" "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz" + "version" "5.0.3" dependencies: - "dom-serializer" "0" - "domelementtype" "1" + "domelementtype" "^2.3.0" -"domutils@^2.4.3": - "integrity" "sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==" - "resolved" "https://registry.npmjs.org/domutils/-/domutils-2.6.0.tgz" - "version" "2.6.0" +"domutils@^2.5.2", "domutils@^2.8.0": + "integrity" "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" + "version" "2.8.0" dependencies: "dom-serializer" "^1.0.1" "domelementtype" "^2.2.0" "domhandler" "^4.2.0" +"domutils@^3.0.1": + "integrity" "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==" + "resolved" "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz" + "version" "3.0.1" + dependencies: + "dom-serializer" "^2.0.0" + "domelementtype" "^2.3.0" + "domhandler" "^5.0.1" + "dot-case@^3.0.4": "integrity" "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==" "resolved" "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" @@ -3694,40 +3662,40 @@ dependencies: "is-obj" "^2.0.0" -"duplexer@^0.1.1", "duplexer@^0.1.2": +"duplexer@^0.1.2": "integrity" "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" "resolved" "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" "version" "0.1.2" "duplexer3@^0.1.4": - "integrity" "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - "resolved" "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" - "version" "0.1.4" + "integrity" "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==" + "resolved" "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz" + "version" "0.1.5" + +"eastasianwidth@^0.2.0": + "integrity" "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "resolved" "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + "version" "0.2.0" "ee-first@1.1.1": - "integrity" "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity" "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" "resolved" "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" "version" "1.1.1" -"electron-to-chromium@^1.3.564", "electron-to-chromium@^1.3.723": - "integrity" "sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==" - "resolved" "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz" - "version" "1.3.727" - -"emoji-regex@^7.0.1": - "integrity" "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" - "version" "7.0.3" +"electron-to-chromium@^1.4.251": + "integrity" "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" + "resolved" "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz" + "version" "1.4.284" "emoji-regex@^8.0.0": "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" "version" "8.0.0" -"emoji-regex@>=6.0.0 <=6.1.1": - "integrity" "sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4=" - "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.1.tgz" - "version" "6.1.1" +"emoji-regex@^9.2.2": + "integrity" "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + "version" "9.2.2" "emojis-list@^3.0.0": "integrity" "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" @@ -3740,7 +3708,7 @@ "version" "3.2.0" "encodeurl@~1.0.2": - "integrity" "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity" "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" "resolved" "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" "version" "1.0.2" @@ -3751,10 +3719,10 @@ dependencies: "once" "^1.4.0" -"enhanced-resolve@^5.8.0": - "integrity" "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==" - "resolved" "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz" - "version" "5.8.2" +"enhanced-resolve@^5.10.0": + "integrity" "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==" + "resolved" "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz" + "version" "5.10.0" dependencies: "graceful-fs" "^4.2.4" "tapable" "^2.2.0" @@ -3766,22 +3734,15 @@ dependencies: "ansi-colors" "^4.1.1" -"entities@^1.1.1", "entities@~1.1.1": - "integrity" "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - "resolved" "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz" - "version" "1.1.2" - "entities@^2.0.0": "integrity" "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" "resolved" "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" "version" "2.2.0" -"errno@^0.1.3": - "integrity" "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==" - "resolved" "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz" - "version" "0.1.8" - dependencies: - "prr" "~1.0.1" +"entities@^4.2.0", "entities@^4.3.0", "entities@^4.4.0": + "integrity" "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + "resolved" "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz" + "version" "4.4.0" "error-ex@^1.3.1": "integrity" "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==" @@ -3790,32 +3751,47 @@ dependencies: "is-arrayish" "^0.2.1" -"es-abstract@^1.17.2", "es-abstract@^1.18.0-next.1", "es-abstract@^1.18.0-next.2": - "integrity" "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==" - "resolved" "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz" - "version" "1.18.0" +"es-abstract@^1.19.0", "es-abstract@^1.19.1", "es-abstract@^1.19.2", "es-abstract@^1.19.5": + "integrity" "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==" + "resolved" "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz" + "version" "1.20.4" dependencies: "call-bind" "^1.0.2" "es-to-primitive" "^1.2.1" "function-bind" "^1.1.1" - "get-intrinsic" "^1.1.1" + "function.prototype.name" "^1.1.5" + "get-intrinsic" "^1.1.3" + "get-symbol-description" "^1.0.0" "has" "^1.0.3" - "has-symbols" "^1.0.2" - "is-callable" "^1.2.3" - "is-negative-zero" "^2.0.1" - "is-regex" "^1.1.2" - "is-string" "^1.0.5" - "object-inspect" "^1.9.0" + "has-property-descriptors" "^1.0.0" + "has-symbols" "^1.0.3" + "internal-slot" "^1.0.3" + "is-callable" "^1.2.7" + "is-negative-zero" "^2.0.2" + "is-regex" "^1.1.4" + "is-shared-array-buffer" "^1.0.2" + "is-string" "^1.0.7" + "is-weakref" "^1.0.2" + "object-inspect" "^1.12.2" "object-keys" "^1.1.1" - "object.assign" "^4.1.2" - "string.prototype.trimend" "^1.0.4" - "string.prototype.trimstart" "^1.0.4" - "unbox-primitive" "^1.0.0" - -"es-module-lexer@^0.4.0": - "integrity" "sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==" - "resolved" "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.4.1.tgz" - "version" "0.4.1" + "object.assign" "^4.1.4" + "regexp.prototype.flags" "^1.4.3" + "safe-regex-test" "^1.0.0" + "string.prototype.trimend" "^1.0.5" + "string.prototype.trimstart" "^1.0.5" + "unbox-primitive" "^1.0.2" + +"es-module-lexer@^0.9.0": + "integrity" "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + "resolved" "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" + "version" "0.9.3" + +"es-shim-unscopables@^1.0.0": + "integrity" "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==" + "resolved" "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" + "version" "1.0.0" + dependencies: + "has" "^1.0.3" "es-to-primitive@^1.2.1": "integrity" "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==" @@ -3826,7 +3802,7 @@ "is-date-object" "^1.0.1" "is-symbol" "^1.0.2" -"escalade@^3.0.2", "escalade@^3.1.1": +"escalade@^3.1.1": "integrity" "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" "resolved" "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" "version" "3.1.1" @@ -3837,12 +3813,12 @@ "version" "2.1.1" "escape-html@^1.0.3", "escape-html@~1.0.3": - "integrity" "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity" "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" "resolved" "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" "version" "1.0.3" "escape-string-regexp@^1.0.5": - "integrity" "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity" "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" "version" "1.0.5" @@ -3851,30 +3827,27 @@ "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" "version" "4.0.0" -"escape-string-regexp@2.0.0": - "integrity" "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" - "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" - "version" "2.0.0" - "eslint-plugin-react@^7.20.0": - "integrity" "sha512-AfjgFQB+nYszudkxRkTFu0UR1zEQig0ArVMPloKhxwlwkzaw/fBiH0QWcBBhZONlXqQC51+nfqFrkn4EzHcGBw==" - "resolved" "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.23.2.tgz" - "version" "7.23.2" + "integrity" "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==" + "resolved" "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz" + "version" "7.31.10" dependencies: - "array-includes" "^3.1.3" - "array.prototype.flatmap" "^1.2.4" + "array-includes" "^3.1.5" + "array.prototype.flatmap" "^1.3.0" "doctrine" "^2.1.0" - "has" "^1.0.3" + "estraverse" "^5.3.0" "jsx-ast-utils" "^2.4.1 || ^3.0.0" - "minimatch" "^3.0.4" - "object.entries" "^1.1.3" - "object.fromentries" "^2.0.4" - "object.values" "^1.1.3" - "prop-types" "^15.7.2" + "minimatch" "^3.1.2" + "object.entries" "^1.1.5" + "object.fromentries" "^2.0.5" + "object.hasown" "^1.1.1" + "object.values" "^1.1.5" + "prop-types" "^15.8.1" "resolve" "^2.0.0-next.3" - "string.prototype.matchall" "^4.0.4" + "semver" "^6.3.0" + "string.prototype.matchall" "^4.0.7" -"eslint-scope@^5.1.1": +"eslint-scope@^5.1.1", "eslint-scope@5.1.1": "integrity" "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==" "resolved" "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" "version" "5.1.1" @@ -3899,28 +3872,31 @@ "resolved" "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" "version" "2.1.0" -"eslint@^3 || ^4 || ^5 || ^6 || ^7", "eslint@^7.3.1", "eslint@>= 4.12.1": - "integrity" "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==" - "resolved" "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz" - "version" "7.26.0" +"eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^7.3.1", "eslint@>= 4.12.1", "eslint@>= 6": + "integrity" "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==" + "resolved" "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz" + "version" "7.32.0" dependencies: "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.1" + "@eslint/eslintrc" "^0.4.3" + "@humanwhocodes/config-array" "^0.5.0" "ajv" "^6.10.0" "chalk" "^4.0.0" "cross-spawn" "^7.0.2" "debug" "^4.0.1" "doctrine" "^3.0.0" "enquirer" "^2.3.5" + "escape-string-regexp" "^4.0.0" "eslint-scope" "^5.1.1" "eslint-utils" "^2.1.0" "eslint-visitor-keys" "^2.0.0" "espree" "^7.3.1" "esquery" "^1.4.0" "esutils" "^2.0.2" + "fast-deep-equal" "^3.1.3" "file-entry-cache" "^6.0.1" "functional-red-black-tree" "^1.0.1" - "glob-parent" "^5.0.0" + "glob-parent" "^5.1.2" "globals" "^13.6.0" "ignore" "^4.0.6" "import-fresh" "^3.0.0" @@ -3929,7 +3905,7 @@ "js-yaml" "^3.13.1" "json-stable-stringify-without-jsonify" "^1.0.1" "levn" "^0.4.1" - "lodash" "^4.17.21" + "lodash.merge" "^4.6.2" "minimatch" "^3.0.4" "natural-compare" "^1.4.0" "optionator" "^0.9.1" @@ -3938,7 +3914,7 @@ "semver" "^7.2.1" "strip-ansi" "^6.0.0" "strip-json-comments" "^3.1.0" - "table" "^6.0.4" + "table" "^6.0.9" "text-table" "^0.2.0" "v8-compile-cache" "^2.0.3" @@ -3975,36 +3951,32 @@ "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" "version" "4.3.0" -"estraverse@^5.1.0": - "integrity" "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" - "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz" - "version" "5.2.0" - -"estraverse@^5.2.0": - "integrity" "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" - "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz" - "version" "5.2.0" +"estraverse@^5.1.0", "estraverse@^5.2.0", "estraverse@^5.3.0": + "integrity" "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "resolved" "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + "version" "5.3.0" "esutils@^2.0.2": "integrity" "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" "resolved" "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" "version" "2.0.3" -"eta@^1.12.1": - "integrity" "sha512-H8npoci2J/7XiPnVcCVulBSPsTNGvGaINyMjQDU8AFqp9LGsEYS88g2CiU+d01Sg44WtX7o4nb8wUJ9vnI+tiA==" - "resolved" "https://registry.npmjs.org/eta/-/eta-1.12.1.tgz" - "version" "1.12.1" +"eta@^1.12.3": + "integrity" "sha512-qHixwbDLtekO/d51Yr4glcaUJCIjGVJyTzuqV4GPlgZo1YpgOKG+avQynErZIYrfM6JIJdtiG2Kox8tbb+DoGg==" + "resolved" "https://registry.npmjs.org/eta/-/eta-1.12.3.tgz" + "version" "1.12.3" "etag@~1.8.1": - "integrity" "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity" "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" "resolved" "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" "version" "1.8.1" -"eval@^0.1.4": - "integrity" "sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ==" - "resolved" "https://registry.npmjs.org/eval/-/eval-0.1.6.tgz" - "version" "0.1.6" +"eval@^0.1.8": + "integrity" "sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==" + "resolved" "https://registry.npmjs.org/eval/-/eval-0.1.8.tgz" + "version" "0.1.8" dependencies: + "@types/node" "*" "require-like" ">= 0.1.1" "eventemitter3@^4.0.0": @@ -4012,40 +3984,15 @@ "resolved" "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" "version" "4.0.7" -"events@^1.1.1": - "integrity" "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - "resolved" "https://registry.npmjs.org/events/-/events-1.1.1.tgz" - "version" "1.1.1" - "events@^3.2.0": "integrity" "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" "resolved" "https://registry.npmjs.org/events/-/events-3.3.0.tgz" "version" "3.3.0" -"eventsource@^1.0.7": - "integrity" "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==" - "resolved" "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz" - "version" "1.1.0" - dependencies: - "original" "^1.0.0" - -"execa@^1.0.0": - "integrity" "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==" - "resolved" "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "cross-spawn" "^6.0.0" - "get-stream" "^4.0.0" - "is-stream" "^1.1.0" - "npm-run-path" "^2.0.0" - "p-finally" "^1.0.0" - "signal-exit" "^3.0.0" - "strip-eof" "^1.0.0" - "execa@^5.0.0": - "integrity" "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==" - "resolved" "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz" - "version" "5.0.0" + "integrity" "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==" + "resolved" "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + "version" "5.1.1" dependencies: "cross-spawn" "^7.0.3" "get-stream" "^6.0.0" @@ -4057,113 +4004,70 @@ "signal-exit" "^3.0.3" "strip-final-newline" "^2.0.0" -"expand-brackets@^2.1.4": - "integrity" "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=" - "resolved" "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz" - "version" "2.1.4" - dependencies: - "debug" "^2.3.3" - "define-property" "^0.2.5" - "extend-shallow" "^2.0.1" - "posix-character-classes" "^0.1.0" - "regex-not" "^1.0.0" - "snapdragon" "^0.8.1" - "to-regex" "^3.0.1" - -"express@^4.17.1": - "integrity" "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==" - "resolved" "https://registry.npmjs.org/express/-/express-4.17.1.tgz" - "version" "4.17.1" +"express@^4.17.3": + "integrity" "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==" + "resolved" "https://registry.npmjs.org/express/-/express-4.18.2.tgz" + "version" "4.18.2" dependencies: - "accepts" "~1.3.7" + "accepts" "~1.3.8" "array-flatten" "1.1.1" - "body-parser" "1.19.0" - "content-disposition" "0.5.3" + "body-parser" "1.20.1" + "content-disposition" "0.5.4" "content-type" "~1.0.4" - "cookie" "0.4.0" + "cookie" "0.5.0" "cookie-signature" "1.0.6" "debug" "2.6.9" - "depd" "~1.1.2" + "depd" "2.0.0" "encodeurl" "~1.0.2" "escape-html" "~1.0.3" "etag" "~1.8.1" - "finalhandler" "~1.1.2" + "finalhandler" "1.2.0" "fresh" "0.5.2" + "http-errors" "2.0.0" "merge-descriptors" "1.0.1" "methods" "~1.1.2" - "on-finished" "~2.3.0" + "on-finished" "2.4.1" "parseurl" "~1.3.3" "path-to-regexp" "0.1.7" - "proxy-addr" "~2.0.5" - "qs" "6.7.0" + "proxy-addr" "~2.0.7" + "qs" "6.11.0" "range-parser" "~1.2.1" - "safe-buffer" "5.1.2" - "send" "0.17.1" - "serve-static" "1.14.1" - "setprototypeof" "1.1.1" - "statuses" "~1.5.0" + "safe-buffer" "5.2.1" + "send" "0.18.0" + "serve-static" "1.15.0" + "setprototypeof" "1.2.0" + "statuses" "2.0.1" "type-is" "~1.6.18" "utils-merge" "1.0.1" "vary" "~1.1.2" "extend-shallow@^2.0.1": - "integrity" "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=" + "integrity" "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==" "resolved" "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" "version" "2.0.1" dependencies: "is-extendable" "^0.1.0" -"extend-shallow@^3.0.0": - "integrity" "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=" - "resolved" "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" - "version" "3.0.2" - dependencies: - "assign-symbols" "^1.0.0" - "is-extendable" "^1.0.1" - -"extend-shallow@^3.0.2": - "integrity" "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=" - "resolved" "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" - "version" "3.0.2" - dependencies: - "assign-symbols" "^1.0.0" - "is-extendable" "^1.0.1" - "extend@^3.0.0": "integrity" "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" "resolved" "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" "version" "3.0.2" -"extglob@^2.0.4": - "integrity" "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==" - "resolved" "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz" - "version" "2.0.4" - dependencies: - "array-unique" "^0.3.2" - "define-property" "^1.0.0" - "expand-brackets" "^2.1.4" - "extend-shallow" "^2.0.1" - "fragment-cache" "^0.2.1" - "regex-not" "^1.0.0" - "snapdragon" "^0.8.1" - "to-regex" "^3.0.1" - -"fast-deep-equal@^3.1.1": +"fast-deep-equal@^3.1.1", "fast-deep-equal@^3.1.3": "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" "resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" "version" "3.1.3" -"fast-glob@^3.1.1", "fast-glob@^3.2.5": - "integrity" "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==" - "resolved" "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz" - "version" "3.2.5" +"fast-glob@^3.2.11", "fast-glob@^3.2.9": + "integrity" "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==" + "resolved" "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" + "version" "3.2.12" dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" - "glob-parent" "^5.1.0" + "glob-parent" "^5.1.2" "merge2" "^1.3.0" - "micromatch" "^4.0.2" - "picomatch" "^2.2.1" + "micromatch" "^4.0.4" "fast-json-stable-stringify@^2.0.0": "integrity" "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" @@ -4171,28 +4075,28 @@ "version" "2.1.0" "fast-levenshtein@^2.0.6": - "integrity" "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + "integrity" "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" "resolved" "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" "version" "2.0.6" "fast-url-parser@1.1.3": - "integrity" "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=" + "integrity" "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==" "resolved" "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz" "version" "1.1.3" dependencies: "punycode" "^1.3.2" "fastq@^1.6.0": - "integrity" "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==" - "resolved" "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz" - "version" "1.11.0" + "integrity" "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==" + "resolved" "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" + "version" "1.13.0" dependencies: "reusify" "^1.0.4" "faye-websocket@^0.11.3": - "integrity" "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==" - "resolved" "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz" - "version" "0.11.3" + "integrity" "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==" + "resolved" "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + "version" "0.11.4" dependencies: "websocket-driver" ">=0.5.1" @@ -4208,18 +4112,25 @@ "resolved" "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz" "version" "1.0.2" -"fbjs@^3.0.0": - "integrity" "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==" - "resolved" "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz" - "version" "3.0.0" +"fbjs@^3.0.0", "fbjs@^3.0.1": + "integrity" "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==" + "resolved" "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz" + "version" "3.0.4" dependencies: - "cross-fetch" "^3.0.4" + "cross-fetch" "^3.1.5" "fbjs-css-vars" "^1.0.0" "loose-envify" "^1.0.0" "object-assign" "^4.1.0" "promise" "^7.1.1" "setimmediate" "^1.0.5" - "ua-parser-js" "^0.7.18" + "ua-parser-js" "^0.7.30" + +"fd-slicer@~1.1.0": + "integrity" "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==" + "resolved" "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" + "version" "1.1.0" + dependencies: + "pend" "~1.2.0" "feed@^4.2.2": "integrity" "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==" @@ -4228,13 +4139,6 @@ dependencies: "xml-js" "^1.6.11" -"figures@^3.2.0": - "integrity" "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==" - "resolved" "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" - "version" "3.2.0" - dependencies: - "escape-string-regexp" "^1.0.5" - "file-entry-cache@^6.0.1": "integrity" "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==" "resolved" "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" @@ -4245,30 +4149,15 @@ "file-loader@*", "file-loader@^6.2.0": "integrity" "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==" "resolved" "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz" - "version" "6.2.0" - dependencies: - "loader-utils" "^2.0.0" - "schema-utils" "^3.0.0" - -"file-uri-to-path@1.0.0": - "integrity" "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - "resolved" "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz" - "version" "1.0.0" - -"filesize@6.1.0": - "integrity" "sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==" - "resolved" "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz" - "version" "6.1.0" - -"fill-range@^4.0.0": - "integrity" "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=" - "resolved" "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz" - "version" "4.0.0" + "version" "6.2.0" dependencies: - "extend-shallow" "^2.0.1" - "is-number" "^3.0.0" - "repeat-string" "^1.6.1" - "to-regex-range" "^2.1.0" + "loader-utils" "^2.0.0" + "schema-utils" "^3.0.0" + +"filesize@^8.0.6": + "integrity" "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" + "resolved" "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz" + "version" "8.0.7" "fill-range@^7.0.1": "integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" @@ -4277,23 +4166,23 @@ dependencies: "to-regex-range" "^5.0.1" -"finalhandler@~1.1.2": - "integrity" "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==" - "resolved" "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" - "version" "1.1.2" +"finalhandler@1.2.0": + "integrity" "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==" + "resolved" "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" + "version" "1.2.0" dependencies: "debug" "2.6.9" "encodeurl" "~1.0.2" "escape-html" "~1.0.3" - "on-finished" "~2.3.0" + "on-finished" "2.4.1" "parseurl" "~1.3.3" - "statuses" "~1.5.0" + "statuses" "2.0.1" "unpipe" "~1.0.0" "find-cache-dir@^3.3.1": - "integrity" "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==" - "resolved" "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz" - "version" "3.3.1" + "integrity" "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==" + "resolved" "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" + "version" "3.3.2" dependencies: "commondir" "^1.0.1" "make-dir" "^3.0.2" @@ -4306,7 +4195,7 @@ dependencies: "locate-path" "^3.0.0" -"find-up@^4.0.0", "find-up@4.1.0": +"find-up@^4.0.0": "integrity" "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==" "resolved" "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" "version" "4.1.0" @@ -4331,64 +4220,67 @@ "rimraf" "^3.0.2" "flatted@^3.1.0": - "integrity" "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" - "resolved" "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz" - "version" "3.1.1" + "integrity" "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + "resolved" "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" + "version" "3.2.7" "flux@^4.0.1": - "integrity" "sha512-emk4RCvJ8RzNP2lNpphKnG7r18q8elDYNAPx7xn+bDeOIo9FFfxEfIQ2y6YbQNmnsGD3nH1noxtLE64Puz1bRQ==" - "resolved" "https://registry.npmjs.org/flux/-/flux-4.0.1.tgz" - "version" "4.0.1" + "integrity" "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==" + "resolved" "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz" + "version" "4.0.3" dependencies: "fbemitter" "^3.0.0" - "fbjs" "^3.0.0" - -"follow-redirects@^1.0.0", "follow-redirects@^1.10.0": - "integrity" "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==" - "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz" - "version" "1.14.1" + "fbjs" "^3.0.1" -"for-in@^1.0.2": - "integrity" "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - "resolved" "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" - "version" "1.0.2" +"follow-redirects@^1.0.0", "follow-redirects@^1.14.7": + "integrity" "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" + "version" "1.15.2" -"fork-ts-checker-webpack-plugin@4.1.6": - "integrity" "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==" - "resolved" "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz" - "version" "4.1.6" +"fork-ts-checker-webpack-plugin@^6.5.0": + "integrity" "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==" + "resolved" "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz" + "version" "6.5.2" dependencies: - "@babel/code-frame" "^7.5.5" - "chalk" "^2.4.1" - "micromatch" "^3.1.10" + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + "chalk" "^4.1.0" + "chokidar" "^3.4.2" + "cosmiconfig" "^6.0.0" + "deepmerge" "^4.2.2" + "fs-extra" "^9.0.0" + "glob" "^7.1.6" + "memfs" "^3.1.2" "minimatch" "^3.0.4" - "semver" "^5.6.0" + "schema-utils" "2.7.0" + "semver" "^7.3.2" "tapable" "^1.0.0" - "worker-rpc" "^0.1.0" - -"forwarded@~0.1.2": - "integrity" "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - "resolved" "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz" - "version" "0.1.2" -"fraction.js@^4.0.13": - "integrity" "sha512-o9lSKpK0TDqDwTL24Hxqi6I99s942l6TYkfl6WvGWgLOIFz/YonSGKfiSeMadoiNvTfqnfOa9mjb5SGVbBK9/w==" - "resolved" "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.0.tgz" - "version" "4.1.0" +"forwarded@0.2.0": + "integrity" "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "resolved" "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + "version" "0.2.0" -"fragment-cache@^0.2.1": - "integrity" "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=" - "resolved" "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" - "version" "0.2.1" - dependencies: - "map-cache" "^0.2.2" +"fraction.js@^4.2.0": + "integrity" "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + "resolved" "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz" + "version" "4.2.0" "fresh@0.5.2": - "integrity" "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity" "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" "resolved" "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" "version" "0.5.2" -"fs-extra@^9.1.0": +"fs-extra@^10.1.0": + "integrity" "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==" + "resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" + "version" "10.1.0" + dependencies: + "graceful-fs" "^4.2.0" + "jsonfile" "^6.0.1" + "universalify" "^2.0.0" + +"fs-extra@^9.0.0": "integrity" "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==" "resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" "version" "9.1.0" @@ -4398,20 +4290,24 @@ "jsonfile" "^6.0.1" "universalify" "^2.0.0" +"fs-minipass@^1.2.7": + "integrity" "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==" + "resolved" "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz" + "version" "1.2.7" + dependencies: + "minipass" "^2.6.0" + +"fs-monkey@^1.0.3": + "integrity" "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + "resolved" "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz" + "version" "1.0.3" + "fs.realpath@^1.0.0": - "integrity" "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity" "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" "version" "1.0.0" -"fsevents@^1.2.7": - "integrity" "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==" - "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz" - "version" "1.2.13" - dependencies: - "bindings" "^1.5.0" - "nan" "^2.12.1" - -"fsevents@~2.3.1": +"fsevents@~2.3.2": "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" "version" "2.3.2" @@ -4421,42 +4317,45 @@ "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" "version" "1.1.1" +"function.prototype.name@^1.1.5": + "integrity" "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==" + "resolved" "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" + "version" "1.1.5" + dependencies: + "call-bind" "^1.0.2" + "define-properties" "^1.1.3" + "es-abstract" "^1.19.0" + "functions-have-names" "^1.2.2" + "functional-red-black-tree@^1.0.1": - "integrity" "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + "integrity" "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" "resolved" "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" "version" "1.0.1" +"functions-have-names@^1.2.2": + "integrity" "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + "resolved" "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + "version" "1.2.3" + "gensync@^1.0.0-beta.1", "gensync@^1.0.0-beta.2": "integrity" "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" "resolved" "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" "version" "1.0.0-beta.2" -"get-caller-file@^2.0.1": - "integrity" "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - "resolved" "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" - "version" "2.0.5" - -"get-intrinsic@^1.0.2", "get-intrinsic@^1.1.0", "get-intrinsic@^1.1.1": - "integrity" "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==" - "resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz" - "version" "1.1.1" +"get-intrinsic@^1.0.2", "get-intrinsic@^1.1.0", "get-intrinsic@^1.1.1", "get-intrinsic@^1.1.3": + "integrity" "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==" + "resolved" "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz" + "version" "1.1.3" dependencies: "function-bind" "^1.1.1" "has" "^1.0.3" - "has-symbols" "^1.0.1" + "has-symbols" "^1.0.3" "get-own-enumerable-property-symbols@^3.0.0": "integrity" "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" "resolved" "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz" "version" "3.0.2" -"get-stream@^4.0.0": - "integrity" "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==" - "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" - "version" "4.1.0" - dependencies: - "pump" "^3.0.0" - "get-stream@^4.1.0": "integrity" "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==" "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" @@ -4476,47 +4375,47 @@ "resolved" "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" "version" "6.0.1" -"get-value@^2.0.3", "get-value@^2.0.6": - "integrity" "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - "resolved" "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz" - "version" "2.0.6" - -"github-slugger@^1.3.0": - "integrity" "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==" - "resolved" "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz" - "version" "1.3.0" +"get-symbol-description@^1.0.0": + "integrity" "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==" + "resolved" "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + "version" "1.0.0" dependencies: - "emoji-regex" ">=6.0.0 <=6.1.1" + "call-bind" "^1.0.2" + "get-intrinsic" "^1.1.1" -"glob-parent@^3.1.0": - "integrity" "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=" - "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz" - "version" "3.1.0" - dependencies: - "is-glob" "^3.1.0" - "path-dirname" "^1.0.0" +"github-slugger@^1.4.0": + "integrity" "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==" + "resolved" "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz" + "version" "1.4.0" -"glob-parent@^5.0.0", "glob-parent@^5.1.0", "glob-parent@^5.1.1", "glob-parent@~5.1.0": +"glob-parent@^5.1.2", "glob-parent@~5.1.2": "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" "version" "5.1.2" dependencies: "is-glob" "^4.0.1" +"glob-parent@^6.0.1": + "integrity" "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + "version" "6.0.2" + dependencies: + "is-glob" "^4.0.3" + "glob-to-regexp@^0.4.1": "integrity" "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" "resolved" "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" "version" "0.4.1" -"glob@^7.0.0", "glob@^7.0.3", "glob@^7.1.3": - "integrity" "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==" - "resolved" "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" - "version" "7.1.7" +"glob@^7.0.0", "glob@^7.1.3", "glob@^7.1.6": + "integrity" "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==" + "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + "version" "7.2.3" dependencies: "fs.realpath" "^1.0.0" "inflight" "^1.0.4" "inherits" "2" - "minimatch" "^3.0.4" + "minimatch" "^3.1.1" "once" "^1.3.0" "path-is-absolute" "^1.0.0" @@ -4527,7 +4426,7 @@ dependencies: "ini" "2.0.0" -"global-modules@2.0.0": +"global-modules@^2.0.0": "integrity" "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==" "resolved" "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" "version" "2.0.0" @@ -4548,61 +4447,42 @@ "resolved" "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" "version" "11.12.0" -"globals@^12.1.0": - "integrity" "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==" - "resolved" "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz" - "version" "12.4.0" - dependencies: - "type-fest" "^0.8.1" - "globals@^13.6.0": - "integrity" "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==" - "resolved" "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz" - "version" "13.8.0" + "integrity" "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==" + "resolved" "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz" + "version" "13.17.0" dependencies: "type-fest" "^0.20.2" -"globby@^11.0.1", "globby@^11.0.2", "globby@^11.0.3": - "integrity" "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==" - "resolved" "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz" - "version" "11.0.3" - dependencies: - "array-union" "^2.1.0" - "dir-glob" "^3.0.1" - "fast-glob" "^3.1.1" - "ignore" "^5.1.4" - "merge2" "^1.3.0" - "slash" "^3.0.0" - -"globby@^6.1.0": - "integrity" "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=" - "resolved" "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz" - "version" "6.1.0" +"globals@^13.9.0": + "integrity" "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==" + "resolved" "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz" + "version" "13.17.0" dependencies: - "array-union" "^1.0.1" - "glob" "^7.0.3" - "object-assign" "^4.0.1" - "pify" "^2.0.0" - "pinkie-promise" "^2.0.0" + "type-fest" "^0.20.2" -"globby@11.0.1": - "integrity" "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==" - "resolved" "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz" - "version" "11.0.1" +"globby@^11.0.1", "globby@^11.0.4", "globby@^11.1.0": + "integrity" "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==" + "resolved" "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + "version" "11.1.0" dependencies: "array-union" "^2.1.0" "dir-glob" "^3.0.1" - "fast-glob" "^3.1.1" - "ignore" "^5.1.4" - "merge2" "^1.3.0" + "fast-glob" "^3.2.9" + "ignore" "^5.2.0" + "merge2" "^1.4.1" "slash" "^3.0.0" -"good-listener@^1.2.2": - "integrity" "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=" - "resolved" "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz" - "version" "1.2.2" +"globby@^13.1.1": + "integrity" "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==" + "resolved" "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz" + "version" "13.1.3" dependencies: - "delegate" "^3.1.2" + "dir-glob" "^3.0.1" + "fast-glob" "^3.2.11" + "ignore" "^5.2.0" + "merge2" "^1.4.1" + "slash" "^4.0.0" "got@^9.6.0": "integrity" "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==" @@ -4621,12 +4501,12 @@ "to-readable-stream" "^1.0.0" "url-parse-lax" "^3.0.0" -"graceful-fs@^4.1.11", "graceful-fs@^4.1.2", "graceful-fs@^4.1.6", "graceful-fs@^4.2.0", "graceful-fs@^4.2.4": - "integrity" "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - "resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz" - "version" "4.2.6" +"graceful-fs@^4.1.2", "graceful-fs@^4.1.6", "graceful-fs@^4.2.0", "graceful-fs@^4.2.4", "graceful-fs@^4.2.6", "graceful-fs@^4.2.9": + "integrity" "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + "resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" + "version" "4.2.10" -"gray-matter@^4.0.2": +"gray-matter@^4.0.3": "integrity" "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==" "resolved" "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz" "version" "4.0.3" @@ -4643,26 +4523,18 @@ dependencies: "duplexer" "^0.1.2" -"gzip-size@5.1.1": - "integrity" "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==" - "resolved" "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz" - "version" "5.1.1" - dependencies: - "duplexer" "^0.1.1" - "pify" "^4.0.1" - "handle-thing@^2.0.0": "integrity" "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" "resolved" "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" "version" "2.0.1" -"has-bigints@^1.0.1": - "integrity" "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" - "resolved" "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" - "version" "1.0.1" +"has-bigints@^1.0.1", "has-bigints@^1.0.2": + "integrity" "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + "resolved" "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + "version" "1.0.2" "has-flag@^3.0.0": - "integrity" "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity" "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" "version" "3.0.0" @@ -4671,41 +4543,24 @@ "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" "version" "4.0.0" -"has-symbols@^1.0.1", "has-symbols@^1.0.2": - "integrity" "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" - "resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz" - "version" "1.0.2" - -"has-value@^0.3.1": - "integrity" "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=" - "resolved" "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" - "version" "0.3.1" - dependencies: - "get-value" "^2.0.3" - "has-values" "^0.1.4" - "isobject" "^2.0.0" - -"has-value@^1.0.0": - "integrity" "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=" - "resolved" "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz" +"has-property-descriptors@^1.0.0": + "integrity" "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==" + "resolved" "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" "version" "1.0.0" dependencies: - "get-value" "^2.0.6" - "has-values" "^1.0.0" - "isobject" "^3.0.0" + "get-intrinsic" "^1.1.1" -"has-values@^0.1.4": - "integrity" "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - "resolved" "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz" - "version" "0.1.4" +"has-symbols@^1.0.2", "has-symbols@^1.0.3": + "integrity" "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "resolved" "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + "version" "1.0.3" -"has-values@^1.0.0": - "integrity" "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=" - "resolved" "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz" +"has-tostringtag@^1.0.0": + "integrity" "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==" + "resolved" "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" "version" "1.0.0" dependencies: - "is-number" "^3.0.0" - "kind-of" "^4.0.0" + "has-symbols" "^1.0.2" "has-yarn@^2.1.0": "integrity" "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" @@ -4732,17 +4587,6 @@ "unist-util-is" "^4.0.0" "web-namespaces" "^1.0.0" -"hast-util-from-parse5@^5.0.0": - "integrity" "sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA==" - "resolved" "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz" - "version" "5.0.3" - dependencies: - "ccount" "^1.0.3" - "hastscript" "^5.0.0" - "property-information" "^5.0.0" - "web-namespaces" "^1.1.2" - "xtend" "^4.0.1" - "hast-util-from-parse5@^6.0.0": "integrity" "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==" "resolved" "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz" @@ -4801,16 +4645,6 @@ "repeat-string" "^1.0.0" "unist-util-find-after" "^3.0.0" -"hastscript@^5.0.0": - "integrity" "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==" - "resolved" "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz" - "version" "5.1.2" - dependencies: - "comma-separated-tokens" "^1.0.0" - "hast-util-parse-selector" "^2.0.0" - "property-information" "^5.0.0" - "space-separated-tokens" "^1.0.0" - "hastscript@^6.0.0": "integrity" "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==" "resolved" "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz" @@ -4827,11 +4661,6 @@ "resolved" "https://registry.npmjs.org/he/-/he-1.2.0.tgz" "version" "1.2.0" -"hex-color-regex@^1.1.0": - "integrity" "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" - "resolved" "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz" - "version" "1.1.0" - "history@^4.9.0": "integrity" "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==" "resolved" "https://registry.npmjs.org/history/-/history-4.10.1.tgz" @@ -4852,7 +4681,7 @@ "react-is" "^16.7.0" "hpack.js@^2.1.6": - "integrity" "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=" + "integrity" "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==" "resolved" "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz" "version" "2.1.6" dependencies: @@ -4861,66 +4690,64 @@ "readable-stream" "^2.0.1" "wbuf" "^1.1.0" -"hsl-regex@^1.0.0": - "integrity" "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=" - "resolved" "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz" - "version" "1.0.0" - -"hsla-regex@^1.0.0": - "integrity" "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=" - "resolved" "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz" - "version" "1.0.0" - -"html-entities@^1.3.1": - "integrity" "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==" - "resolved" "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz" - "version" "1.4.0" +"html-entities@^2.3.2": + "integrity" "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + "resolved" "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz" + "version" "2.3.3" -"html-minifier-terser@^5.0.1", "html-minifier-terser@^5.1.1": - "integrity" "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==" - "resolved" "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz" - "version" "5.1.1" +"html-minifier-terser@^6.0.2", "html-minifier-terser@^6.1.0": + "integrity" "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==" + "resolved" "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + "version" "6.1.0" dependencies: - "camel-case" "^4.1.1" - "clean-css" "^4.2.3" - "commander" "^4.1.1" + "camel-case" "^4.1.2" + "clean-css" "^5.2.2" + "commander" "^8.3.0" "he" "^1.2.0" - "param-case" "^3.0.3" + "param-case" "^3.0.4" "relateurl" "^0.2.7" - "terser" "^4.6.3" + "terser" "^5.10.0" -"html-tags@^3.1.0": - "integrity" "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" - "resolved" "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz" - "version" "3.1.0" +"html-tags@^3.2.0": + "integrity" "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==" + "resolved" "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz" + "version" "3.2.0" "html-void-elements@^1.0.0": "integrity" "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==" "resolved" "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz" "version" "1.0.5" -"html-webpack-plugin@^5.2.0": - "integrity" "sha512-rZsVvPXUYFyME0cuGkyOHfx9hmkFa4pWfxY/mdY38PsBEaVNsRoA+Id+8z6DBDgyv3zaw6XQszdF8HLwfQvcdQ==" - "resolved" "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.3.1.tgz" - "version" "5.3.1" +"html-webpack-plugin@^5.5.0": + "integrity" "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==" + "resolved" "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz" + "version" "5.5.0" dependencies: - "@types/html-minifier-terser" "^5.0.0" - "html-minifier-terser" "^5.0.1" - "lodash" "^4.17.20" - "pretty-error" "^2.1.1" + "@types/html-minifier-terser" "^6.0.0" + "html-minifier-terser" "^6.0.2" + "lodash" "^4.17.21" + "pretty-error" "^4.0.0" "tapable" "^2.0.0" -"htmlparser2@^3.10.1", "htmlparser2@^3.9.1": - "integrity" "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==" - "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz" - "version" "3.10.1" +"htmlparser2@^6.1.0": + "integrity" "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==" + "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz" + "version" "6.1.0" dependencies: - "domelementtype" "^1.3.1" - "domhandler" "^2.3.0" - "domutils" "^1.5.1" - "entities" "^1.1.1" - "inherits" "^2.0.1" - "readable-stream" "^3.1.1" + "domelementtype" "^2.0.1" + "domhandler" "^4.0.0" + "domutils" "^2.5.2" + "entities" "^2.0.0" + +"htmlparser2@^8.0.1": + "integrity" "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==" + "resolved" "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz" + "version" "8.0.1" + dependencies: + "domelementtype" "^2.3.0" + "domhandler" "^5.0.2" + "domutils" "^3.0.1" + "entities" "^4.3.0" "http-cache-semantics@^4.0.0": "integrity" "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" @@ -4928,12 +4755,12 @@ "version" "4.1.0" "http-deceiver@^1.2.7": - "integrity" "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + "integrity" "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" "resolved" "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" "version" "1.2.7" "http-errors@~1.6.2": - "integrity" "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=" + "integrity" "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==" "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" "version" "1.6.3" dependencies: @@ -4942,33 +4769,34 @@ "setprototypeof" "1.1.0" "statuses" ">= 1.4.0 < 2" -"http-errors@~1.7.2", "http-errors@1.7.2": - "integrity" "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==" - "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz" - "version" "1.7.2" +"http-errors@2.0.0": + "integrity" "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==" + "resolved" "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + "version" "2.0.0" dependencies: - "depd" "~1.1.2" - "inherits" "2.0.3" - "setprototypeof" "1.1.1" - "statuses" ">= 1.5.0 < 2" - "toidentifier" "1.0.0" + "depd" "2.0.0" + "inherits" "2.0.4" + "setprototypeof" "1.2.0" + "statuses" "2.0.1" + "toidentifier" "1.0.1" "http-parser-js@>=0.5.1": - "integrity" "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" - "resolved" "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz" - "version" "0.5.3" + "integrity" "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + "resolved" "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" + "version" "0.5.8" -"http-proxy-middleware@0.19.1": - "integrity" "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==" - "resolved" "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz" - "version" "0.19.1" +"http-proxy-middleware@^2.0.3": + "integrity" "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==" + "resolved" "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz" + "version" "2.0.6" dependencies: - "http-proxy" "^1.17.0" - "is-glob" "^4.0.0" - "lodash" "^4.17.11" - "micromatch" "^3.1.10" + "@types/http-proxy" "^1.17.8" + "http-proxy" "^1.18.1" + "is-glob" "^4.0.1" + "is-plain-obj" "^3.0.0" + "micromatch" "^4.0.2" -"http-proxy@^1.17.0": +"http-proxy@^1.18.1": "integrity" "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==" "resolved" "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz" "version" "1.18.1" @@ -4999,17 +4827,24 @@ "resolved" "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" "version" "4.0.6" -"ignore@^5.1.4": - "integrity" "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" - "resolved" "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz" - "version" "5.1.8" +"ignore@^5.2.0": + "integrity" "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" + "resolved" "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" + "version" "5.2.4" -"immer@8.0.1": - "integrity" "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==" - "resolved" "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz" - "version" "8.0.1" +"image-size@^1.0.1": + "integrity" "sha512-xfOoWjceHntRb3qFCrh5ZFORYH8XCdYpASltMhZ/Q0KZiOwjdE/Yl2QCiWdwD+lygV5bMCvauzgu5PxBX/Yerg==" + "resolved" "https://registry.npmjs.org/image-size/-/image-size-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "queue" "6.0.2" + +"immer@^9.0.7": + "integrity" "sha512-+hBruaLSQvkPfxRiTLK/mi4vLH+/VQS6z2KJahdoxlleFOI8ARqzOF17uy12eFDlqWmPoygwc5evgwcp+dlHhg==" + "resolved" "https://registry.npmjs.org/immer/-/immer-9.0.17.tgz" + "version" "9.0.17" -"import-fresh@^3.0.0", "import-fresh@^3.2.1", "import-fresh@^3.2.2", "import-fresh@^3.3.0": +"import-fresh@^3.0.0", "import-fresh@^3.1.0", "import-fresh@^3.2.1", "import-fresh@^3.3.0": "integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==" "resolved" "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" "version" "3.3.0" @@ -5018,20 +4853,12 @@ "resolve-from" "^4.0.0" "import-lazy@^2.1.0": - "integrity" "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + "integrity" "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==" "resolved" "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz" "version" "2.1.0" -"import-local@^2.0.0": - "integrity" "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==" - "resolved" "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz" - "version" "2.0.0" - dependencies: - "pkg-dir" "^3.0.0" - "resolve-cwd" "^2.0.0" - "imurmurhash@^0.1.4": - "integrity" "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + "integrity" "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" "resolved" "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" "version" "0.1.4" @@ -5040,31 +4867,26 @@ "resolved" "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" "version" "4.0.0" -"indexes-of@^1.0.1": - "integrity" "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" - "resolved" "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz" - "version" "1.0.1" - -"infima@0.2.0-alpha.23": - "integrity" "sha512-V0RTjB1otjpH3E2asbydx3gz7ovdSJsuV7r9JTdBggqRilnelTJUcXxLawBQQKsjQi5qPcRTjxnlaV8xyyKhhw==" - "resolved" "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.23.tgz" - "version" "0.2.0-alpha.23" +"infima@0.2.0-alpha.42": + "integrity" "sha512-ift8OXNbQQwtbIt6z16KnSWP7uJ/SysSMFI4F87MNRTicypfl4Pv3E2OGVv6N3nSZFJvA8imYulCBS64iyHYww==" + "resolved" "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.42.tgz" + "version" "0.2.0-alpha.42" "inflight@^1.0.4": - "integrity" "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" + "integrity" "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==" "resolved" "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" "version" "1.0.6" dependencies: "once" "^1.3.0" "wrappy" "1" -"inherits@^2.0.0", "inherits@^2.0.1", "inherits@^2.0.3", "inherits@^2.0.4", "inherits@~2.0.3", "inherits@2": +"inherits@^2.0.0", "inherits@^2.0.1", "inherits@^2.0.3", "inherits@~2.0.3", "inherits@2", "inherits@2.0.4": "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" "version" "2.0.4" "inherits@2.0.3": - "integrity" "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity" "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" "version" "2.0.3" @@ -5083,14 +4905,6 @@ "resolved" "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz" "version" "0.1.1" -"internal-ip@^4.3.0": - "integrity" "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==" - "resolved" "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz" - "version" "4.3.0" - dependencies: - "default-gateway" "^4.2.0" - "ipaddr.js" "^1.9.0" - "internal-slot@^1.0.3": "integrity" "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==" "resolved" "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz" @@ -5105,40 +4919,23 @@ "resolved" "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" "version" "1.4.0" -"ip-regex@^2.1.0": - "integrity" "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" - "resolved" "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz" - "version" "2.1.0" +"invariant@^2.2.4": + "integrity" "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==" + "resolved" "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" + "version" "2.2.4" + dependencies: + "loose-envify" "^1.0.0" -"ip@^1.1.0", "ip@^1.1.5": - "integrity" "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - "resolved" "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz" - "version" "1.1.5" +"ipaddr.js@^2.0.1": + "integrity" "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + "resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz" + "version" "2.0.1" -"ipaddr.js@^1.9.0", "ipaddr.js@1.9.1": +"ipaddr.js@1.9.1": "integrity" "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" "resolved" "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" "version" "1.9.1" -"is-absolute-url@^3.0.3": - "integrity" "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" - "resolved" "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz" - "version" "3.0.3" - -"is-accessor-descriptor@^0.1.6": - "integrity" "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=" - "resolved" "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz" - "version" "0.1.6" - dependencies: - "kind-of" "^3.0.2" - -"is-accessor-descriptor@^1.0.0": - "integrity" "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==" - "resolved" "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "kind-of" "^6.0.0" - "is-alphabetical@^1.0.0", "is-alphabetical@1.0.4": "integrity" "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" "resolved" "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz" @@ -5152,34 +4949,17 @@ "is-alphabetical" "^1.0.0" "is-decimal" "^1.0.0" -"is-arguments@^1.0.4": - "integrity" "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==" - "resolved" "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz" - "version" "1.1.0" - dependencies: - "call-bind" "^1.0.0" - "is-arrayish@^0.2.1": - "integrity" "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity" "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" "resolved" "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" "version" "0.2.1" -"is-arrayish@^0.3.1": - "integrity" "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - "resolved" "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" - "version" "0.3.2" - "is-bigint@^1.0.1": - "integrity" "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==" - "resolved" "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz" - "version" "1.0.2" - -"is-binary-path@^1.0.0": - "integrity" "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=" - "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz" - "version" "1.0.1" + "integrity" "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==" + "resolved" "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + "version" "1.0.4" dependencies: - "binary-extensions" "^1.0.0" + "has-bigints" "^1.0.1" "is-binary-path@~2.1.0": "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" @@ -5189,26 +4969,22 @@ "binary-extensions" "^2.0.0" "is-boolean-object@^1.1.0": - "integrity" "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==" - "resolved" "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz" - "version" "1.1.1" + "integrity" "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==" + "resolved" "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + "version" "1.1.2" dependencies: "call-bind" "^1.0.2" - -"is-buffer@^1.1.5": - "integrity" "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - "resolved" "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" - "version" "1.1.6" + "has-tostringtag" "^1.0.0" "is-buffer@^2.0.0": "integrity" "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" "resolved" "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" "version" "2.0.5" -"is-callable@^1.1.4", "is-callable@^1.2.3": - "integrity" "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" - "resolved" "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz" - "version" "1.2.3" +"is-callable@^1.1.4", "is-callable@^1.2.7": + "integrity" "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + "resolved" "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + "version" "1.2.7" "is-ci@^2.0.0": "integrity" "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==" @@ -5217,110 +4993,49 @@ dependencies: "ci-info" "^2.0.0" -"is-color-stop@^1.1.0": - "integrity" "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=" - "resolved" "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz" - "version" "1.1.0" - dependencies: - "css-color-names" "^0.0.4" - "hex-color-regex" "^1.1.0" - "hsl-regex" "^1.0.0" - "hsla-regex" "^1.0.0" - "rgb-regex" "^1.0.1" - "rgba-regex" "^1.0.0" - -"is-core-module@^2.2.0": - "integrity" "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==" - "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz" - "version" "2.4.0" +"is-core-module@^2.9.0": + "integrity" "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==" + "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" + "version" "2.11.0" dependencies: "has" "^1.0.3" -"is-data-descriptor@^0.1.4": - "integrity" "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=" - "resolved" "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz" - "version" "0.1.4" - dependencies: - "kind-of" "^3.0.2" - -"is-data-descriptor@^1.0.0": - "integrity" "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==" - "resolved" "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "kind-of" "^6.0.0" - "is-date-object@^1.0.1": - "integrity" "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==" - "resolved" "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz" - "version" "1.0.4" + "integrity" "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==" + "resolved" "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + "version" "1.0.5" + dependencies: + "has-tostringtag" "^1.0.0" "is-decimal@^1.0.0": "integrity" "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" "resolved" "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz" "version" "1.0.4" -"is-descriptor@^0.1.0": - "integrity" "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==" - "resolved" "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz" - "version" "0.1.6" - dependencies: - "is-accessor-descriptor" "^0.1.6" - "is-data-descriptor" "^0.1.4" - "kind-of" "^5.0.0" - -"is-descriptor@^1.0.0", "is-descriptor@^1.0.2": - "integrity" "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==" - "resolved" "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "is-accessor-descriptor" "^1.0.0" - "is-data-descriptor" "^1.0.0" - "kind-of" "^6.0.2" - -"is-docker@^2.0.0": +"is-docker@^2.0.0", "is-docker@^2.1.1": "integrity" "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" "resolved" "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" "version" "2.2.1" -"is-extendable@^0.1.0", "is-extendable@^0.1.1": - "integrity" "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" +"is-extendable@^0.1.0": + "integrity" "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==" "resolved" "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" "version" "0.1.1" -"is-extendable@^1.0.1": - "integrity" "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==" - "resolved" "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz" - "version" "1.0.1" - dependencies: - "is-plain-object" "^2.0.4" - -"is-extglob@^2.1.0", "is-extglob@^2.1.1": - "integrity" "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" +"is-extglob@^2.1.1": + "integrity" "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" "version" "2.1.1" -"is-fullwidth-code-point@^2.0.0": - "integrity" "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" - "version" "2.0.0" - "is-fullwidth-code-point@^3.0.0": "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" "version" "3.0.0" -"is-glob@^3.1.0": - "integrity" "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=" - "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz" - "version" "3.1.0" - dependencies: - "is-extglob" "^2.1.0" - -"is-glob@^4.0.0", "is-glob@^4.0.1", "is-glob@~4.0.1": - "integrity" "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==" - "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz" - "version" "4.0.1" +"is-glob@^4.0.0", "is-glob@^4.0.1", "is-glob@^4.0.3", "is-glob@~4.0.1": + "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==" + "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + "version" "4.0.3" dependencies: "is-extglob" "^2.1.1" @@ -5337,10 +5052,10 @@ "global-dirs" "^3.0.0" "is-path-inside" "^3.0.2" -"is-negative-zero@^2.0.1": - "integrity" "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" - "resolved" "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz" - "version" "2.0.1" +"is-negative-zero@^2.0.2": + "integrity" "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + "resolved" "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + "version" "2.0.2" "is-npm@^5.0.0": "integrity" "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==" @@ -5348,16 +5063,11 @@ "version" "5.0.0" "is-number-object@^1.0.4": - "integrity" "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==" - "resolved" "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz" - "version" "1.0.5" - -"is-number@^3.0.0": - "integrity" "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=" - "resolved" "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz" - "version" "3.0.0" + "integrity" "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==" + "resolved" "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + "version" "1.0.7" dependencies: - "kind-of" "^3.0.2" + "has-tostringtag" "^1.0.0" "is-number@^7.0.0": "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" @@ -5365,7 +5075,7 @@ "version" "7.0.0" "is-obj@^1.0.1": - "integrity" "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + "integrity" "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" "resolved" "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" "version" "1.0.1" @@ -5374,25 +5084,11 @@ "resolved" "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" "version" "2.0.0" -"is-path-cwd@^2.0.0", "is-path-cwd@^2.2.0": +"is-path-cwd@^2.2.0": "integrity" "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" "resolved" "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz" "version" "2.2.0" -"is-path-in-cwd@^2.0.0": - "integrity" "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==" - "resolved" "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz" - "version" "2.1.0" - dependencies: - "is-path-inside" "^2.1.0" - -"is-path-inside@^2.1.0": - "integrity" "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==" - "resolved" "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz" - "version" "2.1.0" - dependencies: - "path-is-inside" "^1.0.2" - "is-path-inside@^3.0.2": "integrity" "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" "resolved" "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" @@ -5403,50 +5099,54 @@ "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" "version" "2.1.0" -"is-plain-object@^2.0.3", "is-plain-object@^2.0.4": +"is-plain-obj@^3.0.0": + "integrity" "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" + "version" "3.0.0" + +"is-plain-object@^2.0.4": "integrity" "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==" "resolved" "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" "version" "2.0.4" dependencies: "isobject" "^3.0.1" -"is-regex@^1.0.4", "is-regex@^1.1.2": - "integrity" "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==" - "resolved" "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz" - "version" "1.1.3" +"is-regex@^1.1.4": + "integrity" "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==" + "resolved" "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + "version" "1.1.4" dependencies: "call-bind" "^1.0.2" - "has-symbols" "^1.0.2" + "has-tostringtag" "^1.0.0" "is-regexp@^1.0.0": - "integrity" "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=" + "integrity" "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" "resolved" "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" "version" "1.0.0" -"is-resolvable@^1.1.0": - "integrity" "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" - "resolved" "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz" - "version" "1.1.0" - -"is-root@^2.1.0", "is-root@2.1.0": +"is-root@^2.1.0": "integrity" "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" "resolved" "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz" "version" "2.1.0" -"is-stream@^1.1.0": - "integrity" "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - "resolved" "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" - "version" "1.1.0" +"is-shared-array-buffer@^1.0.2": + "integrity" "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==" + "resolved" "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "call-bind" "^1.0.2" "is-stream@^2.0.0": - "integrity" "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - "resolved" "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz" - "version" "2.0.0" + "integrity" "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + "resolved" "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + "version" "2.0.1" -"is-string@^1.0.5": - "integrity" "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==" - "resolved" "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz" - "version" "1.0.6" +"is-string@^1.0.5", "is-string@^1.0.7": + "integrity" "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==" + "resolved" "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + "version" "1.0.7" + dependencies: + "has-tostringtag" "^1.0.0" "is-symbol@^1.0.2", "is-symbol@^1.0.3": "integrity" "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==" @@ -5456,31 +5156,28 @@ "has-symbols" "^1.0.2" "is-typedarray@^1.0.0": - "integrity" "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity" "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" "resolved" "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" "version" "1.0.0" +"is-weakref@^1.0.2": + "integrity" "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==" + "resolved" "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "call-bind" "^1.0.2" + "is-whitespace-character@^1.0.0": "integrity" "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==" "resolved" "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz" "version" "1.0.4" -"is-windows@^1.0.2": - "integrity" "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - "resolved" "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" - "version" "1.0.2" - "is-word-character@^1.0.0": "integrity" "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==" "resolved" "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz" "version" "1.0.4" -"is-wsl@^1.1.0": - "integrity" "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" - "resolved" "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" - "version" "1.1.0" - -"is-wsl@^2.1.1": +"is-wsl@^2.2.0": "integrity" "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==" "resolved" "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" "version" "2.2.0" @@ -5492,50 +5189,65 @@ "resolved" "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz" "version" "0.3.0" -"isarray@~1.0.0", "isarray@1.0.0": - "integrity" "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" +"isarray@~1.0.0": + "integrity" "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" "resolved" "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" "version" "1.0.0" "isarray@0.0.1": - "integrity" "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "integrity" "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" "resolved" "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" "version" "0.0.1" "isexe@^2.0.0": - "integrity" "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity" "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" "resolved" "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" "version" "2.0.0" -"isobject@^2.0.0": - "integrity" "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=" - "resolved" "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" - "version" "2.1.0" - dependencies: - "isarray" "1.0.0" - -"isobject@^3.0.0", "isobject@^3.0.1": - "integrity" "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" +"isobject@^3.0.1": + "integrity" "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" "resolved" "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" "version" "3.0.1" -"jest-worker@^26.3.0", "jest-worker@^26.6.2": - "integrity" "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==" - "resolved" "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" - "version" "26.6.2" +"jest-util@^29.3.1": + "integrity" "sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==" + "resolved" "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz" + "version" "29.3.1" + dependencies: + "@jest/types" "^29.3.1" + "@types/node" "*" + "chalk" "^4.0.0" + "ci-info" "^3.2.0" + "graceful-fs" "^4.2.9" + "picomatch" "^2.2.3" + +"jest-worker@^27.4.5": + "integrity" "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==" + "resolved" "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" + "version" "27.5.1" + dependencies: + "@types/node" "*" + "merge-stream" "^2.0.0" + "supports-color" "^8.0.0" + +"jest-worker@^29.1.2": + "integrity" "sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==" + "resolved" "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz" + "version" "29.3.1" dependencies: "@types/node" "*" + "jest-util" "^29.3.1" "merge-stream" "^2.0.0" - "supports-color" "^7.0.0" + "supports-color" "^8.0.0" -"joi@^17.3.0", "joi@^17.4.0": - "integrity" "sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg==" - "resolved" "https://registry.npmjs.org/joi/-/joi-17.4.0.tgz" - "version" "17.4.0" +"joi@^17.6.0": + "integrity" "sha512-YlQsIaS9MHYekzf1Qe11LjTkNzx9qhYluK3172z38RxYoAUf82XMX1p1DG1H4Wtk2ED/vPdSn9OggqtDu+aTow==" + "resolved" "https://registry.npmjs.org/joi/-/joi-17.6.3.tgz" + "version" "17.6.3" dependencies: "@hapi/hoek" "^9.0.0" "@hapi/topo" "^5.0.0" - "@sideway/address" "^4.1.0" + "@sideway/address" "^4.1.3" "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" @@ -5552,7 +5264,7 @@ "argparse" "^1.0.7" "esprima" "^4.0.0" -"js-yaml@^4.0.0": +"js-yaml@^4.1.0": "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" "version" "4.1.0" @@ -5565,21 +5277,16 @@ "version" "2.5.2" "jsesc@~0.5.0": - "integrity" "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + "integrity" "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" "resolved" "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" "version" "0.5.0" "json-buffer@3.0.0": - "integrity" "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + "integrity" "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==" "resolved" "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" "version" "3.0.0" -"json-parse-better-errors@^1.0.2": - "integrity" "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - "resolved" "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" - "version" "1.0.2" - -"json-parse-even-better-errors@^2.3.0": +"json-parse-even-better-errors@^2.3.0", "json-parse-even-better-errors@^2.3.1": "integrity" "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" "resolved" "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" "version" "2.3.1" @@ -5595,28 +5302,14 @@ "version" "1.0.0" "json-stable-stringify-without-jsonify@^1.0.1": - "integrity" "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" + "integrity" "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" "resolved" "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" "version" "1.0.1" -"json3@^3.3.3": - "integrity" "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" - "resolved" "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz" - "version" "3.3.3" - -"json5@^1.0.1": - "integrity" "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==" - "resolved" "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz" - "version" "1.0.1" - dependencies: - "minimist" "^1.2.0" - -"json5@^2.1.2": - "integrity" "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==" - "resolved" "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz" - "version" "2.2.0" - dependencies: - "minimist" "^1.2.5" +"json5@^2.1.2", "json5@^2.2.1": + "integrity" "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + "resolved" "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz" + "version" "2.2.1" "jsonfile@^6.0.1": "integrity" "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==" @@ -5628,12 +5321,12 @@ "graceful-fs" "^4.1.6" "jsx-ast-utils@^2.4.1 || ^3.0.0": - "integrity" "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==" - "resolved" "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz" - "version" "3.2.0" + "integrity" "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==" + "resolved" "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz" + "version" "3.3.3" dependencies: - "array-includes" "^3.1.2" - "object.assign" "^4.1.2" + "array-includes" "^3.1.5" + "object.assign" "^4.1.3" "katex@^0.12.0": "integrity" "sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg==" @@ -5649,37 +5342,6 @@ dependencies: "json-buffer" "3.0.0" -"killable@^1.0.1": - "integrity" "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" - "resolved" "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz" - "version" "1.0.1" - -"kind-of@^3.0.2", "kind-of@^3.0.3": - "integrity" "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" - "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" - "version" "3.2.2" - dependencies: - "is-buffer" "^1.1.5" - -"kind-of@^3.2.0": - "integrity" "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" - "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" - "version" "3.2.2" - dependencies: - "is-buffer" "^1.1.5" - -"kind-of@^4.0.0": - "integrity" "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=" - "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" - "version" "4.0.0" - dependencies: - "is-buffer" "^1.1.5" - -"kind-of@^5.0.0": - "integrity" "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz" - "version" "5.1.0" - "kind-of@^6.0.0", "kind-of@^6.0.2": "integrity" "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" "resolved" "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" @@ -5690,10 +5352,10 @@ "resolved" "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" "version" "3.0.3" -"klona@^2.0.4": - "integrity" "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==" - "resolved" "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz" - "version" "2.0.4" +"klona@^2.0.4", "klona@^2.0.5": + "integrity" "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" + "resolved" "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz" + "version" "2.0.5" "latest-version@^5.1.0": "integrity" "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==" @@ -5715,43 +5377,35 @@ "prelude-ls" "^1.2.1" "type-check" "~0.4.0" +"lilconfig@^2.0.3": + "integrity" "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" + "resolved" "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz" + "version" "2.0.6" + "lines-and-columns@^1.1.6": - "integrity" "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" - "resolved" "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz" - "version" "1.1.6" + "integrity" "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "resolved" "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + "version" "1.2.4" "loader-runner@^4.2.0": - "integrity" "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==" - "resolved" "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz" - "version" "4.2.0" - -"loader-utils@^1.2.3": - "integrity" "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==" - "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" - "version" "1.4.0" - dependencies: - "big.js" "^5.2.2" - "emojis-list" "^3.0.0" - "json5" "^1.0.1" - -"loader-utils@^1.4.0": - "integrity" "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==" - "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" - "version" "1.4.0" - dependencies: - "big.js" "^5.2.2" - "emojis-list" "^3.0.0" - "json5" "^1.0.1" + "integrity" "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" + "resolved" "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz" + "version" "4.3.0" -"loader-utils@^2.0.0", "loader-utils@2.0.0": - "integrity" "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==" - "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz" - "version" "2.0.0" +"loader-utils@^2.0.0": + "integrity" "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz" + "version" "2.0.2" dependencies: "big.js" "^5.2.2" "emojis-list" "^3.0.0" "json5" "^2.1.2" +"loader-utils@^3.2.0": + "integrity" "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" + "resolved" "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz" + "version" "3.2.1" + "locate-path@^3.0.0": "integrity" "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==" "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" @@ -5774,116 +5428,46 @@ dependencies: "p-locate" "^5.0.0" -"lodash.assignin@^4.0.9": - "integrity" "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" - "resolved" "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz" - "version" "4.2.0" - -"lodash.bind@^4.1.4": - "integrity" "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" - "resolved" "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz" - "version" "4.2.1" - -"lodash.clonedeep@^4.5.0": - "integrity" "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - "resolved" "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" - "version" "4.5.0" - "lodash.curry@^4.0.1": - "integrity" "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" + "integrity" "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" "resolved" "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz" "version" "4.1.1" "lodash.debounce@^4.0.8": - "integrity" "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity" "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" "resolved" "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" "version" "4.0.8" -"lodash.defaults@^4.0.1": - "integrity" "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - "resolved" "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" - "version" "4.2.0" - -"lodash.filter@^4.4.0": - "integrity" "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" - "resolved" "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz" - "version" "4.6.0" - -"lodash.flatten@^4.2.0": - "integrity" "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - "resolved" "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" - "version" "4.4.0" - "lodash.flow@^3.3.0": - "integrity" "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" + "integrity" "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" "resolved" "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz" "version" "3.5.0" -"lodash.foreach@^4.3.0": - "integrity" "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" - "resolved" "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz" - "version" "4.5.0" - -"lodash.map@^4.4.0": - "integrity" "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" - "resolved" "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz" - "version" "4.6.0" - "lodash.memoize@^4.1.2": - "integrity" "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + "integrity" "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" "resolved" "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" "version" "4.1.2" -"lodash.merge@^4.4.0": +"lodash.merge@^4.6.2": "integrity" "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" "resolved" "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" "version" "4.6.2" -"lodash.pick@^4.2.1": - "integrity" "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" - "resolved" "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz" - "version" "4.4.0" - -"lodash.reduce@^4.4.0": - "integrity" "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" - "resolved" "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz" - "version" "4.6.0" - -"lodash.reject@^4.4.0": - "integrity" "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" - "resolved" "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz" - "version" "4.6.0" - -"lodash.some@^4.4.0": - "integrity" "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" - "resolved" "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz" - "version" "4.6.0" - -"lodash.toarray@^4.4.0": - "integrity" "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=" - "resolved" "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz" - "version" "4.4.0" - "lodash.truncate@^4.4.2": - "integrity" "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=" + "integrity" "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==" "resolved" "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz" "version" "4.4.2" "lodash.uniq@^4.5.0", "lodash.uniq@4.5.0": - "integrity" "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + "integrity" "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" "resolved" "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" "version" "4.5.0" -"lodash@^4.17.11", "lodash@^4.17.14", "lodash@^4.17.19", "lodash@^4.17.20", "lodash@^4.17.21": +"lodash@^4.17.19", "lodash@^4.17.20", "lodash@^4.17.21": "integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" "version" "4.17.21" -"loglevel@^1.6.8": - "integrity" "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==" - "resolved" "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz" - "version" "1.7.1" - "loose-envify@^1.0.0", "loose-envify@^1.1.0", "loose-envify@^1.2.0", "loose-envify@^1.3.1", "loose-envify@^1.4.0": "integrity" "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==" "resolved" "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" @@ -5922,18 +5506,6 @@ dependencies: "semver" "^6.0.0" -"map-cache@^0.2.2": - "integrity" "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - "resolved" "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" - "version" "0.2.2" - -"map-visit@^1.0.0": - "integrity" "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=" - "resolved" "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "object-visit" "^1.0.0" - "markdown-escapes@^1.0.0": "integrity" "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==" "resolved" "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz" @@ -5977,31 +5549,25 @@ "resolved" "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" "version" "2.0.14" -"mdn-data@2.0.4": - "integrity" "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" - "resolved" "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz" - "version" "2.0.4" - "mdurl@^1.0.0": - "integrity" "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" + "integrity" "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" "resolved" "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz" "version" "1.0.1" "media-typer@0.3.0": - "integrity" "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity" "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" "resolved" "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" "version" "0.3.0" -"memory-fs@^0.4.1": - "integrity" "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=" - "resolved" "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz" - "version" "0.4.1" +"memfs@^3.1.2", "memfs@^3.4.3": + "integrity" "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==" + "resolved" "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz" + "version" "3.4.12" dependencies: - "errno" "^0.1.3" - "readable-stream" "^2.0.1" + "fs-monkey" "^1.0.3" "merge-descriptors@1.0.1": - "integrity" "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity" "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" "resolved" "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" "version" "1.0.1" @@ -6010,64 +5576,40 @@ "resolved" "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" "version" "2.0.0" -"merge2@^1.3.0": +"merge2@^1.3.0", "merge2@^1.4.1": "integrity" "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" "resolved" "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" "version" "1.4.1" "methods@~1.1.2": - "integrity" "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity" "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" "resolved" "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" "version" "1.1.2" -"microevent.ts@~0.1.1": - "integrity" "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==" - "resolved" "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz" - "version" "0.1.1" - -"micromatch@^3.1.10", "micromatch@^3.1.4": - "integrity" "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==" - "resolved" "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" - "version" "3.1.10" - dependencies: - "arr-diff" "^4.0.0" - "array-unique" "^0.3.2" - "braces" "^2.3.1" - "define-property" "^2.0.2" - "extend-shallow" "^3.0.2" - "extglob" "^2.0.4" - "fragment-cache" "^0.2.1" - "kind-of" "^6.0.2" - "nanomatch" "^1.2.9" - "object.pick" "^1.3.0" - "regex-not" "^1.0.0" - "snapdragon" "^0.8.1" - "to-regex" "^3.0.2" - -"micromatch@^4.0.2": - "integrity" "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==" - "resolved" "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz" - "version" "4.0.4" - dependencies: - "braces" "^3.0.1" - "picomatch" "^2.2.3" +"micromatch@^4.0.2", "micromatch@^4.0.4", "micromatch@^4.0.5": + "integrity" "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==" + "resolved" "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + "version" "4.0.5" + dependencies: + "braces" "^3.0.2" + "picomatch" "^2.3.1" -"mime-db@>= 1.43.0 < 2", "mime-db@1.47.0": - "integrity" "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" - "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz" - "version" "1.47.0" +"mime-db@>= 1.43.0 < 2", "mime-db@1.52.0": + "integrity" "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + "version" "1.52.0" "mime-db@~1.33.0": "integrity" "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" "resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" "version" "1.33.0" -"mime-types@^2.1.27", "mime-types@~2.1.17", "mime-types@~2.1.24": - "integrity" "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==" - "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz" - "version" "2.1.30" +"mime-types@^2.1.27", "mime-types@^2.1.31", "mime-types@~2.1.17", "mime-types@~2.1.24", "mime-types@~2.1.34": + "integrity" "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==" + "resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + "version" "2.1.35" dependencies: - "mime-db" "1.47.0" + "mime-db" "1.52.0" "mime-types@2.1.18": "integrity" "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==" @@ -6076,16 +5618,6 @@ dependencies: "mime-db" "~1.33.0" -"mime@^2.3.1": - "integrity" "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" - "resolved" "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz" - "version" "2.5.2" - -"mime@^2.4.4": - "integrity" "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" - "resolved" "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz" - "version" "2.5.2" - "mime@1.6.0": "integrity" "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" "resolved" "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" @@ -6101,153 +5633,116 @@ "resolved" "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" "version" "1.0.1" -"mini-create-react-context@^0.4.0": - "integrity" "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==" - "resolved" "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz" - "version" "0.4.1" - dependencies: - "@babel/runtime" "^7.12.1" - "tiny-warning" "^1.0.3" - -"mini-css-extract-plugin@^1.4.0": - "integrity" "sha512-nPFKI7NSy6uONUo9yn2hIfb9vyYvkFu95qki0e21DQ9uaqNKDP15DGpK0KnV6wDroWxPHtExrdEwx/yDQ8nVRw==" - "resolved" "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.0.tgz" - "version" "1.6.0" +"mini-css-extract-plugin@^2.6.1": + "integrity" "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==" + "resolved" "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz" + "version" "2.7.2" dependencies: - "loader-utils" "^2.0.0" - "schema-utils" "^3.0.0" - "webpack-sources" "^1.1.0" + "schema-utils" "^4.0.0" "minimalistic-assert@^1.0.0": "integrity" "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" "resolved" "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" "version" "1.0.1" -"minimatch@^3.0.4", "minimatch@3.0.4": +"minimatch@^3.0.4", "minimatch@^3.0.5", "minimatch@^3.1.1", "minimatch@^3.1.2": + "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + "version" "3.1.2" + dependencies: + "brace-expansion" "^1.1.7" + +"minimatch@3.0.4": "integrity" "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" "version" "3.0.4" dependencies: "brace-expansion" "^1.1.7" -"minimist@^1.2.0", "minimist@^1.2.5": - "integrity" "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" - "version" "1.2.5" +"minimist@^1.2.0", "minimist@^1.2.5", "minimist@^1.2.6": + "integrity" "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" + "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz" + "version" "1.2.7" -"mixin-deep@^1.2.0": - "integrity" "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==" - "resolved" "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz" - "version" "1.3.2" +"minipass@^2.6.0", "minipass@^2.9.0": + "integrity" "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==" + "resolved" "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz" + "version" "2.9.0" dependencies: - "for-in" "^1.0.2" - "is-extendable" "^1.0.1" + "safe-buffer" "^5.1.2" + "yallist" "^3.0.0" -"mkdirp@^0.5.1": - "integrity" "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==" - "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" - "version" "0.5.5" +"minizlib@^1.3.3": + "integrity" "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==" + "resolved" "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz" + "version" "1.3.3" dependencies: - "minimist" "^1.2.5" + "minipass" "^2.9.0" "mkdirp@^0.5.5": - "integrity" "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==" - "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" - "version" "0.5.5" - dependencies: - "minimist" "^1.2.5" - -"mkdirp@^1.0.4": - "integrity" "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" - "version" "1.0.4" - -"mkdirp@~0.5.1": - "integrity" "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==" - "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" - "version" "0.5.5" + "integrity" "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==" + "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" + "version" "0.5.6" dependencies: - "minimist" "^1.2.5" + "minimist" "^1.2.6" -"module-alias@^2.2.2": - "integrity" "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" - "resolved" "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz" - "version" "2.2.2" - -"ms@^2.1.1", "ms@2.1.2": - "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" - "version" "2.1.2" +"mrmime@^1.0.0": + "integrity" "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==" + "resolved" "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz" + "version" "1.0.1" "ms@2.0.0": - "integrity" "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity" "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" "resolved" "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" "version" "2.0.0" -"ms@2.1.1": - "integrity" "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz" - "version" "2.1.1" +"ms@2.1.2": + "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + "version" "2.1.2" -"multicast-dns-service-types@^1.1.0": - "integrity" "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" - "resolved" "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz" - "version" "1.1.0" +"ms@2.1.3": + "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + "version" "2.1.3" -"multicast-dns@^6.0.1": - "integrity" "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==" - "resolved" "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz" - "version" "6.2.3" +"multicast-dns@^7.2.5": + "integrity" "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==" + "resolved" "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" + "version" "7.2.5" dependencies: - "dns-packet" "^1.3.1" + "dns-packet" "^5.2.2" "thunky" "^1.0.2" -"nan@^2.12.1": - "integrity" "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" - "resolved" "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz" - "version" "2.14.2" - -"nanoid@^3.1.23": - "integrity" "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" - "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz" - "version" "3.1.23" - -"nanomatch@^1.2.9": - "integrity" "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==" - "resolved" "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" - "version" "1.2.13" - dependencies: - "arr-diff" "^4.0.0" - "array-unique" "^0.3.2" - "define-property" "^2.0.2" - "extend-shallow" "^3.0.2" - "fragment-cache" "^0.2.1" - "is-windows" "^1.0.2" - "kind-of" "^6.0.2" - "object.pick" "^1.3.0" - "regex-not" "^1.0.0" - "snapdragon" "^0.8.1" - "to-regex" "^3.0.1" +"nanoid@^3.3.4": + "integrity" "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz" + "version" "3.3.4" "natural-compare@^1.4.0": - "integrity" "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + "integrity" "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" "resolved" "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" "version" "1.4.0" -"negotiator@0.6.2": - "integrity" "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - "resolved" "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" - "version" "0.6.2" +"negotiator@0.6.3": + "integrity" "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "resolved" "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + "version" "0.6.3" "neo-async@^2.6.2": "integrity" "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" "resolved" "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" "version" "2.6.2" -"nice-try@^1.0.4": - "integrity" "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - "resolved" "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" - "version" "1.0.5" +"njre@^0.2.0": + "integrity" "sha512-+Wq8R6VmjK+jI8a9NdzfU6Vh50r3tjsdvl5KJE1OyHeH8I/nx5Ptm12qpO3qNUbstXuZfBDgDL0qQZw9JyjhMw==" + "resolved" "https://registry.npmjs.org/njre/-/njre-0.2.0.tgz" + "version" "0.2.0" + dependencies: + "command-exists-promise" "^2.0.2" + "node-fetch" "^2.5.0" + "tar" "^4.4.8" + "yauzl" "^2.10.0" "no-case@^3.0.4": "integrity" "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==" @@ -6258,35 +5753,28 @@ "tslib" "^2.0.3" "node-emoji@^1.10.0": - "integrity" "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==" - "resolved" "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz" - "version" "1.10.0" + "integrity" "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==" + "resolved" "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz" + "version" "1.11.0" dependencies: - "lodash.toarray" "^4.4.0" + "lodash" "^4.17.21" -"node-fetch@2.6.7": +"node-fetch@^2.5.0", "node-fetch@2.6.7": "integrity" "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==" "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" "version" "2.6.7" dependencies: "whatwg-url" "^5.0.0" -"node-forge@^0.10.0": - "integrity" "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" - "resolved" "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz" - "version" "0.10.0" - -"node-releases@^1.1.61", "node-releases@^1.1.71": - "integrity" "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==" - "resolved" "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz" - "version" "1.1.72" +"node-forge@^1": + "integrity" "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + "resolved" "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" + "version" "1.3.1" -"normalize-path@^2.1.1": - "integrity" "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=" - "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" - "version" "2.1.1" - dependencies: - "remove-trailing-separator" "^1.0.1" +"node-releases@^2.0.6": + "integrity" "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + "resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz" + "version" "2.0.6" "normalize-path@^3.0.0", "normalize-path@~3.0.0": "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" @@ -6294,21 +5782,19 @@ "version" "3.0.0" "normalize-range@^0.1.2": - "integrity" "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + "integrity" "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" "resolved" "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" "version" "0.1.2" -"normalize-url@^4.1.0", "normalize-url@^4.5.0": +"normalize-url@^4.1.0": "integrity" "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" "resolved" "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz" "version" "4.5.1" -"npm-run-path@^2.0.0": - "integrity" "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=" - "resolved" "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz" - "version" "2.0.2" - dependencies: - "path-key" "^2.0.0" +"normalize-url@^6.0.1": + "integrity" "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + "resolved" "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" + "version" "6.1.0" "npm-run-path@^4.0.1": "integrity" "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==" @@ -6318,128 +5804,86 @@ "path-key" "^3.0.0" "nprogress@^0.2.0": - "integrity" "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" + "integrity" "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" "resolved" "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz" "version" "0.2.0" -"nth-check@^1.0.2", "nth-check@~1.0.1": - "integrity" "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==" - "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "boolbase" "~1.0.0" - -"nth-check@^2.0.0": - "integrity" "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==" - "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz" - "version" "2.0.0" +"nth-check@^2.0.1": + "integrity" "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==" + "resolved" "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" + "version" "2.1.1" dependencies: "boolbase" "^1.0.0" -"object-assign@^4.0.1", "object-assign@^4.1.0", "object-assign@^4.1.1": - "integrity" "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" +"object-assign@^4.1.0", "object-assign@^4.1.1": + "integrity" "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" "resolved" "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" "version" "4.1.1" -"object-copy@^0.1.0": - "integrity" "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=" - "resolved" "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" - "version" "0.1.0" - dependencies: - "copy-descriptor" "^0.1.0" - "define-property" "^0.2.5" - "kind-of" "^3.0.3" - -"object-inspect@^1.9.0": - "integrity" "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==" - "resolved" "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz" - "version" "1.10.3" - -"object-is@^1.0.1": - "integrity" "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==" - "resolved" "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" - "version" "1.1.5" - dependencies: - "call-bind" "^1.0.2" - "define-properties" "^1.1.3" +"object-inspect@^1.12.2", "object-inspect@^1.9.0": + "integrity" "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + "resolved" "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz" + "version" "1.12.2" -"object-keys@^1.0.12", "object-keys@^1.1.1": +"object-keys@^1.1.1": "integrity" "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" "resolved" "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" "version" "1.1.1" -"object-visit@^1.0.0": - "integrity" "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=" - "resolved" "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz" - "version" "1.0.1" - dependencies: - "isobject" "^3.0.0" - -"object.assign@^4.1.0", "object.assign@^4.1.2": - "integrity" "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==" - "resolved" "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz" - "version" "4.1.2" +"object.assign@^4.1.0", "object.assign@^4.1.3", "object.assign@^4.1.4": + "integrity" "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==" + "resolved" "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" + "version" "4.1.4" dependencies: - "call-bind" "^1.0.0" - "define-properties" "^1.1.3" - "has-symbols" "^1.0.1" + "call-bind" "^1.0.2" + "define-properties" "^1.1.4" + "has-symbols" "^1.0.3" "object-keys" "^1.1.1" -"object.entries@^1.1.3": - "integrity" "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==" - "resolved" "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz" - "version" "1.1.3" - dependencies: - "call-bind" "^1.0.0" - "define-properties" "^1.1.3" - "es-abstract" "^1.18.0-next.1" - "has" "^1.0.3" - -"object.fromentries@^2.0.4": - "integrity" "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==" - "resolved" "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz" - "version" "2.0.4" +"object.entries@^1.1.5": + "integrity" "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==" + "resolved" "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz" + "version" "1.1.5" dependencies: "call-bind" "^1.0.2" "define-properties" "^1.1.3" - "es-abstract" "^1.18.0-next.2" - "has" "^1.0.3" + "es-abstract" "^1.19.1" -"object.getownpropertydescriptors@^2.1.0": - "integrity" "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==" - "resolved" "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz" - "version" "2.1.2" +"object.fromentries@^2.0.5": + "integrity" "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==" + "resolved" "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz" + "version" "2.0.5" dependencies: "call-bind" "^1.0.2" "define-properties" "^1.1.3" - "es-abstract" "^1.18.0-next.2" + "es-abstract" "^1.19.1" -"object.pick@^1.3.0": - "integrity" "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=" - "resolved" "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" - "version" "1.3.0" +"object.hasown@^1.1.1": + "integrity" "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==" + "resolved" "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz" + "version" "1.1.1" dependencies: - "isobject" "^3.0.1" + "define-properties" "^1.1.4" + "es-abstract" "^1.19.5" -"object.values@^1.1.0", "object.values@^1.1.3": - "integrity" "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==" - "resolved" "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz" - "version" "1.1.3" +"object.values@^1.1.5": + "integrity" "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==" + "resolved" "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz" + "version" "1.1.5" dependencies: "call-bind" "^1.0.2" "define-properties" "^1.1.3" - "es-abstract" "^1.18.0-next.2" - "has" "^1.0.3" + "es-abstract" "^1.19.1" "obuf@^1.0.0", "obuf@^1.1.2": "integrity" "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" "resolved" "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" "version" "1.1.2" -"on-finished@~2.3.0": - "integrity" "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" - "resolved" "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" - "version" "2.3.0" +"on-finished@2.4.1": + "integrity" "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==" + "resolved" "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" + "version" "2.4.1" dependencies: "ee-first" "1.1.1" @@ -6449,7 +5893,7 @@ "version" "1.0.2" "once@^1.3.0", "once@^1.3.1", "once@^1.4.0": - "integrity" "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + "integrity" "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==" "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" "version" "1.4.0" dependencies: @@ -6462,26 +5906,20 @@ dependencies: "mimic-fn" "^2.1.0" -"open@^7.0.2": - "integrity" "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==" - "resolved" "https://registry.npmjs.org/open/-/open-7.4.2.tgz" - "version" "7.4.2" +"open@^8.0.9", "open@^8.4.0": + "integrity" "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==" + "resolved" "https://registry.npmjs.org/open/-/open-8.4.0.tgz" + "version" "8.4.0" dependencies: - "is-docker" "^2.0.0" - "is-wsl" "^2.1.1" + "define-lazy-prop" "^2.0.0" + "is-docker" "^2.1.1" + "is-wsl" "^2.2.0" "opener@^1.5.2": "integrity" "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" "resolved" "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz" "version" "1.5.2" -"opn@^5.5.0": - "integrity" "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==" - "resolved" "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz" - "version" "5.5.0" - dependencies: - "is-wsl" "^1.1.0" - "optionator@^0.9.1": "integrity" "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==" "resolved" "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" @@ -6494,38 +5932,19 @@ "type-check" "^0.4.0" "word-wrap" "^1.2.3" -"original@^1.0.0": - "integrity" "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==" - "resolved" "https://registry.npmjs.org/original/-/original-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "url-parse" "^1.4.3" - "p-cancelable@^1.0.0": "integrity" "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" "resolved" "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz" "version" "1.1.0" -"p-finally@^1.0.0": - "integrity" "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - "resolved" "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" - "version" "1.0.0" - -"p-limit@^2.0.0": - "integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==" - "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" - "version" "2.3.0" - dependencies: - "p-try" "^2.0.0" - -"p-limit@^2.2.0": +"p-limit@^2.0.0", "p-limit@^2.2.0": "integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==" "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" "version" "2.3.0" dependencies: "p-try" "^2.0.0" -"p-limit@^3.0.2", "p-limit@^3.1.0": +"p-limit@^3.0.2": "integrity" "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==" "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" "version" "3.1.0" @@ -6553,11 +5972,6 @@ dependencies: "p-limit" "^3.0.2" -"p-map@^2.0.0": - "integrity" "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" - "resolved" "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz" - "version" "2.1.0" - "p-map@^4.0.0": "integrity" "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==" "resolved" "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz" @@ -6565,12 +5979,13 @@ dependencies: "aggregate-error" "^3.0.0" -"p-retry@^3.0.1": - "integrity" "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==" - "resolved" "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz" - "version" "3.0.1" +"p-retry@^4.5.0": + "integrity" "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==" + "resolved" "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz" + "version" "4.6.2" dependencies: - "retry" "^0.12.0" + "@types/retry" "0.12.0" + "retry" "^0.13.1" "p-try@^2.0.0": "integrity" "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" @@ -6587,7 +6002,7 @@ "registry-url" "^5.0.0" "semver" "^6.2.0" -"param-case@^3.0.3": +"param-case@^3.0.4": "integrity" "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==" "resolved" "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" "version" "3.0.4" @@ -6624,21 +6039,31 @@ "json-parse-even-better-errors" "^2.3.0" "lines-and-columns" "^1.1.6" -"parse-numeric-range@^1.2.0": - "integrity" "sha512-1q2tXpAOplPxcl8vrIGPWz1dJxxfmdRkCFcpxxMBerDnGuuHalOWF/xj9L8Nn5XoTUoB/6F0CeQBp2fMgkOYFg==" - "resolved" "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.2.0.tgz" - "version" "1.2.0" +"parse-numeric-range@^1.3.0": + "integrity" "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" + "resolved" "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz" + "version" "1.3.0" -"parse5@^5.0.0": - "integrity" "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" - "resolved" "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz" - "version" "5.1.1" +"parse5-htmlparser2-tree-adapter@^7.0.0": + "integrity" "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==" + "resolved" "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz" + "version" "7.0.0" + dependencies: + "domhandler" "^5.0.2" + "parse5" "^7.0.0" "parse5@^6.0.0": "integrity" "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" "resolved" "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" "version" "6.0.1" +"parse5@^7.0.0": + "integrity" "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==" + "resolved" "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz" + "version" "7.1.2" + dependencies: + "entities" "^4.4.0" + "parseurl@~1.3.2", "parseurl@~1.3.3": "integrity" "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" "resolved" "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" @@ -6652,18 +6077,8 @@ "no-case" "^3.0.4" "tslib" "^2.0.3" -"pascalcase@^0.1.1": - "integrity" "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - "resolved" "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" - "version" "0.1.1" - -"path-dirname@^1.0.0": - "integrity" "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" - "resolved" "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz" - "version" "1.0.2" - "path-exists@^3.0.0": - "integrity" "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "integrity" "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" "version" "3.0.0" @@ -6673,26 +6088,21 @@ "version" "4.0.0" "path-is-absolute@^1.0.0": - "integrity" "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity" "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" "version" "1.0.1" -"path-is-inside@^1.0.2", "path-is-inside@1.0.2": - "integrity" "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" +"path-is-inside@1.0.2": + "integrity" "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" "resolved" "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" "version" "1.0.2" -"path-key@^2.0.0", "path-key@^2.0.1": - "integrity" "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - "resolved" "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" - "version" "2.0.1" - "path-key@^3.0.0", "path-key@^3.1.0": "integrity" "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" "resolved" "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" "version" "3.1.1" -"path-parse@^1.0.6": +"path-parse@^1.0.7": "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" "version" "1.0.7" @@ -6705,7 +6115,7 @@ "isarray" "0.0.1" "path-to-regexp@0.1.7": - "integrity" "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity" "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" "resolved" "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" "version" "0.1.7" @@ -6719,39 +6129,20 @@ "resolved" "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" "version" "4.0.0" -"picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.2.3": - "integrity" "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==" - "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz" - "version" "2.2.3" - -"pify@^2.0.0": - "integrity" "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - "resolved" "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" - "version" "2.3.0" - -"pify@^4.0.1": - "integrity" "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - "resolved" "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" - "version" "4.0.1" - -"pinkie-promise@^2.0.0": - "integrity" "sha1-ITXW36ejWMBprJsXh3YogihFD/o=" - "resolved" "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - "version" "2.0.1" - dependencies: - "pinkie" "^2.0.0" +"pend@~1.2.0": + "integrity" "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + "resolved" "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + "version" "1.2.0" -"pinkie@^2.0.0": - "integrity" "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" - "resolved" "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" - "version" "2.0.4" +"picocolors@^1.0.0": + "integrity" "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + "version" "1.0.0" -"pkg-dir@^3.0.0": - "integrity" "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==" - "resolved" "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz" - "version" "3.0.0" - dependencies: - "find-up" "^3.0.0" +"picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.2.3", "picomatch@^2.3.1": + "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + "version" "2.3.1" "pkg-dir@^4.1.0": "integrity" "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==" @@ -6760,77 +6151,65 @@ dependencies: "find-up" "^4.0.0" -"pkg-up@3.1.0": +"pkg-up@^3.1.0": "integrity" "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==" "resolved" "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz" "version" "3.1.0" dependencies: "find-up" "^3.0.0" -"portfinder@^1.0.26": - "integrity" "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==" - "resolved" "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz" - "version" "1.0.28" - dependencies: - "async" "^2.6.2" - "debug" "^3.1.1" - "mkdirp" "^0.5.5" - -"posix-character-classes@^0.1.0": - "integrity" "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - "resolved" "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" - "version" "0.1.1" - -"postcss-calc@^8.0.0": - "integrity" "sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g==" - "resolved" "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.0.0.tgz" - "version" "8.0.0" +"postcss-calc@^8.2.3": + "integrity" "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==" + "resolved" "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz" + "version" "8.2.4" dependencies: - "postcss-selector-parser" "^6.0.2" - "postcss-value-parser" "^4.0.2" + "postcss-selector-parser" "^6.0.9" + "postcss-value-parser" "^4.2.0" -"postcss-colormin@^5.0.0": - "integrity" "sha512-Yt84+5V6CgS/AhK7d7MA58vG8dSZ7+ytlRtWLaQhag3HXOncTfmYpuUOX4cDoXjvLfw1sHRCHMiBjYhc35CymQ==" - "resolved" "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.0.0.tgz" - "version" "5.0.0" +"postcss-colormin@^5.3.0": + "integrity" "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==" + "resolved" "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz" + "version" "5.3.0" dependencies: - "browserslist" "^4.16.0" - "color" "^3.1.1" - "postcss-value-parser" "^4.1.0" + "browserslist" "^4.16.6" + "caniuse-api" "^3.0.0" + "colord" "^2.9.1" + "postcss-value-parser" "^4.2.0" -"postcss-convert-values@^5.0.0": - "integrity" "sha512-V5kmYm4xoBAjNs+eHY/6XzXJkkGeg4kwNf2ocfqhLb1WBPEa4oaSmoi1fnVO7Dkblqvus9h+AenDvhCKUCK7uQ==" - "resolved" "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.0.tgz" - "version" "5.0.0" +"postcss-convert-values@^5.1.3": + "integrity" "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==" + "resolved" "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz" + "version" "5.1.3" dependencies: - "postcss-value-parser" "^4.1.0" + "browserslist" "^4.21.4" + "postcss-value-parser" "^4.2.0" -"postcss-discard-comments@^5.0.0": - "integrity" "sha512-Umig6Gxs8m20RihiXY6QkePd6mp4FxkA1Dg+f/Kd6uw0gEMfKRjDeQOyFkLibexbJJGHpE3lrN/Q0R9SMrUMbQ==" - "resolved" "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.0.tgz" - "version" "5.0.0" +"postcss-discard-comments@^5.1.2": + "integrity" "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==" + "resolved" "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz" + "version" "5.1.2" -"postcss-discard-duplicates@^5.0.0": - "integrity" "sha512-vEJJ+Y3pFUnO1FyCBA6PSisGjHtnphL3V6GsNvkASq/VkP3OX5/No5RYXXLxHa2QegStNzg6HYrYdo71uR4caQ==" - "resolved" "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.0.tgz" - "version" "5.0.0" +"postcss-discard-duplicates@^5.1.0": + "integrity" "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==" + "resolved" "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz" + "version" "5.1.0" -"postcss-discard-empty@^5.0.0": - "integrity" "sha512-+wigy099Y1xZxG36WG5L1f2zeH1oicntkJEW4TDIqKKDO2g9XVB3OhoiHTu08rDEjLnbcab4rw0BAccwi2VjiQ==" - "resolved" "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.0.tgz" - "version" "5.0.0" +"postcss-discard-empty@^5.1.1": + "integrity" "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==" + "resolved" "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz" + "version" "5.1.1" -"postcss-discard-overridden@^5.0.0": - "integrity" "sha512-hybnScTaZM2iEA6kzVQ6Spozy7kVdLw+lGw8hftLlBEzt93uzXoltkYp9u0tI8xbfhxDLTOOzHsHQCkYdmzRUg==" - "resolved" "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.0.tgz" - "version" "5.0.0" +"postcss-discard-overridden@^5.1.0": + "integrity" "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==" + "resolved" "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz" + "version" "5.1.0" -"postcss-discard-unused@^5.0.0": - "integrity" "sha512-C+bchjnGRoGlSQjACMts/FlpY3LMDEUS5+9rHKxvl/NFUY/5OYWjkA1AEUo9HDWnFB44CFgcm6khLMSIbrjVEQ==" - "resolved" "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.0.0.tgz" - "version" "5.0.0" +"postcss-discard-unused@^5.1.0": + "integrity" "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==" + "resolved" "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz" + "version" "5.1.0" dependencies: - "postcss-selector-parser" "^6.0.4" + "postcss-selector-parser" "^6.0.5" "postcss-loader@^4.2.0": "integrity" "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==" @@ -6843,77 +6222,72 @@ "schema-utils" "^3.0.0" "semver" "^7.3.4" -"postcss-loader@^5.2.0": - "integrity" "sha512-/+Z1RAmssdiSLgIZwnJHwBMnlABPgF7giYzTN2NOfr9D21IJZ4mQC1R2miwp80zno9M4zMD/umGI8cR+2EL5zw==" - "resolved" "https://registry.npmjs.org/postcss-loader/-/postcss-loader-5.3.0.tgz" - "version" "5.3.0" +"postcss-loader@^7.0.0": + "integrity" "sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==" + "resolved" "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz" + "version" "7.0.2" dependencies: "cosmiconfig" "^7.0.0" - "klona" "^2.0.4" - "semver" "^7.3.4" + "klona" "^2.0.5" + "semver" "^7.3.8" -"postcss-merge-idents@^5.0.0": - "integrity" "sha512-s8wwhAB/SJDPkcVxj31s2SGzgrO66ktUYjWh6j4qwY67Mzxx3/TkK+m/+v6tU/xyW4TmGd4yuyTXsHaaLC0jLg==" - "resolved" "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.0.0.tgz" - "version" "5.0.0" +"postcss-merge-idents@^5.1.1": + "integrity" "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==" + "resolved" "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz" + "version" "5.1.1" dependencies: - "cssnano-utils" "^2.0.0" - "postcss-value-parser" "^4.1.0" + "cssnano-utils" "^3.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-merge-longhand@^5.0.1": - "integrity" "sha512-H1RO8le5deFGumQzuhJjuL0bIXPRysa+w7xtk5KrHe38oiaSS9ksPXDo24+IOS3SETPhip0J5+1uCOW+ALs3Yw==" - "resolved" "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.1.tgz" - "version" "5.0.1" +"postcss-merge-longhand@^5.1.7": + "integrity" "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==" + "resolved" "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz" + "version" "5.1.7" dependencies: - "css-color-names" "^1.0.1" - "postcss-value-parser" "^4.1.0" - "stylehacks" "^5.0.0" + "postcss-value-parser" "^4.2.0" + "stylehacks" "^5.1.1" -"postcss-merge-rules@^5.0.0": - "integrity" "sha512-TfsXbKjNYCGfUPEXGIGPySnMiJbdS+3gcVeV8gwmJP4RajyKZHW8E0FYDL1WmggTj3hi+m+WUCAvqRpX2ut4Kg==" - "resolved" "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.0.tgz" - "version" "5.0.0" +"postcss-merge-rules@^5.1.3": + "integrity" "sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==" + "resolved" "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz" + "version" "5.1.3" dependencies: - "browserslist" "^4.16.0" + "browserslist" "^4.21.4" "caniuse-api" "^3.0.0" - "cssnano-utils" "^2.0.0" - "postcss-selector-parser" "^6.0.4" - "vendors" "^1.0.3" + "cssnano-utils" "^3.1.0" + "postcss-selector-parser" "^6.0.5" -"postcss-minify-font-values@^5.0.0": - "integrity" "sha512-zi2JhFaMOcIaNxhndX5uhsqSY1rexKDp23wV8EOmC9XERqzLbHsoRye3aYF716Zm+hkcR4loqKDt8LZlmihwAg==" - "resolved" "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.0.tgz" - "version" "5.0.0" +"postcss-minify-font-values@^5.1.0": + "integrity" "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==" + "resolved" "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz" + "version" "5.1.0" dependencies: - "postcss-value-parser" "^4.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-minify-gradients@^5.0.0": - "integrity" "sha512-/jPtNgs6JySMwgsE5dPOq8a2xEopWTW3RyqoB9fLqxgR+mDUNLSi7joKd+N1z7FXWgVkc4l/dEBMXHgNAaUbvg==" - "resolved" "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.0.tgz" - "version" "5.0.0" +"postcss-minify-gradients@^5.1.1": + "integrity" "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==" + "resolved" "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz" + "version" "5.1.1" dependencies: - "cssnano-utils" "^2.0.0" - "is-color-stop" "^1.1.0" - "postcss-value-parser" "^4.1.0" + "colord" "^2.9.1" + "cssnano-utils" "^3.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-minify-params@^5.0.0": - "integrity" "sha512-KvZYIxTPBVKjdd+XgObq9A+Sfv8lMkXTpbZTsjhr42XbfWIeLaTItMlygsDWfjArEc3muUfDaUFgNSeDiJ5jug==" - "resolved" "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.0.tgz" - "version" "5.0.0" +"postcss-minify-params@^5.1.4": + "integrity" "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==" + "resolved" "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz" + "version" "5.1.4" dependencies: - "alphanum-sort" "^1.0.2" - "browserslist" "^4.16.0" - "cssnano-utils" "^2.0.0" - "postcss-value-parser" "^4.1.0" - "uniqs" "^2.0.0" + "browserslist" "^4.21.4" + "cssnano-utils" "^3.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-minify-selectors@^5.0.0": - "integrity" "sha512-cEM0O0eWwFIvmo6nfB0lH0vO/XFwgqIvymODbfPXZ1gTA3i76FKnb7TGUrEpiTxaXH6tgYQ6DcTHwRiRS+YQLQ==" - "resolved" "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.0.0.tgz" - "version" "5.0.0" +"postcss-minify-selectors@^5.2.1": + "integrity" "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==" + "resolved" "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz" + "version" "5.2.1" dependencies: - "alphanum-sort" "^1.0.2" - "postcss-selector-parser" "^3.1.2" + "postcss-selector-parser" "^6.0.5" "postcss-modules-extract-imports@^3.0.0": "integrity" "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" @@ -6943,166 +6317,147 @@ dependencies: "icss-utils" "^5.0.0" -"postcss-normalize-charset@^5.0.0": - "integrity" "sha512-pqsCkgo9KmQP0ew6DqSA+uP9YN6EfsW20pQ3JU5JoQge09Z6Too4qU0TNDsTNWuEaP8SWsMp+19l15210MsDZQ==" - "resolved" "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.0.tgz" - "version" "5.0.0" +"postcss-normalize-charset@^5.1.0": + "integrity" "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==" + "resolved" "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz" + "version" "5.1.0" -"postcss-normalize-display-values@^5.0.0": - "integrity" "sha512-t4f2d//gH1f7Ns0Jq3eNdnWuPT7TeLuISZ6RQx4j8gpl5XrhkdshdNcOnlrEK48YU6Tcb6jqK7dorME3N4oOGA==" - "resolved" "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.0.tgz" - "version" "5.0.0" +"postcss-normalize-display-values@^5.1.0": + "integrity" "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==" + "resolved" "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz" + "version" "5.1.0" dependencies: - "cssnano-utils" "^2.0.0" - "postcss-value-parser" "^4.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-normalize-positions@^5.0.0": - "integrity" "sha512-0o6/qU5ky74X/eWYj/tv4iiKCm3YqJnrhmVADpIMNXxzFZywsSQxl8F7cKs8jQEtF3VrJBgcDHTexZy1zgDoYg==" - "resolved" "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.0.tgz" - "version" "5.0.0" +"postcss-normalize-positions@^5.1.1": + "integrity" "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==" + "resolved" "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz" + "version" "5.1.1" dependencies: - "postcss-value-parser" "^4.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-normalize-repeat-style@^5.0.0": - "integrity" "sha512-KRT14JbrXKcFMYuc4q7lh8lvv8u22wLyMrq+UpHKLtbx2H/LOjvWXYdoDxmNrrrJzomAWL+ViEXr48/IhSUJnQ==" - "resolved" "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.0.tgz" - "version" "5.0.0" +"postcss-normalize-repeat-style@^5.1.1": + "integrity" "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==" + "resolved" "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz" + "version" "5.1.1" dependencies: - "cssnano-utils" "^2.0.0" - "postcss-value-parser" "^4.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-normalize-string@^5.0.0": - "integrity" "sha512-wSO4pf7GNcDZpmelREWYADF1+XZWrAcbFLQCOqoE92ZwYgaP/RLumkUTaamEzdT2YKRZAH8eLLKGWotU/7FNPw==" - "resolved" "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.0.tgz" - "version" "5.0.0" +"postcss-normalize-string@^5.1.0": + "integrity" "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==" + "resolved" "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz" + "version" "5.1.0" dependencies: - "postcss-value-parser" "^4.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-normalize-timing-functions@^5.0.0": - "integrity" "sha512-TwPaDX+wl9wO3MUm23lzGmOzGCGKnpk+rSDgzB2INpakD5dgWR3L6bJq1P1LQYzBAvz8fRIj2NWdnZdV4EV98Q==" - "resolved" "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.0.tgz" - "version" "5.0.0" +"postcss-normalize-timing-functions@^5.1.0": + "integrity" "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==" + "resolved" "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz" + "version" "5.1.0" dependencies: - "cssnano-utils" "^2.0.0" - "postcss-value-parser" "^4.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-normalize-unicode@^5.0.0": - "integrity" "sha512-2CpVoz/67rXU5s9tsPZDxG1YGS9OFHwoY9gsLAzrURrCxTAb0H7Vp87/62LvVPgRWTa5ZmvgmqTp2rL8tlm72A==" - "resolved" "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.0.tgz" - "version" "5.0.0" +"postcss-normalize-unicode@^5.1.1": + "integrity" "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==" + "resolved" "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz" + "version" "5.1.1" dependencies: - "browserslist" "^4.16.0" - "postcss-value-parser" "^4.1.0" + "browserslist" "^4.21.4" + "postcss-value-parser" "^4.2.0" -"postcss-normalize-url@^5.0.0": - "integrity" "sha512-ICDaGFBqLgA3dlrCIRuhblLl80D13YtgEV9NJPTYJtgR72vu61KgxAHv+z/lKMs1EbwfSQa3ALjOFLSmXiE34A==" - "resolved" "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.0.tgz" - "version" "5.0.0" +"postcss-normalize-url@^5.1.0": + "integrity" "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==" + "resolved" "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz" + "version" "5.1.0" dependencies: - "is-absolute-url" "^3.0.3" - "normalize-url" "^4.5.0" - "postcss-value-parser" "^4.1.0" + "normalize-url" "^6.0.1" + "postcss-value-parser" "^4.2.0" -"postcss-normalize-whitespace@^5.0.0": - "integrity" "sha512-KRnxQvQAVkJfaeXSz7JlnD9nBN9sFZF9lrk9452Q2uRoqrRSkinqifF8Iex7wZGei2DZVG/qpmDFDmRvbNAOGA==" - "resolved" "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.0.tgz" - "version" "5.0.0" +"postcss-normalize-whitespace@^5.1.1": + "integrity" "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==" + "resolved" "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz" + "version" "5.1.1" dependencies: - "postcss-value-parser" "^4.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-ordered-values@^5.0.0": - "integrity" "sha512-dPr+SRObiHueCIc4IUaG0aOGQmYkuNu50wQvdXTGKy+rzi2mjmPsbeDsheLk5WPb9Zyf2tp8E+I+h40cnivm6g==" - "resolved" "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.0.tgz" - "version" "5.0.0" +"postcss-ordered-values@^5.1.3": + "integrity" "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==" + "resolved" "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz" + "version" "5.1.3" dependencies: - "cssnano-utils" "^2.0.0" - "postcss-value-parser" "^4.1.0" + "cssnano-utils" "^3.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-reduce-idents@^5.0.0": - "integrity" "sha512-wDth7wkXAZ91i7GNe+/PJKyC9NOR2n04U0t5nnqlvlkKhMhnRn/8NJLYQRa7ZZHPGOZcOfvugrhblioTTg2X8A==" - "resolved" "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.0.0.tgz" - "version" "5.0.0" +"postcss-reduce-idents@^5.2.0": + "integrity" "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==" + "resolved" "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz" + "version" "5.2.0" dependencies: - "postcss-value-parser" "^4.1.0" + "postcss-value-parser" "^4.2.0" -"postcss-reduce-initial@^5.0.0": - "integrity" "sha512-wR6pXUaFbSMG1oCKx8pKVA+rnSXCHlca5jMrlmkmif+uig0HNUTV9oGN5kjKsM3mATQAldv2PF9Tbl2vqLFjnA==" - "resolved" "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.0.tgz" - "version" "5.0.0" +"postcss-reduce-initial@^5.1.1": + "integrity" "sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==" + "resolved" "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz" + "version" "5.1.1" dependencies: - "browserslist" "^4.16.0" + "browserslist" "^4.21.4" "caniuse-api" "^3.0.0" -"postcss-reduce-transforms@^5.0.0": - "integrity" "sha512-iHdGODW4YzM3WjVecBhPQt6fpJC4lGQZxJKjkBNHpp2b8dzmvj0ogKThqya+IRodQEFzjfXgYeESkf172FH5Lw==" - "resolved" "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.0.tgz" - "version" "5.0.0" - dependencies: - "cssnano-utils" "^2.0.0" - "postcss-value-parser" "^4.1.0" - -"postcss-selector-parser@^3.1.2": - "integrity" "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==" - "resolved" "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz" - "version" "3.1.2" +"postcss-reduce-transforms@^5.1.0": + "integrity" "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==" + "resolved" "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz" + "version" "5.1.0" dependencies: - "dot-prop" "^5.2.0" - "indexes-of" "^1.0.1" - "uniq" "^1.0.1" + "postcss-value-parser" "^4.2.0" -"postcss-selector-parser@^6.0.2", "postcss-selector-parser@^6.0.4": - "integrity" "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==" - "resolved" "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz" - "version" "6.0.6" +"postcss-selector-parser@^6.0.2", "postcss-selector-parser@^6.0.4", "postcss-selector-parser@^6.0.5", "postcss-selector-parser@^6.0.9": + "integrity" "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==" + "resolved" "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz" + "version" "6.0.10" dependencies: "cssesc" "^3.0.0" "util-deprecate" "^1.0.2" -"postcss-sort-media-queries@^3.8.9": - "integrity" "sha512-pyCWbMrpQq4WjcYFrcVAvxS/+iHnXK5pxa1SAm1s9U4HZjGYU4gkCHwbHbzJ2ZFiiRYpRNRp85QuFvg6ZyKHxw==" - "resolved" "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-3.9.10.tgz" - "version" "3.9.10" +"postcss-sort-media-queries@^4.2.1": + "integrity" "sha512-jAl8gJM2DvuIJiI9sL1CuiHtKM4s5aEIomkU8G3LFvbP+p8i7Sz8VV63uieTgoewGqKbi+hxBTiOKJlB35upCg==" + "resolved" "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.3.0.tgz" + "version" "4.3.0" dependencies: - "sort-css-media-queries" "1.5.4" + "sort-css-media-queries" "2.1.0" -"postcss-svgo@^5.0.0": - "integrity" "sha512-M3/VS4sFI1Yp9g0bPL+xzzCNz5iLdRUztoFaugMit5a8sMfkVzzhwqbsOlD8IFFymCdJDmXmh31waYHWw1K4BA==" - "resolved" "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.0.tgz" - "version" "5.0.0" +"postcss-svgo@^5.1.0": + "integrity" "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==" + "resolved" "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz" + "version" "5.1.0" dependencies: - "postcss-value-parser" "^4.1.0" - "svgo" "^2.3.0" + "postcss-value-parser" "^4.2.0" + "svgo" "^2.7.0" -"postcss-unique-selectors@^5.0.0": - "integrity" "sha512-o9l4pF8SRn7aCMTmzb/kNv/kjV7wPZpZ8Nlb1Gq8v/Qvw969K1wanz1RVA0ehHzWe9+wHXaC2DvZlak/gdMJ5w==" - "resolved" "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.0.tgz" - "version" "5.0.0" +"postcss-unique-selectors@^5.1.1": + "integrity" "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==" + "resolved" "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz" + "version" "5.1.1" dependencies: - "alphanum-sort" "^1.0.2" - "postcss-selector-parser" "^6.0.2" - "uniqs" "^2.0.0" + "postcss-selector-parser" "^6.0.5" -"postcss-value-parser@^4.0.2", "postcss-value-parser@^4.1.0": - "integrity" "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" - "resolved" "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz" - "version" "4.1.0" +"postcss-value-parser@^4.1.0", "postcss-value-parser@^4.2.0": + "integrity" "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "resolved" "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + "version" "4.2.0" -"postcss-zindex@^5.0.0": - "integrity" "sha512-thJp90qNZedxzfljsAnu7V35L/Zue/nVvWzPDLKZuqHmwDuy1vd3xkFVYfEa8WZZQaetvHtsi3uwjVD3UJAVeg==" - "resolved" "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.0.0.tgz" - "version" "5.0.0" - dependencies: - "has" "^1.0.3" - "uniqs" "^2.0.0" +"postcss-zindex@^5.1.0": + "integrity" "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==" + "resolved" "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz" + "version" "5.1.0" -"postcss@^7.0.0 || ^8.0.1", "postcss@^8.0.9", "postcss@^8.1.0", "postcss@^8.2.1", "postcss@^8.2.10", "postcss@^8.2.13", "postcss@^8.2.2", "postcss@^8.2.4", "postcss@^8.2.9": - "integrity" "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==" - "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz" - "version" "8.2.15" +"postcss@^7.0.0 || ^8.0.1", "postcss@^8.0.9", "postcss@^8.1.0", "postcss@^8.2.13", "postcss@^8.2.15", "postcss@^8.2.2", "postcss@^8.3.11", "postcss@^8.4.14", "postcss@^8.4.16", "postcss@^8.4.17", "postcss@^8.4.19": + "integrity" "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==" + "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz" + "version" "8.4.21" dependencies: - "colorette" "^1.2.2" - "nanoid" "^3.1.23" - "source-map" "^0.6.1" + "nanoid" "^3.3.4" + "picocolors" "^1.0.0" + "source-map-js" "^1.0.2" "prelude-ls@^1.2.1": "integrity" "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" @@ -7110,39 +6465,37 @@ "version" "1.2.1" "prepend-http@^2.0.0": - "integrity" "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + "integrity" "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==" "resolved" "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" "version" "2.0.0" "prettier@^2.0.5": - "integrity" "sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==" - "resolved" "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz" - "version" "2.3.0" + "integrity" "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==" + "resolved" "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz" + "version" "2.7.1" -"pretty-error@^2.1.1": - "integrity" "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==" - "resolved" "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz" - "version" "2.1.2" +"pretty-error@^4.0.0": + "integrity" "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==" + "resolved" "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz" + "version" "4.0.0" dependencies: "lodash" "^4.17.20" - "renderkid" "^2.0.4" + "renderkid" "^3.0.0" "pretty-time@^1.1.0": "integrity" "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" "resolved" "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz" "version" "1.1.0" -"prism-react-renderer@^1.1.1": - "integrity" "sha512-GHqzxLYImx1iKN1jJURcuRoA/0ygCcNhfGw1IT8nPIMzarmKQ3Nc+JcG0gi8JXQzuh0C5ShE4npMIoqNin40hg==" - "resolved" "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.2.0.tgz" - "version" "1.2.0" +"prism-react-renderer@^1.3.5": + "integrity" "sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==" + "resolved" "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz" + "version" "1.3.5" -"prismjs@^1.23.0": - "integrity" "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==" - "resolved" "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz" - "version" "1.23.0" - optionalDependencies: - "clipboard" "^2.0.0" +"prismjs@^1.28.0": + "integrity" "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" + "resolved" "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz" + "version" "1.29.0" "process-nextick-args@~2.0.0": "integrity" "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" @@ -7161,30 +6514,22 @@ dependencies: "asap" "~2.0.3" -"prompts@^2.4.0": - "integrity" "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==" - "resolved" "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz" - "version" "2.4.1" - dependencies: - "kleur" "^3.0.3" - "sisteransi" "^1.0.5" - -"prompts@2.4.0": - "integrity" "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==" - "resolved" "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz" - "version" "2.4.0" +"prompts@^2.4.2": + "integrity" "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==" + "resolved" "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" + "version" "2.4.2" dependencies: "kleur" "^3.0.3" "sisteransi" "^1.0.5" -"prop-types@^15.0.0", "prop-types@^15.5.0", "prop-types@^15.6.2", "prop-types@^15.7.2": - "integrity" "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==" - "resolved" "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz" - "version" "15.7.2" +"prop-types@^15.5.0", "prop-types@^15.6.2", "prop-types@^15.7.2", "prop-types@^15.8.1": + "integrity" "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==" + "resolved" "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" + "version" "15.8.1" dependencies: "loose-envify" "^1.4.0" "object-assign" "^4.1.1" - "react-is" "^16.8.1" + "react-is" "^16.13.1" "property-information@^5.0.0", "property-information@^5.3.0": "integrity" "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==" @@ -7193,19 +6538,14 @@ dependencies: "xtend" "^4.0.0" -"proxy-addr@~2.0.5": - "integrity" "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==" - "resolved" "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz" - "version" "2.0.6" +"proxy-addr@~2.0.7": + "integrity" "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==" + "resolved" "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + "version" "2.0.7" dependencies: - "forwarded" "~0.1.2" + "forwarded" "0.2.0" "ipaddr.js" "1.9.1" -"prr@~1.0.1": - "integrity" "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - "resolved" "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" - "version" "1.0.1" - "pump@^3.0.0": "integrity" "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==" "resolved" "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" @@ -7215,7 +6555,7 @@ "once" "^1.3.1" "punycode@^1.3.2": - "integrity" "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "integrity" "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" "resolved" "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" "version" "1.4.1" @@ -7224,11 +6564,6 @@ "resolved" "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" "version" "2.1.1" -"punycode@1.3.2": - "integrity" "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - "resolved" "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - "version" "1.3.2" - "pupa@^2.1.1": "integrity" "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==" "resolved" "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz" @@ -7237,35 +6572,29 @@ "escape-goat" "^2.0.0" "pure-color@^1.2.0": - "integrity" "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" + "integrity" "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" "resolved" "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz" "version" "1.3.0" -"q@^1.1.2": - "integrity" "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" - "resolved" "https://registry.npmjs.org/q/-/q-1.5.1.tgz" - "version" "1.5.1" - -"qs@6.7.0": - "integrity" "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - "resolved" "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz" - "version" "6.7.0" - -"querystring@0.2.0": - "integrity" "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - "resolved" "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" - "version" "0.2.0" - -"querystringify@^2.1.1": - "integrity" "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - "resolved" "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" - "version" "2.2.0" +"qs@6.11.0": + "integrity" "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==" + "resolved" "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" + "version" "6.11.0" + dependencies: + "side-channel" "^1.0.4" "queue-microtask@^1.2.2": "integrity" "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" "resolved" "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" "version" "1.2.3" +"queue@6.0.2": + "integrity" "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==" + "resolved" "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz" + "version" "6.0.2" + dependencies: + "inherits" "~2.0.3" + "randombytes@^2.1.0": "integrity" "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==" "resolved" "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" @@ -7279,21 +6608,21 @@ "version" "1.2.1" "range-parser@1.2.0": - "integrity" "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + "integrity" "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==" "resolved" "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz" "version" "1.2.0" -"raw-body@2.4.0": - "integrity" "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==" - "resolved" "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz" - "version" "2.4.0" +"raw-body@2.5.1": + "integrity" "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==" + "resolved" "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" + "version" "2.5.1" dependencies: - "bytes" "3.1.0" - "http-errors" "1.7.2" + "bytes" "3.1.2" + "http-errors" "2.0.0" "iconv-lite" "0.4.24" "unpipe" "1.0.0" -"rc@^1.2.8": +"rc@^1.2.8", "rc@1.2.8": "integrity" "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==" "resolved" "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" "version" "1.2.8" @@ -7304,7 +6633,7 @@ "strip-json-comments" "~2.0.1" "react-base16-styling@^0.6.0": - "integrity" "sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=" + "integrity" "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==" "resolved" "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz" "version" "0.6.0" dependencies: @@ -7313,37 +6642,37 @@ "lodash.flow" "^3.3.0" "pure-color" "^1.2.0" -"react-dev-utils@^11.0.1": - "integrity" "sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A==" - "resolved" "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz" - "version" "11.0.4" - dependencies: - "@babel/code-frame" "7.10.4" - "address" "1.1.2" - "browserslist" "4.14.2" - "chalk" "2.4.2" - "cross-spawn" "7.0.3" - "detect-port-alt" "1.1.6" - "escape-string-regexp" "2.0.0" - "filesize" "6.1.0" - "find-up" "4.1.0" - "fork-ts-checker-webpack-plugin" "4.1.6" - "global-modules" "2.0.0" - "globby" "11.0.1" - "gzip-size" "5.1.1" - "immer" "8.0.1" - "is-root" "2.1.0" - "loader-utils" "2.0.0" - "open" "^7.0.2" - "pkg-up" "3.1.0" - "prompts" "2.4.0" - "react-error-overlay" "^6.0.9" - "recursive-readdir" "2.2.2" - "shell-quote" "1.7.2" - "strip-ansi" "6.0.0" - "text-table" "0.2.0" - -"react-dom@^16.8.4", "react-dom@^16.8.4 || ^17.0.0", "react-dom@^17.0.0 || ^16.3.0 || ^15.5.4", "react-dom@>= 16.8.0 < 18.0.0": +"react-dev-utils@^12.0.1": + "integrity" "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==" + "resolved" "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz" + "version" "12.0.1" + dependencies: + "@babel/code-frame" "^7.16.0" + "address" "^1.1.2" + "browserslist" "^4.18.1" + "chalk" "^4.1.2" + "cross-spawn" "^7.0.3" + "detect-port-alt" "^1.1.6" + "escape-string-regexp" "^4.0.0" + "filesize" "^8.0.6" + "find-up" "^5.0.0" + "fork-ts-checker-webpack-plugin" "^6.5.0" + "global-modules" "^2.0.0" + "globby" "^11.0.4" + "gzip-size" "^6.0.0" + "immer" "^9.0.7" + "is-root" "^2.1.0" + "loader-utils" "^3.2.0" + "open" "^8.4.0" + "pkg-up" "^3.1.0" + "prompts" "^2.4.2" + "react-error-overlay" "^6.0.11" + "recursive-readdir" "^2.2.2" + "shell-quote" "^1.7.3" + "strip-ansi" "^6.0.1" + "text-table" "^0.2.0" + +"react-dom@*", "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.4", "react-dom@^16.8.4 || ^17.0.0", "react-dom@^17.0.0 || ^16.3.0 || ^15.5.4", "react-dom@>= 16.8.0 < 19.0.0": "integrity" "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==" "resolved" "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz" "version" "16.14.0" @@ -7353,32 +6682,33 @@ "prop-types" "^15.6.2" "scheduler" "^0.19.1" -"react-error-overlay@^6.0.9": - "integrity" "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==" - "resolved" "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz" - "version" "6.0.9" +"react-error-overlay@^6.0.11": + "integrity" "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + "resolved" "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz" + "version" "6.0.11" -"react-fast-compare@^3.1.1": +"react-fast-compare@^3.2.0": "integrity" "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" "resolved" "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz" "version" "3.2.0" -"react-helmet@^6.1.0": - "integrity" "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==" - "resolved" "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz" - "version" "6.1.0" +"react-helmet-async@*", "react-helmet-async@^1.3.0": + "integrity" "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==" + "resolved" "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz" + "version" "1.3.0" dependencies: - "object-assign" "^4.1.1" + "@babel/runtime" "^7.12.5" + "invariant" "^2.2.4" "prop-types" "^15.7.2" - "react-fast-compare" "^3.1.1" - "react-side-effect" "^2.1.0" + "react-fast-compare" "^3.2.0" + "shallowequal" "^1.1.0" -"react-is@^16.6.0", "react-is@^16.7.0", "react-is@^16.8.1": +"react-is@^16.13.1", "react-is@^16.6.0", "react-is@^16.7.0": "integrity" "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" "version" "16.13.1" -"react-json-view@^1.21.1": +"react-json-view@^1.21.3": "integrity" "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==" "resolved" "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz" "version" "1.21.3" @@ -7400,13 +6730,21 @@ dependencies: "@babel/runtime" "^7.10.3" -"react-loadable@*", "react-loadable@^5.5.0": +"react-loadable@*": "integrity" "sha512-C8Aui0ZpMd4KokxRdVAm2bQtI03k2RMRNzOB+IipV3yxFTSVICv7WoUr5L9ALB5BmKO1iHgZtWM8EvYG83otdg==" "resolved" "https://registry.npmjs.org/react-loadable/-/react-loadable-5.5.0.tgz" "version" "5.5.0" dependencies: "prop-types" "^15.5.0" +"react-loadable@npm:@docusaurus/react-loadable@5.5.2": + "integrity" "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==" + "resolved" "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz" + "version" "5.5.2" + dependencies: + "@types/react" "*" + "prop-types" "^15.6.2" + "react-router-config@^5.1.1": "integrity" "sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==" "resolved" "https://registry.npmjs.org/react-router-config/-/react-router-config-5.1.1.tgz" @@ -7414,50 +6752,44 @@ dependencies: "@babel/runtime" "^7.1.2" -"react-router-dom@^5.2.0": - "integrity" "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==" - "resolved" "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz" - "version" "5.2.0" +"react-router-dom@^5.3.3": + "integrity" "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==" + "resolved" "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz" + "version" "5.3.4" dependencies: - "@babel/runtime" "^7.1.2" + "@babel/runtime" "^7.12.13" "history" "^4.9.0" "loose-envify" "^1.3.1" "prop-types" "^15.6.2" - "react-router" "5.2.0" + "react-router" "5.3.4" "tiny-invariant" "^1.0.2" "tiny-warning" "^1.0.0" -"react-router@^5.2.0", "react-router@>=5", "react-router@5.2.0": - "integrity" "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==" - "resolved" "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz" - "version" "5.2.0" +"react-router@^5.3.3", "react-router@>=5", "react-router@5.3.4": + "integrity" "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==" + "resolved" "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz" + "version" "5.3.4" dependencies: - "@babel/runtime" "^7.1.2" + "@babel/runtime" "^7.12.13" "history" "^4.9.0" "hoist-non-react-statics" "^3.1.0" "loose-envify" "^1.3.1" - "mini-create-react-context" "^0.4.0" "path-to-regexp" "^1.7.0" "prop-types" "^15.6.2" "react-is" "^16.6.0" "tiny-invariant" "^1.0.2" "tiny-warning" "^1.0.0" -"react-side-effect@^2.1.0": - "integrity" "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==" - "resolved" "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz" - "version" "2.1.1" - "react-textarea-autosize@^8.3.2": - "integrity" "sha512-JrMWVgQSaExQByP3ggI1eA8zF4mF0+ddVuX7acUeK2V7bmrpjVOY72vmLz2IXFJSAXoY3D80nEzrn0GWajWK3Q==" - "resolved" "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.2.tgz" - "version" "8.3.2" + "integrity" "sha512-YrTFaEHLgJsi8sJVYHBzYn+mkP3prGkmP2DKb/tm0t7CLJY5t1Rxix8070LAKb0wby7bl/lf2EeHkuMihMZMwQ==" + "resolved" "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.0.tgz" + "version" "8.4.0" dependencies: "@babel/runtime" "^7.10.2" - "use-composed-ref" "^1.0.0" - "use-latest" "^1.0.0" + "use-composed-ref" "^1.3.0" + "use-latest" "^1.2.1" -"react@*", "react@^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", "react@^15.0.2 || ^16.0.0 || ^17.0.0", "react@^16.13.1 || ^17.0.0", "react@^16.14.0", "react@^16.3.0 || ^17.0.0", "react@^16.8.0 || ^17.0.0", "react@^16.8.4", "react@^16.8.4 || ^17.0.0", "react@^17.0.0 || ^16.3.0 || ^15.5.4", "react@>= 16.8.0 < 18.0.0", "react@>=0.14.9", "react@>=15", "react@>=16.3.0": +"react@*", "react@^15.0.2 || ^16.0.0 || ^17.0.0", "react@^16.13.1 || ^17.0.0", "react@^16.14.0", "react@^16.6.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.4", "react@^16.8.4 || ^17.0.0", "react@^17.0.0 || ^16.3.0 || ^15.5.4", "react@>= 16.8.0 < 19.0.0", "react@>=0.14.9", "react@>=15": "integrity" "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==" "resolved" "https://registry.npmjs.org/react/-/react-16.14.0.tgz" "version" "16.14.0" @@ -7479,20 +6811,7 @@ "string_decoder" "~1.1.1" "util-deprecate" "~1.0.1" -"readable-stream@^2.0.2": - "integrity" "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==" - "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" - "version" "2.3.7" - dependencies: - "core-util-is" "~1.0.0" - "inherits" "~2.0.3" - "isarray" "~1.0.0" - "process-nextick-args" "~2.0.0" - "safe-buffer" "~5.1.1" - "string_decoder" "~1.1.1" - "util-deprecate" "~1.0.1" - -"readable-stream@^3.0.6", "readable-stream@^3.1.1": +"readable-stream@^3.0.6": "integrity" "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==" "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" "version" "3.6.0" @@ -7501,104 +6820,88 @@ "string_decoder" "^1.1.1" "util-deprecate" "^1.0.1" -"readdirp@^2.2.1": - "integrity" "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==" - "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz" - "version" "2.2.1" - dependencies: - "graceful-fs" "^4.1.11" - "micromatch" "^3.1.10" - "readable-stream" "^2.0.2" - -"readdirp@~3.5.0": - "integrity" "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==" - "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz" - "version" "3.5.0" +"readdirp@~3.6.0": + "integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==" + "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + "version" "3.6.0" dependencies: "picomatch" "^2.2.1" -"reading-time@^1.3.0": - "integrity" "sha512-RJ8J5O6UvrclfZpcPSPuKusrdRfoY7uXXoYOOdeswZNtSkQaewT3919yz6RyloDBR+iwcUyz5zGOUjhgvfuv3g==" - "resolved" "https://registry.npmjs.org/reading-time/-/reading-time-1.3.0.tgz" - "version" "1.3.0" +"reading-time@^1.5.0": + "integrity" "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" + "resolved" "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz" + "version" "1.5.0" "rechoir@^0.6.2": - "integrity" "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=" + "integrity" "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==" "resolved" "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" "version" "0.6.2" dependencies: "resolve" "^1.1.6" -"recursive-readdir@2.2.2": - "integrity" "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==" - "resolved" "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz" - "version" "2.2.2" +"recursive-readdir@^2.2.2": + "integrity" "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==" + "resolved" "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz" + "version" "2.2.3" dependencies: - "minimatch" "3.0.4" + "minimatch" "^3.0.5" -"regenerate-unicode-properties@^8.2.0": - "integrity" "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==" - "resolved" "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz" - "version" "8.2.0" +"regenerate-unicode-properties@^10.1.0": + "integrity" "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==" + "resolved" "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz" + "version" "10.1.0" dependencies: - "regenerate" "^1.4.0" + "regenerate" "^1.4.2" -"regenerate@^1.4.0": +"regenerate@^1.4.2": "integrity" "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" "resolved" "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" "version" "1.4.2" "regenerator-runtime@^0.13.4": - "integrity" "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" - "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz" - "version" "0.13.7" + "integrity" "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" + "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz" + "version" "0.13.10" -"regenerator-transform@^0.14.2": - "integrity" "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==" - "resolved" "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz" - "version" "0.14.5" +"regenerator-transform@^0.15.0": + "integrity" "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==" + "resolved" "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz" + "version" "0.15.0" dependencies: "@babel/runtime" "^7.8.4" -"regex-not@^1.0.0", "regex-not@^1.0.2": - "integrity" "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==" - "resolved" "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz" - "version" "1.0.2" - dependencies: - "extend-shallow" "^3.0.2" - "safe-regex" "^1.1.0" - -"regexp.prototype.flags@^1.2.0", "regexp.prototype.flags@^1.3.1": - "integrity" "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==" - "resolved" "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz" - "version" "1.3.1" +"regexp.prototype.flags@^1.4.1", "regexp.prototype.flags@^1.4.3": + "integrity" "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==" + "resolved" "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz" + "version" "1.4.3" dependencies: "call-bind" "^1.0.2" "define-properties" "^1.1.3" + "functions-have-names" "^1.2.2" "regexpp@^3.1.0": - "integrity" "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==" - "resolved" "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz" - "version" "3.1.0" + "integrity" "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" + "resolved" "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" + "version" "3.2.0" -"regexpu-core@^4.7.1": - "integrity" "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==" - "resolved" "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz" - "version" "4.7.1" +"regexpu-core@^5.1.0": + "integrity" "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==" + "resolved" "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz" + "version" "5.2.1" dependencies: - "regenerate" "^1.4.0" - "regenerate-unicode-properties" "^8.2.0" - "regjsgen" "^0.5.1" - "regjsparser" "^0.6.4" - "unicode-match-property-ecmascript" "^1.0.4" - "unicode-match-property-value-ecmascript" "^1.2.0" + "regenerate" "^1.4.2" + "regenerate-unicode-properties" "^10.1.0" + "regjsgen" "^0.7.1" + "regjsparser" "^0.9.1" + "unicode-match-property-ecmascript" "^2.0.0" + "unicode-match-property-value-ecmascript" "^2.0.0" "registry-auth-token@^4.0.0": - "integrity" "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==" - "resolved" "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz" - "version" "4.2.1" + "integrity" "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==" + "resolved" "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz" + "version" "4.2.2" dependencies: - "rc" "^1.2.8" + "rc" "1.2.8" "registry-url@^5.0.0": "integrity" "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==" @@ -7607,15 +6910,15 @@ dependencies: "rc" "^1.2.8" -"regjsgen@^0.5.1": - "integrity" "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" - "resolved" "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz" - "version" "0.5.2" +"regjsgen@^0.7.1": + "integrity" "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==" + "resolved" "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz" + "version" "0.7.1" -"regjsparser@^0.6.4": - "integrity" "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==" - "resolved" "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz" - "version" "0.6.9" +"regjsparser@^0.9.1": + "integrity" "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==" + "resolved" "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz" + "version" "0.9.1" dependencies: "jsesc" "~0.5.0" @@ -7631,15 +6934,6 @@ "unified" "^9.0.0" "unist-util-visit" "^2.0.0" -"rehype-parse@^6.0.2": - "integrity" "sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug==" - "resolved" "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.2.tgz" - "version" "6.0.2" - dependencies: - "hast-util-from-parse5" "^5.0.0" - "parse5" "^5.0.0" - "xtend" "^4.0.0" - "rehype-parse@^7.0.0": "integrity" "sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==" "resolved" "https://registry.npmjs.org/rehype-parse/-/rehype-parse-7.0.1.tgz" @@ -7649,20 +6943,11 @@ "parse5" "^6.0.0" "relateurl@^0.2.7": - "integrity" "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + "integrity" "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" "resolved" "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz" "version" "0.2.7" -"remark-admonitions@^1.2.1": - "integrity" "sha512-Ji6p68VDvD+H1oS95Fdx9Ar5WA2wcDA4kwrrhVU7fGctC6+d3uiMICu7w7/2Xld+lnU7/gi+432+rRbup5S8ow==" - "resolved" "https://registry.npmjs.org/remark-admonitions/-/remark-admonitions-1.2.1.tgz" - "version" "1.2.1" - dependencies: - "rehype-parse" "^6.0.2" - "unified" "^8.4.2" - "unist-util-visit" "^2.0.1" - -"remark-emoji@^2.1.0": +"remark-emoji@^2.2.0": "integrity" "sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w==" "resolved" "https://registry.npmjs.org/remark-emoji/-/remark-emoji-2.2.0.tgz" "version" "2.2.0" @@ -7724,69 +7009,37 @@ dependencies: "mdast-squeeze-paragraphs" "^4.0.0" -"remove-trailing-separator@^1.0.1": - "integrity" "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - "resolved" "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" - "version" "1.1.0" - -"renderkid@^2.0.4": - "integrity" "sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==" - "resolved" "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz" - "version" "2.0.5" +"renderkid@^3.0.0": + "integrity" "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==" + "resolved" "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz" + "version" "3.0.0" dependencies: - "css-select" "^2.0.2" - "dom-converter" "^0.2" - "htmlparser2" "^3.10.1" - "lodash" "^4.17.20" - "strip-ansi" "^3.0.0" - -"repeat-element@^1.1.2": - "integrity" "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" - "resolved" "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz" - "version" "1.1.4" + "css-select" "^4.1.3" + "dom-converter" "^0.2.0" + "htmlparser2" "^6.1.0" + "lodash" "^4.17.21" + "strip-ansi" "^6.0.1" -"repeat-string@^1.0.0", "repeat-string@^1.5.4", "repeat-string@^1.6.1": - "integrity" "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" +"repeat-string@^1.0.0", "repeat-string@^1.5.4": + "integrity" "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==" "resolved" "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" "version" "1.6.1" -"require-directory@^2.1.1": - "integrity" "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - "resolved" "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - "version" "2.1.1" - "require-from-string@^2.0.2": "integrity" "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" "resolved" "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" "version" "2.0.2" "require-like@>= 0.1.1": - "integrity" "sha1-rW8wwTvs15cBDEaK+ndcDAprR/o=" + "integrity" "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==" "resolved" "https://registry.npmjs.org/require-like/-/require-like-0.1.2.tgz" "version" "0.1.2" -"require-main-filename@^2.0.0": - "integrity" "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - "resolved" "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" - "version" "2.0.0" - "requires-port@^1.0.0": - "integrity" "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + "integrity" "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" "resolved" "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" "version" "1.0.0" -"resolve-cwd@^2.0.0": - "integrity" "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=" - "resolved" "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz" - "version" "2.0.0" - dependencies: - "resolve-from" "^3.0.0" - -"resolve-from@^3.0.0": - "integrity" "sha1-six699nWiBvItuZTM17rywoYh0g=" - "resolved" "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" - "version" "3.0.0" - "resolve-from@^4.0.0": "integrity" "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" "resolved" "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" @@ -7797,66 +7050,41 @@ "resolved" "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz" "version" "3.0.0" -"resolve-url@^0.2.1": - "integrity" "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - "resolved" "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" - "version" "0.2.1" - "resolve@^1.1.6", "resolve@^1.12.0", "resolve@^1.14.2", "resolve@^1.3.2": - "integrity" "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==" - "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz" - "version" "1.20.0" + "integrity" "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==" + "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" + "version" "1.22.1" dependencies: - "is-core-module" "^2.2.0" - "path-parse" "^1.0.6" + "is-core-module" "^2.9.0" + "path-parse" "^1.0.7" + "supports-preserve-symlinks-flag" "^1.0.0" "resolve@^2.0.0-next.3": - "integrity" "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==" - "resolved" "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz" - "version" "2.0.0-next.3" + "integrity" "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==" + "resolved" "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz" + "version" "2.0.0-next.4" dependencies: - "is-core-module" "^2.2.0" - "path-parse" "^1.0.6" + "is-core-module" "^2.9.0" + "path-parse" "^1.0.7" + "supports-preserve-symlinks-flag" "^1.0.0" "responselike@^1.0.2": - "integrity" "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=" + "integrity" "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==" "resolved" "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" "version" "1.0.2" dependencies: "lowercase-keys" "^1.0.0" -"ret@~0.1.10": - "integrity" "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - "resolved" "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" - "version" "0.1.15" - -"retry@^0.12.0": - "integrity" "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" - "resolved" "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" - "version" "0.12.0" +"retry@^0.13.1": + "integrity" "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + "resolved" "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" + "version" "0.13.1" "reusify@^1.0.4": "integrity" "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" "resolved" "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" "version" "1.0.4" -"rgb-regex@^1.0.1": - "integrity" "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=" - "resolved" "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz" - "version" "1.0.1" - -"rgba-regex@^1.0.0": - "integrity" "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=" - "resolved" "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz" - "version" "1.0.0" - -"rimraf@^2.6.3": - "integrity" "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==" - "resolved" "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" - "version" "2.7.1" - dependencies: - "glob" "^7.1.3" - "rimraf@^3.0.2": "integrity" "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==" "resolved" "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" @@ -7864,20 +7092,19 @@ dependencies: "glob" "^7.1.3" -"rtl-detect@^1.0.2": - "integrity" "sha512-2sMcZO60tL9YDEFe24gqddg3hJ+xSmJFN8IExcQUxeHxQzydQrN6GHPL+yAWgzItXSI7es53hcZC9pJneuZDKA==" - "resolved" "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.3.tgz" - "version" "1.0.3" +"rtl-detect@^1.0.4": + "integrity" "sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==" + "resolved" "https://registry.npmjs.org/rtl-detect/-/rtl-detect-1.0.4.tgz" + "version" "1.0.4" -"rtlcss@^3.1.2": - "integrity" "sha512-b04YSX37siupPOWUEguEBReWX2w4QT89C0PI9g2JzZycbq7zrgPmTr1DA1pizSWpKRFdCjjnrx/SSvU4fOHmGg==" - "resolved" "https://registry.npmjs.org/rtlcss/-/rtlcss-3.1.2.tgz" - "version" "3.1.2" +"rtlcss@^3.5.0": + "integrity" "sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A==" + "resolved" "https://registry.npmjs.org/rtlcss/-/rtlcss-3.5.0.tgz" + "version" "3.5.0" dependencies: - "chalk" "^4.1.0" "find-up" "^5.0.0" - "mkdirp" "^1.0.4" - "postcss" "^8.2.4" + "picocolors" "^1.0.0" + "postcss" "^8.3.11" "strip-json-comments" "^3.1.1" "run-parallel@^1.1.9": @@ -7887,36 +7114,43 @@ dependencies: "queue-microtask" "^1.2.2" -"rxjs@^6.6.3": - "integrity" "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==" - "resolved" "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" - "version" "6.6.7" +"rxjs@^7.5.4": + "integrity" "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==" + "resolved" "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz" + "version" "7.8.0" dependencies: - "tslib" "^1.9.0" + "tslib" "^2.1.0" + +"safe-buffer@^5.1.0", "safe-buffer@^5.1.2", "safe-buffer@^5.2.1", "safe-buffer@>=5.1.0", "safe-buffer@~5.2.0", "safe-buffer@5.2.1": + "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + "version" "5.2.1" -"safe-buffer@^5.0.1", "safe-buffer@^5.1.0", "safe-buffer@>=5.1.0", "safe-buffer@~5.1.0", "safe-buffer@~5.1.1", "safe-buffer@5.1.2": +"safe-buffer@~5.1.0", "safe-buffer@~5.1.1": "integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" "version" "5.1.2" -"safe-buffer@~5.2.0": - "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - "version" "5.2.1" +"safe-buffer@5.1.2": + "integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + "version" "5.1.2" -"safe-regex@^1.1.0": - "integrity" "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=" - "resolved" "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz" - "version" "1.1.0" +"safe-regex-test@^1.0.0": + "integrity" "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==" + "resolved" "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" + "version" "1.0.0" dependencies: - "ret" "~0.1.10" + "call-bind" "^1.0.2" + "get-intrinsic" "^1.1.3" + "is-regex" "^1.1.4" "safer-buffer@>= 2.1.2 < 3": "integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" "resolved" "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" "version" "2.1.2" -"sax@^1.2.4", "sax@~1.2.4": +"sax@^1.2.4": "integrity" "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" "resolved" "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" "version" "1.2.4" @@ -7929,15 +7163,6 @@ "loose-envify" "^1.1.0" "object-assign" "^4.1.1" -"schema-utils@^1.0.0": - "integrity" "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==" - "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz" - "version" "1.0.0" - dependencies: - "ajv" "^6.1.0" - "ajv-errors" "^1.0.0" - "ajv-keywords" "^3.1.0" - "schema-utils@^2.6.5": "integrity" "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==" "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" @@ -7948,14 +7173,51 @@ "ajv-keywords" "^3.5.2" "schema-utils@^3.0.0": - "integrity" "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==" - "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz" - "version" "3.0.0" + "integrity" "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz" + "version" "3.1.1" + dependencies: + "@types/json-schema" "^7.0.8" + "ajv" "^6.12.5" + "ajv-keywords" "^3.5.2" + +"schema-utils@^3.1.0": + "integrity" "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz" + "version" "3.1.1" + dependencies: + "@types/json-schema" "^7.0.8" + "ajv" "^6.12.5" + "ajv-keywords" "^3.5.2" + +"schema-utils@^3.1.1": + "integrity" "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz" + "version" "3.1.1" dependencies: - "@types/json-schema" "^7.0.6" + "@types/json-schema" "^7.0.8" "ajv" "^6.12.5" "ajv-keywords" "^3.5.2" +"schema-utils@^4.0.0": + "integrity" "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz" + "version" "4.0.0" + dependencies: + "@types/json-schema" "^7.0.9" + "ajv" "^8.8.0" + "ajv-formats" "^2.1.1" + "ajv-keywords" "^5.0.0" + +"schema-utils@2.7.0": + "integrity" "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==" + "resolved" "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + "version" "2.7.0" + dependencies: + "@types/json-schema" "^7.0.4" + "ajv" "^6.12.2" + "ajv-keywords" "^3.4.1" + "section-matter@^1.0.0": "integrity" "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==" "resolved" "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz" @@ -7965,21 +7227,16 @@ "kind-of" "^6.0.0" "select-hose@^2.0.0": - "integrity" "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + "integrity" "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" "resolved" "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" "version" "2.0.0" -"select@^1.1.2": - "integrity" "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" - "resolved" "https://registry.npmjs.org/select/-/select-1.1.2.tgz" - "version" "1.1.2" - -"selfsigned@^1.10.8": - "integrity" "sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==" - "resolved" "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.11.tgz" - "version" "1.10.11" +"selfsigned@^2.1.1": + "integrity" "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==" + "resolved" "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz" + "version" "2.1.1" dependencies: - "node-forge" "^0.10.0" + "node-forge" "^1" "semver-diff@^3.1.1": "integrity" "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==" @@ -7993,16 +7250,6 @@ "resolved" "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" "version" "5.7.1" -"semver@^5.5.0": - "integrity" "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - "resolved" "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - "version" "5.7.1" - -"semver@^5.6.0": - "integrity" "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - "resolved" "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - "version" "5.7.1" - "semver@^6.0.0": "integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" @@ -8028,41 +7275,36 @@ "resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" "version" "6.3.0" -"semver@^7.2.1", "semver@^7.3.4", "semver@^7.3.5": - "integrity" "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==" - "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz" - "version" "7.3.5" +"semver@^7.2.1", "semver@^7.3.2", "semver@^7.3.4", "semver@^7.3.7", "semver@^7.3.8": + "integrity" "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==" + "resolved" "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" + "version" "7.3.8" dependencies: "lru-cache" "^6.0.0" -"semver@7.0.0": - "integrity" "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" - "resolved" "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz" - "version" "7.0.0" - -"send@0.17.1": - "integrity" "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==" - "resolved" "https://registry.npmjs.org/send/-/send-0.17.1.tgz" - "version" "0.17.1" +"send@0.18.0": + "integrity" "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==" + "resolved" "https://registry.npmjs.org/send/-/send-0.18.0.tgz" + "version" "0.18.0" dependencies: "debug" "2.6.9" - "depd" "~1.1.2" - "destroy" "~1.0.4" + "depd" "2.0.0" + "destroy" "1.2.0" "encodeurl" "~1.0.2" "escape-html" "~1.0.3" "etag" "~1.8.1" "fresh" "0.5.2" - "http-errors" "~1.7.2" + "http-errors" "2.0.0" "mime" "1.6.0" - "ms" "2.1.1" - "on-finished" "~2.3.0" + "ms" "2.1.3" + "on-finished" "2.4.1" "range-parser" "~1.2.1" - "statuses" "~1.5.0" + "statuses" "2.0.1" -"serialize-javascript@^5.0.1": - "integrity" "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==" - "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz" - "version" "5.0.1" +"serialize-javascript@^6.0.0": + "integrity" "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==" + "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + "version" "6.0.0" dependencies: "randombytes" "^2.1.0" @@ -8081,7 +7323,7 @@ "range-parser" "1.2.0" "serve-index@^1.9.1": - "integrity" "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=" + "integrity" "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==" "resolved" "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" "version" "1.9.1" dependencies: @@ -8093,33 +7335,18 @@ "mime-types" "~2.1.17" "parseurl" "~1.3.2" -"serve-static@1.14.1": - "integrity" "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==" - "resolved" "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz" - "version" "1.14.1" +"serve-static@1.15.0": + "integrity" "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==" + "resolved" "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" + "version" "1.15.0" dependencies: "encodeurl" "~1.0.2" "escape-html" "~1.0.3" "parseurl" "~1.3.3" - "send" "0.17.1" - -"set-blocking@^2.0.0": - "integrity" "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - "resolved" "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - "version" "2.0.0" - -"set-value@^2.0.0", "set-value@^2.0.1": - "integrity" "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==" - "resolved" "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" - "version" "2.0.1" - dependencies: - "extend-shallow" "^2.0.1" - "is-extendable" "^0.1.1" - "is-plain-object" "^2.0.3" - "split-string" "^3.0.1" + "send" "0.18.0" "setimmediate@^1.0.5": - "integrity" "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + "integrity" "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" "resolved" "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" "version" "1.0.5" @@ -8128,10 +7355,10 @@ "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" "version" "1.1.0" -"setprototypeof@1.1.1": - "integrity" "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz" - "version" "1.1.1" +"setprototypeof@1.2.0": + "integrity" "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "resolved" "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + "version" "1.2.0" "shallow-clone@^3.0.0": "integrity" "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==" @@ -8140,12 +7367,10 @@ dependencies: "kind-of" "^6.0.2" -"shebang-command@^1.2.0": - "integrity" "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=" - "resolved" "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" - "version" "1.2.0" - dependencies: - "shebang-regex" "^1.0.0" +"shallowequal@^1.1.0": + "integrity" "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + "resolved" "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz" + "version" "1.1.0" "shebang-command@^2.0.0": "integrity" "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==" @@ -8154,25 +7379,20 @@ dependencies: "shebang-regex" "^3.0.0" -"shebang-regex@^1.0.0": - "integrity" "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - "resolved" "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" - "version" "1.0.0" - "shebang-regex@^3.0.0": "integrity" "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" "resolved" "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" "version" "3.0.0" -"shell-quote@1.7.2": - "integrity" "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" - "resolved" "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz" - "version" "1.7.2" +"shell-quote@^1.7.3": + "integrity" "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==" + "resolved" "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz" + "version" "1.7.4" -"shelljs@^0.8.4": - "integrity" "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==" - "resolved" "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz" - "version" "0.8.4" +"shelljs@^0.8.4", "shelljs@^0.8.5": + "integrity" "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==" + "resolved" "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz" + "version" "0.8.5" dependencies: "glob" "^7.0.0" "interpret" "^1.0.0" @@ -8187,25 +7407,18 @@ "get-intrinsic" "^1.0.2" "object-inspect" "^1.9.0" -"signal-exit@^3.0.0", "signal-exit@^3.0.2", "signal-exit@^3.0.3": - "integrity" "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - "resolved" "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" - "version" "3.0.3" - -"simple-swizzle@^0.2.2": - "integrity" "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=" - "resolved" "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" - "version" "0.2.2" - dependencies: - "is-arrayish" "^0.3.1" +"signal-exit@^3.0.2", "signal-exit@^3.0.3": + "integrity" "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "resolved" "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + "version" "3.0.7" "sirv@^1.0.7": - "integrity" "sha512-SR36i3/LSWja7AJNRBz4fF/Xjpn7lQFI30tZ434dIy+bitLYSP+ZEenHg36i23V2SGEz+kqjksg0uOGZ5LPiqg==" - "resolved" "https://registry.npmjs.org/sirv/-/sirv-1.0.11.tgz" - "version" "1.0.11" + "integrity" "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==" + "resolved" "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz" + "version" "1.0.19" dependencies: - "@polka/url" "^1.0.0-next.9" - "mime" "^2.3.1" + "@polka/url" "^1.0.0-next.20" + "mrmime" "^1.0.0" "totalist" "^1.0.0" "sisteransi@^1.0.5": @@ -8213,12 +7426,12 @@ "resolved" "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" "version" "1.0.5" -"sitemap@^6.3.6": - "integrity" "sha512-DoPKNc2/apQZTUnfiOONWctwq7s6dZVspxAZe2VPMNtoqNq7HgXRvlRnbIpKjf+8+piQdWncwcy+YhhTGY5USQ==" - "resolved" "https://registry.npmjs.org/sitemap/-/sitemap-6.4.0.tgz" - "version" "6.4.0" +"sitemap@^7.1.1": + "integrity" "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==" + "resolved" "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz" + "version" "7.1.1" dependencies: - "@types/node" "^14.14.28" + "@types/node" "^17.0.5" "@types/sax" "^1.2.1" "arg" "^5.0.0" "sax" "^1.2.4" @@ -8228,6 +7441,11 @@ "resolved" "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" "version" "3.0.0" +"slash@^4.0.0": + "integrity" "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + "resolved" "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" + "version" "4.0.0" + "slice-ansi@^4.0.0": "integrity" "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==" "resolved" "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz" @@ -8237,121 +7455,43 @@ "astral-regex" "^2.0.0" "is-fullwidth-code-point" "^3.0.0" -"snapdragon-node@^2.0.1": - "integrity" "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==" - "resolved" "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" - "version" "2.1.1" - dependencies: - "define-property" "^1.0.0" - "isobject" "^3.0.0" - "snapdragon-util" "^3.0.1" - -"snapdragon-util@^3.0.1": - "integrity" "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==" - "resolved" "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz" - "version" "3.0.1" - dependencies: - "kind-of" "^3.2.0" - -"snapdragon@^0.8.1": - "integrity" "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==" - "resolved" "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz" - "version" "0.8.2" - dependencies: - "base" "^0.11.1" - "debug" "^2.2.0" - "define-property" "^0.2.5" - "extend-shallow" "^2.0.1" - "map-cache" "^0.2.2" - "source-map" "^0.5.6" - "source-map-resolve" "^0.5.0" - "use" "^3.1.0" - -"sockjs-client@^1.5.0": - "integrity" "sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ==" - "resolved" "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.1.tgz" - "version" "1.5.1" - dependencies: - "debug" "^3.2.6" - "eventsource" "^1.0.7" - "faye-websocket" "^0.11.3" - "inherits" "^2.0.4" - "json3" "^3.3.3" - "url-parse" "^1.5.1" - -"sockjs@^0.3.21": - "integrity" "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==" - "resolved" "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz" - "version" "0.3.21" +"sockjs@^0.3.24": + "integrity" "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==" + "resolved" "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" + "version" "0.3.24" dependencies: "faye-websocket" "^0.11.3" - "uuid" "^3.4.0" + "uuid" "^8.3.2" "websocket-driver" "^0.7.4" -"sort-css-media-queries@1.5.4": - "integrity" "sha512-YP5W/h4Sid/YP7Lp87ejJ5jP13/Mtqt2vx33XyhO+IAugKlufRPbOrPlIiEUuxmpNBSBd3EeeQpFhdu3RfI2Ag==" - "resolved" "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-1.5.4.tgz" - "version" "1.5.4" - -"source-list-map@^2.0.0", "source-list-map@^2.0.1": - "integrity" "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" - "resolved" "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz" - "version" "2.0.1" +"sort-css-media-queries@2.1.0": + "integrity" "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==" + "resolved" "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz" + "version" "2.1.0" -"source-map-resolve@^0.5.0": - "integrity" "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==" - "resolved" "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz" - "version" "0.5.3" - dependencies: - "atob" "^2.1.2" - "decode-uri-component" "^0.2.0" - "resolve-url" "^0.2.1" - "source-map-url" "^0.4.0" - "urix" "^0.1.0" +"source-map-js@^1.0.2": + "integrity" "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + "version" "1.0.2" -"source-map-support@~0.5.12", "source-map-support@~0.5.19": - "integrity" "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==" - "resolved" "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz" - "version" "0.5.19" +"source-map-support@~0.5.20": + "integrity" "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==" + "resolved" "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + "version" "0.5.21" dependencies: "buffer-from" "^1.0.0" "source-map" "^0.6.0" -"source-map-url@^0.4.0": - "integrity" "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" - "resolved" "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" - "version" "0.4.1" - -"source-map@^0.5.0", "source-map@^0.5.6": - "integrity" "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" +"source-map@^0.5.0": + "integrity" "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" "version" "0.5.7" -"source-map@^0.6.0": - "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - "version" "0.6.1" - -"source-map@^0.6.1": - "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - "version" "0.6.1" - -"source-map@~0.6.0": - "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - "version" "0.6.1" - -"source-map@~0.6.1": +"source-map@^0.6.0", "source-map@^0.6.1", "source-map@~0.6.0": "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" "version" "0.6.1" -"source-map@~0.7.2": - "integrity" "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" - "version" "0.7.3" - "space-separated-tokens@^1.0.0": "integrity" "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" "resolved" "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz" @@ -8380,15 +7520,8 @@ "select-hose" "^2.0.0" "spdy-transport" "^3.0.0" -"split-string@^3.0.1", "split-string@^3.0.2": - "integrity" "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==" - "resolved" "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz" - "version" "3.1.0" - dependencies: - "extend-shallow" "^3.0.0" - "sprintf-js@~1.0.2": - "integrity" "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity" "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" "resolved" "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" "version" "1.0.3" @@ -8402,25 +7535,20 @@ "resolved" "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz" "version" "1.0.3" -"static-extend@^0.1.1": - "integrity" "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=" - "resolved" "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" - "version" "0.1.2" - dependencies: - "define-property" "^0.2.5" - "object-copy" "^0.1.0" - -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", "statuses@~1.5.0": - "integrity" "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" +"statuses@>= 1.4.0 < 2": + "integrity" "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" "resolved" "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" "version" "1.5.0" -"std-env@^2.2.1": - "integrity" "sha512-4qT5B45+Kjef2Z6pE0BkskzsH0GO7GrND0wGlTM1ioUe3v0dGYx9ZJH0Aro/YyA8fqQ5EyIKDRjZojJYMFTflw==" - "resolved" "https://registry.npmjs.org/std-env/-/std-env-2.3.0.tgz" - "version" "2.3.0" - dependencies: - "ci-info" "^3.0.0" +"statuses@2.0.1": + "integrity" "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + "resolved" "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + "version" "2.0.1" + +"std-env@^3.0.1": + "integrity" "sha512-cNNS+VYsXIs5gI6gJipO4qZ8YYT274JHvNnQ1/R/x8Q8mdP0qj0zoMchRXmBNPqp/0eOEhX+3g7g6Fgb7meLIQ==" + "resolved" "https://registry.npmjs.org/std-env/-/std-env-3.3.0.tgz" + "version" "3.3.0" "string_decoder@^1.1.1": "integrity" "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==" @@ -8436,52 +7564,55 @@ dependencies: "safe-buffer" "~5.1.0" -"string-width@^3.0.0", "string-width@^3.1.0": - "integrity" "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==" - "resolved" "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" - "version" "3.1.0" - dependencies: - "emoji-regex" "^7.0.1" - "is-fullwidth-code-point" "^2.0.0" - "strip-ansi" "^5.1.0" - -"string-width@^4.0.0", "string-width@^4.1.0", "string-width@^4.2.0": - "integrity" "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==" - "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz" - "version" "4.2.2" +"string-width@^4.0.0", "string-width@^4.1.0", "string-width@^4.2.0", "string-width@^4.2.2", "string-width@^4.2.3": + "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + "version" "4.2.3" dependencies: "emoji-regex" "^8.0.0" "is-fullwidth-code-point" "^3.0.0" - "strip-ansi" "^6.0.0" + "strip-ansi" "^6.0.1" -"string.prototype.matchall@^4.0.4": - "integrity" "sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ==" - "resolved" "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz" - "version" "4.0.4" +"string-width@^5.0.1": + "integrity" "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "eastasianwidth" "^0.2.0" + "emoji-regex" "^9.2.2" + "strip-ansi" "^7.0.1" + +"string.prototype.matchall@^4.0.7": + "integrity" "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==" + "resolved" "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz" + "version" "4.0.7" dependencies: "call-bind" "^1.0.2" "define-properties" "^1.1.3" - "es-abstract" "^1.18.0-next.2" - "has-symbols" "^1.0.1" + "es-abstract" "^1.19.1" + "get-intrinsic" "^1.1.1" + "has-symbols" "^1.0.3" "internal-slot" "^1.0.3" - "regexp.prototype.flags" "^1.3.1" + "regexp.prototype.flags" "^1.4.1" "side-channel" "^1.0.4" -"string.prototype.trimend@^1.0.4": - "integrity" "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==" - "resolved" "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz" - "version" "1.0.4" +"string.prototype.trimend@^1.0.5": + "integrity" "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==" + "resolved" "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz" + "version" "1.0.5" dependencies: "call-bind" "^1.0.2" - "define-properties" "^1.1.3" + "define-properties" "^1.1.4" + "es-abstract" "^1.19.5" -"string.prototype.trimstart@^1.0.4": - "integrity" "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==" - "resolved" "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz" - "version" "1.0.4" +"string.prototype.trimstart@^1.0.5": + "integrity" "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==" + "resolved" "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz" + "version" "1.0.5" dependencies: "call-bind" "^1.0.2" - "define-properties" "^1.1.3" + "define-properties" "^1.1.4" + "es-abstract" "^1.19.5" "stringify-object@^3.3.0": "integrity" "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==" @@ -8492,44 +7623,25 @@ "is-obj" "^1.0.1" "is-regexp" "^1.0.0" -"strip-ansi@^3.0.0": - "integrity" "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=" - "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - "version" "3.0.1" - dependencies: - "ansi-regex" "^2.0.0" - -"strip-ansi@^3.0.1": - "integrity" "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=" - "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - "version" "3.0.1" - dependencies: - "ansi-regex" "^2.0.0" - -"strip-ansi@^5.0.0", "strip-ansi@^5.1.0", "strip-ansi@^5.2.0": - "integrity" "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==" - "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" - "version" "5.2.0" +"strip-ansi@^6.0.0", "strip-ansi@^6.0.1": + "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + "version" "6.0.1" dependencies: - "ansi-regex" "^4.1.0" + "ansi-regex" "^5.0.1" -"strip-ansi@^6.0.0", "strip-ansi@6.0.0": - "integrity" "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==" - "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz" - "version" "6.0.0" +"strip-ansi@^7.0.1": + "integrity" "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz" + "version" "7.0.1" dependencies: - "ansi-regex" "^5.0.0" + "ansi-regex" "^6.0.1" "strip-bom-string@^1.0.0": - "integrity" "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=" + "integrity" "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==" "resolved" "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz" "version" "1.0.0" -"strip-eof@^1.0.0": - "integrity" "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - "resolved" "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz" - "version" "1.0.0" - "strip-final-newline@^2.0.0": "integrity" "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" "resolved" "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" @@ -8541,7 +7653,7 @@ "version" "3.1.1" "strip-json-comments@~2.0.1": - "integrity" "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "integrity" "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" "version" "2.0.1" @@ -8552,12 +7664,12 @@ dependencies: "inline-style-parser" "0.1.1" -"stylehacks@^5.0.0": - "integrity" "sha512-QOWm6XivDLb+fqffTZP8jrmPmPITVChl2KCY2R05nsCWwLi3VGhCdVc3IVGNwd1zzTt1jPd67zIKjpQfxzQZeA==" - "resolved" "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.0.tgz" - "version" "5.0.0" +"stylehacks@^5.1.1": + "integrity" "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==" + "resolved" "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz" + "version" "5.1.1" dependencies: - "browserslist" "^4.16.0" + "browserslist" "^4.21.4" "postcss-selector-parser" "^6.0.4" "supports-color@^5.3.0": @@ -8567,68 +7679,53 @@ dependencies: "has-flag" "^3.0.0" -"supports-color@^6.1.0": - "integrity" "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==" - "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz" - "version" "6.1.0" - dependencies: - "has-flag" "^3.0.0" - -"supports-color@^7.0.0", "supports-color@^7.1.0": +"supports-color@^7.1.0": "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" "version" "7.2.0" dependencies: "has-flag" "^4.0.0" -"svg-parser@^2.0.2": +"supports-color@^8.0.0": + "integrity" "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + "version" "8.1.1" + dependencies: + "has-flag" "^4.0.0" + +"supports-preserve-symlinks-flag@^1.0.0": + "integrity" "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "resolved" "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + "version" "1.0.0" + +"svg-parser@^2.0.4": "integrity" "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" "resolved" "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz" "version" "2.0.4" -"svgo@^1.2.2": - "integrity" "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==" - "resolved" "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz" - "version" "1.3.2" - dependencies: - "chalk" "^2.4.1" - "coa" "^2.0.2" - "css-select" "^2.0.0" - "css-select-base-adapter" "^0.1.1" - "css-tree" "1.0.0-alpha.37" - "csso" "^4.0.2" - "js-yaml" "^3.13.1" - "mkdirp" "~0.5.1" - "object.values" "^1.1.0" - "sax" "~1.2.4" - "stable" "^0.1.8" - "unquote" "~1.1.1" - "util.promisify" "~1.0.0" - -"svgo@^2.3.0": - "integrity" "sha512-fz4IKjNO6HDPgIQxu4IxwtubtbSfGEAJUq/IXyTPIkGhWck/faiiwfkvsB8LnBkKLvSoyNNIY6d13lZprJMc9Q==" - "resolved" "https://registry.npmjs.org/svgo/-/svgo-2.3.0.tgz" - "version" "2.3.0" +"svgo@^2.7.0", "svgo@^2.8.0": + "integrity" "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==" + "resolved" "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz" + "version" "2.8.0" dependencies: - "@trysound/sax" "0.1.1" - "chalk" "^4.1.0" - "commander" "^7.1.0" - "css-select" "^3.1.2" - "css-tree" "^1.1.2" + "@trysound/sax" "0.2.0" + "commander" "^7.2.0" + "css-select" "^4.1.3" + "css-tree" "^1.1.3" "csso" "^4.2.0" + "picocolors" "^1.0.0" "stable" "^0.1.8" -"table@^6.0.4": - "integrity" "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==" - "resolved" "https://registry.npmjs.org/table/-/table-6.7.1.tgz" - "version" "6.7.1" +"table@^6.0.9": + "integrity" "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==" + "resolved" "https://registry.npmjs.org/table/-/table-6.8.0.tgz" + "version" "6.8.0" dependencies: "ajv" "^8.0.1" - "lodash.clonedeep" "^4.5.0" "lodash.truncate" "^4.4.2" "slice-ansi" "^4.0.0" - "string-width" "^4.2.0" - "strip-ansi" "^6.0.0" + "string-width" "^4.2.3" + "strip-ansi" "^6.0.1" "tapable@^1.0.0": "integrity" "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" @@ -8636,42 +7733,46 @@ "version" "1.1.3" "tapable@^2.0.0", "tapable@^2.1.1", "tapable@^2.2.0": - "integrity" "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==" - "resolved" "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz" - "version" "2.2.0" - -"terser-webpack-plugin@^5.1.1": - "integrity" "sha512-6QhDaAiVHIQr5Ab3XUWZyDmrIPCHMiqJVljMF91YKyqwKkL5QHnYMkrMBy96v9Z7ev1hGhSEw1HQZc2p/s5Z8Q==" - "resolved" "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz" - "version" "5.1.2" - dependencies: - "jest-worker" "^26.6.2" - "p-limit" "^3.1.0" - "schema-utils" "^3.0.0" - "serialize-javascript" "^5.0.1" - "source-map" "^0.6.1" - "terser" "^5.7.0" - -"terser@^4.6.3": - "integrity" "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==" - "resolved" "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz" - "version" "4.8.0" - dependencies: - "commander" "^2.20.0" - "source-map" "~0.6.1" - "source-map-support" "~0.5.12" + "integrity" "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + "resolved" "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + "version" "2.2.1" -"terser@^5.7.0": - "integrity" "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==" - "resolved" "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz" - "version" "5.7.0" +"tar@^4.4.8": + "integrity" "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==" + "resolved" "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz" + "version" "4.4.19" dependencies: + "chownr" "^1.1.4" + "fs-minipass" "^1.2.7" + "minipass" "^2.9.0" + "minizlib" "^1.3.3" + "mkdirp" "^0.5.5" + "safe-buffer" "^5.2.1" + "yallist" "^3.1.1" + +"terser-webpack-plugin@^5.1.3", "terser-webpack-plugin@^5.3.3": + "integrity" "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==" + "resolved" "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz" + "version" "5.3.6" + dependencies: + "@jridgewell/trace-mapping" "^0.3.14" + "jest-worker" "^27.4.5" + "schema-utils" "^3.1.1" + "serialize-javascript" "^6.0.0" + "terser" "^5.14.1" + +"terser@^5.10.0", "terser@^5.14.1": + "integrity" "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==" + "resolved" "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz" + "version" "5.16.1" + dependencies: + "@jridgewell/source-map" "^0.3.2" + "acorn" "^8.5.0" "commander" "^2.20.0" - "source-map" "~0.7.2" - "source-map-support" "~0.5.19" + "source-map-support" "~0.5.20" -"text-table@^0.2.0", "text-table@0.2.0": - "integrity" "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" +"text-table@^0.2.0": + "integrity" "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" "resolved" "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" "version" "0.2.0" @@ -8680,51 +7781,26 @@ "resolved" "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" "version" "1.1.0" -"timsort@^0.3.0": - "integrity" "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" - "resolved" "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" - "version" "0.3.0" - -"tiny-emitter@^2.0.0": - "integrity" "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" - "resolved" "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz" - "version" "2.1.0" - "tiny-invariant@^1.0.2": - "integrity" "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" - "resolved" "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz" - "version" "1.1.0" + "integrity" "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + "resolved" "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz" + "version" "1.3.1" -"tiny-warning@^1.0.0", "tiny-warning@^1.0.3": +"tiny-warning@^1.0.0": "integrity" "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" "resolved" "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" "version" "1.0.3" "to-fast-properties@^2.0.0": - "integrity" "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + "integrity" "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" "resolved" "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" "version" "2.0.0" -"to-object-path@^0.3.0": - "integrity" "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=" - "resolved" "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz" - "version" "0.3.0" - dependencies: - "kind-of" "^3.0.2" - "to-readable-stream@^1.0.0": "integrity" "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" "resolved" "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" "version" "1.0.0" -"to-regex-range@^2.1.0": - "integrity" "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=" - "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz" - "version" "2.1.1" - dependencies: - "is-number" "^3.0.0" - "repeat-string" "^1.6.1" - "to-regex-range@^5.0.1": "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" @@ -8732,20 +7808,10 @@ dependencies: "is-number" "^7.0.0" -"to-regex@^3.0.1", "to-regex@^3.0.2": - "integrity" "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==" - "resolved" "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz" - "version" "3.0.2" - dependencies: - "define-property" "^2.0.2" - "extend-shallow" "^3.0.2" - "regex-not" "^1.0.2" - "safe-regex" "^1.1.0" - -"toidentifier@1.0.0": - "integrity" "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - "resolved" "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz" - "version" "1.0.0" +"toidentifier@1.0.1": + "integrity" "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + "resolved" "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + "version" "1.0.1" "totalist@^1.0.0": "integrity" "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" @@ -8753,7 +7819,7 @@ "version" "1.1.0" "tr46@~0.0.3": - "integrity" "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + "integrity" "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" "version" "0.0.3" @@ -8763,7 +7829,7 @@ "version" "1.1.4" "trim@0.0.1": - "integrity" "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + "integrity" "sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ==" "resolved" "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz" "version" "0.0.1" @@ -8772,20 +7838,10 @@ "resolved" "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz" "version" "1.0.5" -"ts-essentials@^2.0.3": - "integrity" "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==" - "resolved" "https://registry.npmjs.org/ts-essentials/-/ts-essentials-2.0.12.tgz" - "version" "2.0.12" - -"tslib@^1.9.0": - "integrity" "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - "resolved" "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" - "version" "1.14.1" - -"tslib@^2.0.3", "tslib@^2.1.0": - "integrity" "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" - "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz" - "version" "2.2.0" +"tslib@^2.0.3", "tslib@^2.1.0", "tslib@^2.4.0": + "integrity" "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz" + "version" "2.4.0" "type-check@^0.4.0", "type-check@~0.4.0": "integrity" "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==" @@ -8799,17 +7855,12 @@ "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" "version" "0.20.2" -"type-fest@^0.21.3": - "integrity" "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" - "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" - "version" "0.21.3" - -"type-fest@^0.8.1": - "integrity" "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" - "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" - "version" "0.8.1" +"type-fest@^2.5.0": + "integrity" "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" + "resolved" "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz" + "version" "2.19.0" -"type-is@~1.6.17", "type-is@~1.6.18": +"type-is@~1.6.18": "integrity" "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==" "resolved" "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" "version" "1.6.18" @@ -8824,19 +7875,24 @@ dependencies: "is-typedarray" "^1.0.0" -"ua-parser-js@^0.7.18": - "integrity" "sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g==" - "resolved" "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz" - "version" "0.7.28" +"typescript@>= 2.7": + "integrity" "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz" + "version" "4.9.4" -"unbox-primitive@^1.0.0": - "integrity" "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==" - "resolved" "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" - "version" "1.0.1" +"ua-parser-js@^0.7.30": + "integrity" "sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw==" + "resolved" "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.32.tgz" + "version" "0.7.32" + +"unbox-primitive@^1.0.2": + "integrity" "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==" + "resolved" "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + "version" "1.0.2" dependencies: - "function-bind" "^1.1.1" - "has-bigints" "^1.0.1" - "has-symbols" "^1.0.2" + "call-bind" "^1.0.2" + "has-bigints" "^1.0.2" + "has-symbols" "^1.0.3" "which-boxed-primitive" "^1.0.2" "unherit@^1.0.4": @@ -8847,44 +7903,45 @@ "inherits" "^2.0.0" "xtend" "^4.0.0" -"unicode-canonical-property-names-ecmascript@^1.0.4": - "integrity" "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" - "resolved" "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz" - "version" "1.0.4" +"unicode-canonical-property-names-ecmascript@^2.0.0": + "integrity" "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + "resolved" "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz" + "version" "2.0.0" -"unicode-match-property-ecmascript@^1.0.4": - "integrity" "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==" - "resolved" "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz" - "version" "1.0.4" +"unicode-match-property-ecmascript@^2.0.0": + "integrity" "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==" + "resolved" "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" + "version" "2.0.0" dependencies: - "unicode-canonical-property-names-ecmascript" "^1.0.4" - "unicode-property-aliases-ecmascript" "^1.0.4" + "unicode-canonical-property-names-ecmascript" "^2.0.0" + "unicode-property-aliases-ecmascript" "^2.0.0" -"unicode-match-property-value-ecmascript@^1.2.0": - "integrity" "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==" - "resolved" "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz" - "version" "1.2.0" +"unicode-match-property-value-ecmascript@^2.0.0": + "integrity" "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" + "resolved" "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz" + "version" "2.0.0" -"unicode-property-aliases-ecmascript@^1.0.4": - "integrity" "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==" - "resolved" "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz" - "version" "1.1.0" +"unicode-property-aliases-ecmascript@^2.0.0": + "integrity" "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" + "resolved" "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz" + "version" "2.1.0" -"unified@^8.4.2": - "integrity" "sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA==" - "resolved" "https://registry.npmjs.org/unified/-/unified-8.4.2.tgz" - "version" "8.4.2" +"unified@^9.0.0", "unified@9.2.0": + "integrity" "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==" + "resolved" "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz" + "version" "9.2.0" dependencies: "bail" "^1.0.0" "extend" "^3.0.0" + "is-buffer" "^2.0.0" "is-plain-obj" "^2.0.0" "trough" "^1.0.0" "vfile" "^4.0.0" -"unified@^9.0.0", "unified@9.2.0": - "integrity" "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==" - "resolved" "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz" - "version" "9.2.0" +"unified@^9.2.2": + "integrity" "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==" + "resolved" "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz" + "version" "9.2.2" dependencies: "bail" "^1.0.0" "extend" "^3.0.0" @@ -8893,26 +7950,6 @@ "trough" "^1.0.0" "vfile" "^4.0.0" -"union-value@^1.0.0": - "integrity" "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==" - "resolved" "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz" - "version" "1.0.1" - dependencies: - "arr-union" "^3.1.0" - "get-value" "^2.0.6" - "is-extendable" "^0.1.1" - "set-value" "^2.0.1" - -"uniq@^1.0.1": - "integrity" "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" - "resolved" "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz" - "version" "1.0.1" - -"uniqs@^2.0.0": - "integrity" "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" - "resolved" "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz" - "version" "2.0.0" - "unique-string@^2.0.0": "integrity" "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==" "resolved" "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" @@ -8976,7 +8013,7 @@ "@types/unist" "^2.0.0" "unist-util-is" "^4.0.0" -"unist-util-visit@^2.0.0", "unist-util-visit@^2.0.1", "unist-util-visit@^2.0.2", "unist-util-visit@^2.0.3", "unist-util-visit@2.0.3": +"unist-util-visit@^2.0.0", "unist-util-visit@^2.0.3", "unist-util-visit@2.0.3": "integrity" "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==" "resolved" "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz" "version" "2.0.3" @@ -8991,27 +8028,17 @@ "version" "2.0.0" "unpipe@~1.0.0", "unpipe@1.0.0": - "integrity" "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity" "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" "resolved" "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" "version" "1.0.0" -"unquote@~1.1.1": - "integrity" "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" - "resolved" "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz" - "version" "1.1.1" - -"unset-value@^1.0.0": - "integrity" "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=" - "resolved" "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz" - "version" "1.0.0" +"update-browserslist-db@^1.0.9": + "integrity" "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==" + "resolved" "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz" + "version" "1.0.10" dependencies: - "has-value" "^0.3.1" - "isobject" "^3.0.0" - -"upath@^1.1.1": - "integrity" "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" - "resolved" "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz" - "version" "1.2.0" + "escalade" "^3.1.1" + "picocolors" "^1.0.0" "update-notifier@^5.1.0": "integrity" "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==" @@ -9040,11 +8067,6 @@ dependencies: "punycode" "^2.1.0" -"urix@^0.1.0": - "integrity" "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - "resolved" "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" - "version" "0.1.0" - "url-loader@^4.1.1": "integrity" "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==" "resolved" "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz" @@ -9055,69 +8077,36 @@ "schema-utils" "^3.0.0" "url-parse-lax@^3.0.0": - "integrity" "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=" + "integrity" "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==" "resolved" "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" "version" "3.0.0" dependencies: "prepend-http" "^2.0.0" -"url-parse@^1.4.3", "url-parse@^1.5.1": - "integrity" "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==" - "resolved" "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz" - "version" "1.5.1" - dependencies: - "querystringify" "^2.1.1" - "requires-port" "^1.0.0" - -"url@^0.11.0": - "integrity" "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=" - "resolved" "https://registry.npmjs.org/url/-/url-0.11.0.tgz" - "version" "0.11.0" - dependencies: - "punycode" "1.3.2" - "querystring" "0.2.0" - -"use-composed-ref@^1.0.0": - "integrity" "sha512-my1lNHGWsSDAhhVAT4MKs6IjBUtG6ZG11uUqexPH9PptiIZDQOzaF4f5tEbJ2+7qvNbtXNBbU3SfmN+fXlWDhg==" - "resolved" "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.1.0.tgz" - "version" "1.1.0" - dependencies: - "ts-essentials" "^2.0.3" +"use-composed-ref@^1.3.0": + "integrity" "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==" + "resolved" "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz" + "version" "1.3.0" -"use-isomorphic-layout-effect@^1.0.0": - "integrity" "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==" - "resolved" "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz" - "version" "1.1.1" +"use-isomorphic-layout-effect@^1.1.1": + "integrity" "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" + "resolved" "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz" + "version" "1.1.2" -"use-latest@^1.0.0": - "integrity" "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==" - "resolved" "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz" - "version" "1.2.0" +"use-latest@^1.2.1": + "integrity" "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==" + "resolved" "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz" + "version" "1.2.1" dependencies: - "use-isomorphic-layout-effect" "^1.0.0" - -"use@^3.1.0": - "integrity" "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - "resolved" "https://registry.npmjs.org/use/-/use-3.1.1.tgz" - "version" "3.1.1" + "use-isomorphic-layout-effect" "^1.1.1" "util-deprecate@^1.0.1", "util-deprecate@^1.0.2", "util-deprecate@~1.0.1": - "integrity" "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity" "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" "resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" "version" "1.0.2" -"util.promisify@~1.0.0": - "integrity" "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==" - "resolved" "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz" - "version" "1.0.1" - dependencies: - "define-properties" "^1.1.3" - "es-abstract" "^1.17.2" - "has-symbols" "^1.0.1" - "object.getownpropertydescriptors" "^2.1.0" - "utila@~0.4": - "integrity" "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=" + "integrity" "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" "resolved" "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz" "version" "0.4.0" @@ -9127,14 +8116,14 @@ "version" "3.10.0" "utils-merge@1.0.1": - "integrity" "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity" "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" "resolved" "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" "version" "1.0.1" -"uuid@^3.3.2", "uuid@^3.4.0": - "integrity" "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - "resolved" "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" - "version" "3.4.0" +"uuid@^8.3.2": + "integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + "version" "8.3.2" "v8-compile-cache@^2.0.3": "integrity" "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==" @@ -9147,15 +8136,10 @@ "version" "1.0.1" "vary@~1.1.2": - "integrity" "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity" "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" "resolved" "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" "version" "1.1.2" -"vendors@^1.0.3": - "integrity" "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==" - "resolved" "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz" - "version" "1.0.4" - "vfile-location@^3.0.0", "vfile-location@^3.2.0": "integrity" "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==" "resolved" "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz" @@ -9179,21 +8163,21 @@ "unist-util-stringify-position" "^2.0.0" "vfile-message" "^2.0.0" -"wait-on@^5.2.1": - "integrity" "sha512-DwrHrnTK+/0QFaB9a8Ol5Lna3k7WvUR4jzSKmz0YaPBpuN2sACyiPVKVfj6ejnjcajAcvn3wlbTyMIn9AZouOg==" - "resolved" "https://registry.npmjs.org/wait-on/-/wait-on-5.3.0.tgz" - "version" "5.3.0" +"wait-on@^6.0.1": + "integrity" "sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw==" + "resolved" "https://registry.npmjs.org/wait-on/-/wait-on-6.0.1.tgz" + "version" "6.0.1" dependencies: - "axios" "^0.21.1" - "joi" "^17.3.0" + "axios" "^0.25.0" + "joi" "^17.6.0" "lodash" "^4.17.21" "minimist" "^1.2.5" - "rxjs" "^6.6.3" + "rxjs" "^7.5.4" -"watchpack@^2.0.0": - "integrity" "sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==" - "resolved" "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz" - "version" "2.1.1" +"watchpack@^2.4.0": + "integrity" "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==" + "resolved" "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz" + "version" "2.4.0" dependencies: "glob-to-regexp" "^0.4.1" "graceful-fs" "^4.1.2" @@ -9205,155 +8189,134 @@ dependencies: "minimalistic-assert" "^1.0.0" -"web-namespaces@^1.0.0", "web-namespaces@^1.1.2": +"web-namespaces@^1.0.0": "integrity" "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==" "resolved" "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz" "version" "1.1.4" "webidl-conversions@^3.0.0": - "integrity" "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + "integrity" "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" "version" "3.0.1" -"webpack-bundle-analyzer@^4.4.0": - "integrity" "sha512-j5m7WgytCkiVBoOGavzNokBOqxe6Mma13X1asfVYtKWM3wxBiRRu1u1iG0Iol5+qp9WgyhkMmBAcvjEfJ2bdDw==" - "resolved" "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.1.tgz" - "version" "4.4.1" +"webpack-bundle-analyzer@^4.5.0": + "integrity" "sha512-oKz9Oz9j3rUciLNfpGFjOb49/jEpXNmWdVH8Ls//zNcnLlQdTGXQQMsBbb/gR7Zl8WNLxVCq+0Hqbx3zv6twBw==" + "resolved" "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.1.tgz" + "version" "4.6.1" dependencies: "acorn" "^8.0.4" "acorn-walk" "^8.0.0" "chalk" "^4.1.0" - "commander" "^6.2.0" + "commander" "^7.2.0" "gzip-size" "^6.0.0" "lodash" "^4.17.20" "opener" "^1.5.2" "sirv" "^1.0.7" "ws" "^7.3.1" -"webpack-dev-middleware@^3.7.2": - "integrity" "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==" - "resolved" "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz" - "version" "3.7.3" +"webpack-dev-middleware@^5.3.1": + "integrity" "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==" + "resolved" "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz" + "version" "5.3.3" dependencies: - "memory-fs" "^0.4.1" - "mime" "^2.4.4" - "mkdirp" "^0.5.1" + "colorette" "^2.0.10" + "memfs" "^3.4.3" + "mime-types" "^2.1.31" "range-parser" "^1.2.1" - "webpack-log" "^2.0.0" - -"webpack-dev-server@^3.11.2": - "integrity" "sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==" - "resolved" "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz" - "version" "3.11.2" - dependencies: - "ansi-html" "0.0.7" - "bonjour" "^3.5.0" - "chokidar" "^2.1.8" + "schema-utils" "^4.0.0" + +"webpack-dev-server@^4.9.3": + "integrity" "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==" + "resolved" "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz" + "version" "4.11.1" + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.1" + "ansi-html-community" "^0.0.8" + "bonjour-service" "^1.0.11" + "chokidar" "^3.5.3" + "colorette" "^2.0.10" "compression" "^1.7.4" - "connect-history-api-fallback" "^1.6.0" - "debug" "^4.1.1" - "del" "^4.1.1" - "express" "^4.17.1" - "html-entities" "^1.3.1" - "http-proxy-middleware" "0.19.1" - "import-local" "^2.0.0" - "internal-ip" "^4.3.0" - "ip" "^1.1.5" - "is-absolute-url" "^3.0.3" - "killable" "^1.0.1" - "loglevel" "^1.6.8" - "opn" "^5.5.0" - "p-retry" "^3.0.1" - "portfinder" "^1.0.26" - "schema-utils" "^1.0.0" - "selfsigned" "^1.10.8" - "semver" "^6.3.0" + "connect-history-api-fallback" "^2.0.0" + "default-gateway" "^6.0.3" + "express" "^4.17.3" + "graceful-fs" "^4.2.6" + "html-entities" "^2.3.2" + "http-proxy-middleware" "^2.0.3" + "ipaddr.js" "^2.0.1" + "open" "^8.0.9" + "p-retry" "^4.5.0" + "rimraf" "^3.0.2" + "schema-utils" "^4.0.0" + "selfsigned" "^2.1.1" "serve-index" "^1.9.1" - "sockjs" "^0.3.21" - "sockjs-client" "^1.5.0" + "sockjs" "^0.3.24" "spdy" "^4.0.2" - "strip-ansi" "^3.0.1" - "supports-color" "^6.1.0" - "url" "^0.11.0" - "webpack-dev-middleware" "^3.7.2" - "webpack-log" "^2.0.0" - "ws" "^6.2.1" - "yargs" "^13.3.2" - -"webpack-log@^2.0.0": - "integrity" "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==" - "resolved" "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz" - "version" "2.0.0" - dependencies: - "ansi-colors" "^3.0.0" - "uuid" "^3.3.2" + "webpack-dev-middleware" "^5.3.1" + "ws" "^8.4.2" -"webpack-merge@^5.7.3": - "integrity" "sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==" - "resolved" "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.7.3.tgz" - "version" "5.7.3" +"webpack-merge@^5.8.0": + "integrity" "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==" + "resolved" "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz" + "version" "5.8.0" dependencies: "clone-deep" "^4.0.1" "wildcard" "^2.0.0" -"webpack-sources@^1.1.0", "webpack-sources@^1.4.3": - "integrity" "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==" - "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz" - "version" "1.4.3" - dependencies: - "source-list-map" "^2.0.0" - "source-map" "~0.6.1" - -"webpack-sources@^2.1.1": - "integrity" "sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==" - "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.2.0.tgz" - "version" "2.2.0" - dependencies: - "source-list-map" "^2.0.1" - "source-map" "^0.6.1" - -"webpack@^4.0.0 || ^5.0.0", "webpack@^4.27.0 || ^5.0.0", "webpack@^4.4.0 || ^5.0.0", "webpack@^5.0.0", "webpack@^5.1.0", "webpack@^5.20.0", "webpack@^5.28.0", "webpack@>=2", "webpack@>=4.41.1 || 5.x", "webpack@3 || 4 || 5": - "integrity" "sha512-yvdhgcI6QkQkDe1hINBAJ1UNevqNGTVaCkD2SSJcB8rcrNNl922RI8i2DXUAuNfANoxwsiXXEA4ZPZI9q2oGLA==" - "resolved" "https://registry.npmjs.org/webpack/-/webpack-5.37.0.tgz" - "version" "5.37.0" - dependencies: - "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.47" - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/wasm-edit" "1.11.0" - "@webassemblyjs/wasm-parser" "1.11.0" - "acorn" "^8.2.1" +"webpack-sources@^3.2.2": + "integrity" "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + "version" "3.2.3" + +"webpack-sources@^3.2.3": + "integrity" "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + "resolved" "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + "version" "3.2.3" + +"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", "webpack@^5.0.0", "webpack@^5.1.0", "webpack@^5.20.0", "webpack@^5.73.0", "webpack@>= 4", "webpack@>=2", "webpack@>=4.41.1 || 5.x", "webpack@3 || 4 || 5": + "integrity" "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==" + "resolved" "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz" + "version" "5.74.0" + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "acorn" "^8.7.1" + "acorn-import-assertions" "^1.7.6" "browserslist" "^4.14.5" "chrome-trace-event" "^1.0.2" - "enhanced-resolve" "^5.8.0" - "es-module-lexer" "^0.4.0" - "eslint-scope" "^5.1.1" + "enhanced-resolve" "^5.10.0" + "es-module-lexer" "^0.9.0" + "eslint-scope" "5.1.1" "events" "^3.2.0" "glob-to-regexp" "^0.4.1" - "graceful-fs" "^4.2.4" - "json-parse-better-errors" "^1.0.2" + "graceful-fs" "^4.2.9" + "json-parse-even-better-errors" "^2.3.1" "loader-runner" "^4.2.0" "mime-types" "^2.1.27" "neo-async" "^2.6.2" - "schema-utils" "^3.0.0" + "schema-utils" "^3.1.0" "tapable" "^2.1.1" - "terser-webpack-plugin" "^5.1.1" - "watchpack" "^2.0.0" - "webpack-sources" "^2.1.1" + "terser-webpack-plugin" "^5.1.3" + "watchpack" "^2.4.0" + "webpack-sources" "^3.2.3" -"webpackbar@^5.0.0-3": - "integrity" "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==" - "resolved" "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz" - "version" "5.0.0-3" +"webpackbar@^5.0.2": + "integrity" "sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==" + "resolved" "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.2.tgz" + "version" "5.0.2" dependencies: - "ansi-escapes" "^4.3.1" "chalk" "^4.1.0" - "consola" "^2.15.0" - "figures" "^3.2.0" + "consola" "^2.15.3" "pretty-time" "^1.1.0" - "std-env" "^2.2.1" - "text-table" "^0.2.0" - "wrap-ansi" "^7.0.0" + "std-env" "^3.0.1" "websocket-driver@^0.7.4", "websocket-driver@>=0.5.1": "integrity" "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==" @@ -9370,7 +8333,7 @@ "version" "0.1.4" "whatwg-url@^5.0.0": - "integrity" "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=" + "integrity" "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==" "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" "version" "5.0.0" dependencies: @@ -9388,18 +8351,6 @@ "is-string" "^1.0.5" "is-symbol" "^1.0.3" -"which-module@^2.0.0": - "integrity" "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - "resolved" "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" - "version" "2.0.0" - -"which@^1.2.9": - "integrity" "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==" - "resolved" "https://registry.npmjs.org/which/-/which-1.3.1.tgz" - "version" "1.3.1" - dependencies: - "isexe" "^2.0.0" - "which@^1.3.1": "integrity" "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==" "resolved" "https://registry.npmjs.org/which/-/which-1.3.1.tgz" @@ -9421,6 +8372,13 @@ dependencies: "string-width" "^4.0.0" +"widest-line@^4.0.1": + "integrity" "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==" + "resolved" "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "string-width" "^5.0.1" + "wildcard@^2.0.0": "integrity" "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" "resolved" "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz" @@ -9431,22 +8389,6 @@ "resolved" "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" "version" "1.2.3" -"worker-rpc@^0.1.0": - "integrity" "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==" - "resolved" "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz" - "version" "0.1.1" - dependencies: - "microevent.ts" "~0.1.1" - -"wrap-ansi@^5.1.0": - "integrity" "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==" - "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz" - "version" "5.1.0" - dependencies: - "ansi-styles" "^3.2.0" - "string-width" "^3.0.0" - "strip-ansi" "^5.0.0" - "wrap-ansi@^7.0.0": "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" @@ -9456,8 +8398,17 @@ "string-width" "^4.1.0" "strip-ansi" "^6.0.0" +"wrap-ansi@^8.0.1": + "integrity" "sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==" + "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.0.1.tgz" + "version" "8.0.1" + dependencies: + "ansi-styles" "^6.1.0" + "string-width" "^5.0.1" + "strip-ansi" "^7.0.1" + "wrappy@1": - "integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity" "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" "version" "1.0.2" @@ -9471,17 +8422,15 @@ "signal-exit" "^3.0.2" "typedarray-to-buffer" "^3.1.5" -"ws@^6.2.1": - "integrity" "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==" - "resolved" "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz" - "version" "6.2.1" - dependencies: - "async-limiter" "~1.0.0" - "ws@^7.3.1": - "integrity" "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==" - "resolved" "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz" - "version" "7.4.5" + "integrity" "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" + "resolved" "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" + "version" "7.5.9" + +"ws@^8.4.2": + "integrity" "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==" + "resolved" "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz" + "version" "8.11.0" "xdg-basedir@^4.0.0": "integrity" "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" @@ -9500,44 +8449,33 @@ "resolved" "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" "version" "4.0.2" -"y18n@^4.0.0": - "integrity" "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - "resolved" "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" - "version" "4.0.3" +"yallist@^3.0.0": + "integrity" "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + "version" "3.1.1" + +"yallist@^3.1.1": + "integrity" "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "resolved" "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + "version" "3.1.1" "yallist@^4.0.0": "integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" "resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" "version" "4.0.0" -"yaml@^1.10.0": +"yaml@^1.10.0", "yaml@^1.10.2", "yaml@^1.7.2": "integrity" "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" "resolved" "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" "version" "1.10.2" -"yargs-parser@^13.1.2": - "integrity" "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==" - "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz" - "version" "13.1.2" - dependencies: - "camelcase" "^5.0.0" - "decamelize" "^1.2.0" - -"yargs@^13.3.2": - "integrity" "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==" - "resolved" "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz" - "version" "13.3.2" +"yauzl@^2.10.0": + "integrity" "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==" + "resolved" "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" + "version" "2.10.0" dependencies: - "cliui" "^5.0.0" - "find-up" "^3.0.0" - "get-caller-file" "^2.0.1" - "require-directory" "^2.1.1" - "require-main-filename" "^2.0.0" - "set-blocking" "^2.0.0" - "string-width" "^3.0.0" - "which-module" "^2.0.0" - "y18n" "^4.0.0" - "yargs-parser" "^13.1.2" + "buffer-crc32" "~0.2.3" + "fd-slicer" "~1.1.0" "yocto-queue@^0.1.0": "integrity" "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" diff --git a/dos/Cargo.toml b/dos/Cargo.toml index ff2fe81182a48a..872dd457306e69 100644 --- a/dos/Cargo.toml +++ b/dos/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-dos" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -17,23 +17,23 @@ itertools = "0.10.3" log = "0.4.17" rand = "0.7.0" serde = "1.0.138" -solana-bench-tps = { path = "../bench-tps", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-core = { path = "../core", version = "=1.11.6" } -solana-faucet = { path = "../faucet", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-rpc = { path = "../rpc", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-bench-tps = { path = "../bench-tps", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-core = { path = "../core", version = "=1.14.24" } +solana-faucet = { path = "../faucet", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-rpc = { path = "../rpc", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dev-dependencies] serial_test = "0.8.0" -solana-local-cluster = { path = "../local-cluster", version = "=1.11.6" } +solana-local-cluster = { path = "../local-cluster", version = "=1.14.24" } diff --git a/dos/src/main.rs b/dos/src/main.rs index fa75fe90b79435..fa9c897f966372 100644 --- a/dos/src/main.rs +++ b/dos/src/main.rs @@ -53,8 +53,8 @@ use { solana_core::serve_repair::{RepairProtocol, RepairRequestHeader, ServeRepair}, solana_dos::cli::*, solana_gossip::{ - contact_info::ContactInfo, gossip_service::{discover, get_multi_client}, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_measure::measure::Measure, solana_sdk::{ @@ -646,7 +646,7 @@ fn run_dos( slot, shred_index: 0, }; - ServeRepair::repair_proto_to_bytes(&req, Some(&keypair)).unwrap() + ServeRepair::repair_proto_to_bytes(&req, &keypair).unwrap() } DataType::RepairShred => { let slot = 100; @@ -657,14 +657,14 @@ fn run_dos( slot, shred_index: 0, }; - ServeRepair::repair_proto_to_bytes(&req, Some(&keypair)).unwrap() + ServeRepair::repair_proto_to_bytes(&req, &keypair).unwrap() } DataType::RepairOrphan => { let slot = 100; let keypair = Keypair::new(); let header = RepairRequestHeader::new(keypair.pubkey(), target_id, timestamp(), 0); let req = RepairProtocol::Orphan { header, slot }; - ServeRepair::repair_proto_to_bytes(&req, Some(&keypair)).unwrap() + ServeRepair::repair_proto_to_bytes(&req, &keypair).unwrap() } DataType::Random => { vec![0; params.data_size] diff --git a/download-utils/Cargo.toml b/download-utils/Cargo.toml index be1af0277aba6b..bed545c383a710 100644 --- a/download-utils/Cargo.toml +++ b/download-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-download-utils" -version = "1.11.6" +version = "1.14.24" description = "Solana Download Utils" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,8 +14,8 @@ console = "0.15.0" indicatif = "0.16.2" log = "0.4.17" reqwest = { version = "0.11.11", default-features = false, features = ["blocking", "brotli", "deflate", "gzip", "rustls-tls", "json"] } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/entry/Cargo.toml b/entry/Cargo.toml index cbd5649257360e..18fbc0fca39341 100644 --- a/entry/Cargo.toml +++ b/entry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-entry" -version = "1.11.6" +version = "1.14.24" description = "Solana Entry" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -19,16 +19,16 @@ log = "0.4.17" rand = "0.7.0" rayon = "1.5.3" serde = "1.0.138" -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-merkle-tree = { path = "../merkle-tree", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-merkle-tree = { path = "../merkle-tree", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } [dev-dependencies] matches = "0.1.9" -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/entry/src/entry.rs b/entry/src/entry.rs index bfdba187d00743..c732f19ab6fcb7 100644 --- a/entry/src/entry.rs +++ b/entry/src/entry.rs @@ -46,7 +46,7 @@ use { lazy_static! { static ref PAR_THREAD_POOL: ThreadPool = rayon::ThreadPoolBuilder::new() .num_threads(get_max_thread_count()) - .thread_name(|ix| format!("entry_{}", ix)) + .thread_name(|ix| format!("solEntry{:02}", ix)) .build() .unwrap(); } @@ -525,21 +525,24 @@ pub fn start_verify_transactions( let tx_offset_recycler = verify_recyclers.tx_offset_recycler; let out_recycler = verify_recyclers.out_recycler; let num_packets = entry_txs.len(); - let gpu_verify_thread = thread::spawn(move || { - let mut verify_time = Measure::start("sigverify"); - sigverify::ed25519_verify( - &mut packet_batches, - &tx_offset_recycler, - &out_recycler, - false, - num_packets, - ); - let verified = packet_batches - .iter() - .all(|batch| batch.iter().all(|p| !p.meta.discard())); - verify_time.stop(); - (verified, verify_time.as_us()) - }); + let gpu_verify_thread = thread::Builder::new() + .name("solGpuSigVerify".into()) + .spawn(move || { + let mut verify_time = Measure::start("sigverify"); + sigverify::ed25519_verify( + &mut packet_batches, + &tx_offset_recycler, + &out_recycler, + false, + num_packets, + ); + let verified = packet_batches + .iter() + .all(|batch| batch.iter().all(|p| !p.meta.discard())); + verify_time.stop(); + (verified, verify_time.as_us()) + }) + .unwrap(); Ok(EntrySigVerificationState { verification_status: EntryVerificationStatus::Pending, entries: Some(entries), @@ -733,11 +736,10 @@ impl EntrySlice for [Entry] { recyclers: VerifyRecyclers, ) -> EntryVerificationState { let start = Instant::now(); - let api = perf_libs::api(); - if api.is_none() { - return self.verify_cpu(start_hash); - } - let api = api.unwrap(); + let api = match perf_libs::api() { + None => return self.verify_cpu(start_hash), + Some(api) => api, + }; inc_new_counter_info!("entry_verify-num_entries", self.len() as usize); let genesis = [Entry { @@ -770,25 +772,28 @@ impl EntrySlice for [Entry] { let hashes = Arc::new(Mutex::new(hashes_pinned)); let hashes_clone = hashes.clone(); - let gpu_verify_thread = thread::spawn(move || { - let mut hashes = hashes_clone.lock().unwrap(); - let gpu_wait = Instant::now(); - let res; - unsafe { - res = (api.poh_verify_many)( - hashes.as_mut_ptr() as *mut u8, - num_hashes_vec.as_ptr(), - length, - 1, + let gpu_verify_thread = thread::Builder::new() + .name("solGpuPohVerify".into()) + .spawn(move || { + let mut hashes = hashes_clone.lock().unwrap(); + let gpu_wait = Instant::now(); + let res; + unsafe { + res = (api.poh_verify_many)( + hashes.as_mut_ptr() as *mut u8, + num_hashes_vec.as_ptr(), + length, + 1, + ); + } + assert!(res == 0, "GPU PoH verify many failed"); + inc_new_counter_info!( + "entry_verify-gpu_thread", + timing::duration_as_us(&gpu_wait.elapsed()) as usize ); - } - assert!(res == 0, "GPU PoH verify many failed"); - inc_new_counter_info!( - "entry_verify-gpu_thread", - timing::duration_as_us(&gpu_wait.elapsed()) as usize - ); - timing::duration_as_us(&gpu_wait.elapsed()) - }); + timing::duration_as_us(&gpu_wait.elapsed()) + }) + .unwrap(); let verifications = PAR_THREAD_POOL.install(|| { self.into_par_iter() diff --git a/faucet/Cargo.toml b/faucet/Cargo.toml index 7ef1507d38857d..ce14e4c1fec315 100644 --- a/faucet/Cargo.toml +++ b/faucet/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-faucet" -version = "1.11.6" +version = "1.14.24" description = "Solana Faucet" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -17,15 +17,15 @@ crossbeam-channel = "0.5" log = "0.4.17" serde = "1.0.138" serde_derive = "1.0.103" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] } thiserror = "1.0" -tokio = { version = "~1.14.1", features = ["full"] } +tokio = { version = "1", features = ["full"] } [lib] crate-type = ["lib"] diff --git a/faucet/src/faucet.rs b/faucet/src/faucet.rs index d440d34ffffdcb..d74acec6b8cf4e 100644 --- a/faucet/src/faucet.rs +++ b/faucet/src/faucet.rs @@ -203,7 +203,7 @@ impl Faucet { ) ); let memo_instruction = Instruction { - program_id: Pubkey::new(&spl_memo::id().to_bytes()), + program_id: Pubkey::from(spl_memo::id().to_bytes()), accounts: vec![], data: memo.as_bytes().to_vec(), }; @@ -634,7 +634,7 @@ mod tests { assert_eq!(tx.signatures.len(), 1); assert_eq!( message.account_keys, - vec![mint_pubkey, Pubkey::new(&spl_memo::id().to_bytes())] + vec![mint_pubkey, Pubkey::from(spl_memo::id().to_bytes())] ); assert_eq!(message.recent_blockhash, blockhash); diff --git a/fetch-spl.sh b/fetch-spl.sh index 542da6af3f1a6f..f3487f4d282e85 100755 --- a/fetch-spl.sh +++ b/fetch-spl.sh @@ -38,10 +38,11 @@ fetch_program() { } -fetch_program token 3.3.0 TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA BPFLoader2111111111111111111111111111111111 +fetch_program token 3.5.0 TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA BPFLoader2111111111111111111111111111111111 +fetch_program token-2022 0.6.0 TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb BPFLoaderUpgradeab1e11111111111111111111111 fetch_program memo 1.0.0 Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo BPFLoader1111111111111111111111111111111111 fetch_program memo 3.0.0 MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr BPFLoader2111111111111111111111111111111111 -fetch_program associated-token-account 1.0.5 ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL BPFLoader2111111111111111111111111111111111 +fetch_program associated-token-account 1.1.2 ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL BPFLoader2111111111111111111111111111111111 fetch_program feature-proposal 1.0.0 Feat1YXHhH6t1juaWF74WLcfv4XoNocjXA6sPWHNgAse BPFLoader2111111111111111111111111111111111 echo "${genesis_args[@]}" > spl-genesis-args.sh diff --git a/firedancer b/firedancer new file mode 160000 index 00000000000000..43f0b7566c9fd1 --- /dev/null +++ b/firedancer @@ -0,0 +1 @@ +Subproject commit 43f0b7566c9fd199a834bb3ed96c5309f22f9fd3 diff --git a/frozen-abi/Cargo.toml b/frozen-abi/Cargo.toml index e004106334b902..227bd209675c52 100644 --- a/frozen-abi/Cargo.toml +++ b/frozen-abi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-frozen-abi" -version = "1.11.6" +version = "1.14.24" description = "Solana Frozen ABI" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -20,12 +20,12 @@ serde_bytes = "0.11" serde_derive = "1.0" serde_json = "1.0" sha2 = "0.10.2" -solana-frozen-abi-macro = { path = "macro", version = "=1.11.6" } +solana-frozen-abi-macro = { path = "macro", version = "=1.14.24" } thiserror = "1.0" [target.'cfg(not(target_os = "solana"))'.dependencies] ahash = { version = "0.7.6", features = ["default", "std"] } -blake3 = { version = "1.3.1", features = ["digest", "traits-preview"] } +blake3 = { version = "=1.3.1", features = ["digest", "traits-preview"] } block-buffer = { version = "0.9.0", features = ["block-padding"] } byteorder = { version = "1.4.3", features = ["default", "i128", "std"] } cc = { version = "1.0.67", features = ["jobserver", "parallel"] } @@ -43,7 +43,7 @@ rand_core = { version = "0.6.3", features = ["alloc", "getrandom", "std"] } subtle = { version = "2.4.1", features = ["default", "i128", "std"] } [target.'cfg(not(target_os = "solana"))'.dev-dependencies] -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [build-dependencies] rustc_version = "0.4" diff --git a/frozen-abi/macro/Cargo.toml b/frozen-abi/macro/Cargo.toml index c1f8afa1a50a37..f7d2aaf93ddb4b 100644 --- a/frozen-abi/macro/Cargo.toml +++ b/frozen-abi/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-frozen-abi-macro" -version = "1.11.6" +version = "1.14.24" description = "Solana Frozen ABI Macro" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" diff --git a/genesis-utils/Cargo.toml b/genesis-utils/Cargo.toml index bb5b29ea8d5793..85924b662ec497 100644 --- a/genesis-utils/Cargo.toml +++ b/genesis-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-genesis-utils" -version = "1.11.6" +version = "1.14.24" description = "Solana Genesis Utils" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,9 +10,9 @@ documentation = "https://docs.rs/solana-download-utils" edition = "2021" [dependencies] -solana-download-utils = { path = "../download-utils", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-download-utils = { path = "../download-utils", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/genesis/Cargo.toml b/genesis/Cargo.toml index df6f07b62e57a8..4ddfcfcfe93cc5 100644 --- a/genesis/Cargo.toml +++ b/genesis/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-genesis" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -15,17 +15,17 @@ clap = "2.33.1" serde = "1.0.138" serde_json = "1.0.81" serde_yaml = "0.8.26" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-stake-program = { path = "../programs/stake", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } -tempfile = "3.3.0" +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } +tempfile = "3.4.0" [[bin]] name = "solana-genesis" diff --git a/genesis/src/genesis_accounts.rs b/genesis/src/genesis_accounts.rs index 1f540d4c6f72b6..0704985913838f 100644 --- a/genesis/src/genesis_accounts.rs +++ b/genesis/src/genesis_accounts.rs @@ -276,8 +276,8 @@ mod tests { let lamports = genesis_config .accounts - .iter() - .map(|(_, account)| account.lamports) + .values() + .map(|account| account.lamports) .sum::(); assert_eq!(500_000_000 * LAMPORTS_PER_SOL, lamports); diff --git a/genesis/src/main.rs b/genesis/src/main.rs index 01989e331b9892..d88516c11003c8 100644 --- a/genesis/src/main.rs +++ b/genesis/src/main.rs @@ -580,8 +580,8 @@ fn main() -> Result<(), Box> { let issued_lamports = genesis_config .accounts - .iter() - .map(|(_key, account)| account.lamports) + .values() + .map(|account| account.lamports) .sum::(); add_genesis_accounts(&mut genesis_config, issued_lamports - faucet_lamports); diff --git a/genesis/src/stakes.rs b/genesis/src/stakes.rs index a3b645ed3edc54..c48a3ec86326d5 100644 --- a/genesis/src/stakes.rs +++ b/genesis/src/stakes.rs @@ -183,8 +183,8 @@ mod tests { assert_eq!( genesis_config .accounts - .iter() - .map(|(_pubkey, account)| account.lamports) + .values() + .map(|account| account.lamports) .sum::(), total_lamports, ); @@ -216,7 +216,7 @@ mod tests { // print( // "\n\"{}\", // {:?}", // hex, - // Pubkey::new(&hex::decode(hex).unwrap()) + // Pubkey::try_from(&hex::decode(hex).unwrap()).unwrap() // ); // }); // println(); diff --git a/geyser-plugin-interface/Cargo.toml b/geyser-plugin-interface/Cargo.toml index d7c918c28f1ead..593f63826eb69b 100644 --- a/geyser-plugin-interface/Cargo.toml +++ b/geyser-plugin-interface/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-geyser-plugin-interface" description = "The Solana Geyser plugin interface." -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -11,8 +11,8 @@ documentation = "https://docs.rs/solana-geyser-plugin-interface" [dependencies] log = "0.4.17" -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } thiserror = "1.0.31" [package.metadata.docs.rs] diff --git a/geyser-plugin-interface/README.md b/geyser-plugin-interface/README.md index f2d20bee9fe695..761601d0dc4be1 100644 --- a/geyser-plugin-interface/README.md +++ b/geyser-plugin-interface/README.md @@ -22,4 +22,4 @@ an external PostgreSQL databases. More information about Solana is available in the [Solana documentation](https://docs.solana.com/). -Still have questions? Ask us on [Discord](https://discordapp.com/invite/pquxPsq) +Still have questions? Ask us on [Stack Exchange](https://sola.na/sse) diff --git a/geyser-plugin-manager/Cargo.toml b/geyser-plugin-manager/Cargo.toml index f880746b9018e1..784efe99b9f456 100644 --- a/geyser-plugin-manager/Cargo.toml +++ b/geyser-plugin-manager/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-geyser-plugin-manager" description = "The Solana Geyser plugin manager." -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -16,13 +16,13 @@ json5 = "0.4.1" libloading = "0.7.3" log = "0.4.17" serde_json = "1.0.81" -solana-geyser-plugin-interface = { path = "../geyser-plugin-interface", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-rpc = { path = "../rpc", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } +solana-geyser-plugin-interface = { path = "../geyser-plugin-interface", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-rpc = { path = "../rpc", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } thiserror = "1.0.31" [package.metadata.docs.rs] diff --git a/geyser-plugin-manager/src/geyser_plugin_service.rs b/geyser-plugin-manager/src/geyser_plugin_service.rs index e203bd1deb5453..764c7fbdc48a7d 100644 --- a/geyser-plugin-manager/src/geyser_plugin_service.rs +++ b/geyser-plugin-manager/src/geyser_plugin_service.rs @@ -9,7 +9,7 @@ use { crossbeam_channel::Receiver, log::*, solana_rpc::{ - optimistically_confirmed_bank_tracker::BankNotification, + optimistically_confirmed_bank_tracker::SlotNotification, transaction_notifier_interface::TransactionNotifierLock, }, solana_runtime::accounts_update_notifier_interface::AccountsUpdateNotifier, @@ -68,7 +68,7 @@ impl GeyserPluginService { /// It is usually used to configure the connection information for the external data store. pub fn new( - confirmed_bank_receiver: Receiver, + confirmed_bank_receiver: Receiver, geyser_plugin_config_files: &[PathBuf], ) -> Result { info!( diff --git a/geyser-plugin-manager/src/slot_status_observer.rs b/geyser-plugin-manager/src/slot_status_observer.rs index bad8fa90ece13c..7eba6e54eb6c58 100644 --- a/geyser-plugin-manager/src/slot_status_observer.rs +++ b/geyser-plugin-manager/src/slot_status_observer.rs @@ -1,7 +1,7 @@ use { crate::slot_status_notifier::SlotStatusNotifier, crossbeam_channel::Receiver, - solana_rpc::optimistically_confirmed_bank_tracker::BankNotification, + solana_rpc::optimistically_confirmed_bank_tracker::SlotNotification, std::{ sync::{ atomic::{AtomicBool, Ordering}, @@ -19,7 +19,7 @@ pub(crate) struct SlotStatusObserver { impl SlotStatusObserver { pub fn new( - bank_notification_receiver: Receiver, + bank_notification_receiver: Receiver, slot_status_notifier: SlotStatusNotifier, ) -> Self { let exit_updated_slot_server = Arc::new(AtomicBool::new(false)); @@ -43,33 +43,33 @@ impl SlotStatusObserver { } fn run_bank_notification_receiver( - bank_notification_receiver: Receiver, + bank_notification_receiver: Receiver, exit: Arc, slot_status_notifier: SlotStatusNotifier, ) -> JoinHandle<()> { Builder::new() - .name("bank_notification_receiver".to_string()) + .name("solBankNotif".to_string()) .spawn(move || { while !exit.load(Ordering::Relaxed) { if let Ok(slot) = bank_notification_receiver.recv() { match slot { - BankNotification::OptimisticallyConfirmed(slot) => { + SlotNotification::OptimisticallyConfirmed(slot) => { slot_status_notifier .read() .unwrap() .notify_slot_confirmed(slot, None); } - BankNotification::Frozen(bank) => { + SlotNotification::Frozen((slot, parent)) => { slot_status_notifier .read() .unwrap() - .notify_slot_processed(bank.slot(), Some(bank.parent_slot())); + .notify_slot_processed(slot, Some(parent)); } - BankNotification::Root(bank) => { + SlotNotification::Root((slot, parent)) => { slot_status_notifier .read() .unwrap() - .notify_slot_rooted(bank.slot(), Some(bank.parent_slot())); + .notify_slot_rooted(slot, Some(parent)); } } } diff --git a/gossip/Cargo.toml b/gossip/Cargo.toml index 9abb3a62c70d73..b5fa155de1444e 100644 --- a/gossip/Cargo.toml +++ b/gossip/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-gossip" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -27,24 +27,24 @@ rayon = "1.5.3" serde = "1.0.138" serde_bytes = "0.11" serde_derive = "1.0.103" -solana-bloom = { path = "../bloom", version = "=1.11.6" } -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } +solana-bloom = { path = "../bloom", version = "=1.14.24" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-frozen-abi = { path = "../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } thiserror = "1.0" [dev-dependencies] diff --git a/gossip/src/cluster_info.rs b/gossip/src/cluster_info.rs index 4994a82309447e..0cd6d2485e38ac 100644 --- a/gossip/src/cluster_info.rs +++ b/gossip/src/cluster_info.rs @@ -24,7 +24,6 @@ use { cluster_info_metrics::{ submit_gossip_stats, Counter, GossipStats, ScopedTimer, TimedGuard, }, - contact_info::ContactInfo, crds::{Crds, Cursor, GossipRoute}, crds_gossip::CrdsGossip, crds_gossip_error::CrdsGossipError, @@ -33,8 +32,10 @@ use { self, CrdsData, CrdsValue, CrdsValueLabel, EpochSlotsIndex, IncrementalSnapshotHashes, LowestSlot, NodeInstance, SnapshotHashes, Version, Vote, MAX_WALLCLOCK, }, + duplicate_shred::DuplicateShred, epoch_slots::EpochSlots, gossip_error::GossipError, + legacy_contact_info::LegacyContactInfo as ContactInfo, ping_pong::{self, PingCache, Pong}, socketaddr, socketaddr_any, weighted_shuffle::WeightedShuffle, @@ -48,7 +49,7 @@ use { solana_ledger::shred::Shred, solana_measure::measure::Measure, solana_net_utils::{ - bind_common, bind_common_in_range, bind_in_range, bind_two_consecutive_in_range, + bind_common, bind_common_in_range, bind_in_range, bind_two_in_range_with_offset, find_available_port_in_range, multi_bind_in_range, PortRange, }, solana_perf::{ @@ -92,6 +93,7 @@ use { thread::{sleep, Builder, JoinHandle}, time::{Duration, Instant}, }, + thiserror::Error, }; /// The Data plane fanout size, also used as the neighborhood size @@ -126,6 +128,7 @@ const MAX_PRUNE_DATA_NODES: usize = 32; const GOSSIP_PING_TOKEN_SIZE: usize = 32; const GOSSIP_PING_CACHE_CAPACITY: usize = 65536; const GOSSIP_PING_CACHE_TTL: Duration = Duration::from_secs(1280); +const GOSSIP_PING_CACHE_RATE_LIMIT_DELAY: Duration = Duration::from_secs(1280 / 64); pub const DEFAULT_CONTACT_DEBUG_INTERVAL_MILLIS: u64 = 10_000; pub const DEFAULT_CONTACT_SAVE_INTERVAL_MILLIS: u64 = 60_000; /// Minimum serialized size of a Protocol::PullResponse packet. @@ -138,12 +141,17 @@ const MIN_STAKE_FOR_GOSSIP: u64 = solana_sdk::native_token::LAMPORTS_PER_SOL; /// Minimum number of staked nodes for enforcing stakes in gossip. const MIN_NUM_STAKED_NODES: usize = 500; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Error)] pub enum ClusterInfoError { + #[error("NoPeers")] NoPeers, + #[error("NoLeader")] NoLeader, + #[error("BadContactInfo")] BadContactInfo, + #[error("BadGossipAddress")] BadGossipAddress, + #[error("TooManyIncrementalSnapshotHashes")] TooManyIncrementalSnapshotHashes, } @@ -259,7 +267,7 @@ pub fn make_accounts_hashes_message( pub(crate) type Ping = ping_pong::Ping<[u8; GOSSIP_PING_TOKEN_SIZE]>; // TODO These messages should go through the gpu pipeline for spam filtering -#[frozen_abi(digest = "C1nR7B7CgMyUYo6h3z2KXcS38JSwF6y8jmZ6Y9Cz7XEd")] +#[frozen_abi(digest = "32XMrR8yTPMsw7TASQDinrbcB4bdVGyLeTvQrk95hS4i")] #[derive(Serialize, Deserialize, Debug, AbiEnumVisitor, AbiExample)] #[allow(clippy::large_enum_variant)] pub(crate) enum Protocol { @@ -272,6 +280,7 @@ pub(crate) enum Protocol { PruneMessage(Pubkey, PruneData), PingMessage(Ping), PongMessage(Pong), + // Update count_packets_received if new variants are added here. } impl Protocol { @@ -368,7 +377,7 @@ impl Sanitize for Protocol { fn retain_staked(values: &mut Vec, stakes: &HashMap) { values.retain(|value| { match value.data { - CrdsData::ContactInfo(_) => true, + CrdsData::LegacyContactInfo(_) => true, // May Impact new validators starting up without any stake yet. CrdsData::Vote(_, _) => true, // Unstaked nodes can still help repair. @@ -406,6 +415,7 @@ impl ClusterInfo { my_contact_info: RwLock::new(contact_info), ping_cache: Mutex::new(PingCache::new( GOSSIP_PING_CACHE_TTL, + GOSSIP_PING_CACHE_RATE_LIMIT_DELAY, GOSSIP_PING_CACHE_CAPACITY, )), stats: GossipStats::default(), @@ -418,7 +428,7 @@ impl ClusterInfo { socket_addr_space, }; me.insert_self(); - me.push_self(&HashMap::new(), None); + me.push_self(); me } @@ -457,15 +467,11 @@ impl ClusterInfo { &self.socket_addr_space } - fn push_self( - &self, - stakes: &HashMap, - gossip_validators: Option<&HashSet>, - ) { + fn push_self(&self) { let now = timestamp(); self.my_contact_info.write().unwrap().wallclock = now; let entries: Vec<_> = vec![ - CrdsData::ContactInfo(self.my_contact_info()), + CrdsData::LegacyContactInfo(self.my_contact_info()), CrdsData::NodeInstance(self.instance.read().unwrap().with_wallclock(now)), ] .into_iter() @@ -475,23 +481,51 @@ impl ClusterInfo { .lock() .unwrap() .extend(entries); - let ContactInfo { - id: self_pubkey, - shred_version, - .. - } = *self.my_contact_info.read().unwrap(); + } + + fn refresh_push_active_set( + &self, + recycler: &PacketBatchRecycler, + stakes: &HashMap, + gossip_validators: Option<&HashSet>, + sender: &PacketBatchSender, + ) { + let ContactInfo { shred_version, .. } = *self.my_contact_info.read().unwrap(); + let self_keypair: Arc = self.keypair().clone(); + let mut pings = Vec::new(); self.gossip.refresh_push_active_set( - &self_pubkey, + &self_keypair, shred_version, stakes, gossip_validators, + &self.ping_cache, + &mut pings, &self.socket_addr_space, ); + self.stats + .new_pull_requests_pings_count + .add_relaxed(pings.len() as u64); + let pings: Vec<_> = pings + .into_iter() + .map(|(addr, ping)| (addr, Protocol::PingMessage(ping))) + .collect(); + if !pings.is_empty() { + self.stats + .packets_sent_gossip_requests_count + .add_relaxed(pings.len() as u64); + let packet_batch = PacketBatch::new_unpinned_with_recycler_data_and_dests( + recycler.clone(), + "refresh_push_active_set", + &pings, + ); + let _ = sender.send(packet_batch); + } } // TODO kill insert_info, only used by tests pub fn insert_info(&self, contact_info: ContactInfo) { - let value = CrdsValue::new_signed(CrdsData::ContactInfo(contact_info), &self.keypair()); + let value = + CrdsValue::new_signed(CrdsData::LegacyContactInfo(contact_info), &self.keypair()); let mut gossip_crds = self.gossip.crds.write().unwrap(); let _ = gossip_crds.insert(value, timestamp(), GossipRoute::LocalMessage); } @@ -542,7 +576,7 @@ impl ClusterInfo { let filename = self.contact_info_path.join("contact-info.bin"); let tmp_filename = &filename.with_extension("tmp"); - match File::create(&tmp_filename) { + match File::create(tmp_filename) { Ok(mut file) => { if let Err(err) = bincode::serialize_into(&mut file, &nodes) { warn!( @@ -559,7 +593,7 @@ impl ClusterInfo { } } - match fs::rename(&tmp_filename, &filename) { + match fs::rename(tmp_filename, &filename) { Ok(()) => { info!( "Saved contact info for {} nodes into {}", @@ -636,7 +670,7 @@ impl ClusterInfo { CrdsData::Version(Version::new(self.id())), &self.keypair(), )); - self.push_self(&HashMap::new(), None); + self.push_self(); } pub fn lookup_contact_info(&self, id: &Pubkey, map: F) -> Option @@ -1091,7 +1125,7 @@ impl ClusterInfo { ) -> Result<(), GossipError> { let tpu = tpu.unwrap_or_else(|| self.my_contact_info().tpu); let buf = serialize(transaction)?; - self.socket.send_to(&buf, &tpu)?; + self.socket.send_to(&buf, tpu)?; Ok(()) } @@ -1196,6 +1230,19 @@ impl ClusterInfo { .collect() } + /// Returns duplicate-shreds inserted since the given cursor. + #[allow(dead_code)] + pub(crate) fn get_duplicate_shreds(&self, cursor: &mut Cursor) -> Vec { + let gossip_crds = self.gossip.crds.read().unwrap(); + gossip_crds + .get_duplicate_shreds(cursor) + .map(|entry| match &entry.value.data { + CrdsData::DuplicateShred(_, dup) => dup.clone(), + _ => panic!("this should not happen!"), + }) + .collect() + } + pub fn get_node_version(&self, pubkey: &Pubkey) -> Option { let gossip_crds = self.gossip.crds.read().unwrap(); if let Some(version) = gossip_crds.get::<&Version>(*pubkey) { @@ -1263,7 +1310,7 @@ impl ClusterInfo { .filter(|node| { node.id != self_pubkey && node.shred_version == self_shred_version - && ContactInfo::is_valid_tvu_address(&node.tvu) + && ContactInfo::is_valid_address(&node.tvu, &self.socket_addr_space) }) .cloned() .collect() @@ -1280,7 +1327,7 @@ impl ClusterInfo { .filter(|node| { node.id != self_pubkey && node.shred_version == self_shred_version - && ContactInfo::is_valid_tvu_address(&node.tvu) + && ContactInfo::is_valid_address(&node.tvu, &self.socket_addr_space) && ContactInfo::is_valid_address(&node.serve_repair, &self.socket_addr_space) && match gossip_crds.get::<&LowestSlot>(node.id) { None => true, // fallback to legacy behavior @@ -1313,7 +1360,7 @@ impl ClusterInfo { fn insert_self(&self) { let value = CrdsValue::new_signed( - CrdsData::ContactInfo(self.my_contact_info()), + CrdsData::LegacyContactInfo(self.my_contact_info()), &self.keypair(), ); let mut gossip_crds = self.gossip.crds.write().unwrap(); @@ -1449,7 +1496,7 @@ impl ClusterInfo { self.gossip.mark_pull_request_creation_time(peer.id, now); } } - let self_info = CrdsData::ContactInfo(self.my_contact_info()); + let self_info = CrdsData::LegacyContactInfo(self.my_contact_info()); let self_info = CrdsValue::new_signed(self_info, &self.keypair()); let pulls = pulls .into_iter() @@ -1479,11 +1526,17 @@ impl ClusterInfo { } fn new_push_requests(&self, stakes: &HashMap) -> Vec<(SocketAddr, Protocol)> { let self_id = self.id(); - let mut push_messages = { + let (mut push_messages, num_entries, num_nodes) = { let _st = ScopedTimer::from(&self.stats.new_push_requests); self.gossip .new_push_messages(self.drain_push_queue(), timestamp()) }; + self.stats + .push_fanout_num_entries + .add_relaxed(num_entries as u64); + self.stats + .push_fanout_num_nodes + .add_relaxed(num_nodes as u64); if self.require_stake_for_gossip(stakes) { push_messages.retain(|_, data| { retain_staked(data, stakes); @@ -1676,13 +1729,13 @@ impl ClusterInfo { ) -> JoinHandle<()> { let thread_pool = ThreadPoolBuilder::new() .num_threads(std::cmp::min(get_thread_count(), 8)) - .thread_name(|i| format!("ClusterInfo::gossip-{}", i)) + .thread_name(|i| format!("solRunGossip{:02}", i)) .build() .unwrap(); Builder::new() - .name("solana-gossip".to_string()) + .name("solGossip".to_string()) .spawn(move || { - let mut last_push = timestamp(); + let mut last_push = 0; let mut last_contact_info_trace = timestamp(); let mut last_contact_info_save = timestamp(); let mut entrypoints_processed = false; @@ -1745,7 +1798,13 @@ impl ClusterInfo { //TODO: possibly tune this parameter //we saw a deadlock passing an self.read().unwrap().timeout into sleep if start - last_push > CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS / 2 { - self.push_self(&stakes, gossip_validators.as_ref()); + self.push_self(); + self.refresh_push_active_set( + &recycler, + &stakes, + gossip_validators.as_ref(), + &sender, + ); last_push = timestamp(); } let elapsed = timestamp() - start; @@ -1759,7 +1818,7 @@ impl ClusterInfo { .unwrap() } - fn handle_batch_prune_messages(&self, messages: Vec<(Pubkey, PruneData)>) { + fn handle_batch_prune_messages(&self, messages: Vec) { let _st = ScopedTimer::from(&self.stats.handle_batch_prune_messages_time); if messages.is_empty() { return; @@ -1767,22 +1826,19 @@ impl ClusterInfo { self.stats .prune_message_count .add_relaxed(messages.len() as u64); - self.stats.prune_message_len.add_relaxed( - messages - .iter() - .map(|(_, data)| data.prunes.len() as u64) - .sum(), - ); + self.stats + .prune_message_len + .add_relaxed(messages.iter().map(|data| data.prunes.len() as u64).sum()); let mut prune_message_timeout = 0; let mut bad_prune_destination = 0; let self_pubkey = self.id(); { let _st = ScopedTimer::from(&self.stats.process_prune); let now = timestamp(); - for (from, data) in messages { + for data in messages { match self.gossip.process_prune_msg( &self_pubkey, - &from, + &data.pubkey, &data.destination, &data.prunes, data.wallclock, @@ -1978,7 +2034,7 @@ impl ClusterInfo { score }; let score = match response.data { - CrdsData::ContactInfo(_) => 2 * score, + CrdsData::LegacyContactInfo(_) => 2 * score, _ => score, }; ((addr, response), score) @@ -2222,17 +2278,7 @@ impl ClusterInfo { let origins: HashSet<_> = { let _st = ScopedTimer::from(&self.stats.process_push_message); let now = timestamp(); - messages - .into_iter() - .flat_map(|(from, crds_values)| { - let (num_success, origins) = - self.gossip.process_push_message(&from, crds_values, now); - self.stats - .process_push_success - .add_relaxed(num_success as u64); - origins - }) - .collect() + self.gossip.process_push_message(messages, now) }; // Generate prune messages. let self_pubkey = self.id(); @@ -2393,23 +2439,11 @@ impl ClusterInfo { check_duplicate_instance(&data)?; push_messages.push((from, data)); } - Protocol::PruneMessage(from, data) => prune_messages.push((from, data)), + Protocol::PruneMessage(_from, data) => prune_messages.push(data), Protocol::PingMessage(ping) => ping_messages.push((from_addr, ping)), Protocol::PongMessage(pong) => pong_messages.push((from_addr, pong)), } } - self.stats - .packets_received_pull_requests_count - .add_relaxed(pull_requests.len() as u64); - self.stats - .packets_received_pull_responses_count - .add_relaxed(pull_responses.len() as u64); - self.stats - .packets_received_push_messages_count - .add_relaxed(push_messages.len() as u64); - self.stats - .packets_received_prune_messages_count - .add_relaxed(prune_messages.len() as u64); if self.require_stake_for_gossip(stakes) { for (_, data) in &mut pull_responses { retain_staked(data, stakes); @@ -2455,9 +2489,26 @@ impl ClusterInfo { thread_pool: &ThreadPool, ) -> Result<(), GossipError> { const RECV_TIMEOUT: Duration = Duration::from_secs(1); - let packets: Vec<_> = receiver.recv_timeout(RECV_TIMEOUT)?.into(); + fn count_packets_received(packets: &PacketBatch, counts: &mut [u64; 7]) { + for packet in packets { + let k = match packet + .data(..4) + .and_then(|data| <[u8; 4]>::try_from(data).ok()) + .map(u32::from_le_bytes) + { + Some(k @ 0..=6) => k as usize, + None | Some(_) => 6, + }; + counts[k] += 1; + } + } + let packets = receiver.recv_timeout(RECV_TIMEOUT)?; + let mut counts = [0u64; 7]; + count_packets_received(&packets, &mut counts); + let packets = Vec::from(packets); let mut packets = VecDeque::from(packets); for packet_batch in receiver.try_iter() { + count_packets_received(&packet_batch, &mut counts); packets.extend(packet_batch.iter().cloned()); let excess_count = packets.len().saturating_sub(MAX_GOSSIP_TRAFFIC); if excess_count > 0 { @@ -2467,9 +2518,6 @@ impl ClusterInfo { .add_relaxed(excess_count as u64); } } - self.stats - .packets_received_count - .add_relaxed(packets.len() as u64); let verify_packet = |packet: Packet| { let protocol: Protocol = packet.deserialize_slice(..).ok()?; protocol.sanitize().ok()?; @@ -2480,6 +2528,30 @@ impl ClusterInfo { let _st = ScopedTimer::from(&self.stats.verify_gossip_packets_time); thread_pool.install(|| packets.into_par_iter().filter_map(verify_packet).collect()) }; + self.stats + .packets_received_count + .add_relaxed(counts.iter().sum::()); + self.stats + .packets_received_pull_requests_count + .add_relaxed(counts[0]); + self.stats + .packets_received_pull_responses_count + .add_relaxed(counts[1]); + self.stats + .packets_received_push_messages_count + .add_relaxed(counts[2]); + self.stats + .packets_received_prune_messages_count + .add_relaxed(counts[3]); + self.stats + .packets_received_ping_messages_count + .add_relaxed(counts[4]); + self.stats + .packets_received_pong_messages_count + .add_relaxed(counts[5]); + self.stats + .packets_received_unknown_count + .add_relaxed(counts[6]); self.stats .packets_received_verified_count .add_relaxed(packets.len() as u64); @@ -2550,7 +2622,7 @@ impl ClusterInfo { ) -> JoinHandle<()> { let thread_pool = ThreadPoolBuilder::new() .num_threads(get_thread_count().min(8)) - .thread_name(|i| format!("gossip-consume-{}", i)) + .thread_name(|i| format!("solGossipCons{:02}", i)) .build() .unwrap(); let run_consume = move || { @@ -2566,7 +2638,7 @@ impl ClusterInfo { } } }; - let thread_name = String::from("gossip-consume"); + let thread_name = String::from("solGossipConsum"); Builder::new().name(thread_name).spawn(run_consume).unwrap() } @@ -2582,11 +2654,11 @@ impl ClusterInfo { let recycler = PacketBatchRecycler::default(); let thread_pool = ThreadPoolBuilder::new() .num_threads(get_thread_count().min(8)) - .thread_name(|i| format!("sol-gossip-work-{}", i)) + .thread_name(|i| format!("solGossipWork{:02}", i)) .build() .unwrap(); Builder::new() - .name("solana-listen".to_string()) + .name("solGossipListen".to_string()) .spawn(move || { while !exit.load(Ordering::Relaxed) { if let Err(err) = self.run_listen( @@ -2745,21 +2817,22 @@ impl Node { Self::new_localhost_with_pubkey(&pubkey) } pub fn new_localhost_with_pubkey(pubkey: &Pubkey) -> Self { - let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); + let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); + let port_range = (1024, 65535); let ((_tpu_port, tpu), (_tpu_quic_port, tpu_quic)) = - bind_two_consecutive_in_range(bind_ip_addr, (1024, 65535)).unwrap(); + bind_two_in_range_with_offset(bind_ip_addr, port_range, QUIC_PORT_OFFSET).unwrap(); let (gossip_port, (gossip, ip_echo)) = - bind_common_in_range(bind_ip_addr, (1024, 65535)).unwrap(); + bind_common_in_range(bind_ip_addr, port_range).unwrap(); let gossip_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), gossip_port); let tvu = UdpSocket::bind("127.0.0.1:0").unwrap(); let tvu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap(); let ((_tpu_forwards_port, tpu_forwards), (_tpu_forwards_quic_port, tpu_forwards_quic)) = - bind_two_consecutive_in_range(bind_ip_addr, (1024, 65535)).unwrap(); + bind_two_in_range_with_offset(bind_ip_addr, port_range, QUIC_PORT_OFFSET).unwrap(); let tpu_vote = UdpSocket::bind("127.0.0.1:0").unwrap(); let repair = UdpSocket::bind("127.0.0.1:0").unwrap(); - let rpc_port = find_available_port_in_range(bind_ip_addr, (1024, 65535)).unwrap(); + let rpc_port = find_available_port_in_range(bind_ip_addr, port_range).unwrap(); let rpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_port); - let rpc_pubsub_port = find_available_port_in_range(bind_ip_addr, (1024, 65535)).unwrap(); + let rpc_pubsub_port = find_available_port_in_range(bind_ip_addr, port_range).unwrap(); let rpc_pubsub_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_pubsub_port); @@ -2835,9 +2908,9 @@ impl Node { let (tvu_port, tvu) = Self::bind(bind_ip_addr, port_range); let (tvu_forwards_port, tvu_forwards) = Self::bind(bind_ip_addr, port_range); let ((tpu_port, tpu), (_tpu_quic_port, tpu_quic)) = - bind_two_consecutive_in_range(bind_ip_addr, port_range).unwrap(); + bind_two_in_range_with_offset(bind_ip_addr, port_range, QUIC_PORT_OFFSET).unwrap(); let ((tpu_forwards_port, tpu_forwards), (_tpu_forwards_quic_port, tpu_forwards_quic)) = - bind_two_consecutive_in_range(bind_ip_addr, port_range).unwrap(); + bind_two_in_range_with_offset(bind_ip_addr, port_range, QUIC_PORT_OFFSET).unwrap(); let (tpu_vote_port, tpu_vote) = Self::bind(bind_ip_addr, port_range); let (_, retransmit_socket) = Self::bind(bind_ip_addr, port_range); let (repair_port, repair) = Self::bind(bind_ip_addr, port_range); @@ -3009,7 +3082,7 @@ fn filter_on_shred_version( if crds.get_shred_version(from) == Some(self_shred_version) { values.retain(|value| match &value.data { // Allow contact-infos so that shred-versions are updated. - CrdsData::ContactInfo(_) => true, + CrdsData::LegacyContactInfo(_) => true, CrdsData::NodeInstance(_) => true, // Only retain values with the same shred version. _ => crds.get_shred_version(&value.pubkey()) == Some(self_shred_version), @@ -3018,7 +3091,7 @@ fn filter_on_shred_version( values.retain(|value| match &value.data { // Allow node to update its own contact info in case their // shred-version changes - CrdsData::ContactInfo(node) => node.id == *from, + CrdsData::LegacyContactInfo(node) => node.id == *from, CrdsData::NodeInstance(_) => true, _ => false, }) @@ -3031,7 +3104,7 @@ fn filter_on_shred_version( match &mut msg { Protocol::PullRequest(_, caller) => match &caller.data { // Allow spy nodes with shred-verion == 0 to pull from other nodes. - CrdsData::ContactInfo(node) + CrdsData::LegacyContactInfo(node) if node.shred_version == 0 || node.shred_version == self_shred_version => { Some(msg) @@ -3352,7 +3425,7 @@ RPC Enabled Nodes: 1"#; fn test_crds_values(pubkey: Pubkey) -> Vec { let entrypoint = ContactInfo::new_localhost(&pubkey, timestamp()); - let entrypoint_crdsvalue = CrdsValue::new_unsigned(CrdsData::ContactInfo(entrypoint)); + let entrypoint_crdsvalue = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(entrypoint)); vec![entrypoint_crdsvalue] } @@ -3516,10 +3589,12 @@ RPC Enabled Nodes: 1"#; )); cluster_info.insert_info(spy); cluster_info.gossip.refresh_push_active_set( - &cluster_info.id(), + &cluster_info.keypair(), cluster_info.my_shred_version(), &HashMap::new(), // stakes None, // gossip validators + &cluster_info.ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); let reqs = cluster_info.generate_new_gossip_requests( @@ -3553,7 +3628,7 @@ RPC Enabled Nodes: 1"#; let cluster_info = ClusterInfo::new(d, Arc::new(Keypair::new()), SocketAddrSpace::Unspecified); let d = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), timestamp()); - let label = CrdsValueLabel::ContactInfo(d.id); + let label = CrdsValueLabel::LegacyContactInfo(d.id); cluster_info.insert_info(d); let gossip_crds = cluster_info.gossip.crds.read().unwrap(); assert!(gossip_crds.get::<&CrdsValue>(&label).is_some()); @@ -3647,14 +3722,16 @@ RPC Enabled Nodes: 1"#; .mock_pong(peer.id, peer.gossip, Instant::now()); cluster_info.insert_info(peer); cluster_info.gossip.refresh_push_active_set( - &cluster_info.id(), + &cluster_info.keypair(), cluster_info.my_shred_version(), &HashMap::new(), // stakes None, // gossip validators + &cluster_info.ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); //check that all types of gossip messages are signed correctly - let push_messages = cluster_info + let (push_messages, _, _) = cluster_info .gossip .new_push_messages(cluster_info.drain_push_queue(), timestamp()); // there should be some pushes ready @@ -3928,7 +4005,7 @@ RPC Enabled Nodes: 1"#; node.shred_version = 42; let epoch_slots = EpochSlots::new_rand(&mut rng, Some(node_pubkey)); let entries = vec![ - CrdsValue::new_unsigned(CrdsData::ContactInfo(node)), + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(node)), CrdsValue::new_unsigned(CrdsData::EpochSlots(0, epoch_slots)), ]; { @@ -3949,10 +4026,7 @@ RPC Enabled Nodes: 1"#; let mut node = cluster_info.my_contact_info.write().unwrap(); node.shred_version = 42; } - cluster_info.push_self( - &HashMap::default(), // stakes - None, // gossip validators - ); + cluster_info.push_self(); cluster_info.flush_push_queue(); // Should now include both epoch slots. let slots = cluster_info.get_epoch_slots(&mut Cursor::default()); @@ -3988,7 +4062,7 @@ RPC Enabled Nodes: 1"#; } // now add this message back to the table and make sure after the next pull, the entrypoint is unset let entrypoint_crdsvalue = - CrdsValue::new_unsigned(CrdsData::ContactInfo(entrypoint.clone())); + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(entrypoint.clone())); let cluster_info = Arc::new(cluster_info); let timeouts = cluster_info.gossip.make_timeouts( cluster_info.id(), @@ -4009,7 +4083,7 @@ RPC Enabled Nodes: 1"#; #[test] fn test_split_messages_small() { - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); test_split_messages(value); } @@ -4111,14 +4185,14 @@ RPC Enabled Nodes: 1"#; } fn check_pull_request_size(filter: CrdsFilter) { - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); let protocol = Protocol::PullRequest(filter, value); assert!(serialized_size(&protocol).unwrap() <= PACKET_DATA_SIZE as u64); } #[test] fn test_tvu_peers_and_stakes() { - let d = ContactInfo::new_localhost(&Pubkey::new(&[0; 32]), timestamp()); + let d = ContactInfo::new_localhost(&Pubkey::from([0; 32]), timestamp()); let cluster_info = ClusterInfo::new( d.clone(), Arc::new(Keypair::new()), @@ -4127,12 +4201,12 @@ RPC Enabled Nodes: 1"#; let mut stakes = HashMap::new(); // no stake - let id = Pubkey::new(&[1u8; 32]); + let id = Pubkey::from([1u8; 32]); let contact_info = ContactInfo::new_localhost(&id, timestamp()); cluster_info.insert_info(contact_info); // normal - let id2 = Pubkey::new(&[2u8; 32]); + let id2 = Pubkey::from([2u8; 32]); let mut contact_info = ContactInfo::new_localhost(&id2, timestamp()); cluster_info.insert_info(contact_info.clone()); stakes.insert(id2, 10); @@ -4142,14 +4216,14 @@ RPC Enabled Nodes: 1"#; cluster_info.insert_info(contact_info); // no tvu - let id3 = Pubkey::new(&[3u8; 32]); + let id3 = Pubkey::from([3u8; 32]); let mut contact_info = ContactInfo::new_localhost(&id3, timestamp()); contact_info.tvu = "0.0.0.0:0".parse().unwrap(); cluster_info.insert_info(contact_info); stakes.insert(id3, 10); // normal but with different shred version - let id4 = Pubkey::new(&[4u8; 32]); + let id4 = Pubkey::from([4u8; 32]); let mut contact_info = ContactInfo::new_localhost(&id4, timestamp()); contact_info.shred_version = 1; assert_ne!(contact_info.shred_version, d.shred_version); @@ -4278,7 +4352,7 @@ RPC Enabled Nodes: 1"#; .expect("unable to serialize default filter") as usize; let protocol = Protocol::PullRequest( CrdsFilter::default(), - CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())), + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())), ); let protocol_size = serialized_size(&protocol).expect("unable to serialize gossip protocol") as usize; @@ -4617,7 +4691,7 @@ RPC Enabled Nodes: 1"#; let mut rand_ci = ContactInfo::new_rand(&mut rng, Some(keypair.pubkey())); rand_ci.shred_version = shred_version; rand_ci.wallclock = timestamp(); - CrdsValue::new_signed(CrdsData::ContactInfo(rand_ci), &keypair) + CrdsValue::new_signed(CrdsData::LegacyContactInfo(rand_ci), &keypair) }) .take(NO_ENTRIES) .collect(); diff --git a/gossip/src/cluster_info_metrics.rs b/gossip/src/cluster_info_metrics.rs index 81e63a0163e478..44f02577680418 100644 --- a/gossip/src/cluster_info_metrics.rs +++ b/gossip/src/cluster_info_metrics.rs @@ -131,10 +131,13 @@ pub struct GossipStats { pub(crate) new_push_requests: Counter, pub(crate) new_push_requests_num: Counter, pub(crate) packets_received_count: Counter, + pub(crate) packets_received_ping_messages_count: Counter, + pub(crate) packets_received_pong_messages_count: Counter, pub(crate) packets_received_prune_messages_count: Counter, pub(crate) packets_received_pull_requests_count: Counter, pub(crate) packets_received_pull_responses_count: Counter, pub(crate) packets_received_push_messages_count: Counter, + pub(crate) packets_received_unknown_count: Counter, pub(crate) packets_received_verified_count: Counter, pub(crate) packets_sent_gossip_requests_count: Counter, pub(crate) packets_sent_prune_messages_count: Counter, @@ -153,7 +156,6 @@ pub struct GossipStats { pub(crate) process_pull_response_success: Counter, pub(crate) process_pull_response_timeout: Counter, pub(crate) process_push_message: Counter, - pub(crate) process_push_success: Counter, pub(crate) prune_message_count: Counter, pub(crate) prune_message_len: Counter, pub(crate) prune_message_timeout: Counter, @@ -163,6 +165,8 @@ pub struct GossipStats { pub(crate) pull_requests_count: Counter, pub(crate) purge: Counter, pub(crate) purge_count: Counter, + pub(crate) push_fanout_num_entries: Counter, + pub(crate) push_fanout_num_nodes: Counter, pub(crate) push_message_count: Counter, pub(crate) push_message_pushes: Counter, pub(crate) push_message_value_count: Counter, @@ -230,11 +234,6 @@ pub(crate) fn submit_gossip_stats( ("repair_peers", stats.repair_peers.clear(), i64), ("new_push_requests", stats.new_push_requests.clear(), i64), ("new_push_requests2", stats.new_push_requests2.clear(), i64), - ( - "process_push_success", - stats.process_push_success.clear(), - i64 - ), ("purge", stats.purge.clear(), i64), ("purge_count", stats.purge_count.clear(), i64), ( @@ -440,6 +439,16 @@ pub(crate) fn submit_gossip_stats( i64 ), ("push_message_count", stats.push_message_count.clear(), i64), + ( + "push_fanout_num_entries", + stats.push_fanout_num_entries.clear(), + i64 + ), + ( + "push_fanout_num_nodes", + stats.push_fanout_num_nodes.clear(), + i64 + ), ( "push_message_pushes", stats.push_message_pushes.clear(), @@ -490,6 +499,16 @@ pub(crate) fn submit_gossip_stats( stats.packets_received_count.clear(), i64 ), + ( + "packets_received_ping_messages_count", + stats.packets_received_ping_messages_count.clear(), + i64 + ), + ( + "packets_received_pong_messages_count", + stats.packets_received_pong_messages_count.clear(), + i64 + ), ( "packets_received_prune_messages_count", stats.packets_received_prune_messages_count.clear(), @@ -510,6 +529,11 @@ pub(crate) fn submit_gossip_stats( stats.packets_received_push_messages_count.clear(), i64 ), + ( + "packets_received_unknown_count", + stats.packets_received_unknown_count.clear(), + i64 + ), ( "packets_received_verified_count", stats.packets_received_verified_count.clear(), @@ -589,8 +613,8 @@ pub(crate) fn submit_gossip_stats( ); datapoint_info!( "cluster_info_crds_stats", - ("ContactInfo-push", crds_stats.push.counts[0], i64), - ("ContactInfo-pull", crds_stats.pull.counts[0], i64), + ("LegacyContactInfo-push", crds_stats.push.counts[0], i64), + ("LegacyContactInfo-pull", crds_stats.pull.counts[0], i64), ("Vote-push", crds_stats.push.counts[1], i64), ("Vote-pull", crds_stats.pull.counts[1], i64), ("LowestSlot-push", crds_stats.push.counts[2], i64), @@ -632,8 +656,8 @@ pub(crate) fn submit_gossip_stats( ); datapoint_info!( "cluster_info_crds_stats_fails", - ("ContactInfo-push", crds_stats.push.fails[0], i64), - ("ContactInfo-pull", crds_stats.pull.fails[0], i64), + ("LegacyContactInfo-push", crds_stats.push.fails[0], i64), + ("LegacyContactInfo-pull", crds_stats.pull.fails[0], i64), ("Vote-push", crds_stats.push.fails[1], i64), ("Vote-pull", crds_stats.pull.fails[1], i64), ("LowestSlot-push", crds_stats.push.fails[2], i64), diff --git a/gossip/src/crds.rs b/gossip/src/crds.rs index 2c215c928cbb8b..9864182ec6beb1 100644 --- a/gossip/src/crds.rs +++ b/gossip/src/crds.rs @@ -26,10 +26,10 @@ use { crate::{ - contact_info::ContactInfo, crds_entry::CrdsEntry, crds_shards::CrdsShards, crds_value::{CrdsData, CrdsValue, CrdsValueLabel}, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, bincode::serialize, indexmap::{ @@ -66,6 +66,8 @@ pub struct Crds { votes: BTreeMap, // Indices of EpochSlots keyed by insert order. epoch_slots: BTreeMap, + // Indices of DuplicateShred keyed by insert order. + duplicate_shreds: BTreeMap, // Indices of all crds values associated with a node. records: HashMap>, // Indices of all entries keyed by insert order. @@ -153,6 +155,7 @@ impl Default for Crds { nodes: IndexSet::default(), votes: BTreeMap::default(), epoch_slots: BTreeMap::default(), + duplicate_shreds: BTreeMap::default(), records: HashMap::default(), entries: BTreeMap::default(), purged: VecDeque::default(), @@ -213,7 +216,7 @@ impl Crds { let entry_index = entry.index(); self.shards.insert(entry_index, &value); match &value.value.data { - CrdsData::ContactInfo(node) => { + CrdsData::LegacyContactInfo(node) => { self.nodes.insert(entry_index); self.shred_versions.insert(pubkey, node.shred_version); } @@ -223,6 +226,9 @@ impl Crds { CrdsData::EpochSlots(_, _) => { self.epoch_slots.insert(value.ordinal, entry_index); } + CrdsData::DuplicateShred(_, _) => { + self.duplicate_shreds.insert(value.ordinal, entry_index); + } _ => (), }; self.entries.insert(value.ordinal, entry_index); @@ -237,11 +243,14 @@ impl Crds { self.shards.remove(entry_index, entry.get()); self.shards.insert(entry_index, &value); match &value.value.data { - CrdsData::ContactInfo(node) => { + CrdsData::LegacyContactInfo(node) => { self.shred_versions.insert(pubkey, node.shred_version); // self.nodes does not need to be updated since the // entry at this index was and stays contact-info. - debug_assert_matches!(entry.get().value.data, CrdsData::ContactInfo(_)); + debug_assert_matches!( + entry.get().value.data, + CrdsData::LegacyContactInfo(_) + ); } CrdsData::Vote(_, _) => { self.votes.remove(&entry.get().ordinal); @@ -251,6 +260,10 @@ impl Crds { self.epoch_slots.remove(&entry.get().ordinal); self.epoch_slots.insert(value.ordinal, entry_index); } + CrdsData::DuplicateShred(_, _) => { + self.duplicate_shreds.remove(&entry.get().ordinal); + self.duplicate_shreds.insert(value.ordinal, entry_index); + } _ => (), } self.entries.remove(&entry.get().ordinal); @@ -297,7 +310,7 @@ impl Crds { /// Returns ContactInfo of all known nodes. pub(crate) fn get_nodes_contact_info(&self) -> impl Iterator { self.get_nodes().map(|v| match &v.value.data { - CrdsData::ContactInfo(info) => info, + CrdsData::LegacyContactInfo(info) => info, _ => panic!("this should not happen!"), }) } @@ -328,6 +341,21 @@ impl Crds { }) } + /// Returns duplicate-shreds inserted since the given cursor. + /// Updates the cursor as the values are consumed. + pub(crate) fn get_duplicate_shreds<'a>( + &'a self, + cursor: &'a mut Cursor, + ) -> impl Iterator { + let range = (Bound::Included(cursor.ordinal()), Bound::Unbounded); + self.duplicate_shreds + .range(range) + .map(move |(ordinal, index)| { + cursor.consume(*ordinal); + self.table.index(*index) + }) + } + /// Returns all entries inserted since the given cursor. pub(crate) fn get_entries<'a>( &'a self, @@ -411,7 +439,7 @@ impl Crds { // It suffices to only overwrite the origin's timestamp since that is // used when purging old values. If the origin does not exist in the // table, fallback to exhaustive update on all associated records. - let origin = CrdsValueLabel::ContactInfo(*pubkey); + let origin = CrdsValueLabel::LegacyContactInfo(*pubkey); if let Some(origin) = self.table.get_mut(&origin) { if origin.local_timestamp < now { origin.local_timestamp = now; @@ -443,7 +471,7 @@ impl Crds { let timeout = timeouts.get(pubkey).copied().unwrap_or(default_timeout); // If the origin's contact-info hasn't expired yet then preserve // all associated values. - let origin = CrdsValueLabel::ContactInfo(*pubkey); + let origin = CrdsValueLabel::LegacyContactInfo(*pubkey); if let Some(origin) = self.table.get(&origin) { if now < origin.local_timestamp.saturating_add(timeout) { return vec![]; @@ -478,7 +506,7 @@ impl Crds { self.purged.push_back((value.value_hash, now)); self.shards.remove(index, &value); match value.value.data { - CrdsData::ContactInfo(_) => { + CrdsData::LegacyContactInfo(_) => { self.nodes.swap_remove(&index); } CrdsData::Vote(_, _) => { @@ -487,6 +515,9 @@ impl Crds { CrdsData::EpochSlots(_, _) => { self.epoch_slots.remove(&value.ordinal); } + CrdsData::DuplicateShred(_, _) => { + self.duplicate_shreds.remove(&value.ordinal); + } _ => (), } self.entries.remove(&value.ordinal); @@ -512,7 +543,7 @@ impl Crds { self.shards.remove(size, value); self.shards.insert(index, value); match value.value.data { - CrdsData::ContactInfo(_) => { + CrdsData::LegacyContactInfo(_) => { self.nodes.swap_remove(&size); self.nodes.insert(index); } @@ -522,6 +553,9 @@ impl Crds { CrdsData::EpochSlots(_, _) => { self.epoch_slots.insert(value.ordinal, index); } + CrdsData::DuplicateShred(_, _) => { + self.duplicate_shreds.insert(value.ordinal, index); + } _ => (), }; self.entries.insert(value.ordinal, index); @@ -606,6 +640,7 @@ impl Crds { nodes: self.nodes.clone(), votes: self.votes.clone(), epoch_slots: self.epoch_slots.clone(), + duplicate_shreds: self.duplicate_shreds.clone(), records: self.records.clone(), entries: self.entries.clone(), purged: self.purged.clone(), @@ -642,7 +677,7 @@ impl CrdsDataStats { fn ordinal(entry: &VersionedCrdsValue) -> usize { match &entry.value.data { - CrdsData::ContactInfo(_) => 0, + CrdsData::LegacyContactInfo(_) => 0, CrdsData::Vote(_, _) => 1, CrdsData::LowestSlot(_, _) => 2, CrdsData::SnapshotHashes(_) => 3, @@ -682,8 +717,8 @@ mod tests { use { super::*, crate::{ - contact_info::ContactInfo, crds_value::{new_rand_timestamp, NodeInstance, SnapshotHashes}, + socketaddr, }, rand::{thread_rng, Rng, SeedableRng}, rand_chacha::ChaChaRng, @@ -698,7 +733,7 @@ mod tests { #[test] fn test_insert() { let mut crds = Crds::default(); - let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let val = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); assert_eq!( crds.insert(val.clone(), 0, GossipRoute::LocalMessage), Ok(()) @@ -710,7 +745,7 @@ mod tests { #[test] fn test_update_old() { let mut crds = Crds::default(); - let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let val = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); assert_eq!( crds.insert(val.clone(), 0, GossipRoute::LocalMessage), Ok(()) @@ -725,13 +760,12 @@ mod tests { #[test] fn test_update_new() { let mut crds = Crds::default(); - let original = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &Pubkey::default(), - 0, - ))); + let original = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&Pubkey::default(), 0), + )); let value_hash = hash(&serialize(&original).unwrap()); assert_matches!(crds.insert(original, 0, GossipRoute::LocalMessage), Ok(())); - let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + let val = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &Pubkey::default(), 1, ))); @@ -745,7 +779,7 @@ mod tests { #[test] fn test_update_timestamp() { let mut crds = Crds::default(); - let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + let val = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &Pubkey::default(), 0, ))); @@ -755,7 +789,7 @@ mod tests { ); assert_eq!(crds.table[&val.label()].ordinal, 0); - let val2 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let val2 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); let value_hash = hash(&serialize(&val2).unwrap()); assert_eq!(val2.label().pubkey(), val.label().pubkey()); assert_eq!( @@ -775,7 +809,7 @@ mod tests { let mut ci = ContactInfo::default(); ci.wallclock += 1; - let val3 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci)); + let val3 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci)); assert_eq!(crds.insert(val3, 3, GossipRoute::LocalMessage), Ok(())); assert_eq!(*crds.purged.back().unwrap(), (value_hash, 3)); assert_eq!(crds.table[&val2.label()].local_timestamp, 3); @@ -844,7 +878,7 @@ mod tests { fn test_find_old_records_default() { let thread_pool = ThreadPoolBuilder::new().build().unwrap(); let mut crds = Crds::default(); - let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let val = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); assert_eq!( crds.insert(val.clone(), 1, GossipRoute::LocalMessage), Ok(()) @@ -896,7 +930,7 @@ mod tests { fn test_remove_default() { let thread_pool = ThreadPoolBuilder::new().build().unwrap(); let mut crds = Crds::default(); - let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let val = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); assert_matches!( crds.insert(val.clone(), 1, GossipRoute::LocalMessage), Ok(_) @@ -914,7 +948,7 @@ mod tests { fn test_find_old_records_staked() { let thread_pool = ThreadPoolBuilder::new().build().unwrap(); let mut crds = Crds::default(); - let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let val = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); assert_eq!( crds.insert(val.clone(), 1, GossipRoute::LocalMessage), Ok(()) @@ -1067,7 +1101,7 @@ mod tests { let num_nodes = crds .table .values() - .filter(|v| matches!(v.value.data, CrdsData::ContactInfo(_))) + .filter(|v| matches!(v.value.data, CrdsData::LegacyContactInfo(_))) .count(); let num_votes = crds .table @@ -1197,7 +1231,7 @@ mod tests { let mut node = ContactInfo::new_rand(&mut rng, Some(pubkey)); let wallclock = node.wallclock; node.shred_version = 42; - let node = CrdsData::ContactInfo(node); + let node = CrdsData::LegacyContactInfo(node); let node = CrdsValue::new_unsigned(node); assert_eq!( crds.insert(node, timestamp(), GossipRoute::LocalMessage), @@ -1208,7 +1242,7 @@ mod tests { let mut node = ContactInfo::new_rand(&mut rng, Some(pubkey)); node.wallclock = wallclock - 1; // outdated. node.shred_version = 8; - let node = CrdsData::ContactInfo(node); + let node = CrdsData::LegacyContactInfo(node); let node = CrdsValue::new_unsigned(node); assert_eq!( crds.insert(node, timestamp(), GossipRoute::LocalMessage), @@ -1219,7 +1253,7 @@ mod tests { let mut node = ContactInfo::new_rand(&mut rng, Some(pubkey)); node.wallclock = wallclock + 1; // so that it overrides the prev one. node.shred_version = 8; - let node = CrdsData::ContactInfo(node); + let node = CrdsData::LegacyContactInfo(node); let node = CrdsValue::new_unsigned(node); assert_eq!( crds.insert(node, timestamp(), GossipRoute::LocalMessage), @@ -1237,7 +1271,7 @@ mod tests { assert_eq!(crds.get_shred_version(&pubkey), Some(8)); // Remove contact-info. Shred version should stay there since there // are still values associated with the pubkey. - crds.remove(&CrdsValueLabel::ContactInfo(pubkey), timestamp()); + crds.remove(&CrdsValueLabel::LegacyContactInfo(pubkey), timestamp()); assert_eq!(crds.get::<&ContactInfo>(pubkey), None); assert_eq!(crds.get_shred_version(&pubkey), Some(8)); // Remove the remaining entry with the same pubkey. @@ -1310,7 +1344,7 @@ mod tests { fn test_remove_staked() { let thread_pool = ThreadPoolBuilder::new().build().unwrap(); let mut crds = Crds::default(); - let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let val = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); assert_matches!( crds.insert(val.clone(), 1, GossipRoute::LocalMessage), Ok(_) @@ -1331,7 +1365,7 @@ mod tests { #[test] #[allow(clippy::neg_cmp_op_on_partial_ord)] fn test_equal() { - let val = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let val = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::default())); let v1 = VersionedCrdsValue::new(val.clone(), Cursor::default(), 1); let v2 = VersionedCrdsValue::new(val, Cursor::default(), 1); assert_eq!(v1, v2); @@ -1343,7 +1377,7 @@ mod tests { #[allow(clippy::neg_cmp_op_on_partial_ord)] fn test_hash_order() { let v1 = VersionedCrdsValue::new( - CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &Pubkey::default(), 0, ))), @@ -1354,7 +1388,7 @@ mod tests { { let mut contact_info = ContactInfo::new_localhost(&Pubkey::default(), 0); contact_info.rpc = socketaddr!("0.0.0.0:0"); - CrdsValue::new_unsigned(CrdsData::ContactInfo(contact_info)) + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(contact_info)) }, Cursor::default(), 1, // local_timestamp @@ -1377,7 +1411,7 @@ mod tests { #[allow(clippy::neg_cmp_op_on_partial_ord)] fn test_wallclock_order() { let v1 = VersionedCrdsValue::new( - CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &Pubkey::default(), 1, ))), @@ -1385,7 +1419,7 @@ mod tests { 1, // local_timestamp ); let v2 = VersionedCrdsValue::new( - CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &Pubkey::default(), 0, ))), @@ -1403,7 +1437,7 @@ mod tests { #[allow(clippy::neg_cmp_op_on_partial_ord)] fn test_label_order() { let v1 = VersionedCrdsValue::new( - CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &solana_sdk::pubkey::new_rand(), 0, ))), @@ -1411,7 +1445,7 @@ mod tests { 1, // local_timestamp ); let v2 = VersionedCrdsValue::new( - CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &solana_sdk::pubkey::new_rand(), 0, ))), diff --git a/gossip/src/crds_entry.rs b/gossip/src/crds_entry.rs index c2b5235b80ed3c..0d0b0281504c5a 100644 --- a/gossip/src/crds_entry.rs +++ b/gossip/src/crds_entry.rs @@ -1,11 +1,11 @@ use { crate::{ - contact_info::ContactInfo, crds::VersionedCrdsValue, crds_value::{ CrdsData, CrdsValue, CrdsValueLabel, IncrementalSnapshotHashes, LegacyVersion, LowestSlot, SnapshotHashes, Version, }, + legacy_contact_info::LegacyContactInfo, }, indexmap::IndexMap, solana_sdk::pubkey::Pubkey, @@ -53,7 +53,7 @@ impl_crds_entry!(CrdsValue, |entry| Some(&entry?.value)); impl_crds_entry!(VersionedCrdsValue, |entry| entry); // Lookup by Pubkey. -impl_crds_entry!(ContactInfo, CrdsData::ContactInfo(node), node); +impl_crds_entry!(LegacyContactInfo, CrdsData::LegacyContactInfo(node), node); impl_crds_entry!(LegacyVersion, CrdsData::LegacyVersion(version), version); impl_crds_entry!(LowestSlot, CrdsData::LowestSlot(_, slot), slot); impl_crds_entry!(Version, CrdsData::Version(version), version); @@ -114,8 +114,8 @@ mod tests { assert_eq!(crds.get::<&VersionedCrdsValue>(&key).unwrap().value, *entry); let key = entry.pubkey(); match &entry.data { - CrdsData::ContactInfo(node) => { - assert_eq!(crds.get::<&ContactInfo>(key), Some(node)) + CrdsData::LegacyContactInfo(node) => { + assert_eq!(crds.get::<&LegacyContactInfo>(key), Some(node)) } CrdsData::LowestSlot(_, slot) => { assert_eq!(crds.get::<&LowestSlot>(key), Some(slot)) diff --git a/gossip/src/crds_gossip.rs b/gossip/src/crds_gossip.rs index afb31c8de1fa59..3c8ad948aaeaa0 100644 --- a/gossip/src/crds_gossip.rs +++ b/gossip/src/crds_gossip.rs @@ -8,15 +8,16 @@ use { crate::{ cluster_info::Ping, cluster_info_metrics::GossipStats, - contact_info::ContactInfo, crds::{Crds, GossipRoute}, crds_gossip_error::CrdsGossipError, crds_gossip_pull::{CrdsFilter, CrdsGossipPull, ProcessPullStats}, crds_gossip_push::{CrdsGossipPush, CRDS_GOSSIP_NUM_ACTIVE}, crds_value::{CrdsData, CrdsValue}, duplicate_shred::{self, DuplicateShredIndex, LeaderScheduleFn, MAX_DUPLICATE_SHREDS}, + legacy_contact_info::LegacyContactInfo as ContactInfo, ping_pong::PingCache, }, + itertools::Itertools, rayon::ThreadPool, solana_ledger::shred::Shred, solana_sdk::{ @@ -47,25 +48,10 @@ impl CrdsGossip { /// Returns unique origins' pubkeys of upserted values. pub fn process_push_message( &self, - from: &Pubkey, - values: Vec, + messages: Vec<(/*from:*/ Pubkey, Vec)>, now: u64, - ) -> (usize, HashSet) { - let results = self - .push - .process_push_message(&self.crds, from, values, now); - let mut success_count = 0; - let successfully_inserted_origin_set: HashSet = results - .into_iter() - .filter_map(|result| { - if result.is_ok() { - success_count += 1; - } - Result::ok(result) - }) - .collect(); - - (success_count, successfully_inserted_origin_set) + ) -> HashSet { + self.push.process_push_message(&self.crds, messages, now) } /// Remove redundant paths in the network. @@ -86,7 +72,11 @@ impl CrdsGossip { &self, pending_push_messages: Vec, now: u64, - ) -> HashMap> { + ) -> ( + HashMap>, + usize, // number of values + usize, // number of push messages + ) { { let mut crds = self.crds.write().unwrap(); for entry in pending_push_messages { @@ -169,11 +159,9 @@ impl CrdsGossip { wallclock: u64, now: u64, ) -> Result<(), CrdsGossipError> { - let expired = now > wallclock + self.push.prune_timeout; - if expired { - return Err(CrdsGossipError::PruneMessageTimeout); - } - if self_pubkey == destination { + if now > wallclock.saturating_add(self.push.prune_timeout) { + Err(CrdsGossipError::PruneMessageTimeout) + } else if self_pubkey == destination { self.push.process_prune_msg(self_pubkey, peer, origin); Ok(()) } else { @@ -184,10 +172,12 @@ impl CrdsGossip { /// Refresh the push active set. pub fn refresh_push_active_set( &self, - self_pubkey: &Pubkey, + self_keypair: &Keypair, self_shred_version: u16, stakes: &HashMap, gossip_validators: Option<&HashSet>, + ping_cache: &Mutex, + pings: &mut Vec<(SocketAddr, Ping)>, socket_addr_space: &SocketAddrSpace, ) { let network_size = self.crds.read().unwrap().num_nodes(); @@ -195,10 +185,12 @@ impl CrdsGossip { &self.crds, stakes, gossip_validators, - self_pubkey, + self_keypair, self_shred_version, network_size, CRDS_GOSSIP_NUM_ACTIVE, + ping_cache, + pings, socket_addr_space, ) } @@ -367,35 +359,60 @@ pub fn get_weight(max_weight: f32, time_since_last_selected: u32, stake: f32) -> 1.0_f32.max(weight.min(max_weight)) } +// Dedups gossip addresses, keeping only the one with the highest weight. +pub(crate) fn dedup_gossip_addresses( + nodes: I, +) -> HashMap +where + I: IntoIterator, +{ + nodes + .into_iter() + .into_grouping_map_by(|(_weight, node)| node.gossip) + .aggregate(|acc, _node_gossip, (weight, node)| match acc { + Some((ref w, _)) if w >= &weight => acc, + Some(_) | None => Some((weight, node)), + }) +} + #[cfg(test)] mod test { use { super::*, - crate::{contact_info::ContactInfo, crds_value::CrdsData}, + crate::crds_value::CrdsData, solana_sdk::{hash::hash, timing::timestamp}, }; #[test] fn test_prune_errors() { let crds_gossip = CrdsGossip::default(); - let id = Pubkey::new(&[0; 32]); - let ci = ContactInfo::new_localhost(&Pubkey::new(&[1; 32]), 0); - let prune_pubkey = Pubkey::new(&[2; 32]); + let keypair = Keypair::new(); + let id = keypair.pubkey(); + let ci = ContactInfo::new_localhost(&Pubkey::from([1; 32]), 0); + let prune_pubkey = Pubkey::from([2; 32]); crds_gossip .crds .write() .unwrap() .insert( - CrdsValue::new_unsigned(CrdsData::ContactInfo(ci.clone())), + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci.clone())), 0, GossipRoute::LocalMessage, ) .unwrap(); + let ping_cache = PingCache::new( + Duration::from_secs(20 * 60), // ttl + Duration::from_secs(20 * 60) / 64, // rate_limit_delay + 128, // capacity + ); + let ping_cache = Mutex::new(ping_cache); crds_gossip.refresh_push_active_set( - &id, + &keypair, 0, // shred version &HashMap::new(), // stakes None, // gossip validators + &ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); let now = timestamp(); @@ -403,7 +420,7 @@ mod test { let mut res = crds_gossip.process_prune_msg( &id, &ci.id, - &Pubkey::new(hash(&[1; 32]).as_ref()), + &Pubkey::from(hash(&[1; 32]).to_bytes()), &[prune_pubkey], now, now, diff --git a/gossip/src/crds_gossip_pull.rs b/gossip/src/crds_gossip_pull.rs index 2780bf7dabf56b..0288e98a019880 100644 --- a/gossip/src/crds_gossip_pull.rs +++ b/gossip/src/crds_gossip_pull.rs @@ -15,11 +15,11 @@ use { crate::{ cluster_info::{Ping, CRDS_UNIQUE_PUBKEY_CAPACITY}, cluster_info_metrics::GossipStats, - contact_info::ContactInfo, crds::{Crds, GossipRoute, VersionedCrdsValue}, - crds_gossip::{get_stake, get_weight}, + crds_gossip::{self, get_stake, get_weight}, crds_gossip_error::CrdsGossipError, crds_value::CrdsValue, + legacy_contact_info::LegacyContactInfo as ContactInfo, ping_pong::PingCache, }, itertools::Itertools, @@ -244,22 +244,25 @@ impl CrdsGossipPull { ); // Check for nodes which have responded to ping messages. let mut rng = rand::thread_rng(); - let (weights, peers): (Vec<_>, Vec<_>) = { + let peers: Vec<_> = { let mut ping_cache = ping_cache.lock().unwrap(); let mut pingf = move || Ping::new_rand(&mut rng, self_keypair).ok(); let now = Instant::now(); peers .into_iter() - .filter_map(|(weight, peer)| { + .filter(|(_weight, peer)| { let node = (peer.id, peer.gossip); let (check, ping) = ping_cache.check(now, node, &mut pingf); if let Some(ping) = ping { pings.push((peer.gossip, ping)); } - check.then(|| (weight, peer)) + check }) - .unzip() + .collect() }; + let (weights, peers): (Vec<_>, Vec<_>) = crds_gossip::dedup_gossip_addresses(peers) + .into_values() + .unzip(); if peers.is_empty() { return Err(CrdsGossipError::NoPeers); } @@ -659,8 +662,8 @@ pub(crate) mod tests { super::*, crate::{ cluster_info::MAX_BLOOM_SIZE, - contact_info::ContactInfo, crds_value::{CrdsData, Vote}, + socketaddr, }, itertools::Itertools, rand::{seq::SliceRandom, thread_rng, SeedableRng}, @@ -719,17 +722,16 @@ pub(crate) mod tests { let mut crds = Crds::default(); let mut stakes = HashMap::new(); let node = CrdsGossipPull::default(); - let me = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + let me = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &solana_sdk::pubkey::new_rand(), 0, ))); crds.insert(me.clone(), 0, GossipRoute::LocalMessage) .unwrap(); for i in 1..=30 { - let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0), + )); let id = entry.label().pubkey(); crds.insert(entry.clone(), 0, GossipRoute::LocalMessage) .unwrap(); @@ -760,25 +762,25 @@ pub(crate) mod tests { let gossip = socketaddr!("127.0.0.1:1234"); - let me = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let me = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), shred_version: 123, gossip, ..ContactInfo::default() })); - let spy = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let spy = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), shred_version: 0, gossip, ..ContactInfo::default() })); - let node_123 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let node_123 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), shred_version: 123, gossip, ..ContactInfo::default() })); - let node_456 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let node_456 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), shred_version: 456, gossip, @@ -840,12 +842,12 @@ pub(crate) mod tests { let node = CrdsGossipPull::default(); let gossip = socketaddr!("127.0.0.1:1234"); - let me = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let me = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), gossip, ..ContactInfo::default() })); - let node_123 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let node_123 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), gossip, ..ContactInfo::default() @@ -1003,15 +1005,15 @@ pub(crate) mod tests { let thread_pool = ThreadPoolBuilder::new().build().unwrap(); let crds = RwLock::::default(); let node_keypair = Keypair::new(); - let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &node_keypair.pubkey(), - 0, - ))); + let entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&node_keypair.pubkey(), 0), + )); let node = CrdsGossipPull::default(); let mut pings = Vec::new(); let ping_cache = Mutex::new(PingCache::new( - Duration::from_secs(20 * 60), // ttl - 128, // capacity + Duration::from_secs(20 * 60), // ttl + Duration::from_secs(20 * 60) / 64, // rate_limit_delay + 128, // capacity )); assert_eq!( node.new_pull_request( @@ -1056,7 +1058,7 @@ pub(crate) mod tests { .lock() .unwrap() .mock_pong(new.id, new.gossip, Instant::now()); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new)); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(new)); crds.write() .unwrap() .insert(new.clone(), now, GossipRoute::LocalMessage) @@ -1079,7 +1081,7 @@ pub(crate) mod tests { node.mark_pull_request_creation_time(new.contact_info().unwrap().id, now); let offline = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), now); - let offline = CrdsValue::new_unsigned(CrdsData::ContactInfo(offline)); + let offline = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(offline)); crds.write() .unwrap() .insert(offline, now, GossipRoute::LocalMessage) @@ -1108,25 +1110,25 @@ pub(crate) mod tests { let now: u64 = 1_605_127_770_789; let thread_pool = ThreadPoolBuilder::new().build().unwrap(); let mut ping_cache = PingCache::new( - Duration::from_secs(20 * 60), // ttl - 128, // capacity + Duration::from_secs(20 * 60), // ttl + Duration::from_secs(20 * 60) / 64, // rate_limit_delay + 128, // capacity ); let mut crds = Crds::default(); let node_keypair = Keypair::new(); - let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &node_keypair.pubkey(), - 0, - ))); + let entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&node_keypair.pubkey(), 0), + )); let node = CrdsGossipPull::default(); crds.insert(entry, now, GossipRoute::LocalMessage).unwrap(); let old = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); ping_cache.mock_pong(old.id, old.gossip, Instant::now()); - let old = CrdsValue::new_unsigned(CrdsData::ContactInfo(old)); + let old = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(old)); crds.insert(old.clone(), now, GossipRoute::LocalMessage) .unwrap(); let new = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); ping_cache.mock_pong(new.id, new.gossip, Instant::now()); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new)); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(new)); crds.insert(new.clone(), now, GossipRoute::LocalMessage) .unwrap(); let crds = RwLock::new(crds); @@ -1207,13 +1209,13 @@ pub(crate) mod tests { let node_keypair = Keypair::new(); let mut node_crds = Crds::default(); let mut ping_cache = PingCache::new( - Duration::from_secs(20 * 60), // ttl - 128, // capacity + Duration::from_secs(20 * 60), // ttl + Duration::from_secs(20 * 60) / 64, // rate_limit_delay + 128, // capacity ); - let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &node_keypair.pubkey(), - 0, - ))); + let entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&node_keypair.pubkey(), 0), + )); let caller = entry.clone(); let node = CrdsGossipPull::default(); node_crds @@ -1221,7 +1223,7 @@ pub(crate) mod tests { .unwrap(); let new = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); ping_cache.mock_pong(new.id, new.gossip, Instant::now()); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new)); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(new)); node_crds.insert(new, 0, GossipRoute::LocalMessage).unwrap(); let node_crds = RwLock::new(node_crds); let mut pings = Vec::new(); @@ -1253,7 +1255,7 @@ pub(crate) mod tests { assert_eq!(rsp[0].len(), 0); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &solana_sdk::pubkey::new_rand(), CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS, ))); @@ -1282,7 +1284,7 @@ pub(crate) mod tests { // Should return new value since caller is new. let now = CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS + 1; let caller = ContactInfo::new_localhost(&Pubkey::new_unique(), now); - let caller = CrdsValue::new_unsigned(CrdsData::ContactInfo(caller)); + let caller = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(caller)); filters .iter() .map(|(_, filter)| (caller.clone(), filter.clone())) @@ -1309,22 +1311,22 @@ pub(crate) mod tests { let thread_pool = ThreadPoolBuilder::new().build().unwrap(); let node_keypair = Keypair::new(); let mut node_crds = Crds::default(); - let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &node_keypair.pubkey(), - 0, - ))); + let entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&node_keypair.pubkey(), 0), + )); let caller = entry.clone(); let node = CrdsGossipPull::default(); node_crds .insert(entry, 0, GossipRoute::LocalMessage) .unwrap(); let mut ping_cache = PingCache::new( - Duration::from_secs(20 * 60), // ttl - 128, // capacity + Duration::from_secs(20 * 60), // ttl + Duration::from_secs(20 * 60) / 64, // rate_limit_delay + 128, // capacity ); let new = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); ping_cache.mock_pong(new.id, new.gossip, Instant::now()); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new)); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(new)); node_crds.insert(new, 0, GossipRoute::LocalMessage).unwrap(); let node_crds = RwLock::new(node_crds); let mut pings = Vec::new(); @@ -1368,10 +1370,9 @@ pub(crate) mod tests { let thread_pool = ThreadPoolBuilder::new().build().unwrap(); let node_keypair = Keypair::new(); let mut node_crds = Crds::default(); - let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &node_keypair.pubkey(), - 1, - ))); + let entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&node_keypair.pubkey(), 1), + )); let caller = entry.clone(); let node_pubkey = entry.label().pubkey(); let node = CrdsGossipPull::default(); @@ -1379,19 +1380,20 @@ pub(crate) mod tests { .insert(entry, 0, GossipRoute::LocalMessage) .unwrap(); let mut ping_cache = PingCache::new( - Duration::from_secs(20 * 60), // ttl - 128, // capacity + Duration::from_secs(20 * 60), // ttl + Duration::from_secs(20 * 60) / 64, // rate_limit_delay + 128, // capacity ); let new = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 1); ping_cache.mock_pong(new.id, new.gossip, Instant::now()); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new)); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(new)); node_crds.insert(new, 0, GossipRoute::LocalMessage).unwrap(); let mut dest_crds = Crds::default(); let new_id = solana_sdk::pubkey::new_rand(); let new = ContactInfo::new_localhost(&new_id, 1); ping_cache.mock_pong(new.id, new.gossip, Instant::now()); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new)); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(new)); dest_crds .insert(new.clone(), 0, GossipRoute::LocalMessage) .unwrap(); @@ -1400,7 +1402,7 @@ pub(crate) mod tests { // node contains a key from the dest node, but at an older local timestamp let same_key = ContactInfo::new_localhost(&new_id, 0); ping_cache.mock_pong(same_key.id, same_key.gossip, Instant::now()); - let same_key = CrdsValue::new_unsigned(CrdsData::ContactInfo(same_key)); + let same_key = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(same_key)); assert_eq!(same_key.label(), new.label()); assert!(same_key.wallclock() < new.wallclock()); node_crds @@ -1484,17 +1486,16 @@ pub(crate) mod tests { fn test_gossip_purge() { let thread_pool = ThreadPoolBuilder::new().build().unwrap(); let mut node_crds = Crds::default(); - let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0), + )); let node_label = entry.label(); let node_pubkey = node_label.pubkey(); let node = CrdsGossipPull::default(); node_crds .insert(entry, 0, GossipRoute::LocalMessage) .unwrap(); - let old = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( + let old = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo::new_localhost( &solana_sdk::pubkey::new_rand(), 0, ))); @@ -1624,7 +1625,7 @@ pub(crate) mod tests { let node = CrdsGossipPull::default(); let peer_pubkey = solana_sdk::pubkey::new_rand(); - let peer_entry = CrdsValue::new_unsigned(CrdsData::ContactInfo( + let peer_entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( ContactInfo::new_localhost(&peer_pubkey, 0), )); let mut timeouts = HashMap::new(); @@ -1644,7 +1645,7 @@ pub(crate) mod tests { ); let node_crds = RwLock::::default(); - let unstaked_peer_entry = CrdsValue::new_unsigned(CrdsData::ContactInfo( + let unstaked_peer_entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( ContactInfo::new_localhost(&peer_pubkey, 0), )); // check that old contact infos fail if they are too old, regardless of "timeouts" diff --git a/gossip/src/crds_gossip_push.rs b/gossip/src/crds_gossip_push.rs index 76356ab215bd02..ae67027e96a1ca 100644 --- a/gossip/src/crds_gossip_push.rs +++ b/gossip/src/crds_gossip_push.rs @@ -13,12 +13,12 @@ use { crate::{ - cluster_info::CRDS_UNIQUE_PUBKEY_CAPACITY, - contact_info::ContactInfo, + cluster_info::{Ping, CRDS_UNIQUE_PUBKEY_CAPACITY}, crds::{Crds, Cursor, GossipRoute}, - crds_gossip::{get_stake, get_weight}, - crds_gossip_error::CrdsGossipError, + crds_gossip::{self, get_stake, get_weight}, crds_value::CrdsValue, + legacy_contact_info::LegacyContactInfo as ContactInfo, + ping_pong::PingCache, weighted_shuffle::WeightedShuffle, }, bincode::serialized_size, @@ -27,17 +27,24 @@ use { lru::LruCache, rand::{seq::SliceRandom, Rng}, solana_bloom::bloom::{AtomicBloom, Bloom}, - solana_sdk::{packet::PACKET_DATA_SIZE, pubkey::Pubkey, timing::timestamp}, + solana_sdk::{ + packet::PACKET_DATA_SIZE, + pubkey::Pubkey, + signature::{Keypair, Signer}, + timing::timestamp, + }, solana_streamer::socket::SocketAddrSpace, std::{ cmp, collections::{HashMap, HashSet}, iter::repeat, + net::SocketAddr, ops::{DerefMut, RangeBounds}, sync::{ atomic::{AtomicUsize, Ordering}, Mutex, RwLock, }, + time::Instant, }, }; @@ -210,45 +217,37 @@ impl CrdsGossipPush { pub(crate) fn process_push_message( &self, crds: &RwLock, - from: &Pubkey, - values: Vec, + messages: Vec<(/*from:*/ Pubkey, Vec)>, now: u64, - ) -> Vec> { - self.num_total.fetch_add(values.len(), Ordering::Relaxed); - let values: Vec<_> = { - let wallclock_window = self.wallclock_window(now); - let mut received_cache = self.received_cache.lock().unwrap(); - values - .into_iter() - .map(|value| { - if !wallclock_window.contains(&value.wallclock()) { - return Err(CrdsGossipError::PushMessageTimeout); - } - let origin = value.pubkey(); - let peers = received_cache.entry(origin).or_default(); - peers - .entry(*from) - .and_modify(|(_pruned, timestamp)| *timestamp = now) - .or_insert((/*pruned:*/ false, now)); - Ok(value) - }) - .collect() - }; + ) -> HashSet { + let mut received_cache = self.received_cache.lock().unwrap(); let mut crds = crds.write().unwrap(); - values - .into_iter() - .map(|value| { - let value = value?; + let wallclock_window = self.wallclock_window(now); + let mut origins = HashSet::new(); + for (from, values) in messages { + self.num_total.fetch_add(values.len(), Ordering::Relaxed); + for value in values { + if !wallclock_window.contains(&value.wallclock()) { + continue; + } let origin = value.pubkey(); + received_cache + .entry(origin) + .or_default() + .entry(from) + .and_modify(|(_pruned, timestamp)| *timestamp = now) + .or_insert((/*pruned:*/ false, now)); match crds.insert(value, now, GossipRoute::PushMessage) { - Ok(()) => Ok(origin), + Ok(()) => { + origins.insert(origin); + } Err(_) => { self.num_old.fetch_add(1, Ordering::Relaxed); - Err(CrdsGossipError::PushMessageOldVersion) } } - }) - .collect() + } + } + origins } /// New push message to broadcast to peers. @@ -261,12 +260,16 @@ impl CrdsGossipPush { &self, crds: &RwLock, now: u64, - ) -> HashMap> { + ) -> ( + HashMap>, + usize, // number of values + usize, // number of push messages + ) { let active_set = self.active_set.read().unwrap(); let active_set_len = active_set.len(); let push_fanout = self.push_fanout.min(active_set_len); if push_fanout == 0 { - return HashMap::default(); + return (HashMap::default(), 0, 0); } let mut num_pushes = 0; let mut num_values = 0; @@ -310,11 +313,16 @@ impl CrdsGossipPush { for target_pubkey in push_messages.keys().copied() { last_pushed_to.put(target_pubkey, now); } - push_messages + (push_messages, num_values, num_pushes) } /// Add the `from` to the peer's filter of nodes. - pub fn process_prune_msg(&self, self_pubkey: &Pubkey, peer: &Pubkey, origins: &[Pubkey]) { + pub(crate) fn process_prune_msg( + &self, + self_pubkey: &Pubkey, + peer: &Pubkey, + origins: &[Pubkey], + ) { if let Some(filter) = self.active_set.read().unwrap().get(peer) { for origin in origins { if origin != self_pubkey { @@ -334,15 +342,18 @@ impl CrdsGossipPush { /// # Arguments /// /// * ratio - active_set.len()/ratio is the number of actives to rotate + #[allow(clippy::too_many_arguments)] pub(crate) fn refresh_push_active_set( &self, crds: &RwLock, stakes: &HashMap, gossip_validators: Option<&HashSet>, - self_id: &Pubkey, + self_keypair: &Keypair, self_shred_version: u16, network_size: usize, ratio: usize, + ping_cache: &Mutex, + pings: &mut Vec<(SocketAddr, Ping)>, socket_addr_space: &SocketAddrSpace, ) { const BLOOM_FALSE_RATE: f64 = 0.1; @@ -353,18 +364,36 @@ impl CrdsGossipPush { const MIN_NUM_BLOOM_ITEMS: usize = CRDS_UNIQUE_PUBKEY_CAPACITY; let mut rng = rand::thread_rng(); let mut new_items = HashMap::new(); - let (weights, peers): (Vec<_>, Vec<_>) = { - self.push_options( - crds, - self_id, - self_shred_version, - stakes, - gossip_validators, - socket_addr_space, - ) - .into_iter() - .unzip() + // Gossip peers and respective sampling weights. + let peers = self.push_options( + crds, + &self_keypair.pubkey(), + self_shred_version, + stakes, + gossip_validators, + socket_addr_space, + ); + // Check for nodes which have responded to ping messages. + let peers: Vec<_> = { + let mut ping_cache = ping_cache.lock().unwrap(); + let mut pingf = move || Ping::new_rand(&mut rng, self_keypair).ok(); + let now = Instant::now(); + peers + .into_iter() + .filter(|(_weight, peer)| { + let node = (peer.id, peer.gossip); + let (check, ping) = ping_cache.check(now, node, &mut pingf); + if let Some(ping) = ping { + pings.push((peer.gossip, ping)); + } + check + }) + .collect() }; + let (weights, peers): (Vec<_>, Vec<_>) = crds_gossip::dedup_gossip_addresses(peers) + .into_values() + .map(|(weight, node)| (weight, node.id)) + .unzip(); if peers.is_empty() { return; } @@ -406,7 +435,7 @@ impl CrdsGossipPush { stakes: &HashMap, gossip_validators: Option<&HashSet>, socket_addr_space: &SocketAddrSpace, - ) -> Vec<(/*weight:*/ u64, /*node:*/ Pubkey)> { + ) -> Vec<(/*weight:*/ u64, /*node:*/ ContactInfo)> { let now = timestamp(); let mut rng = rand::thread_rng(); let max_weight = u16::MAX as f32 - 1.0; @@ -443,7 +472,7 @@ impl CrdsGossipPush { let weight = get_weight(max_weight, since, stake); // Weights are bounded by max_weight defined above. // So this type-cast should be safe. - ((weight * 100.0) as u64, info.id) + ((weight * 100.0) as u64, info.clone()) }) .collect() } @@ -489,12 +518,21 @@ impl CrdsGossipPush { } #[cfg(test)] -mod test { +mod tests { use { super::*, - crate::{contact_info::ContactInfo, crds_value::CrdsData}, + crate::{crds_value::CrdsData, socketaddr}, + std::time::Duration, }; + fn new_ping_cache() -> PingCache { + PingCache::new( + Duration::from_secs(20 * 60), // ttl + Duration::from_secs(20 * 60) / 64, // rate_limit_delay + 128, // capacity + ) + } + #[test] fn test_prune() { let crds = RwLock::::default(); @@ -506,13 +544,13 @@ mod test { stakes.insert(self_id, 100); stakes.insert(origin, 100); - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &origin, 0, - ))); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&origin, 0), + )); let low_staked_peers = (0..10).map(|_| solana_sdk::pubkey::new_rand()); let mut low_staked_set = HashSet::new(); low_staked_peers.for_each(|p| { - push.process_push_message(&crds, &p, vec![value.clone()], 0); + push.process_push_message(&crds, vec![(p, vec![value.clone()])], 0); low_staked_set.insert(p); stakes.insert(p, 1); }); @@ -534,7 +572,7 @@ mod test { let high_staked_peer = solana_sdk::pubkey::new_rand(); let high_stake = CrdsGossipPush::prune_stake_threshold(100, 100) + 10; stakes.insert(high_staked_peer, high_stake); - push.process_push_message(&crds, &high_staked_peer, vec![value], 0); + push.process_push_message(&crds, vec![(high_staked_peer, vec![value])], 0); let pruned = { let mut received_cache = push.received_cache.lock().unwrap(); @@ -561,23 +599,21 @@ mod test { fn test_process_push_one() { let crds = RwLock::::default(); let push = CrdsGossipPush::default(); - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0), + )); let label = value.label(); // push a new message assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value.clone()], 0), - [Ok(label.pubkey())], + push.process_push_message(&crds, vec![(Pubkey::default(), vec![value.clone()])], 0), + [label.pubkey()].into_iter().collect(), ); assert_eq!(crds.read().unwrap().get::<&CrdsValue>(&label), Some(&value)); // push it again - assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value], 0), - [Err(CrdsGossipError::PushMessageOldVersion)], - ); + assert!(push + .process_push_message(&crds, vec![(Pubkey::default(), vec![value])], 0) + .is_empty()); } #[test] fn test_process_push_old_version() { @@ -585,21 +621,20 @@ mod test { let push = CrdsGossipPush::default(); let mut ci = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); ci.wallclock = 1; - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci.clone())); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci.clone())); // push a new message assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value], 0), - [Ok(ci.id)], + push.process_push_message(&crds, vec![(Pubkey::default(), vec![value])], 0), + [ci.id].into_iter().collect() ); // push an old version ci.wallclock = 0; - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci)); - assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value], 0), - [Err(CrdsGossipError::PushMessageOldVersion)], - ); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci)); + assert!(push + .process_push_message(&crds, vec![(Pubkey::default(), vec![value])], 0) + .is_empty()); } #[test] fn test_process_push_timeout() { @@ -610,19 +645,17 @@ mod test { // push a version to far in the future ci.wallclock = timeout + 1; - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci.clone())); - assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value], 0), - [Err(CrdsGossipError::PushMessageTimeout)], - ); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci.clone())); + assert!(push + .process_push_message(&crds, vec![(Pubkey::default(), vec![value])], 0) + .is_empty()); // push a version to far in the past ci.wallclock = 0; - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci)); - assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value], timeout + 1), - [Err(CrdsGossipError::PushMessageTimeout)] - ); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci)); + assert!(push + .process_push_message(&crds, vec![(Pubkey::default(), vec![value])], timeout + 1) + .is_empty()); } #[test] fn test_process_push_update() { @@ -631,20 +664,20 @@ mod test { let mut ci = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); let origin = ci.id; ci.wallclock = 0; - let value_old = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci.clone())); + let value_old = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci.clone())); // push a new message assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value_old], 0), - [Ok(origin)], + push.process_push_message(&crds, vec![(Pubkey::default(), vec![value_old])], 0), + [origin].into_iter().collect() ); // push an old version ci.wallclock = 1; - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci)); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci)); assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value], 0), - [Ok(origin)], + push.process_push_message(&crds, vec![(Pubkey::default(), vec![value])], 0), + [origin].into_iter().collect() ); } #[test] @@ -660,33 +693,40 @@ mod test { let now = timestamp(); let mut crds = Crds::default(); let push = CrdsGossipPush::default(); - let value1 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let mut ping_cache = new_ping_cache(); + let value1 = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); + ping_cache.mock_pong(value1.id, value1.gossip, Instant::now()); + let value1 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(value1)); assert_eq!( crds.insert(value1.clone(), now, GossipRoute::LocalMessage), Ok(()) ); + let keypair = Keypair::new(); let crds = RwLock::new(crds); + let ping_cache = Mutex::new(ping_cache); push.refresh_push_active_set( &crds, - &HashMap::new(), - None, - &Pubkey::default(), - 0, - 1, - 1, + &HashMap::new(), // stakes + None, // gossip_validators + &keypair, + 0, // self_shred_version + 1, // network_sizer + 1, // ratio + &ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); let active_set = push.active_set.read().unwrap(); assert!(active_set.get(&value1.label().pubkey()).is_some()); - let value2 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let mut value2 = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); + value2.gossip.set_port(1245); + ping_cache + .lock() + .unwrap() + .mock_pong(value2.id, value2.gossip, Instant::now()); + let value2 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(value2)); assert!(active_set.get(&value2.label().pubkey()).is_none()); drop(active_set); assert_eq!( @@ -698,12 +738,14 @@ mod test { for _ in 0..30 { push.refresh_push_active_set( &crds, - &HashMap::new(), - None, - &Pubkey::default(), - 0, - 1, - 1, + &HashMap::new(), // stakes + None, // gossip_validators + &keypair, + 0, // self_shred_version + 1, // network_size + 1, // ratio + &ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); let active_set = push.active_set.read().unwrap(); @@ -715,10 +757,14 @@ mod test { let active_set = push.active_set.read().unwrap(); assert!(active_set.get(&value2.label().pubkey()).is_some()); } - for _ in 0..push.num_active { - let value2 = CrdsValue::new_unsigned(CrdsData::ContactInfo( - ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0), - )); + for k in 0..push.num_active { + let mut value2 = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); + value2.gossip.set_port(1246 + k as u16); + ping_cache + .lock() + .unwrap() + .mock_pong(value2.id, value2.gossip, Instant::now()); + let value2 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(value2)); assert_eq!( crds.write() .unwrap() @@ -726,14 +772,17 @@ mod test { Ok(()) ); } + push.refresh_push_active_set( &crds, - &HashMap::new(), - None, - &Pubkey::default(), - 0, - 1, - 1, + &HashMap::new(), // stakes + None, // gossip_validators + &keypair, + 0, // self_shred_version + 1, // network_size + 1, // ratio + &ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); assert_eq!(push.active_set.read().unwrap().len(), push.num_active); @@ -746,10 +795,9 @@ mod test { let push = CrdsGossipPush::default(); let mut stakes = HashMap::new(); for i in 1..=100 { - let peer = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - time, - ))); + let peer = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), time), + )); let id = peer.label().pubkey(); crds.insert(peer.clone(), time, GossipRoute::LocalMessage) .unwrap(); @@ -768,7 +816,7 @@ mod test { assert!(!options.is_empty()); options.sort_by(|(weight_l, _), (weight_r, _)| weight_r.partial_cmp(weight_l).unwrap()); // check that the highest stake holder is also the heaviest weighted. - assert_eq!(stakes[&options[0].1], 10_000_u64); + assert_eq!(stakes[&options[0].1.id], 10_000_u64); } #[test] @@ -780,25 +828,25 @@ mod test { let gossip = socketaddr!("127.0.0.1:1234"); - let me = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let me = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), shred_version: 123, gossip, ..ContactInfo::default() })); - let spy = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let spy = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), shred_version: 0, gossip, ..ContactInfo::default() })); - let node_123 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let node_123 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), shred_version: 123, gossip, ..ContactInfo::default() })); - let node_456 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let node_456 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), shred_version: 456, gossip, @@ -826,7 +874,7 @@ mod test { &SocketAddrSpace::Unspecified, ) .iter() - .map(|(_, pk)| *pk) + .map(|(_, node)| node.id) .collect::>(); assert_eq!(options.len(), 1); assert!(!options.contains(&spy.pubkey())); @@ -852,12 +900,12 @@ mod test { let node = CrdsGossipPush::default(); let gossip = socketaddr!("127.0.0.1:1234"); - let me = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let me = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), gossip, ..ContactInfo::default() })); - let node_123 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo { + let node_123 = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ContactInfo { id: solana_sdk::pubkey::new_rand(), gossip, ..ContactInfo::default() @@ -906,7 +954,7 @@ mod test { ); assert_eq!(options.len(), 1); - assert_eq!(options[0].1, node_123.pubkey()); + assert_eq!(options[0].1.id, node_123.pubkey()); } #[test] @@ -914,39 +962,41 @@ mod test { let now = timestamp(); let mut crds = Crds::default(); let push = CrdsGossipPush::default(); - let peer = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let mut ping_cache = new_ping_cache(); + let peer = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); + ping_cache.mock_pong(peer.id, peer.gossip, Instant::now()); + let peer = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(peer)); assert_eq!( crds.insert(peer.clone(), now, GossipRoute::LocalMessage), Ok(()) ); let crds = RwLock::new(crds); + let ping_cache = Mutex::new(ping_cache); push.refresh_push_active_set( &crds, - &HashMap::new(), - None, - &Pubkey::default(), - 0, - 1, - 1, + &HashMap::new(), // stakes + None, // gossip_validtors + &Keypair::new(), + 0, // self_shred_version + 1, // network_size + 1, // ratio + &ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); - let new_msg = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let new_msg = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0), + )); let mut expected = HashMap::new(); expected.insert(peer.label().pubkey(), vec![new_msg.clone()]); let origin = new_msg.pubkey(); assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![new_msg], 0), - [Ok(origin)] + push.process_push_message(&crds, vec![(Pubkey::default(), vec![new_msg])], 0), + [origin].into_iter().collect() ); assert_eq!(push.active_set.read().unwrap().len(), 1); - assert_eq!(push.new_push_messages(&crds, 0), expected); + assert_eq!(push.new_push_messages(&crds, 0).0, expected); } #[test] fn test_personalized_push_messages() { @@ -954,12 +1004,14 @@ mod test { let mut rng = rand::thread_rng(); let mut crds = Crds::default(); let push = CrdsGossipPush::default(); + let mut ping_cache = new_ping_cache(); let peers: Vec<_> = vec![0, 0, now] .into_iter() .map(|wallclock| { let mut peer = ContactInfo::new_rand(&mut rng, /*pubkey=*/ None); peer.wallclock = wallclock; - CrdsValue::new_unsigned(CrdsData::ContactInfo(peer)) + ping_cache.mock_pong(peer.id, peer.gossip, Instant::now()); + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(peer)) }) .collect(); let origin: Vec<_> = peers.iter().map(|node| node.pubkey()).collect(); @@ -973,17 +1025,24 @@ mod test { ); let crds = RwLock::new(crds); assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![peers[2].clone()], now), - [Ok(origin[2])], + push.process_push_message( + &crds, + vec![(Pubkey::default(), vec![peers[2].clone()])], + now + ), + [origin[2]].into_iter().collect() ); + let ping_cache = Mutex::new(ping_cache); push.refresh_push_active_set( &crds, - &HashMap::new(), - None, - &Pubkey::default(), - 0, - 1, - 1, + &HashMap::new(), // stakes + None, // gossip_validators + &Keypair::new(), + 0, // self_shred_version + 1, // network_size + 1, // ratio + &ping_cache, + &mut Vec::new(), &SocketAddrSpace::Unspecified, ); @@ -995,81 +1054,84 @@ mod test { .into_iter() .collect(); assert_eq!(push.active_set.read().unwrap().len(), 3); - assert_eq!(push.new_push_messages(&crds, now), expected); + assert_eq!(push.new_push_messages(&crds, now).0, expected); } #[test] fn test_process_prune() { let mut crds = Crds::default(); let self_id = solana_sdk::pubkey::new_rand(); let push = CrdsGossipPush::default(); - let peer = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let peer = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0), + )); assert_eq!( crds.insert(peer.clone(), 0, GossipRoute::LocalMessage), Ok(()) ); let crds = RwLock::new(crds); + let ping_cache = Mutex::new(new_ping_cache()); push.refresh_push_active_set( &crds, - &HashMap::new(), - None, - &Pubkey::default(), - 0, - 1, - 1, + &HashMap::new(), // stakes + None, // gossip_validators + &Keypair::new(), + 0, // self_shred_version + 1, // network_size + 1, // ratio + &ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); - let new_msg = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let new_msg = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0), + )); let expected = HashMap::new(); let origin = new_msg.pubkey(); assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![new_msg.clone()], 0), - [Ok(origin)], + push.process_push_message(&crds, vec![(Pubkey::default(), vec![new_msg.clone()])], 0), + [origin].into_iter().collect() ); push.process_prune_msg( &self_id, &peer.label().pubkey(), &[new_msg.label().pubkey()], ); - assert_eq!(push.new_push_messages(&crds, 0), expected); + assert_eq!(push.new_push_messages(&crds, 0).0, expected); } #[test] fn test_purge_old_pending_push_messages() { let mut crds = Crds::default(); let push = CrdsGossipPush::default(); - let peer = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &solana_sdk::pubkey::new_rand(), - 0, - ))); + let peer = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0), + )); assert_eq!(crds.insert(peer, 0, GossipRoute::LocalMessage), Ok(())); let crds = RwLock::new(crds); + let ping_cache = Mutex::new(new_ping_cache()); push.refresh_push_active_set( &crds, - &HashMap::new(), - None, - &Pubkey::default(), - 0, - 1, - 1, + &HashMap::new(), // stakes + None, // gossip_validators + &Keypair::new(), + 0, // self_shred_version + 1, // network_size + 1, // ratio + &ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); let mut ci = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); ci.wallclock = 1; - let new_msg = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci)); + let new_msg = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci)); let expected = HashMap::new(); let origin = new_msg.pubkey(); assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![new_msg], 1), - [Ok(origin)], + push.process_push_message(&crds, vec![(Pubkey::default(), vec![new_msg])], 1), + [origin].into_iter().collect() ); - assert_eq!(push.new_push_messages(&crds, 0), expected); + assert_eq!(push.new_push_messages(&crds, 0).0, expected); } #[test] @@ -1078,12 +1140,12 @@ mod test { let push = CrdsGossipPush::default(); let mut ci = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0); ci.wallclock = 0; - let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ci)); + let value = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci)); let label = value.label(); // push a new message assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value.clone()], 0), - [Ok(label.pubkey())] + push.process_push_message(&crds, vec![(Pubkey::default(), vec![value.clone()])], 0), + [label.pubkey()].into_iter().collect() ); assert_eq!( crds.write().unwrap().get::<&CrdsValue>(&label), @@ -1091,18 +1153,16 @@ mod test { ); // push it again - assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value.clone()], 0), - [Err(CrdsGossipError::PushMessageOldVersion)], - ); + assert!(push + .process_push_message(&crds, vec![(Pubkey::default(), vec![value.clone()])], 0) + .is_empty()); // purge the old pushed push.purge_old_received_cache(1); // push it again - assert_eq!( - push.process_push_message(&crds, &Pubkey::default(), vec![value], 0), - [Err(CrdsGossipError::PushMessageOldVersion)], - ); + assert!(push + .process_push_message(&crds, vec![(Pubkey::default(), vec![value])], 0) + .is_empty()); } } diff --git a/gossip/src/crds_value.rs b/gossip/src/crds_value.rs index cbee1542f92008..ec63ac51399ba6 100644 --- a/gossip/src/crds_value.rs +++ b/gossip/src/crds_value.rs @@ -1,10 +1,10 @@ use { crate::{ cluster_info::MAX_SNAPSHOT_HASHES, - contact_info::ContactInfo, deprecated, duplicate_shred::{DuplicateShred, DuplicateShredIndex, MAX_DUPLICATE_SHREDS}, epoch_slots::EpochSlots, + legacy_contact_info::LegacyContactInfo, }, bincode::{serialize, serialized_size}, rand::{CryptoRng, Rng}, @@ -81,7 +81,7 @@ impl Signable for CrdsValue { #[allow(clippy::large_enum_variant)] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, AbiExample, AbiEnumVisitor)] pub enum CrdsData { - ContactInfo(ContactInfo), + LegacyContactInfo(LegacyContactInfo), Vote(VoteIndex, Vote), LowestSlot(/*DEPRECATED:*/ u8, LowestSlot), SnapshotHashes(SnapshotHashes), @@ -97,7 +97,7 @@ pub enum CrdsData { impl Sanitize for CrdsData { fn sanitize(&self) -> Result<(), SanitizeError> { match self { - CrdsData::ContactInfo(val) => val.sanitize(), + CrdsData::LegacyContactInfo(val) => val.sanitize(), CrdsData::Vote(ix, val) => { if *ix >= MAX_VOTES { return Err(SanitizeError::ValueOutOfBounds); @@ -147,8 +147,9 @@ impl CrdsData { // TODO: Assign ranges to each arm proportional to their frequency in // the mainnet crds table. match kind { - 0 => CrdsData::ContactInfo(ContactInfo::new_rand(rng, pubkey)), - 1 => CrdsData::LowestSlot(rng.gen(), LowestSlot::new_rand(rng, pubkey)), + 0 => CrdsData::LegacyContactInfo(LegacyContactInfo::new_rand(rng, pubkey)), + // Index for LowestSlot is deprecated and should be zero. + 1 => CrdsData::LowestSlot(0, LowestSlot::new_rand(rng, pubkey)), 2 => CrdsData::SnapshotHashes(SnapshotHashes::new_rand(rng, pubkey)), 3 => CrdsData::AccountsHashes(SnapshotHashes::new_rand(rng, pubkey)), 4 => CrdsData::Version(Version::new_rand(rng, pubkey)), @@ -480,7 +481,7 @@ impl Sanitize for NodeInstance { /// These are labels for values in a record that is associated with `Pubkey` #[derive(PartialEq, Hash, Eq, Clone, Debug)] pub enum CrdsValueLabel { - ContactInfo(Pubkey), + LegacyContactInfo(Pubkey), Vote(VoteIndex, Pubkey), LowestSlot(Pubkey), SnapshotHashes(Pubkey), @@ -496,7 +497,9 @@ pub enum CrdsValueLabel { impl fmt::Display for CrdsValueLabel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - CrdsValueLabel::ContactInfo(_) => write!(f, "ContactInfo({})", self.pubkey()), + CrdsValueLabel::LegacyContactInfo(_) => { + write!(f, "LegacyContactInfo({})", self.pubkey()) + } CrdsValueLabel::Vote(ix, _) => write!(f, "Vote({}, {})", ix, self.pubkey()), CrdsValueLabel::LowestSlot(_) => write!(f, "LowestSlot({})", self.pubkey()), CrdsValueLabel::SnapshotHashes(_) => write!(f, "SnapshotHashes({})", self.pubkey()), @@ -516,7 +519,7 @@ impl fmt::Display for CrdsValueLabel { impl CrdsValueLabel { pub fn pubkey(&self) -> Pubkey { match self { - CrdsValueLabel::ContactInfo(p) => *p, + CrdsValueLabel::LegacyContactInfo(p) => *p, CrdsValueLabel::Vote(_, p) => *p, CrdsValueLabel::LowestSlot(p) => *p, CrdsValueLabel::SnapshotHashes(p) => *p, @@ -565,7 +568,7 @@ impl CrdsValue { /// This is used to time out push messages. pub fn wallclock(&self) -> u64 { match &self.data { - CrdsData::ContactInfo(contact_info) => contact_info.wallclock, + CrdsData::LegacyContactInfo(contact_info) => contact_info.wallclock, CrdsData::Vote(_, vote) => vote.wallclock, CrdsData::LowestSlot(_, obj) => obj.wallclock, CrdsData::SnapshotHashes(hash) => hash.wallclock, @@ -580,7 +583,7 @@ impl CrdsValue { } pub fn pubkey(&self) -> Pubkey { match &self.data { - CrdsData::ContactInfo(contact_info) => contact_info.id, + CrdsData::LegacyContactInfo(contact_info) => contact_info.id, CrdsData::Vote(_, vote) => vote.from, CrdsData::LowestSlot(_, slots) => slots.from, CrdsData::SnapshotHashes(hash) => hash.from, @@ -595,7 +598,7 @@ impl CrdsValue { } pub fn label(&self) -> CrdsValueLabel { match &self.data { - CrdsData::ContactInfo(_) => CrdsValueLabel::ContactInfo(self.pubkey()), + CrdsData::LegacyContactInfo(_) => CrdsValueLabel::LegacyContactInfo(self.pubkey()), CrdsData::Vote(ix, _) => CrdsValueLabel::Vote(*ix, self.pubkey()), CrdsData::LowestSlot(_, _) => CrdsValueLabel::LowestSlot(self.pubkey()), CrdsData::SnapshotHashes(_) => CrdsValueLabel::SnapshotHashes(self.pubkey()), @@ -610,9 +613,9 @@ impl CrdsValue { } } } - pub fn contact_info(&self) -> Option<&ContactInfo> { + pub fn contact_info(&self) -> Option<&LegacyContactInfo> { match &self.data { - CrdsData::ContactInfo(contact_info) => Some(contact_info), + CrdsData::LegacyContactInfo(contact_info) => Some(contact_info), _ => None, } } @@ -682,7 +685,6 @@ pub(crate) fn sanitize_wallclock(wallclock: u64) -> Result<(), SanitizeError> { mod test { use { super::*, - crate::contact_info::ContactInfo, bincode::{deserialize, Options}, rand::SeedableRng, rand_chacha::ChaChaRng, @@ -698,10 +700,10 @@ mod test { #[test] fn test_keys_and_values() { let mut rng = rand::thread_rng(); - let v = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); + let v = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(LegacyContactInfo::default())); assert_eq!(v.wallclock(), 0); let key = v.contact_info().unwrap().id; - assert_eq!(v.label(), CrdsValueLabel::ContactInfo(key)); + assert_eq!(v.label(), CrdsValueLabel::LegacyContactInfo(key)); let v = Vote::new(Pubkey::default(), new_test_vote_tx(&mut rng), 0).unwrap(); let v = CrdsValue::new_unsigned(CrdsData::Vote(0, v)); @@ -755,10 +757,9 @@ mod test { let mut rng = rand::thread_rng(); let keypair = Keypair::new(); let wrong_keypair = Keypair::new(); - let mut v = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost( - &keypair.pubkey(), - timestamp(), - ))); + let mut v = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + LegacyContactInfo::new_localhost(&keypair.pubkey(), timestamp()), + )); verify_signatures(&mut v, &keypair, &wrong_keypair); let v = Vote::new(keypair.pubkey(), new_test_vote_tx(&mut rng), timestamp()).unwrap(); let mut v = CrdsValue::new_unsigned(CrdsData::Vote(0, v)); @@ -864,7 +865,7 @@ mod test { let index = rng.gen_range(0, keys.len()); CrdsValue::new_rand(&mut rng, Some(&keys[index])) }) - .take(2048) + .take(1 << 12) .collect(); let mut currents = HashMap::new(); for value in filter_current(&values) { @@ -888,10 +889,12 @@ mod test { } } assert_eq!(count, currents.len()); - // Currently CrdsData::new_rand is only implemented for 5 different - // kinds and excludes EpochSlots, and so the unique labels cannot be - // more than (5 + MAX_VOTES) times number of keys. - assert!(currents.len() <= keys.len() * (5 + MAX_VOTES as usize)); + // Currently CrdsData::new_rand is implemented for: + // AccountsHashes, ContactInfo, LowestSlot, SnapshotHashes, Version + // EpochSlots x MAX_EPOCH_SLOTS + // Vote x MAX_VOTES + let num_kinds = 5 + MAX_VOTES as usize + MAX_EPOCH_SLOTS as usize; + assert!(currents.len() <= keys.len() * num_kinds); } #[test] @@ -1032,8 +1035,8 @@ mod test { assert_eq!(node.overrides(&other_crds), None); assert_eq!(other.overrides(&node_crds), None); // Differnt crds value is not a duplicate. - let other = ContactInfo::new_rand(&mut rng, Some(pubkey)); - let other = CrdsValue::new_unsigned(CrdsData::ContactInfo(other)); + let other = LegacyContactInfo::new_rand(&mut rng, Some(pubkey)); + let other = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(other)); assert!(!node.check_duplicate(&other)); assert_eq!(node.overrides(&other), None); } @@ -1042,13 +1045,10 @@ mod test { fn test_should_force_push() { let mut rng = rand::thread_rng(); let pubkey = Pubkey::new_unique(); - assert!( - !CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_rand( - &mut rng, - Some(pubkey), - ))) - .should_force_push(&pubkey) - ); + assert!(!CrdsValue::new_unsigned(CrdsData::LegacyContactInfo( + LegacyContactInfo::new_rand(&mut rng, Some(pubkey)) + )) + .should_force_push(&pubkey)); let node = CrdsValue::new_unsigned(CrdsData::NodeInstance(NodeInstance::new( &mut rng, pubkey, diff --git a/gossip/src/duplicate_shred.rs b/gossip/src/duplicate_shred.rs index 6ce635159101ab..d6861c1fc54daf 100644 --- a/gossip/src/duplicate_shred.rs +++ b/gossip/src/duplicate_shred.rs @@ -284,7 +284,7 @@ pub(crate) mod tests { super::*, rand::Rng, solana_entry::entry::Entry, - solana_ledger::shred::{ProcessShredsStats, Shredder}, + solana_ledger::shred::{ProcessShredsStats, ReedSolomonCache, Shredder}, solana_sdk::{ hash, signature::{Keypair, Signer}, @@ -342,6 +342,8 @@ pub(crate) mod tests { true, // is_last_in_slot next_shred_index, next_shred_index, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); data_shreds.swap_remove(0) diff --git a/gossip/src/gossip_service.rs b/gossip/src/gossip_service.rs index 9bc911b405d49f..85f9e0166e49b0 100644 --- a/gossip/src/gossip_service.rs +++ b/gossip/src/gossip_service.rs @@ -1,7 +1,7 @@ //! The `gossip_service` module implements the network control plane. use { - crate::{cluster_info::ClusterInfo, contact_info::ContactInfo}, + crate::{cluster_info::ClusterInfo, legacy_contact_info::LegacyContactInfo as ContactInfo}, crossbeam_channel::{unbounded, Sender}, rand::{thread_rng, Rng}, solana_client::{connection_cache::ConnectionCache, thin_client::ThinClient}, @@ -80,7 +80,7 @@ impl GossipService { exit.clone(), ); let t_responder = streamer::responder( - "gossip", + "Gossip", gossip_socket, response_receiver, socket_addr_space, diff --git a/gossip/src/contact_info.rs b/gossip/src/legacy_contact_info.rs similarity index 86% rename from gossip/src/contact_info.rs rename to gossip/src/legacy_contact_info.rs index 1a5a33aa79d256..9f41c79d65e4c5 100644 --- a/gossip/src/contact_info.rs +++ b/gossip/src/legacy_contact_info.rs @@ -15,7 +15,7 @@ use { #[derive( Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, AbiExample, Deserialize, Serialize, )] -pub struct ContactInfo { +pub struct LegacyContactInfo { pub id: Pubkey, /// gossip address pub gossip: SocketAddr, @@ -43,7 +43,7 @@ pub struct ContactInfo { pub shred_version: u16, } -impl Sanitize for ContactInfo { +impl Sanitize for LegacyContactInfo { fn sanitize(&self) -> std::result::Result<(), SanitizeError> { if self.wallclock >= MAX_WALLCLOCK { return Err(SanitizeError::ValueOutOfBounds); @@ -68,9 +68,9 @@ macro_rules! socketaddr_any { }; } -impl Default for ContactInfo { +impl Default for LegacyContactInfo { fn default() -> Self { - ContactInfo { + LegacyContactInfo { id: Pubkey::default(), gossip: socketaddr_any!(), tvu: socketaddr_any!(), @@ -88,7 +88,7 @@ impl Default for ContactInfo { } } -impl ContactInfo { +impl LegacyContactInfo { pub fn new_localhost(id: &Pubkey, now: u64) -> Self { Self { id: *id, @@ -107,16 +107,18 @@ impl ContactInfo { } } - /// New random ContactInfo for tests and simulations. + /// New random LegacyContactInfo for tests and simulations. pub fn new_rand(rng: &mut R, pubkey: Option) -> Self { let delay = 10 * 60 * 1000; // 10 minutes let now = timestamp() - delay + rng.gen_range(0, 2 * delay); let pubkey = pubkey.unwrap_or_else(solana_sdk::pubkey::new_rand); - ContactInfo::new_localhost(&pubkey, now) + let mut node = LegacyContactInfo::new_localhost(&pubkey, now); + node.gossip.set_port(rng.gen_range(1024, u16::MAX)); + node } #[cfg(test)] - /// ContactInfo with multicast addresses for adversarial testing. + /// LegacyContactInfo with multicast addresses for adversarial testing. pub fn new_multicast() -> Self { let addr = socketaddr!("224.0.1.255:1000"); assert!(addr.ip().is_multicast()); @@ -178,13 +180,13 @@ impl ContactInfo { Self::new_with_pubkey_socketaddr(&keypair.pubkey(), bind_addr) } - // Construct a ContactInfo that's only usable for gossip + // Construct a LegacyContactInfo that's only usable for gossip pub fn new_gossip_entry_point(gossip_addr: &SocketAddr) -> Self { Self { id: Pubkey::default(), gossip: *gossip_addr, wallclock: timestamp(), - ..ContactInfo::default() + ..LegacyContactInfo::default() } } @@ -197,16 +199,9 @@ impl ContactInfo { /// port must not be 0 /// ip must be specified and not multicast /// loopback ip is only allowed in tests - // Keeping this for now not to break tvu-peers and turbine shuffle order of - // nodes when arranging nodes on retransmit tree. Private IP addresses in - // turbine are filtered out just before sending packets. - pub(crate) fn is_valid_tvu_address(addr: &SocketAddr) -> bool { - (addr.port() != 0) && Self::is_valid_ip(addr.ip()) - } - // TODO: Replace this entirely with streamer SocketAddrSpace. pub fn is_valid_address(addr: &SocketAddr, socket_addr_space: &SocketAddrSpace) -> bool { - Self::is_valid_tvu_address(addr) && socket_addr_space.check(addr) + addr.port() != 0u16 && Self::is_valid_ip(addr.ip()) && socket_addr_space.check(addr) } pub fn client_facing_addr(&self) -> (SocketAddr, SocketAddr) { @@ -217,8 +212,8 @@ impl ContactInfo { &self, socket_addr_space: &SocketAddrSpace, ) -> Option<(SocketAddr, SocketAddr)> { - if ContactInfo::is_valid_address(&self.rpc, socket_addr_space) - && ContactInfo::is_valid_address(&self.tpu, socket_addr_space) + if LegacyContactInfo::is_valid_address(&self.rpc, socket_addr_space) + && LegacyContactInfo::is_valid_address(&self.tpu, socket_addr_space) { Some((self.rpc, self.tpu)) } else { @@ -234,31 +229,31 @@ mod tests { #[test] fn test_is_valid_address() { let bad_address_port = socketaddr!("127.0.0.1:0"); - assert!(!ContactInfo::is_valid_address( + assert!(!LegacyContactInfo::is_valid_address( &bad_address_port, &SocketAddrSpace::Unspecified )); let bad_address_unspecified = socketaddr!(0, 1234); - assert!(!ContactInfo::is_valid_address( + assert!(!LegacyContactInfo::is_valid_address( &bad_address_unspecified, &SocketAddrSpace::Unspecified )); let bad_address_multicast = socketaddr!([224, 254, 0, 0], 1234); - assert!(!ContactInfo::is_valid_address( + assert!(!LegacyContactInfo::is_valid_address( &bad_address_multicast, &SocketAddrSpace::Unspecified )); let loopback = socketaddr!("127.0.0.1:1234"); - assert!(ContactInfo::is_valid_address( + assert!(LegacyContactInfo::is_valid_address( &loopback, &SocketAddrSpace::Unspecified )); - // assert!(!ContactInfo::is_valid_ip_internal(loopback.ip(), false)); + // assert!(!LegacyContactInfo::is_valid_ip_internal(loopback.ip(), false)); } #[test] fn test_default() { - let ci = ContactInfo::default(); + let ci = LegacyContactInfo::default(); assert!(ci.gossip.ip().is_unspecified()); assert!(ci.tvu.ip().is_unspecified()); assert!(ci.tpu_forwards.ip().is_unspecified()); @@ -270,7 +265,7 @@ mod tests { } #[test] fn test_multicast() { - let ci = ContactInfo::new_multicast(); + let ci = LegacyContactInfo::new_multicast(); assert!(ci.gossip.ip().is_multicast()); assert!(ci.tvu.ip().is_multicast()); assert!(ci.tpu_forwards.ip().is_multicast()); @@ -283,7 +278,7 @@ mod tests { #[test] fn test_entry_point() { let addr = socketaddr!("127.0.0.1:10"); - let ci = ContactInfo::new_gossip_entry_point(&addr); + let ci = LegacyContactInfo::new_gossip_entry_point(&addr); assert_eq!(ci.gossip, addr); assert!(ci.tvu.ip().is_unspecified()); assert!(ci.tpu_forwards.ip().is_unspecified()); @@ -296,7 +291,7 @@ mod tests { #[test] fn test_socketaddr() { let addr = socketaddr!("127.0.0.1:10"); - let ci = ContactInfo::new_with_socketaddr(&addr); + let ci = LegacyContactInfo::new_with_socketaddr(&addr); assert_eq!(ci.tpu, addr); assert_eq!(ci.tpu_vote.port(), 17); assert_eq!(ci.gossip.port(), 11); @@ -310,7 +305,7 @@ mod tests { #[test] fn replayed_data_new_with_socketaddr_with_pubkey() { let keypair = Keypair::new(); - let d1 = ContactInfo::new_with_pubkey_socketaddr( + let d1 = LegacyContactInfo::new_with_pubkey_socketaddr( &keypair.pubkey(), &socketaddr!("127.0.0.1:1234"), ); @@ -335,7 +330,7 @@ mod tests { #[test] fn test_valid_client_facing() { - let mut ci = ContactInfo::default(); + let mut ci = LegacyContactInfo::default(); assert_eq!( ci.valid_client_facing_addr(&SocketAddrSpace::Unspecified), None @@ -353,7 +348,7 @@ mod tests { #[test] fn test_sanitize() { - let mut ci = ContactInfo::default(); + let mut ci = LegacyContactInfo::default(); assert_eq!(ci.sanitize(), Ok(())); ci.wallclock = MAX_WALLCLOCK; assert_eq!(ci.sanitize(), Err(SanitizeError::ValueOutOfBounds)); diff --git a/gossip/src/lib.rs b/gossip/src/lib.rs index 7b37240f896a8e..2435c70d6d6e6f 100644 --- a/gossip/src/lib.rs +++ b/gossip/src/lib.rs @@ -3,8 +3,6 @@ pub mod cluster_info; pub mod cluster_info_metrics; -#[macro_use] -pub mod contact_info; pub mod crds; pub mod crds_entry; pub mod crds_gossip; @@ -18,6 +16,8 @@ pub mod duplicate_shred; pub mod epoch_slots; pub mod gossip_error; pub mod gossip_service; +#[macro_use] +pub mod legacy_contact_info; pub mod ping_pong; pub mod weighted_shuffle; diff --git a/gossip/src/main.rs b/gossip/src/main.rs index f2bb071b2aa82d..8904e1ab974f2c 100644 --- a/gossip/src/main.rs +++ b/gossip/src/main.rs @@ -9,7 +9,9 @@ use { input_parsers::keypair_of, input_validators::{is_keypair_or_ask_keyword, is_port, is_pubkey}, }, - solana_gossip::{contact_info::ContactInfo, gossip_service::discover}, + solana_gossip::{ + gossip_service::discover, legacy_contact_info::LegacyContactInfo as ContactInfo, + }, solana_sdk::pubkey::Pubkey, solana_streamer::socket::SocketAddrSpace, std::{ diff --git a/gossip/src/ping_pong.rs b/gossip/src/ping_pong.rs index 6c3a219cfdb81b..6c7399281e9913 100644 --- a/gossip/src/ping_pong.rs +++ b/gossip/src/ping_pong.rs @@ -16,6 +16,8 @@ use { }, }; +const PING_PONG_HASH_PREFIX: &[u8] = "SOLANA_PING_PONG".as_bytes(); + #[derive(AbiExample, Debug, Deserialize, Serialize)] pub struct Ping { from: Pubkey, @@ -36,6 +38,8 @@ pub struct Pong { pub struct PingCache { // Time-to-live of received pong messages. ttl: Duration, + // Rate limit delay to generate pings for a given address + rate_limit_delay: Duration, // Timestamp of last ping message sent to a remote node. // Used to rate limit pings to remote nodes. pings: LruCache<(Pubkey, SocketAddr), Instant>, @@ -101,7 +105,8 @@ impl Signable for Ping { impl Pong { pub fn new(ping: &Ping, keypair: &Keypair) -> Result { - let hash = hash::hash(&serialize(&ping.token)?); + let token = serialize(&ping.token)?; + let hash = hash::hashv(&[PING_PONG_HASH_PREFIX, &token]); let pong = Pong { from: keypair.pubkey(), hash, @@ -142,9 +147,12 @@ impl Signable for Pong { } impl PingCache { - pub fn new(ttl: Duration, cap: usize) -> Self { + pub fn new(ttl: Duration, rate_limit_delay: Duration, cap: usize) -> Self { + // Sanity check ttl/rate_limit_delay + assert!(rate_limit_delay <= ttl / 2); Self { ttl, + rate_limit_delay, pings: LruCache::new(cap), pongs: LruCache::new(cap), pending_cache: LruCache::new(cap), @@ -181,15 +189,15 @@ impl PingCache { T: Serialize, F: FnMut() -> Option>, { - // Rate limit consecutive pings sent to a remote node. - let delay = self.ttl / 64; match self.pings.peek(&node) { - Some(t) if now.saturating_duration_since(*t) < delay => None, + // Rate limit consecutive pings sent to a remote node. + Some(t) if now.saturating_duration_since(*t) < self.rate_limit_delay => None, _ => { let ping = pingf()?; - let hash = hash::hash(&serialize(&ping.token).ok()?); - self.pings.put(node, now); + let token = serialize(&ping.token).ok()?; + let hash = hash::hashv(&[PING_PONG_HASH_PREFIX, &token]); self.pending_cache.put(hash, node); + self.pings.put(node, now); Some(ping) } } @@ -238,6 +246,7 @@ impl PingCache { pub(crate) fn mock_clone(&self) -> Self { let mut clone = Self { ttl: self.ttl, + rate_limit_delay: self.rate_limit_delay, pings: LruCache::new(self.pings.cap()), pongs: LruCache::new(self.pongs.cap()), pending_cache: LruCache::new(self.pending_cache.cap()), @@ -284,7 +293,10 @@ mod tests { let pong = Pong::new(&ping, &keypair).unwrap(); assert!(pong.verify()); assert!(pong.sanitize().is_ok()); - assert_eq!(hash::hash(&ping.token), pong.hash); + assert_eq!( + hash::hashv(&[PING_PONG_HASH_PREFIX, &ping.token]), + pong.hash + ); } #[test] @@ -292,7 +304,8 @@ mod tests { let now = Instant::now(); let mut rng = rand::thread_rng(); let ttl = Duration::from_millis(256); - let mut cache = PingCache::new(ttl, /*cap=*/ 1000); + let delay = ttl / 64; + let mut cache = PingCache::new(ttl, delay, /*cap=*/ 1000); let this_node = Keypair::new(); let keypairs: Vec<_> = repeat_with(Keypair::new).take(8).collect(); let sockets: Vec<_> = repeat_with(|| { diff --git a/gossip/tests/cluster_info.rs b/gossip/tests/cluster_info.rs index 1b45ec2aa591ca..15583a63b247ab 100644 --- a/gossip/tests/cluster_info.rs +++ b/gossip/tests/cluster_info.rs @@ -8,7 +8,7 @@ use { serial_test::serial, solana_gossip::{ cluster_info::{compute_retransmit_peers, ClusterInfo}, - contact_info::ContactInfo, + legacy_contact_info::LegacyContactInfo as ContactInfo, weighted_shuffle::WeightedShuffle, }, solana_sdk::{pubkey::Pubkey, signer::keypair::Keypair}, diff --git a/gossip/tests/crds_gossip.rs b/gossip/tests/crds_gossip.rs index 7095465c38fa54..0295ff03f66e17 100644 --- a/gossip/tests/crds_gossip.rs +++ b/gossip/tests/crds_gossip.rs @@ -7,13 +7,13 @@ use { solana_gossip::{ cluster_info, cluster_info_metrics::GossipStats, - contact_info::ContactInfo, crds::GossipRoute, crds_gossip::*, crds_gossip_error::CrdsGossipError, crds_gossip_pull::{ProcessPullStats, CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS}, crds_gossip_push::CRDS_GOSSIP_PUSH_MSG_TIMEOUT_MS, crds_value::{CrdsData, CrdsValue, CrdsValueLabel}, + legacy_contact_info::LegacyContactInfo as ContactInfo, ping_pong::PingCache, }, solana_rayon_threadlimit::get_thread_count, @@ -52,10 +52,7 @@ impl Node { gossip: Arc, stake: u64, ) -> Self { - let ping_cache = Arc::new(Mutex::new(PingCache::new( - Duration::from_secs(20 * 60), // ttl - 2048, // capacity - ))); + let ping_cache = Arc::new(new_ping_cache()); Node { keypair, contact_info, @@ -101,12 +98,12 @@ fn stakes(network: &Network) -> HashMap { fn star_network_create(num: usize) -> Network { let node_keypair = Arc::new(Keypair::new()); let contact_info = ContactInfo::new_localhost(&node_keypair.pubkey(), 0); - let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(contact_info.clone())); + let entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(contact_info.clone())); let mut network: HashMap<_, _> = (1..num) .map(|_| { let node_keypair = Arc::new(Keypair::new()); let contact_info = ContactInfo::new_localhost(&node_keypair.pubkey(), 0); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(contact_info.clone())); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(contact_info.clone())); let node = CrdsGossip::default(); { let mut node_crds = node.crds.write().unwrap(); @@ -136,7 +133,7 @@ fn star_network_create(num: usize) -> Network { fn rstar_network_create(num: usize) -> Network { let node_keypair = Arc::new(Keypair::new()); let contact_info = ContactInfo::new_localhost(&node_keypair.pubkey(), 0); - let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(contact_info.clone())); + let entry = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(contact_info.clone())); let origin = CrdsGossip::default(); let id = entry.label().pubkey(); origin @@ -149,7 +146,7 @@ fn rstar_network_create(num: usize) -> Network { .map(|_| { let node_keypair = Arc::new(Keypair::new()); let contact_info = ContactInfo::new_localhost(&node_keypair.pubkey(), 0); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(contact_info.clone())); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(contact_info.clone())); let node = CrdsGossip::default(); node.crds .write() @@ -176,7 +173,7 @@ fn ring_network_create(num: usize) -> Network { .map(|_| { let node_keypair = Arc::new(Keypair::new()); let contact_info = ContactInfo::new_localhost(&node_keypair.pubkey(), 0); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(contact_info.clone())); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(contact_info.clone())); let node = CrdsGossip::default(); node.crds .write() @@ -192,7 +189,7 @@ fn ring_network_create(num: usize) -> Network { let start_info = { let start = &network[&keys[k]]; let start_id = keys[k]; - let label = CrdsValueLabel::ContactInfo(start_id); + let label = CrdsValueLabel::LegacyContactInfo(start_id); let gossip_crds = start.gossip.crds.read().unwrap(); gossip_crds.get::<&CrdsValue>(&label).unwrap().clone() }; @@ -211,7 +208,7 @@ fn connected_staked_network_create(stakes: &[u64]) -> Network { .map(|n| { let node_keypair = Arc::new(Keypair::new()); let contact_info = ContactInfo::new_localhost(&node_keypair.pubkey(), 0); - let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(contact_info.clone())); + let new = CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(contact_info.clone())); let node = CrdsGossip::default(); node.crds .write() @@ -228,7 +225,7 @@ fn connected_staked_network_create(stakes: &[u64]) -> Network { .iter() .map(|k| { let start = &network[k]; - let start_label = CrdsValueLabel::ContactInfo(*k); + let start_label = CrdsValueLabel::LegacyContactInfo(*k); let gossip_crds = start.gossip.crds.read().unwrap(); gossip_crds.get::<&CrdsValue>(&start_label).unwrap().clone() }) @@ -267,12 +264,13 @@ fn network_simulator(thread_pool: &ThreadPool, network: &mut Network, max_conver // make sure there is someone in the active set let network_values: Vec = network.values().cloned().collect(); network_values.par_iter().for_each(|node| { - let node_pubkey = node.keypair.pubkey(); node.gossip.refresh_push_active_set( - &node_pubkey, + &node.keypair, 0, // shred version &HashMap::new(), // stakes None, // gossip validators + &node.ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); }); @@ -292,8 +290,10 @@ fn network_simulator(thread_pool: &ThreadPool, network: &mut Network, max_conver }; m.wallclock = now; node.gossip.process_push_message( - &Pubkey::default(), - vec![CrdsValue::new_unsigned(CrdsData::ContactInfo(m))], + vec![( + Pubkey::default(), + vec![CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(m))], + )], now, ); }); @@ -349,7 +349,7 @@ fn network_run_push( Duration::from_millis(node.gossip.pull.crds_timeout), ); node.gossip.purge(&node_pubkey, thread_pool, now, &timeouts); - (node_pubkey, node.gossip.new_push_messages(vec![], now)) + (node_pubkey, node.gossip.new_push_messages(vec![], now).0) }) .collect(); let transfered: Vec<_> = requests @@ -366,8 +366,7 @@ fn network_run_push( .get(&to) .unwrap() .gossip - .process_push_message(&from, msgs.clone(), now) - .1 + .process_push_message(vec![(from, msgs.clone())], now) .into_iter() .collect(); let prunes_map = network @@ -427,12 +426,13 @@ fn network_run_push( } if now % CRDS_GOSSIP_PUSH_MSG_TIMEOUT_MS == 0 && now > 0 { network_values.par_iter().for_each(|node| { - let node_pubkey = node.keypair.pubkey(); node.gossip.refresh_push_active_set( - &node_pubkey, + &node.keypair, 0, // shred version &HashMap::new(), // stakes None, // gossip validators + &node.ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); }); @@ -508,7 +508,7 @@ fn network_run_pull( ) .unwrap_or_default(); let from_pubkey = from.keypair.pubkey(); - let label = CrdsValueLabel::ContactInfo(from_pubkey); + let label = CrdsValueLabel::LegacyContactInfo(from_pubkey); let gossip_crds = from.gossip.crds.read().unwrap(); let self_info = gossip_crds.get::<&CrdsValue>(&label).unwrap().clone(); requests @@ -608,11 +608,20 @@ fn network_run_pull( fn build_gossip_thread_pool() -> ThreadPool { ThreadPoolBuilder::new() .num_threads(get_thread_count().min(2)) - .thread_name(|i| format!("crds_gossip_test_{}", i)) + .thread_name(|i| format!("gossipTest{:02}", i)) .build() .unwrap() } +fn new_ping_cache() -> Mutex { + let ping_cache = PingCache::new( + Duration::from_secs(20 * 60), // ttl + Duration::from_secs(20 * 60) / 64, // rate_limit_delay + 2048, // capacity + ); + Mutex::new(ping_cache) +} + #[test] #[serial] fn test_star_network_pull_50() { @@ -712,33 +721,37 @@ fn test_star_network_large_push() { #[test] fn test_prune_errors() { let crds_gossip = CrdsGossip::default(); - let id = Pubkey::new(&[0; 32]); - let ci = ContactInfo::new_localhost(&Pubkey::new(&[1; 32]), 0); - let prune_pubkey = Pubkey::new(&[2; 32]); + let keypair = Keypair::new(); + let id = keypair.pubkey(); + let ci = ContactInfo::new_localhost(&Pubkey::from([1; 32]), 0); + let prune_pubkey = Pubkey::from([2; 32]); crds_gossip .crds .write() .unwrap() .insert( - CrdsValue::new_unsigned(CrdsData::ContactInfo(ci.clone())), + CrdsValue::new_unsigned(CrdsData::LegacyContactInfo(ci.clone())), 0, GossipRoute::LocalMessage, ) .unwrap(); + let ping_cache = new_ping_cache(); crds_gossip.refresh_push_active_set( - &id, + &keypair, 0, // shred version &HashMap::new(), // stakes None, // gossip validators + &ping_cache, + &mut Vec::new(), // pings &SocketAddrSpace::Unspecified, ); let now = timestamp(); //incorrect dest let mut res = crds_gossip.process_prune_msg( - &id, // self_pubkey - &ci.id, // peer - &Pubkey::new(hash(&[1; 32]).as_ref()), // destination - &[prune_pubkey], // origins + &id, // self_pubkey + &ci.id, // peer + &Pubkey::from(hash(&[1; 32]).to_bytes()), // destination + &[prune_pubkey], // origins now, now, ); diff --git a/gossip/tests/gossip.rs b/gossip/tests/gossip.rs index f3e136cdba7f72..954a88b023e972 100644 --- a/gossip/tests/gossip.rs +++ b/gossip/tests/gossip.rs @@ -6,9 +6,9 @@ use { rayon::iter::*, solana_gossip::{ cluster_info::{ClusterInfo, Node}, - contact_info::ContactInfo, crds::Cursor, gossip_service::GossipService, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_perf::packet::Packet, solana_runtime::bank_forks::BankForks, diff --git a/install/Cargo.toml b/install/Cargo.toml index 6734dca7384cf5..23b5198c2ab36c 100644 --- a/install/Cargo.toml +++ b/install/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-install" description = "The solana cluster software installer" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -12,7 +12,7 @@ documentation = "https://docs.rs/solana-install" [dependencies] atty = "0.2.11" bincode = "1.3.3" -bzip2 = "0.4.3" +bzip2 = "0.4.4" chrono = { version = "0.4.11", features = ["serde"] } clap = { version = "2.33.1" } console = "0.15.0" @@ -26,14 +26,14 @@ reqwest = { version = "0.11.11", default-features = false, features = ["blocking semver = "1.0.10" serde = { version = "1.0.138", features = ["derive"] } serde_yaml = "0.8.26" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-config-program = { path = "../programs/config", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-config-program = { path = "../programs/config", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } tar = "0.4.38" -tempfile = "3.3.0" +tempfile = "3.4.0" url = "2.2.2" [target."cfg(windows)".dependencies] diff --git a/install/src/command.rs b/install/src/command.rs index 13ec7c6b47e282..df2034d2538340 100644 --- a/install/src/command.rs +++ b/install/src/command.rs @@ -163,7 +163,7 @@ fn extract_release_archive( progress_bar.set_message(format!("{}Extracting...", PACKAGE)); if extract_dir.exists() { - let _ = fs::remove_dir_all(&extract_dir); + let _ = fs::remove_dir_all(extract_dir); } let tmp_extract_dir = extract_dir.with_file_name("tmp-extract"); @@ -184,7 +184,7 @@ fn extract_release_archive( } fn load_release_version(version_yml: &Path) -> Result { - let file = File::open(&version_yml) + let file = File::open(version_yml) .map_err(|err| format!("Unable to open {:?}: {:?}", version_yml, err))?; let version: ReleaseVersion = serde_yaml::from_reader(file) .map_err(|err| format!("Unable to parse {:?}: {:?}", version_yml, err))?; @@ -851,6 +851,11 @@ pub struct GithubRelease { pub prerelease: bool, } +#[derive(Debug, Deserialize, Serialize)] +pub struct GithubError { + pub message: String, +} + #[derive(Debug, Deserialize, Serialize)] pub struct GithubReleases(Vec); @@ -864,14 +869,41 @@ fn semver_of(string: &str) -> Result { } fn check_for_newer_github_release( - version_filter: Option, + current_release_semver: &str, + semver_update_type: SemverUpdateType, prerelease_allowed: bool, -) -> reqwest::Result> { - let mut page = 1; - const PER_PAGE: usize = 100; +) -> Result, String> { let client = reqwest::blocking::Client::builder() .user_agent("solana-install") - .build()?; + .build() + .map_err(|err| err.to_string())?; + + // If we want a fixed version, we don't need to stress the API to check whether it exists + if semver_update_type == SemverUpdateType::Fixed { + let download_url = github_release_download_url(current_release_semver); + let response = client + .head(download_url.as_str()) + .send() + .map_err(|err| err.to_string())?; + + if response.status() == reqwest::StatusCode::OK { + return Ok(Some(current_release_semver.to_string())); + } + } + + let version_filter = semver::VersionReq::parse(&format!( + "{}{}", + match semver_update_type { + SemverUpdateType::Fixed => "=", + SemverUpdateType::Patch => "~", + SemverUpdateType::_Minor => "^", + }, + current_release_semver + )) + .ok(); + + let mut page = 1; + const PER_PAGE: usize = 100; let mut all_releases = vec![]; let mut releases = vec![]; @@ -884,39 +916,48 @@ fn check_for_newer_github_release( ], ) .unwrap(); - let request = client.get(url).build()?; - let response = client.execute(request)?; - - releases = response - .json::()? - .0 - .into_iter() - .filter_map( - |GithubRelease { - tag_name, - prerelease, - }| { - if let Ok(version) = semver_of(&tag_name) { - if (prerelease_allowed || !prerelease) - && version_filter - .as_ref() - .map_or(true, |version_filter| version_filter.matches(&version)) - { - return Some(version); + let request = client.get(url).build().map_err(|err| err.to_string())?; + let response = client.execute(request).map_err(|err| err.to_string())?; + + if response.status() == reqwest::StatusCode::OK { + releases = response + .json::() + .map_err(|err| err.to_string())? + .0 + .into_iter() + .filter_map( + |GithubRelease { + tag_name, + prerelease, + }| { + if let Ok(version) = semver_of(&tag_name) { + if (prerelease_allowed || !prerelease) + && version_filter + .as_ref() + .map_or(true, |version_filter| version_filter.matches(&version)) + { + return Some(version); + } } - } - None - }, - ) - .collect::>(); - all_releases.extend_from_slice(&releases); - page += 1; + None + }, + ) + .collect::>(); + all_releases.extend_from_slice(&releases); + page += 1; + } else { + return Err(response + .json::() + .map_err(|err| err.to_string())? + .message); + } } all_releases.sort(); Ok(all_releases.pop().map(|r| r.to_string())) } +#[derive(Debug, PartialEq, Eq)] pub enum SemverUpdateType { Fixed, Patch, @@ -945,19 +986,11 @@ pub fn init_or_update(config_file: &str, is_init: bool, check_only: bool) -> Res progress_bar.set_message(format!("{}Checking for updates...", LOOKING_GLASS)); let github_release = check_for_newer_github_release( - semver::VersionReq::parse(&format!( - "{}{}", - match semver_update_type { - SemverUpdateType::Fixed => "=", - SemverUpdateType::Patch => "~", - SemverUpdateType::_Minor => "^", - }, - current_release_semver - )) - .ok(), + current_release_semver, + semver_update_type, is_init, - ) - .map_err(|err| err.to_string())?; + )?; + progress_bar.finish_and_clear(); match github_release { diff --git a/install/src/defaults.rs b/install/src/defaults.rs index 78aa1bc720beed..6851eb9819180f 100644 --- a/install/src/defaults.rs +++ b/install/src/defaults.rs @@ -3,19 +3,19 @@ pub const JSON_RPC_URL: &str = "http://api.devnet.solana.com"; lazy_static! { pub static ref CONFIG_FILE: Option = { dirs_next::home_dir().map(|mut path| { - path.extend(&[".config", "solana", "install", "config.yml"]); + path.extend([".config", "solana", "install", "config.yml"]); path.to_str().unwrap().to_string() }) }; pub static ref USER_KEYPAIR: Option = { dirs_next::home_dir().map(|mut path| { - path.extend(&[".config", "solana", "id.json"]); + path.extend([".config", "solana", "id.json"]); path.to_str().unwrap().to_string() }) }; pub static ref DATA_DIR: Option = { dirs_next::home_dir().map(|mut path| { - path.extend(&[".local", "share", "solana", "install"]); + path.extend([".local", "share", "solana", "install"]); path.to_str().unwrap().to_string() }) }; diff --git a/keygen/Cargo.toml b/keygen/Cargo.toml index fbb493249b68a8..c4119c7cbde4b8 100644 --- a/keygen/Cargo.toml +++ b/keygen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-keygen" -version = "1.11.6" +version = "1.14.24" description = "Solana key generation utility" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,11 +14,11 @@ bs58 = "0.4.0" clap = { version = "3.1.5", features = ["cargo"] } dirs-next = "2.0.0" num_cpus = "1.13.1" -solana-clap-v3-utils = { path = "../clap-v3-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-remote-wallet = { path = "../remote-wallet", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-clap-v3-utils = { path = "../clap-v3-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-remote-wallet = { path = "../remote-wallet", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } tiny-bip39 = "0.8.2" [[bin]] diff --git a/keygen/src/keygen.rs b/keygen/src/keygen.rs index 953c9153bc6695..2c201c10692cd7 100644 --- a/keygen/src/keygen.rs +++ b/keygen/src/keygen.rs @@ -68,7 +68,7 @@ const NO_OUTFILE_ARG: ArgConstant<'static> = ArgConstant { fn word_count_arg<'a>() -> Arg<'a> { Arg::new(WORD_COUNT_ARG.name) .long(WORD_COUNT_ARG.long) - .possible_values(&["12", "15", "18", "21", "24"]) + .possible_values(["12", "15", "18", "21", "24"]) .default_value("12") .value_name("NUMBER") .takes_value(true) @@ -78,7 +78,7 @@ fn word_count_arg<'a>() -> Arg<'a> { fn language_arg<'a>() -> Arg<'a> { Arg::new(LANGUAGE_ARG.name) .long(LANGUAGE_ARG.long) - .possible_values(&[ + .possible_values([ "english", "chinese-simplified", "chinese-traditional", @@ -138,7 +138,7 @@ fn get_keypair_from_matches( } else if !config.keypair_path.is_empty() { &config.keypair_path } else { - path.extend(&[".config", "solana", "id.json"]); + path.extend([".config", "solana", "id.json"]); path.to_str().unwrap() }; signer_from_path(matches, path, "pubkey recovery", wallet_manager) @@ -558,7 +558,7 @@ fn do_main(matches: &ArgMatches) -> Result<(), Box> { } else if matches.is_present(NO_OUTFILE_ARG.name) { None } else { - path.extend(&[".config", "solana", "id.json"]); + path.extend([".config", "solana", "id.json"]); Some(path.to_str().unwrap()) }; @@ -601,7 +601,7 @@ fn do_main(matches: &ArgMatches) -> Result<(), Box> { let outfile = if matches.is_present("outfile") { matches.value_of("outfile").unwrap() } else { - path.extend(&[".config", "solana", "id.json"]); + path.extend([".config", "solana", "id.json"]); path.to_str().unwrap() }; diff --git a/ledger-tool/Cargo.toml b/ledger-tool/Cargo.toml index 6c138e395ac409..1f5f82a2e407eb 100644 --- a/ledger-tool/Cargo.toml +++ b/ledger-tool/Cargo.toml @@ -3,14 +3,14 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-ledger-tool" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" documentation = "https://docs.rs/solana-ledger-tool" [dependencies] -bs58 = "0.4.0" +base64 = "0.13.0" chrono = "0.4.11" clap = "2.33.1" crossbeam-channel = "0.5" @@ -22,21 +22,21 @@ log = { version = "0.4.17" } regex = "1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-output = { path = "../cli-output", version = "=1.11.6" } -solana-core = { path = "../core", version = "=1.11.6" } -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-stake-program = { path = "../programs/stake", version = "=1.11.6" } -solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } -tokio = { version = "~1.14.1", features = ["full"] } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-output = { path = "../cli-output", version = "=1.14.24" } +solana-core = { path = "../core", version = "=1.14.24" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } +solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } +tokio = { version = "1", features = ["full"] } [target.'cfg(not(target_env = "msvc"))'.dependencies] jemallocator = { package = "tikv-jemallocator", version = "0.4.1", features = ["unprefixed_malloc_on_supported_platforms"] } diff --git a/ledger-tool/src/bigtable.rs b/ledger-tool/src/bigtable.rs index 8dcce92534e4ec..7d2cdff22c3bcf 100644 --- a/ledger-tool/src/bigtable.rs +++ b/ledger-tool/src/bigtable.rs @@ -213,7 +213,7 @@ async fn confirm( let decoded_tx = confirmed_tx.get_transaction(); let encoded_tx_with_meta = confirmed_tx .tx_with_meta - .encode(UiTransactionEncoding::Json, Some(0)) + .encode(UiTransactionEncoding::Json, Some(0), true) .map_err(|_| "Failed to encode transaction in block".to_string())?; transaction = Some(CliTransaction { transaction: encoded_tx_with_meta.transaction, @@ -277,7 +277,7 @@ pub async fn transaction_history( "{}, slot={}, memo=\"{}\", status={}", result.signature, result.slot, - result.memo.unwrap_or_else(|| "".to_string()), + result.memo.unwrap_or_default(), match result.err { None => "Confirmed".to_string(), Some(err) => format!("Failed: {:?}", err), diff --git a/ledger-tool/src/ledger_path.rs b/ledger-tool/src/ledger_path.rs index b446652c707c54..34274e85c0b935 100644 --- a/ledger-tool/src/ledger_path.rs +++ b/ledger-tool/src/ledger_path.rs @@ -19,7 +19,7 @@ pub fn parse_ledger_path(matches: &ArgMatches<'_>, name: &str) -> PathBuf { // Canonicalize ledger path to avoid issues with symlink creation pub fn canonicalize_ledger_path(ledger_path: &Path) -> PathBuf { - fs::canonicalize(&ledger_path).unwrap_or_else(|err| { + fs::canonicalize(ledger_path).unwrap_or_else(|err| { eprintln!( "Unable to access ledger path '{}': {}", ledger_path.display(), diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 431ee64b2d001e..008c76ed659f10 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -51,8 +51,7 @@ use { snapshot_minimizer::SnapshotMinimizer, snapshot_utils::{ self, ArchiveFormat, SnapshotVersion, DEFAULT_ARCHIVE_COMPRESSION, - DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN, - DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN, SUPPORTED_ARCHIVE_COMPRESSION, + SUPPORTED_ARCHIVE_COMPRESSION, }, }, solana_sdk::{ @@ -70,7 +69,9 @@ use { shred_version::compute_shred_version, stake::{self, state::StakeState}, system_program, - transaction::{MessageHash, SanitizedTransaction, SimpleAddressLoader}, + transaction::{ + MessageHash, SanitizedTransaction, SimpleAddressLoader, VersionedTransaction, + }, }, solana_stake_program::stake_state::{self, PointValue}, solana_vote_program::{ @@ -102,6 +103,16 @@ enum LedgerOutputMethod { Json, } +fn get_program_ids(tx: &VersionedTransaction) -> impl Iterator + '_ { + let message = &tx.message; + let account_keys = message.static_account_keys(); + + message + .instructions() + .iter() + .map(|ix| ix.program_id(account_keys)) +} + fn output_slot_rewards(blockstore: &Blockstore, slot: Slot, method: &LedgerOutputMethod) { // Note: rewards are not output in JSON yet if *method == LedgerOutputMethod::Print { @@ -242,27 +253,8 @@ fn output_slot( transactions += entry.transactions.len(); num_hashes += entry.num_hashes; for transaction in entry.transactions { - let tx_signature = transaction.signatures[0]; - let sanitize_result = SanitizedTransaction::try_create( - transaction, - MessageHash::Compute, - None, - SimpleAddressLoader::Disabled, - true, // require_static_program_ids - ); - - match sanitize_result { - Ok(transaction) => { - for (program_id, _) in transaction.message().program_instructions_iter() { - *program_ids.entry(*program_id).or_insert(0) += 1; - } - } - Err(err) => { - warn!( - "Failed to analyze unsupported transaction {}: {:?}", - tx_signature, err - ); - } + for program_id in get_program_ids(&transaction) { + *program_ids.entry(*program_id).or_insert(0) += 1; } } } @@ -374,7 +366,7 @@ fn output_account( println!(" rent_epoch: {}", account.rent_epoch()); println!(" data_len: {}", account.data().len()); if print_account_data { - println!(" data: '{}'", bs58::encode(account.data()).into_string()); + println!(" data: '{}'", base64::encode(account.data())); } } @@ -884,9 +876,9 @@ fn load_bank_forks( }; if let Some(halt_slot) = process_options.halt_at_slot { - // Check if we have the slot data necessary to replay from starting_slot to halt_slot. + // Check if we have the slot data necessary to replay from starting_slot to >= halt_slot. // - This will not catch the case when loading from genesis without a full slot 0. - if !blockstore.slots_connected(starting_slot, halt_slot) { + if !blockstore.slot_range_connected(starting_slot, halt_slot) { eprintln!( "Unable to load bank forks at slot {} due to disconnected blocks.", halt_slot, @@ -896,11 +888,31 @@ fn load_bank_forks( } let account_paths = if let Some(account_paths) = arg_matches.value_of("account_paths") { + // If this blockstore access is Primary, no other process (solana-validator) can hold + // Primary access. So, allow a custom accounts path without worry of wiping the accounts + // of solana-validator. if !blockstore.is_primary_access() { - // Be defensive, when default account dir is explicitly specified, it's still possible - // to wipe the dir possibly shared by the running validator! - eprintln!("Error: custom accounts path is not supported under secondary access"); - exit(1); + // Attempt to open the Blockstore in Primary access; if successful, no other process + // was holding Primary so allow things to proceed with custom accounts path. Release + // the Primary access instead of holding it to give priority to solana-validator over + // solana-ledger-tool should solana-validator start before we've finished. + info!( + "Checking if another process currently holding Primary access to {:?}", + blockstore.ledger_path() + ); + if Blockstore::open_with_options( + blockstore.ledger_path(), + BlockstoreOptions { + access_type: AccessType::PrimaryForMaintenance, + ..BlockstoreOptions::default() + }, + ) + .is_err() + { + // Couldn't get Primary access, error out to be defensive. + eprintln!("Error: custom accounts path is not supported under secondary access"); + exit(1); + } } account_paths.split(',').map(PathBuf::from).collect() } else if blockstore.is_primary_access() { @@ -1089,6 +1101,11 @@ fn main() { const DEFAULT_ROOT_COUNT: &str = "1"; const DEFAULT_LATEST_OPTIMISTIC_SLOTS_COUNT: &str = "1"; const DEFAULT_MAX_SLOTS_ROOT_REPAIR: &str = "2000"; + // Use std::usize::MAX for DEFAULT_MAX_*_SNAPSHOTS_TO_RETAIN such that + // ledger-tool commands won't accidentally remove any snapshots by default + const DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN: usize = std::usize::MAX; + const DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN: usize = std::usize::MAX; + solana_logger::setup_with_default("solana=info"); let starting_slot_arg = Arg::with_name("starting_slot") @@ -1110,10 +1127,6 @@ fn main() { .long("no-bpf-jit") .takes_value(false) .help("Disable the just-in-time compiler and instead use the interpreter for BP"); - let no_accounts_db_caching_arg = Arg::with_name("no_accounts_db_caching") - .long("no-accounts-db-caching") - .takes_value(false) - .help("Disables accounts-db caching"); let accounts_index_bins = Arg::with_name("accounts_index_bins") .long("accounts-index-bins") .value_name("BINS") @@ -1125,11 +1138,16 @@ fn main() { .value_name("MEGABYTES") .validator(is_parsable::) .takes_value(true) + .requires("enable_accounts_disk_index") .help("How much memory the accounts index can consume. If this is exceeded, some account index entries will be stored on disk."); let disable_disk_index = Arg::with_name("disable_accounts_disk_index") .long("disable-accounts-disk-index") .help("Disable the disk-based accounts index. It is enabled by default. The entire accounts index will be kept in memory.") - .conflicts_with("accounts_index_memory_limit_mb"); + .conflicts_with("enable_accounts_disk_index"); + let enable_disk_index = Arg::with_name("enable_accounts_disk_index") + .long("enable-accounts-disk-index") + .conflicts_with("disable_accounts_disk_index") + .help("Enable the disk-based accounts index if it is disabled by default."); let accountsdb_skip_shrink = Arg::with_name("accounts_db_skip_shrink") .long("accounts-db-skip-shrink") .help( @@ -1543,6 +1561,7 @@ fn main() { .arg(&accounts_index_bins) .arg(&accounts_index_limit) .arg(&disable_disk_index) + .arg(&enable_disk_index) .arg(&accountsdb_skip_shrink) .arg(&accounts_filler_count) .arg(&accounts_filler_size) @@ -1552,7 +1571,6 @@ fn main() { .arg(&ancient_append_vecs) .arg(&halt_at_slot_store_hash_raw_data) .arg(&hard_forks_arg) - .arg(&no_accounts_db_caching_arg) .arg(&accounts_db_test_hash_calculation_arg) .arg(&no_os_memory_stats_reporting_arg) .arg(&no_bpf_jit_arg) @@ -1605,6 +1623,9 @@ fn main() { .about("Create a new ledger snapshot") .arg(&no_snapshot_arg) .arg(&account_paths_arg) + .arg(&accounts_index_limit) + .arg(&disable_disk_index) + .arg(&enable_disk_index) .arg(&skip_rewrites_arg) .arg(&accounts_db_skip_initial_hash_calc_arg) .arg(&ancient_append_vecs) @@ -1881,7 +1902,20 @@ fn main() { .long("no-compaction") .required(false) .takes_value(false) - .help("Skip ledger compaction after purge") + .help("--no-compaction is deprecated, ledger compaction \ + after purge is disabled by default") + .conflicts_with("enable_compaction") + .hidden(true) + ) + .arg( + Arg::with_name("enable_compaction") + .long("enable-compaction") + .required(false) + .takes_value(false) + .help("Perform ledger compaction after purge. Compaction \ + will optimize storage space, but may take a long \ + time to complete.") + .conflicts_with("no_compaction") ) .arg( Arg::with_name("dead_slots_only") @@ -2438,10 +2472,13 @@ fn main() { value_t!(arg_matches, "accounts_index_memory_limit_mb", usize).ok() { IndexLimitMb::Limit(limit) - } else if arg_matches.is_present("disable_accounts_disk_index") { - IndexLimitMb::InMemOnly - } else { + } else if arg_matches.is_present("enable_accounts_disk_index") { IndexLimitMb::Unspecified + } else { + if arg_matches.is_present("disable_accounts_disk_index") { + warn!("ignoring `--disable-accounts-disk-index` as it specifies default behavior"); + } + IndexLimitMb::InMemOnly }; { @@ -2484,9 +2521,11 @@ fn main() { poh_verify: !arg_matches.is_present("skip_poh_verify"), on_halt_store_hash_raw_data_for_debug: arg_matches .is_present("halt_at_slot_store_hash_raw_data"), + // ledger tool verify always runs the accounts hash calc at the end of processing the blockstore + run_final_accounts_hash_calc: true, halt_at_slot: value_t!(arg_matches, "halt_at_slot", Slot).ok(), debug_keys, - accounts_db_caching_enabled: !arg_matches.is_present("no_accounts_db_caching"), + accounts_db_caching_enabled: true, limit_load_slot_count_from_snapshot: value_t!( arg_matches, "limit_load_slot_count_from_snapshot", @@ -2686,6 +2725,11 @@ fn main() { value_t_or_exit!(arg_matches, "snapshot_slot", Slot) }; + assert!( + blockstore.meta(snapshot_slot).unwrap().is_some(), + "snapshot slot doesn't exist" + ); + let ending_slot = if is_minimized { let ending_slot = value_t_or_exit!(arg_matches, "ending_slot", Slot); if ending_slot <= snapshot_slot { @@ -2716,7 +2760,24 @@ fn main() { output_directory.display() ); + let accounts_index_config = AccountsIndexConfig { + index_limit_mb: if let Some(limit) = + value_t!(arg_matches, "accounts_index_memory_limit_mb", usize).ok() + { + IndexLimitMb::Limit(limit) + } else if arg_matches.is_present("enable_accounts_disk_index") { + IndexLimitMb::Unspecified + } else { + if arg_matches.is_present("disable_accounts_disk_index") { + warn!("ignoring `--disable-accounts-disk-index` as it specifies default behavior"); + } + IndexLimitMb::InMemOnly + }, + ..AccountsIndexConfig::default() + }; + let accounts_db_config = Some(AccountsDbConfig { + index: Some(accounts_index_config), skip_rewrites: arg_matches.is_present("accounts_db_skip_rewrites"), ancient_append_vecs: arg_matches.is_present("accounts_db_ancient_append_vecs"), skip_initial_hash_calc: arg_matches @@ -3648,17 +3709,16 @@ fn main() { ("purge", Some(arg_matches)) => { let start_slot = value_t_or_exit!(arg_matches, "start_slot", Slot); let end_slot = value_t!(arg_matches, "end_slot", Slot).ok(); - let no_compaction = arg_matches.is_present("no_compaction"); + let perform_compaction = arg_matches.is_present("enable_compaction"); + if arg_matches.is_present("no_compaction") { + warn!("--no-compaction is deprecated and is now the default behavior."); + } let dead_slots_only = arg_matches.is_present("dead_slots_only"); let batch_size = value_t_or_exit!(arg_matches, "batch_size", usize); - let access_type = if !no_compaction { - AccessType::Primary - } else { - AccessType::PrimaryForMaintenance - }; + let blockstore = open_blockstore( &ledger_path, - access_type, + AccessType::PrimaryForMaintenance, wal_recovery_mode, &shred_storage_type, ); @@ -3689,19 +3749,19 @@ fn main() { exit(1); } info!( - "Purging data from slots {} to {} ({} slots) (skip compaction: {}) (dead slot only: {})", + "Purging data from slots {} to {} ({} slots) (do compaction: {}) (dead slot only: {})", start_slot, end_slot, end_slot - start_slot, - no_compaction, + perform_compaction, dead_slots_only, ); let purge_from_blockstore = |start_slot, end_slot| { blockstore.purge_from_next_slots(start_slot, end_slot); - if no_compaction { - blockstore.purge_slots(start_slot, end_slot, PurgeType::Exact); - } else { + if perform_compaction { blockstore.purge_and_compact_slots(start_slot, end_slot); + } else { + blockstore.purge_slots(start_slot, end_slot, PurgeType::Exact); } }; if !dead_slots_only { diff --git a/ledger/Cargo.toml b/ledger/Cargo.toml index 4ed6240598ac4d..d55888cbf8feb5 100644 --- a/ledger/Cargo.toml +++ b/ledger/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-ledger" -version = "1.11.6" +version = "1.14.24" description = "Solana ledger" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,6 +10,7 @@ documentation = "https://docs.rs/solana-ledger" edition = "2021" [dependencies] +assert_matches = "1.5.0" bincode = "1.3.3" bitflags = "1.3.1" byteorder = "1.4.3" @@ -30,46 +31,49 @@ prost = "0.11.0" rand = "0.7.0" rand_chacha = "0.2.2" rayon = "1.5.3" -reed-solomon-erasure = { version = "5.0.3", features = ["simd-accel"] } +reed-solomon-erasure = { version = "6.0.0", features = ["simd-accel"] } serde = "1.0.138" serde_bytes = "0.11.6" sha2 = "0.10.2" -solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.11.6" } -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-program-runtime = { path = "../program-runtime", version = "=1.11.6" } -solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-stake-program = { path = "../programs/stake", version = "=1.11.6" } -solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.11.6" } -solana-storage-proto = { path = "../storage-proto", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.14.24" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-frozen-abi = { path = "../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-program-runtime = { path = "../program-runtime", version = "=1.14.24" } +solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } +solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.14.24" } +solana-storage-proto = { path = "../storage-proto", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } +spl-token = { version = "=3.5.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "=0.6.1", features = ["no-entrypoint"] } static_assertions = "1.1.0" -tempfile = "3.3.0" +tempfile = "3.4.0" thiserror = "1.0" -tokio = { version = "~1.14.1", features = ["full"] } +tokio = { version = "1", features = ["full"] } tokio-stream = "0.1" trees = "0.4.2" [dependencies.rocksdb] # Avoid the vendored bzip2 within rocksdb-sys that can cause linker conflicts # when also using the bzip2 crate -version = "0.18.0" +version = "0.19.0" default-features = false features = ["lz4"] [dev-dependencies] -assert_matches = "1.5.0" bs58 = "0.4.0" matches = "0.1.9" -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +test-case = "2.1.0" [build-dependencies] rustc_version = "0.4" diff --git a/ledger/benches/blockstore.rs b/ledger/benches/blockstore.rs index d9ac870ec7d0d8..c55562b19c83d9 100644 --- a/ledger/benches/blockstore.rs +++ b/ledger/benches/blockstore.rs @@ -20,7 +20,7 @@ fn bench_write_shreds(bench: &mut Bencher, entries: Vec, ledger_path: &Pa let blockstore = Blockstore::open(ledger_path).expect("Expected to be able to open database ledger"); bench.iter(move || { - let shreds = entries_to_test_shreds(&entries, 0, 0, true, 0); + let shreds = entries_to_test_shreds(&entries, 0, 0, true, 0, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); }); @@ -42,7 +42,14 @@ fn setup_read_bench( ); // Convert the entries to shreds, write the shreds to the ledger - let shreds = entries_to_test_shreds(&entries, slot, slot.saturating_sub(1), true, 0); + let shreds = entries_to_test_shreds( + &entries, + slot, + slot.saturating_sub(1), // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); blockstore .insert_shreds(shreds, None, false) .expect("Expectd successful insertion of shreds into ledger"); @@ -135,7 +142,7 @@ fn bench_insert_data_shred_small(bench: &mut Bencher) { let num_entries = 32 * 1024; let entries = create_ticks(num_entries, 0, Hash::default()); bench.iter(move || { - let shreds = entries_to_test_shreds(&entries, 0, 0, true, 0); + let shreds = entries_to_test_shreds(&entries, 0, 0, true, 0, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); }); Blockstore::destroy(&ledger_path).expect("Expected successful database destruction"); @@ -150,7 +157,7 @@ fn bench_insert_data_shred_big(bench: &mut Bencher) { let num_entries = 32 * 1024; let entries = create_ticks(num_entries, 0, Hash::default()); bench.iter(move || { - let shreds = entries_to_test_shreds(&entries, 0, 0, true, 0); + let shreds = entries_to_test_shreds(&entries, 0, 0, true, 0, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); }); Blockstore::destroy(&ledger_path).expect("Expected successful database destruction"); diff --git a/ledger/benches/sigverify_shreds.rs b/ledger/benches/sigverify_shreds.rs index 4007291d2da772..ab69441c18aa4c 100644 --- a/ledger/benches/sigverify_shreds.rs +++ b/ledger/benches/sigverify_shreds.rs @@ -2,6 +2,7 @@ extern crate test; use { + rayon::ThreadPoolBuilder, solana_ledger::{ shred::{Shred, ShredFlags, LEGACY_SHRED_DATA_CAPACITY}, sigverify_shreds::{sign_shreds_cpu, sign_shreds_gpu, sign_shreds_gpu_pinned_keypair}, @@ -10,6 +11,7 @@ use { packet::{Packet, PacketBatch}, recycler_cache::RecyclerCache, }, + solana_rayon_threadlimit::get_thread_count, solana_sdk::signature::Keypair, std::sync::Arc, test::Bencher, @@ -19,6 +21,10 @@ const NUM_PACKETS: usize = 256; const NUM_BATCHES: usize = 1; #[bench] fn bench_sigverify_shreds_sign_gpu(bencher: &mut Bencher) { + let thread_pool = ThreadPoolBuilder::new() + .num_threads(get_thread_count()) + .build() + .unwrap(); let recycler_cache = RecyclerCache::default(); let mut packet_batch = PacketBatch::new_pinned_with_capacity(NUM_PACKETS); @@ -43,15 +49,31 @@ fn bench_sigverify_shreds_sign_gpu(bencher: &mut Bencher) { let pinned_keypair = Some(Arc::new(pinned_keypair)); //warmup for _ in 0..100 { - sign_shreds_gpu(&keypair, &pinned_keypair, &mut batches, &recycler_cache); + sign_shreds_gpu( + &thread_pool, + &keypair, + &pinned_keypair, + &mut batches, + &recycler_cache, + ); } bencher.iter(|| { - sign_shreds_gpu(&keypair, &pinned_keypair, &mut batches, &recycler_cache); + sign_shreds_gpu( + &thread_pool, + &keypair, + &pinned_keypair, + &mut batches, + &recycler_cache, + ); }) } #[bench] fn bench_sigverify_shreds_sign_cpu(bencher: &mut Bencher) { + let thread_pool = ThreadPoolBuilder::new() + .num_threads(get_thread_count()) + .build() + .unwrap(); let mut packet_batch = PacketBatch::default(); let slot = 0xdead_c0de; packet_batch.resize(NUM_PACKETS, Packet::default()); @@ -71,6 +93,6 @@ fn bench_sigverify_shreds_sign_cpu(bencher: &mut Bencher) { let mut batches = vec![packet_batch; NUM_BATCHES]; let keypair = Keypair::new(); bencher.iter(|| { - sign_shreds_cpu(&keypair, &mut batches); + sign_shreds_cpu(&thread_pool, &keypair, &mut batches); }) } diff --git a/ledger/src/ancestor_iterator.rs b/ledger/src/ancestor_iterator.rs index 8e723be5eaa0be..dc1abc774114bc 100644 --- a/ledger/src/ancestor_iterator.rs +++ b/ledger/src/ancestor_iterator.rs @@ -124,11 +124,11 @@ mod tests { let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); - let (shreds, _) = make_slot_entries(0, 0, 42); + let (shreds, _) = make_slot_entries(0, 0, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); - let (shreds, _) = make_slot_entries(1, 0, 42); + let (shreds, _) = make_slot_entries(1, 0, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); - let (shreds, _) = make_slot_entries(2, 1, 42); + let (shreds, _) = make_slot_entries(2, 1, 42, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); assert_eq!( diff --git a/ledger/src/bigtable_upload.rs b/ledger/src/bigtable_upload.rs index f43b07db12592a..de717c4d9b1d84 100644 --- a/ledger/src/bigtable_upload.rs +++ b/ledger/src/bigtable_upload.rs @@ -164,35 +164,37 @@ pub async fn upload_confirmed_blocks( let sender = sender.clone(); let slot_receiver = slot_receiver.clone(); let exit = exit.clone(); + std::thread::Builder::new() + .name("solBigTGetBlk".into()) + .spawn(move || { + let start = Instant::now(); + let mut num_blocks_read = 0; + + while let Ok(slot) = slot_receiver.recv() { + if exit.load(Ordering::Relaxed) { + break; + } - std::thread::spawn(move || { - let start = Instant::now(); - let mut num_blocks_read = 0; - - while let Ok(slot) = slot_receiver.recv() { - if exit.load(Ordering::Relaxed) { - break; + let _ = match blockstore.get_rooted_block(slot, true) { + Ok(confirmed_block) => { + num_blocks_read += 1; + sender.send((slot, Some(confirmed_block))) + } + Err(err) => { + warn!( + "Failed to get load confirmed block from slot {}: {:?}", + slot, err + ); + sender.send((slot, None)) + } + }; } - - let _ = match blockstore.get_rooted_block(slot, true) { - Ok(confirmed_block) => { - num_blocks_read += 1; - sender.send((slot, Some(confirmed_block))) - } - Err(err) => { - warn!( - "Failed to get load confirmed block from slot {}: {:?}", - slot, err - ); - sender.send((slot, None)) - } - }; - } - BlockstoreLoadStats { - num_blocks_read, - elapsed: start.elapsed(), - } - }) + BlockstoreLoadStats { + num_blocks_read, + elapsed: start.elapsed(), + } + }) + .unwrap() }) .collect(), receiver, diff --git a/ledger/src/bigtable_upload_service.rs b/ledger/src/bigtable_upload_service.rs index 812f87cf8704a9..3149eb96a32d8e 100644 --- a/ledger/src/bigtable_upload_service.rs +++ b/ledger/src/bigtable_upload_service.rs @@ -26,6 +26,7 @@ impl BigTableUploadService { blockstore: Arc, block_commitment_cache: Arc>, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, exit: Arc, ) -> Self { Self::new_with_config( @@ -34,6 +35,7 @@ impl BigTableUploadService { blockstore, block_commitment_cache, max_complete_transaction_status_slot, + max_complete_rewards_slot, ConfirmedBlockUploadConfig::default(), exit, ) @@ -45,12 +47,13 @@ impl BigTableUploadService { blockstore: Arc, block_commitment_cache: Arc>, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, config: ConfirmedBlockUploadConfig, exit: Arc, ) -> Self { info!("Starting BigTable upload service"); let thread = Builder::new() - .name("bigtable-upload".to_string()) + .name("solBigTUpload".to_string()) .spawn(move || { Self::run( runtime, @@ -58,6 +61,7 @@ impl BigTableUploadService { blockstore, block_commitment_cache, max_complete_transaction_status_slot, + max_complete_rewards_slot, config, exit, ) @@ -73,6 +77,7 @@ impl BigTableUploadService { blockstore: Arc, block_commitment_cache: Arc>, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, config: ConfirmedBlockUploadConfig, exit: Arc, ) { @@ -83,11 +88,15 @@ impl BigTableUploadService { } // The highest slot eligible for upload is the highest root that has complete - // transaction-status metadata - let highest_complete_root = min( + // transaction-status metadata and rewards + let highest_complete_root = [ max_complete_transaction_status_slot.load(Ordering::SeqCst), + max_complete_rewards_slot.load(Ordering::SeqCst), block_commitment_cache.read().unwrap().root(), - ); + ] + .into_iter() + .min() + .expect("root and max_complete slots exist"); let end_slot = min( highest_complete_root, start_slot.saturating_add(config.max_num_slots_to_check as u64 * 2), diff --git a/ledger/src/blockstore.rs b/ledger/src/blockstore.rs index 93423fd0f5fdee..439685f163d114 100644 --- a/ledger/src/blockstore.rs +++ b/ledger/src/blockstore.rs @@ -17,11 +17,12 @@ use { leader_schedule_cache::LeaderScheduleCache, next_slots_iterator::NextSlotsIterator, shred::{ - self, max_ticks_per_n_shreds, ErasureSetId, ProcessShredsStats, Shred, ShredData, - ShredId, ShredType, Shredder, + self, max_ticks_per_n_shreds, ErasureSetId, ProcessShredsStats, ReedSolomonCache, + Shred, ShredData, ShredId, ShredType, Shredder, }, slot_stats::{ShredSource, SlotsStats}, }, + assert_matches::debug_assert_matches, bincode::deserialize, crossbeam_channel::{bounded, Receiver, Sender, TrySendError}, dashmap::DashSet, @@ -90,12 +91,12 @@ pub use { lazy_static! { static ref PAR_THREAD_POOL: ThreadPool = rayon::ThreadPoolBuilder::new() .num_threads(get_max_thread_count()) - .thread_name(|ix| format!("blockstore_{}", ix)) + .thread_name(|ix| format!("solBstore{:02}", ix)) .build() .unwrap(); static ref PAR_THREAD_POOL_ALL_CPUS: ThreadPool = rayon::ThreadPoolBuilder::new() .num_threads(num_cpus::get()) - .thread_name(|ix| format!("blockstore_{}", ix)) + .thread_name(|ix| format!("solBstoreAll{:02}", ix)) .build() .unwrap(); } @@ -133,6 +134,21 @@ impl std::fmt::Display for InsertDataShredError { } } +pub struct InsertResults { + completed_data_set_infos: Vec, + inserted_indices: Vec, + duplicate_shreds: Vec, +} + +/// A "complete data set" is a range of [`Shred`]s that combined in sequence carry a single +/// serialized [`Vec`]. +/// +/// Services such as the `WindowService` for a TVU, and `ReplayStage` for a TPU, piece together +/// these sets by inserting shreds via direct or indirect calls to +/// [`Blockstore::insert_shreds_handle_duplicate()`]. +/// +/// `solana_core::completed_data_sets_service::CompletedDataSetsService` is the main receiver of +/// `CompletedDataSetInfo`. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CompletedDataSetInfo { pub slot: Slot, @@ -234,7 +250,7 @@ impl Blockstore { } fn do_open(ledger_path: &Path, options: BlockstoreOptions) -> Result { - fs::create_dir_all(&ledger_path)?; + fs::create_dir_all(ledger_path)?; let blockstore_path = ledger_path.join( options .column_options @@ -401,6 +417,7 @@ impl Blockstore { parent.unwrap_or(slot), is_slot_complete, 0, + true, // merkle_variant ); self.insert_shreds(shreds, None, false).unwrap(); } @@ -546,9 +563,9 @@ impl Blockstore { self.prepare_rooted_slot_iterator(slot, IteratorDirection::Reverse) } - /// Determines if `starting_slot` and `ending_slot` are connected by full slots + /// Determines if we can iterate from `starting_slot` to >= `ending_slot` by full slots /// `starting_slot` is excluded from the `is_full()` check - pub fn slots_connected(&self, starting_slot: Slot, ending_slot: Slot) -> bool { + pub fn slot_range_connected(&self, starting_slot: Slot, ending_slot: Slot) -> bool { if starting_slot == ending_slot { return true; } @@ -562,8 +579,7 @@ impl Blockstore { if slot_meta.is_full() { match slot.cmp(&ending_slot) { cmp::Ordering::Less => next_slots.extend(slot_meta.next_slots), - cmp::Ordering::Equal => return true, - cmp::Ordering::Greater => {} // slot is greater than the ending slot, so all its children would be as well + _ => return true, } } } @@ -626,9 +642,10 @@ impl Blockstore { index: &mut Index, erasure_meta: &ErasureMeta, prev_inserted_shreds: &HashMap, - recovered_data_shreds: &mut Vec, + recovered_shreds: &mut Vec, data_cf: &LedgerColumn, code_cf: &LedgerColumn, + reed_solomon_cache: &ReedSolomonCache, ) { // Find shreds for this erasure set and try recovery let slot = index.slot; @@ -647,9 +664,9 @@ impl Blockstore { code_cf, )) .collect(); - if let Ok(mut result) = Shredder::try_recovery(available_shreds) { + if let Ok(mut result) = shred::recover(available_shreds, reed_solomon_cache) { Self::submit_metrics(slot, erasure_meta, true, "complete".into(), result.len()); - recovered_data_shreds.append(&mut result); + recovered_shreds.append(&mut result); } else { Self::submit_metrics(slot, erasure_meta, true, "incomplete".into(), 0); } @@ -707,10 +724,11 @@ impl Blockstore { erasure_metas: &HashMap, index_working_set: &mut HashMap, prev_inserted_shreds: &HashMap, + reed_solomon_cache: &ReedSolomonCache, ) -> Vec { let data_cf = db.column::(); let code_cf = db.column::(); - let mut recovered_data_shreds = vec![]; + let mut recovered_shreds = vec![]; // Recovery rules: // 1. Only try recovery around indexes for which new data or coding shreds are received // 2. For new data shreds, check if an erasure set exists. If not, don't try recovery @@ -726,9 +744,10 @@ impl Blockstore { index, erasure_meta, prev_inserted_shreds, - &mut recovered_data_shreds, + &mut recovered_shreds, &data_cf, &code_cf, + reed_solomon_cache, ); } ErasureMetaStatus::DataFull => { @@ -745,7 +764,7 @@ impl Blockstore { } }; } - recovered_data_shreds + recovered_shreds } /// The main helper function that performs the shred insertion logic @@ -799,19 +818,16 @@ impl Blockstore { /// On success, the function returns an Ok result with a vector of /// `CompletedDataSetInfo` and a vector of its corresponding index in the /// input `shreds` vector. - pub fn insert_shreds_handle_duplicate( + fn do_insert_shreds( &self, shreds: Vec, is_repaired: Vec, leader_schedule: Option<&LeaderScheduleCache>, is_trusted: bool, retransmit_sender: Option<&Sender>>>, - handle_duplicate: &F, + reed_solomon_cache: &ReedSolomonCache, metrics: &mut BlockstoreInsertionMetrics, - ) -> Result<(Vec, Vec)> - where - F: Fn(Shred), - { + ) -> Result { assert_eq!(shreds.len(), is_repaired.len()); let mut total_start = Measure::start("Total elapsed"); let mut start = Measure::start("Blockstore lock"); @@ -826,6 +842,7 @@ impl Blockstore { let mut erasure_metas = HashMap::new(); let mut slot_meta_working_set = HashMap::new(); let mut index_working_set = HashMap::new(); + let mut duplicate_shreds = vec![]; metrics.num_shreds += shreds.len(); let mut start = Measure::start("Shred insertion"); @@ -849,7 +866,7 @@ impl Blockstore { &mut just_inserted_shreds, &mut index_meta_time_us, is_trusted, - handle_duplicate, + &mut duplicate_shreds, leader_schedule, shred_source, ) { @@ -876,7 +893,7 @@ impl Blockstore { &mut write_batch, &mut just_inserted_shreds, &mut index_meta_time_us, - handle_duplicate, + &mut duplicate_shreds, is_trusted, shred_source, metrics, @@ -889,15 +906,19 @@ impl Blockstore { metrics.insert_shreds_elapsed_us += start.as_us(); let mut start = Measure::start("Shred recovery"); if let Some(leader_schedule_cache) = leader_schedule { - let recovered_data_shreds = Self::try_shred_recovery( + let recovered_shreds = Self::try_shred_recovery( db, &erasure_metas, &mut index_working_set, &just_inserted_shreds, + reed_solomon_cache, ); - metrics.num_recovered += recovered_data_shreds.len(); - let recovered_data_shreds: Vec<_> = recovered_data_shreds + metrics.num_recovered += recovered_shreds + .iter() + .filter(|shred| shred.is_data()) + .count(); + let recovered_shreds: Vec<_> = recovered_shreds .into_iter() .filter_map(|shred| { let leader = @@ -906,6 +927,12 @@ impl Blockstore { metrics.num_recovered_failed_sig += 1; return None; } + // Since the data shreds are fully recovered from the + // erasure batch, no need to store coding shreds in + // blockstore. + if shred.is_code() { + return Some(shred); + } match self.check_insert_data_shred( shred.clone(), &mut erasure_metas, @@ -915,7 +942,7 @@ impl Blockstore { &mut just_inserted_shreds, &mut index_meta_time_us, is_trusted, - &handle_duplicate, + &mut duplicate_shreds, leader_schedule, ShredSource::Recovered, ) { @@ -942,10 +969,10 @@ impl Blockstore { // Always collect recovered-shreds so that above insert code is // executed even if retransmit-sender is None. .collect(); - if !recovered_data_shreds.is_empty() { + if !recovered_shreds.is_empty() { if let Some(retransmit_sender) = retransmit_sender { let _ = retransmit_sender.send( - recovered_data_shreds + recovered_shreds .into_iter() .map(Shred::into_payload) .collect(), @@ -999,7 +1026,46 @@ impl Blockstore { metrics.total_elapsed_us += total_start.as_us(); metrics.index_meta_time_us += index_meta_time_us; - Ok((newly_completed_data_sets, inserted_indices)) + Ok(InsertResults { + completed_data_set_infos: newly_completed_data_sets, + inserted_indices, + duplicate_shreds, + }) + } + + pub fn insert_shreds_handle_duplicate( + &self, + shreds: Vec, + is_repaired: Vec, + leader_schedule: Option<&LeaderScheduleCache>, + is_trusted: bool, + retransmit_sender: Option<&Sender>>>, + handle_duplicate: &F, + reed_solomon_cache: &ReedSolomonCache, + metrics: &mut BlockstoreInsertionMetrics, + ) -> Result<(Vec, Vec)> + where + F: Fn(Shred), + { + let InsertResults { + completed_data_set_infos, + inserted_indices, + duplicate_shreds, + } = self.do_insert_shreds( + shreds, + is_repaired, + leader_schedule, + is_trusted, + retransmit_sender, + reed_solomon_cache, + metrics, + )?; + + for shred in duplicate_shreds { + handle_duplicate(shred); + } + + Ok((completed_data_set_infos, inserted_indices)) } pub fn add_new_shred_signal(&self, s: Sender) { @@ -1077,19 +1143,23 @@ impl Blockstore { is_trusted: bool, ) -> Result<(Vec, Vec)> { let shreds_len = shreds.len(); - self.insert_shreds_handle_duplicate( + let insert_results = self.do_insert_shreds( shreds, vec![false; shreds_len], leader_schedule, is_trusted, - None, // retransmit-sender - &|_| {}, // handle-duplicates + None, // retransmit-sender + &ReedSolomonCache::default(), &mut BlockstoreInsertionMetrics::default(), - ) + )?; + Ok(( + insert_results.completed_data_set_infos, + insert_results.inserted_indices, + )) } #[allow(clippy::too_many_arguments)] - fn check_insert_coding_shred( + fn check_insert_coding_shred( &self, shred: Shred, erasure_metas: &mut HashMap, @@ -1097,14 +1167,11 @@ impl Blockstore { write_batch: &mut WriteBatch, just_received_shreds: &mut HashMap, index_meta_time_us: &mut u64, - handle_duplicate: &F, + duplicate_shreds: &mut Vec, is_trusted: bool, shred_source: ShredSource, metrics: &mut BlockstoreInsertionMetrics, - ) -> bool - where - F: Fn(Shred), - { + ) -> bool { let slot = shred.slot(); let shred_index = u64::from(shred.index()); @@ -1119,7 +1186,7 @@ impl Blockstore { if !is_trusted { if index_meta.coding().contains(shred_index) { metrics.num_coding_shreds_exists += 1; - handle_duplicate(shred); + duplicate_shreds.push(shred); return false; } @@ -1263,7 +1330,7 @@ impl Blockstore { /// whether it is okay to insert the input shred. /// - `shred_source`: the source of the shred. #[allow(clippy::too_many_arguments)] - fn check_insert_data_shred( + fn check_insert_data_shred( &self, shred: Shred, erasure_metas: &mut HashMap, @@ -1273,13 +1340,10 @@ impl Blockstore { just_inserted_shreds: &mut HashMap, index_meta_time_us: &mut u64, is_trusted: bool, - handle_duplicate: &F, + duplicate_shreds: &mut Vec, leader_schedule: Option<&LeaderScheduleCache>, shred_source: ShredSource, - ) -> std::result::Result, InsertDataShredError> - where - F: Fn(Shred), - { + ) -> std::result::Result, InsertDataShredError> { let slot = shred.slot(); let shred_index = u64::from(shred.index()); @@ -1300,7 +1364,7 @@ impl Blockstore { if !is_trusted { if Self::is_data_shred_present(&shred, slot_meta, index_meta.data()) { - handle_duplicate(shred); + duplicate_shreds.push(shred); return Err(InsertDataShredError::Exists); } @@ -1350,7 +1414,8 @@ impl Blockstore { } fn should_insert_coding_shred(shred: &Shred, last_root: &RwLock) -> bool { - shred.is_code() && shred.sanitize().is_ok() && shred.slot() > *last_root.read().unwrap() + debug_assert_matches!(shred.sanitize(), Ok(())); + shred.is_code() && shred.slot() > *last_root.read().unwrap() } fn insert_coding_shred( @@ -1364,7 +1429,8 @@ impl Blockstore { // Assert guaranteed by integrity checks on the shred that happen before // `insert_coding_shred` is called - assert!(shred.is_code() && shred.sanitize().is_ok()); + debug_assert_matches!(shred.sanitize(), Ok(())); + assert!(shred.is_code()); // Commit step: commit all changes to the mutable structures at once, or none at all. // We don't want only a subset of these changes going through. @@ -1413,23 +1479,7 @@ impl Blockstore { } else { false }; - if let Err(err) = shred.sanitize() { - let leader_pubkey = leader_schedule - .and_then(|leader_schedule| leader_schedule.slot_leader_at(slot, None)); - - datapoint_error!( - "blockstore_error", - ( - "error", - format!( - "Leader {:?}, slot {}: received invalid shred: {:?}", - leader_pubkey, slot, err, - ), - String - ) - ); - return false; - } + debug_assert_matches!(shred.sanitize(), Ok(())); // Check that we do not receive shred_index >= than the last_index // for the slot let last_index = slot_meta.last_index; @@ -1704,6 +1754,7 @@ impl Blockstore { let mut shredder = Shredder::new(current_slot, parent_slot, 0, version).unwrap(); let mut all_shreds = vec![]; let mut slot_entries = vec![]; + let reed_solomon_cache = ReedSolomonCache::default(); // Find all the entries for start_slot for entry in entries.into_iter() { if remaining_ticks_in_slot == 0 { @@ -1724,6 +1775,8 @@ impl Blockstore { true, // is_last_in_slot start_index, // next_shred_index start_index, // next_code_index + true, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); all_shreds.append(&mut data_shreds); @@ -1748,8 +1801,10 @@ impl Blockstore { keypair, &slot_entries, is_full_slot, - 0, // next_shred_index - 0, // next_code_index + 0, // next_shred_index + 0, // next_code_index + true, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); all_shreds.append(&mut data_shreds); @@ -2559,7 +2614,7 @@ impl Blockstore { // Check the active_transaction_status_index to see if it contains slot. If so, start with // that index, as it will contain higher slots let starting_primary_index = *self.active_transaction_status_index.read().unwrap(); - let next_primary_index = if starting_primary_index == 0 { 1 } else { 0 }; + let next_primary_index = u64::from(starting_primary_index == 0); let next_max_slot = self .transaction_status_index_cf .get(next_primary_index)? @@ -2774,14 +2829,28 @@ impl Blockstore { .map(|(_, end_index)| u64::from(*end_index) - start_index + 1) .unwrap_or(0); - let entries: Result>> = PAR_THREAD_POOL.install(|| { + let entries: Result>> = if completed_ranges.len() <= 1 { completed_ranges - .par_iter() + .into_iter() .map(|(start_index, end_index)| { - self.get_entries_in_data_block(slot, *start_index, *end_index, Some(&slot_meta)) + self.get_entries_in_data_block(slot, start_index, end_index, Some(&slot_meta)) }) .collect() - }); + } else { + PAR_THREAD_POOL.install(|| { + completed_ranges + .into_par_iter() + .map(|(start_index, end_index)| { + self.get_entries_in_data_block( + slot, + start_index, + end_index, + Some(&slot_meta), + ) + }) + .collect() + }) + }; let entries: Vec = entries?.into_iter().flatten().collect(); Ok((entries, num_shreds, slot_meta.is_full())) } @@ -2872,28 +2941,29 @@ impl Blockstore { .and_then(|serialized_shred| { if serialized_shred.is_none() { if let Some(slot_meta) = slot_meta { - panic!( - "Shred with - slot: {}, - index: {}, - consumed: {}, - completed_indexes: {:?} - must exist if shred index was included in a range: {} {}", - slot, - i, - slot_meta.consumed, - slot_meta.completed_data_indexes, - start_index, - end_index - ); - } else { - return Err(BlockstoreError::InvalidShredData(Box::new( - bincode::ErrorKind::Custom(format!( - "Missing shred for slot {}, index {}", - slot, i - )), - ))); + if slot > self.lowest_cleanup_slot() { + panic!( + "Shred with + slot: {}, + index: {}, + consumed: {}, + completed_indexes: {:?} + must exist if shred index was included in a range: {} {}", + slot, + i, + slot_meta.consumed, + slot_meta.completed_data_indexes, + start_index, + end_index + ); + } } + return Err(BlockstoreError::InvalidShredData(Box::new( + bincode::ErrorKind::Custom(format!( + "Missing shred for slot {}, index {}", + slot, i + )), + ))); } Shred::new_from_serialized_shred(serialized_shred.unwrap()).map_err(|err| { @@ -3138,7 +3208,7 @@ impl Blockstore { // given slot and index as this implies the leader generated two different shreds with // the same slot and index pub fn is_shred_duplicate(&self, shred: ShredId, payload: Vec) -> Option> { - let (slot, index, shred_type) = shred.unwrap(); + let (slot, index, shred_type) = shred.unpack(); let existing_shred = match shred_type { ShredType::Data => self.get_data_shred(slot, index as u64), ShredType::Code => self.get_coding_shred(slot, index as u64), @@ -3207,6 +3277,16 @@ impl Blockstore { self.last_root() } + /// Returns the highest available slot in the blockstore + pub fn highest_slot(&self) -> Result> { + let highest_slot = self + .db + .iter::(IteratorMode::End)? + .next() + .map(|(slot, _)| slot); + Ok(highest_slot) + } + pub fn lowest_cleanup_slot(&self) -> Slot { *self.lowest_cleanup_slot.read().unwrap() } @@ -3701,13 +3781,13 @@ fn handle_chaining_for_slot( // connected to trunk of the ledger let should_propagate_is_connected = is_newly_completed_slot(&RefCell::borrow(meta), meta_backup) - && RefCell::borrow(meta).is_connected; + && RefCell::borrow(meta).is_connected(); if should_propagate_is_connected { // slot_function returns a boolean indicating whether to explore the children // of the input slot let slot_function = |slot: &mut SlotMeta| { - slot.is_connected = true; + slot.set_connected(); // We don't want to set the is_connected flag on the children of non-full // slots @@ -3787,7 +3867,9 @@ fn chain_new_slot_to_prev_slot( current_slot_meta: &mut SlotMeta, ) { prev_slot_meta.next_slots.push(current_slot); - current_slot_meta.is_connected = prev_slot_meta.is_connected && prev_slot_meta.is_full(); + if prev_slot_meta.is_connected() && prev_slot_meta.is_full() { + current_slot_meta.set_connected(); + } } fn is_newly_completed_slot(slot_meta: &SlotMeta, backup_slot_meta: &Option) -> bool { @@ -3801,7 +3883,7 @@ fn slot_has_updates(slot_meta: &SlotMeta, slot_meta_backup: &Option) - // from block 0, which is true iff: // 1) The block with index prev_block_index is itself part of the trunk of consecutive blocks // starting from block 0, - slot_meta.is_connected && + slot_meta.is_connected() && // AND either: // 1) The slot didn't exist in the database before, and now we have a consecutive // block for that slot @@ -3847,6 +3929,8 @@ pub fn create_new_ledger( true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); assert!(shreds.last().unwrap().last_in_slot()); @@ -4102,6 +4186,7 @@ pub fn entries_to_test_shreds( parent_slot: Slot, is_full_slot: bool, version: u16, + merkle_variant: bool, ) -> Vec { Shredder::new(slot, parent_slot, 0, version) .unwrap() @@ -4111,6 +4196,8 @@ pub fn entries_to_test_shreds( is_full_slot, 0, // next_shred_index, 0, // next_code_index + merkle_variant, + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ) .0 @@ -4121,9 +4208,10 @@ pub fn make_slot_entries( slot: Slot, parent_slot: Slot, num_entries: u64, + merkle_variant: bool, ) -> (Vec, Vec) { let entries = create_ticks(num_entries, 0, Hash::default()); - let shreds = entries_to_test_shreds(&entries, slot, parent_slot, true, 0); + let shreds = entries_to_test_shreds(&entries, slot, parent_slot, true, 0, merkle_variant); (shreds, entries) } @@ -4138,7 +4226,12 @@ pub fn make_many_slot_entries( for slot in start_slot..start_slot + num_slots { let parent_slot = if slot == 0 { 0 } else { slot - 1 }; - let (slot_shreds, slot_entries) = make_slot_entries(slot, parent_slot, entries_per_slot); + let (slot_shreds, slot_entries) = make_slot_entries( + slot, + parent_slot, + entries_per_slot, + true, // merkle_variant + ); shreds.extend(slot_shreds); entries.extend(slot_entries); } @@ -4267,7 +4360,12 @@ pub fn make_chaining_slot_entries( } }; - let result = make_slot_entries(*slot, parent_slot, entries_per_slot); + let result = make_slot_entries( + *slot, + parent_slot, + entries_per_slot, + true, // merkle_variant + ); slots_shreds_and_entries.push(result); } @@ -4378,7 +4476,12 @@ pub mod tests { } fn make_and_insert_slot(blockstore: &Blockstore, slot: Slot, parent_slot: Slot) { - let (shreds, _) = make_slot_entries(slot, parent_slot, 100); + let (shreds, _) = make_slot_entries( + slot, + parent_slot, + 100, // num_entries + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, true).unwrap(); let meta = blockstore.meta(slot).unwrap().unwrap(); @@ -4442,7 +4545,12 @@ pub mod tests { let num_entries = max_ticks_per_n_shreds(1, None) + 1; assert!(num_entries > 1); - let (mut shreds, _) = make_slot_entries(0, 0, num_entries); + let (mut shreds, _) = make_slot_entries( + 0, // slot + 0, // parent_slot + num_entries, + true, // merkle_variant + ); let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); @@ -4610,7 +4718,7 @@ pub mod tests { #[test] fn test_read_shred_bytes() { let slot = 0; - let (shreds, _) = make_slot_entries(slot, 0, 100); + let (shreds, _) = make_slot_entries(slot, 0, 100, /*merkle_variant:*/ true); let num_shreds = shreds.len() as u64; let shred_bufs: Vec<_> = shreds.iter().map(Shred::payload).cloned().collect(); @@ -4666,7 +4774,7 @@ pub mod tests { #[test] fn test_shred_cleanup_check() { let slot = 1; - let (shreds, _) = make_slot_entries(slot, 0, 100); + let (shreds, _) = make_slot_entries(slot, 0, 100, /*merkle_variant:*/ true); let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); @@ -4691,7 +4799,12 @@ pub mod tests { let num_entries = max_ticks_per_n_shreds(1, None) + 1; assert!(num_entries > 1); - let (mut shreds, entries) = make_slot_entries(0, 0, num_entries); + let (mut shreds, entries) = make_slot_entries( + 0, // slot + 0, // parent_slot + num_entries, + true, // merkle_variant + ); let num_shreds = shreds.len() as u64; let ledger_path = get_tmp_ledger_path_auto_delete!(); @@ -4727,14 +4840,19 @@ pub mod tests { assert_eq!(meta.parent_slot, Some(0)); assert_eq!(meta.last_index, Some(num_shreds - 1)); assert!(meta.next_slots.is_empty()); - assert!(meta.is_connected); + assert!(meta.is_connected()); } #[test] fn test_insert_data_shreds_reverse() { let num_shreds = 10; let num_entries = max_ticks_per_n_shreds(num_shreds, None); - let (mut shreds, entries) = make_slot_entries(0, 0, num_entries); + let (mut shreds, entries) = make_slot_entries( + 0, // slot + 0, // parent_slot + num_entries, + true, // merkle_variant + ); let num_shreds = shreds.len() as u64; let ledger_path = get_tmp_ledger_path_auto_delete!(); @@ -4812,12 +4930,26 @@ pub mod tests { let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); let entries = create_ticks(8, 0, Hash::default()); - let shreds = entries_to_test_shreds(&entries[0..4], 1, 0, false, 0); + let shreds = entries_to_test_shreds( + &entries[0..4], + 1, + 0, + false, + 0, + true, // merkle_variant + ); blockstore .insert_shreds(shreds, None, false) .expect("Expected successful write of shreds"); - let mut shreds1 = entries_to_test_shreds(&entries[4..], 1, 0, false, 0); + let mut shreds1 = entries_to_test_shreds( + &entries[4..], + 1, + 0, + false, + 0, + false, // merkle_variant + ); for (i, b) in shreds1.iter_mut().enumerate() { b.set_index(8 + i as u32); } @@ -4845,8 +4977,14 @@ pub mod tests { for slot in 0..num_slots { let entries = create_ticks(slot + 1, 0, Hash::default()); let last_entry = entries.last().unwrap().clone(); - let mut shreds = - entries_to_test_shreds(&entries, slot, slot.saturating_sub(1), false, 0); + let mut shreds = entries_to_test_shreds( + &entries, + slot, + slot.saturating_sub(1), + false, + 0, + true, // merkle_variant + ); for b in shreds.iter_mut() { b.set_index(index); b.set_slot(slot as u64); @@ -4879,7 +5017,14 @@ pub mod tests { // Write entries for slot in 0..num_slots { let entries = create_ticks(entries_per_slot, 0, Hash::default()); - let shreds = entries_to_test_shreds(&entries, slot, slot.saturating_sub(1), false, 0); + let shreds = entries_to_test_shreds( + &entries, + slot, + slot.saturating_sub(1), + false, + 0, + true, // merkle_variant + ); assert!(shreds.len() as u64 >= shreds_per_slot); blockstore .insert_shreds(shreds, None, false) @@ -4899,7 +5044,12 @@ pub mod tests { let parent_slot = if i == 0 { 0 } else { i - 1 }; // Write entries let num_entries = min_entries * (i + 1); - let (shreds, original_entries) = make_slot_entries(slot, parent_slot, num_entries); + let (shreds, original_entries) = make_slot_entries( + slot, + parent_slot, + num_entries, + true, // merkle_variant + ); let num_shreds = shreds.len() as u64; assert!(num_shreds > 1); @@ -4957,7 +5107,8 @@ pub mod tests { let slot = 0; let num_entries = max_ticks_per_n_shreds(1, None) + 1; let entries = create_ticks(num_entries, slot, Hash::default()); - let shreds = entries_to_test_shreds(&entries, slot, 0, true, 0); + let shreds = + entries_to_test_shreds(&entries, slot, 0, true, 0, /*merkle_variant:*/ true); let num_shreds = shreds.len(); assert!(num_shreds > 1); assert!(blockstore @@ -4996,7 +5147,12 @@ pub mod tests { let entries_per_slot = 50; // Create entries for slot 0 - let (mut shreds, _) = make_slot_entries(0, 0, entries_per_slot); + let (mut shreds, _) = make_slot_entries( + 0, // slot + 0, // parent_slot + entries_per_slot, + false, // merkle_variant + ); let shreds_per_slot = shreds.len() as u64; // Insert second shred, but we're missing the first shred, so no consecutive @@ -5026,7 +5182,12 @@ pub mod tests { let mut shreds = vec![]; let mut missing_shreds = vec![]; for slot in 1..num_slots + 1 { - let (mut slot_shreds, _) = make_slot_entries(slot, slot - 1, entries_per_slot); + let (mut slot_shreds, _) = make_slot_entries( + slot, + slot - 1, // parent_slot + entries_per_slot, + false, // merkle_variant + ); let missing_shred = slot_shreds.remove(slot as usize - 1); shreds.extend(slot_shreds); missing_shreds.push(missing_shred); @@ -5040,7 +5201,12 @@ pub mod tests { // should get no updates let shreds: Vec<_> = (1..num_slots + 1) .flat_map(|slot| { - let (mut shred, _) = make_slot_entries(slot, slot - 1, 1); + let (mut shred, _) = make_slot_entries( + slot, + slot - 1, // parent_slot + 1, // num_entries + false, // merkle_variant + ); shred[0].set_index(2 * num_slots as u32); shred }) @@ -5080,7 +5246,8 @@ pub mod tests { let entries_per_slot = 10; // Create shreds for slot 0 - let (mut shreds, _) = make_slot_entries(0, 0, entries_per_slot); + let (mut shreds, _) = + make_slot_entries(0, 0, entries_per_slot, /*merkle_variant:*/ true); let shred0 = shreds.remove(0); // Insert all but the first shred in the slot, should not be considered complete @@ -5155,7 +5322,12 @@ pub mod tests { let (shreds0, _) = all_shreds.remove(0); let (shreds1, _) = all_shreds.remove(0); let (shreds2, _) = all_shreds.remove(0); - let (shreds3, _) = make_slot_entries(disconnected_slot, 1, entries_per_slot); + let (shreds3, _) = make_slot_entries( + disconnected_slot, + 1, // parent_slot + entries_per_slot, + true, // merkle_variant + ); let mut all_shreds: Vec<_> = vec![shreds0, shreds1, shreds2, shreds3] .into_iter() @@ -5191,7 +5363,7 @@ pub mod tests { let s1 = blockstore.meta(1).unwrap().unwrap(); assert!(s1.next_slots.is_empty()); // Slot 1 is not trunk because slot 0 hasn't been inserted yet - assert!(!s1.is_connected); + assert!(!s1.is_connected()); assert_eq!(s1.parent_slot, Some(0)); assert_eq!(s1.last_index, Some(shreds_per_slot as u64 - 1)); @@ -5203,7 +5375,7 @@ pub mod tests { let s2 = blockstore.meta(2).unwrap().unwrap(); assert!(s2.next_slots.is_empty()); // Slot 2 is not trunk because slot 0 hasn't been inserted yet - assert!(!s2.is_connected); + assert!(!s2.is_connected()); assert_eq!(s2.parent_slot, Some(1)); assert_eq!(s2.last_index, Some(shreds_per_slot as u64 - 1)); @@ -5211,7 +5383,7 @@ pub mod tests { // but still isn't part of the trunk let s1 = blockstore.meta(1).unwrap().unwrap(); assert_eq!(s1.next_slots, vec![2]); - assert!(!s1.is_connected); + assert!(!s1.is_connected()); assert_eq!(s1.parent_slot, Some(0)); assert_eq!(s1.last_index, Some(shreds_per_slot as u64 - 1)); @@ -5230,7 +5402,7 @@ pub mod tests { assert_eq!(s.parent_slot, Some(i - 1)); } assert_eq!(s.last_index, Some(shreds_per_slot as u64 - 1)); - assert!(s.is_connected); + assert!(s.is_connected()); } } @@ -5254,7 +5426,12 @@ pub mod tests { slot - 1 } }; - let (slot_shreds, _) = make_slot_entries(slot, parent_slot, entries_per_slot); + let (slot_shreds, _) = make_slot_entries( + slot, + parent_slot, + entries_per_slot, + true, // merkle_variant + ); shreds_per_slot = slot_shreds.len(); if slot % 2 == 1 { @@ -5284,9 +5461,9 @@ pub mod tests { } if i == 0 { - assert!(s.is_connected); + assert!(s.is_connected()); } else { - assert!(!s.is_connected); + assert!(!s.is_connected()); } } @@ -5311,7 +5488,7 @@ pub mod tests { assert_eq!(s.parent_slot, Some(i - 1)); } assert_eq!(s.last_index, Some(shreds_per_slot as u64 - 1)); - assert!(s.is_connected); + assert!(s.is_connected()); } } @@ -5363,9 +5540,9 @@ pub mod tests { // Other than slot 0, no slots should be part of the trunk if i != 0 { - assert!(!s.is_connected); + assert!(!s.is_connected()); } else { - assert!(s.is_connected); + assert!(s.is_connected()); } } @@ -5384,9 +5561,9 @@ pub mod tests { assert!(s.next_slots.is_empty()); } if i <= slot_index as u64 + 3 { - assert!(s.is_connected); + assert!(s.is_connected()); } else { - assert!(!s.is_connected); + assert!(!s.is_connected()); } if i == 0 { @@ -5466,7 +5643,7 @@ pub mod tests { let slot_meta = blockstore.meta(slot).unwrap().unwrap(); assert_eq!(slot_meta.consumed, entries_per_slot); assert_eq!(slot_meta.received, entries_per_slot); - assert!(slot_meta.is_connected); + assert!(slot_meta.is_connected()); let slot_parent = { if slot == 0 { 0 @@ -5501,7 +5678,7 @@ pub mod tests { } */ #[test] - fn test_slots_connected_chain() { + fn test_slot_range_connected_chain() { let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); @@ -5510,12 +5687,12 @@ pub mod tests { make_and_insert_slot(&blockstore, slot, slot.saturating_sub(1)); } - assert!(blockstore.slots_connected(1, 3)); - assert!(!blockstore.slots_connected(1, 4)); // slot 4 does not exist + assert!(blockstore.slot_range_connected(1, 3)); + assert!(!blockstore.slot_range_connected(1, 4)); // slot 4 does not exist } #[test] - fn test_slots_connected_disconnected() { + fn test_slot_range_connected_disconnected() { let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); @@ -5523,20 +5700,20 @@ pub mod tests { make_and_insert_slot(&blockstore, 2, 1); make_and_insert_slot(&blockstore, 4, 2); - assert!(!blockstore.slots_connected(1, 3)); // Slot 3 does not exit - assert!(blockstore.slots_connected(1, 4)); + assert!(blockstore.slot_range_connected(1, 3)); // Slot 3 does not exist, but we can still replay this range to slot 4 + assert!(blockstore.slot_range_connected(1, 4)); } #[test] - fn test_slots_connected_same_slot() { + fn test_slot_range_connected_same_slot() { let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); - assert!(blockstore.slots_connected(54, 54)); + assert!(blockstore.slot_range_connected(54, 54)); } #[test] - fn test_slots_connected_starting_slot_not_full() { + fn test_slot_range_connected_starting_slot_not_full() { let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); @@ -5544,7 +5721,7 @@ pub mod tests { make_and_insert_slot(&blockstore, 6, 5); assert!(!blockstore.meta(4).unwrap().unwrap().is_full()); - assert!(blockstore.slots_connected(4, 6)); + assert!(blockstore.slot_range_connected(4, 6)); } #[test] @@ -5627,8 +5804,8 @@ pub mod tests { // Write some slot that also chains to existing slots and orphan, // nothing should change - let (shred4, _) = make_slot_entries(4, 0, 1); - let (shred5, _) = make_slot_entries(5, 1, 1); + let (shred4, _) = make_slot_entries(4, 0, 1, /*merkle_variant:*/ true); + let (shred5, _) = make_slot_entries(5, 1, 1, /*merkle_variant:*/ true); blockstore.insert_shreds(shred4, None, false).unwrap(); blockstore.insert_shreds(shred5, None, false).unwrap(); assert_eq!( @@ -5667,7 +5844,8 @@ pub mod tests { } }; - let (mut shred, entry) = make_slot_entries(slot, parent_slot, 1); + let (mut shred, entry) = + make_slot_entries(slot, parent_slot, 1, /*merkle_variant:*/ false); num_shreds_per_slot = shred.len() as u64; shred .iter_mut() @@ -5717,9 +5895,11 @@ pub mod tests { let gap: u64 = 10; assert!(gap > 3); // Create enough entries to ensure there are at least two shreds created - let num_entries = max_ticks_per_n_shreds(1, None) + 1; + let data_buffer_size = ShredData::capacity(/*merkle_proof_size:*/ None).unwrap(); + let num_entries = max_ticks_per_n_shreds(1, Some(data_buffer_size)) + 1; let entries = create_ticks(num_entries, 0, Hash::default()); - let mut shreds = entries_to_test_shreds(&entries, slot, 0, true, 0); + let mut shreds = + entries_to_test_shreds(&entries, slot, 0, true, 0, /*merkle_variant:*/ false); let num_shreds = shreds.len(); assert!(num_shreds > 1); for (i, s) in shreds.iter_mut().enumerate() { @@ -5857,7 +6037,8 @@ pub mod tests { ); let entries = create_ticks(100, 0, Hash::default()); - let mut shreds = entries_to_test_shreds(&entries, slot, 0, true, 0); + let mut shreds = + entries_to_test_shreds(&entries, slot, 0, true, 0, /*merkle_variant:*/ false); assert!(shreds.len() > 2); shreds.drain(2..); @@ -5896,7 +6077,8 @@ pub mod tests { // Write entries let num_entries = 10; let entries = create_ticks(num_entries, 0, Hash::default()); - let shreds = entries_to_test_shreds(&entries, slot, 0, true, 0); + let shreds = + entries_to_test_shreds(&entries, slot, 0, true, 0, /*merkle_variant:*/ true); let num_shreds = shreds.len(); blockstore.insert_shreds(shreds, None, false).unwrap(); @@ -5930,7 +6112,7 @@ pub mod tests { #[test] fn test_should_insert_data_shred() { solana_logger::setup(); - let (mut shreds, _) = make_slot_entries(0, 0, 200); + let (mut shreds, _) = make_slot_entries(0, 0, 200, /*merkle_variant:*/ false); let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); @@ -6016,7 +6198,7 @@ pub mod tests { #[test] fn test_is_data_shred_present() { - let (shreds, _) = make_slot_entries(0, 0, 200); + let (shreds, _) = make_slot_entries(0, 0, 200, /*merkle_variant:*/ true); let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); let index_cf = &blockstore.index_cf; @@ -6077,32 +6259,27 @@ pub mod tests { &mut write_batch, &mut just_received_shreds, &mut index_meta_time_us, - &|_shred| { - panic!("no dupes"); - }, + &mut vec![], false, ShredSource::Turbine, &mut BlockstoreInsertionMetrics::default(), )); // insert again fails on dupe - use std::sync::atomic::{AtomicUsize, Ordering}; - let counter = AtomicUsize::new(0); + let mut duplicate_shreds = vec![]; assert!(!blockstore.check_insert_coding_shred( - coding_shred, + coding_shred.clone(), &mut erasure_metas, &mut index_working_set, &mut write_batch, &mut just_received_shreds, &mut index_meta_time_us, - &|_shred| { - counter.fetch_add(1, Ordering::Relaxed); - }, + &mut duplicate_shreds, false, ShredSource::Turbine, &mut BlockstoreInsertionMetrics::default(), )); - assert_eq!(counter.load(Ordering::Relaxed), 1); + assert_eq!(duplicate_shreds, vec![coding_shred]); } #[test] @@ -6150,18 +6327,6 @@ pub mod tests { &last_root )); - // Trying to insert a shred with index < position should fail - { - let mut coding_shred = coding_shred.clone(); - let index = coding_shred.index() - coding_shred.fec_set_index() - 1; - coding_shred.set_index(index as u32); - - assert!(!Blockstore::should_insert_coding_shred( - &coding_shred, - &last_root - )); - } - // Trying to insert value into slot <= than last root should fail { let mut coding_shred = coding_shred.clone(); @@ -6176,7 +6341,7 @@ pub mod tests { #[test] fn test_insert_multiple_is_last() { solana_logger::setup(); - let (shreds, _) = make_slot_entries(0, 0, 20); + let (shreds, _) = make_slot_entries(0, 0, 20, /*merkle_variant:*/ true); let num_shreds = shreds.len() as u64; let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); @@ -6189,7 +6354,7 @@ pub mod tests { assert_eq!(slot_meta.last_index, Some(num_shreds - 1)); assert!(slot_meta.is_full()); - let (shreds, _) = make_slot_entries(0, 0, 22); + let (shreds, _) = make_slot_entries(0, 0, 22, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); let slot_meta = blockstore.meta(0).unwrap().unwrap(); @@ -6342,7 +6507,8 @@ pub mod tests { let num_ticks = 8; let entries = create_ticks(num_ticks, 0, Hash::default()); let slot = 1; - let shreds = entries_to_test_shreds(&entries, slot, 0, false, 0); + let shreds = + entries_to_test_shreds(&entries, slot, 0, false, 0, /*merkle_variant:*/ true); let next_shred_index = shreds.len(); blockstore .insert_shreds(shreds, None, false) @@ -6376,7 +6542,7 @@ pub mod tests { fn test_no_insert_but_modify_slot_meta() { // This tests correctness of the SlotMeta in various cases in which a shred // that gets filtered out by checks - let (shreds0, _) = make_slot_entries(0, 0, 200); + let (shreds0, _) = make_slot_entries(0, 0, 200, /*merkle_variant:*/ true); let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); @@ -6388,8 +6554,8 @@ pub mod tests { // Insert a repetitive shred for slot 's', should get ignored, but also // insert shreds that chains to 's', should see the update in the SlotMeta // for 's'. - let (mut shreds2, _) = make_slot_entries(2, 0, 200); - let (mut shreds3, _) = make_slot_entries(3, 0, 200); + let (mut shreds2, _) = make_slot_entries(2, 0, 200, /*merkle_variant:*/ true); + let (mut shreds3, _) = make_slot_entries(3, 0, 200, /*merkle_variant:*/ true); shreds2.push(shreds0[1].clone()); shreds3.insert(0, shreds0[1].clone()); blockstore.insert_shreds(shreds2, None, false).unwrap(); @@ -6406,7 +6572,7 @@ pub mod tests { let blockstore = Blockstore::open(ledger_path.path()).unwrap(); // Make shred for slot 1 - let (shreds1, _) = make_slot_entries(1, 0, 1); + let (shreds1, _) = make_slot_entries(1, 0, 1, /*merkle_variant:*/ true); let last_root = 100; blockstore.set_roots(std::iter::once(&last_root)).unwrap(); @@ -6435,7 +6601,14 @@ pub mod tests { assert_eq!(blockstore.lowest_slot(), 0); for slot in 1..4 { let entries = make_slot_entries_with_transactions(100); - let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0); + let shreds = entries_to_test_shreds( + &entries, + slot, + slot - 1, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.set_roots(vec![slot].iter()).unwrap(); } @@ -6454,9 +6627,30 @@ pub mod tests { let slot = 10; let entries = make_slot_entries_with_transactions(100); let blockhash = get_last_hash(entries.iter()).unwrap(); - let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0); - let more_shreds = entries_to_test_shreds(&entries, slot + 1, slot, true, 0); - let unrooted_shreds = entries_to_test_shreds(&entries, slot + 2, slot + 1, true, 0); + let shreds = entries_to_test_shreds( + &entries, + slot, + slot - 1, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); + let more_shreds = entries_to_test_shreds( + &entries, + slot + 1, + slot, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); + let unrooted_shreds = entries_to_test_shreds( + &entries, + slot + 2, + slot + 1, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); blockstore.insert_shreds(shreds, None, false).unwrap(); @@ -6485,6 +6679,7 @@ pub mod tests { pre_balances.push(i as u64 * 10); post_balances.push(i as u64 * 11); } + let compute_units_consumed = Some(12345); let signature = transaction.signatures[0]; let status = TransactionStatusMeta { status: Ok(()), @@ -6498,6 +6693,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed, } .into(); blockstore @@ -6516,6 +6712,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed, } .into(); blockstore @@ -6534,6 +6731,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed, } .into(); blockstore @@ -6554,6 +6752,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed, }, } }) @@ -6670,6 +6869,8 @@ pub mod tests { program_id: Pubkey::new_unique(), data: vec![1, 2, 3], }; + let compute_units_consumed_1 = Some(3812649u64); + let compute_units_consumed_2 = Some(42u64); // result not found assert!(transaction_status_cf @@ -6690,6 +6891,7 @@ pub mod tests { rewards: Some(rewards_vec.clone()), loaded_addresses: test_loaded_addresses.clone(), return_data: Some(test_return_data.clone()), + compute_units_consumed: compute_units_consumed_1, } .into(); assert!(transaction_status_cf @@ -6709,6 +6911,7 @@ pub mod tests { rewards, loaded_addresses, return_data, + compute_units_consumed, } = transaction_status_cf .get_protobuf_or_bincode::((0, Signature::default(), 0)) .unwrap() @@ -6726,6 +6929,7 @@ pub mod tests { assert_eq!(rewards.unwrap(), rewards_vec); assert_eq!(loaded_addresses, test_loaded_addresses); assert_eq!(return_data.unwrap(), test_return_data); + assert_eq!(compute_units_consumed, compute_units_consumed_1); // insert value let status = TransactionStatusMeta { @@ -6740,6 +6944,7 @@ pub mod tests { rewards: Some(rewards_vec.clone()), loaded_addresses: test_loaded_addresses.clone(), return_data: Some(test_return_data.clone()), + compute_units_consumed: compute_units_consumed_2, } .into(); assert!(transaction_status_cf @@ -6759,6 +6964,7 @@ pub mod tests { rewards, loaded_addresses, return_data, + compute_units_consumed, } = transaction_status_cf .get_protobuf_or_bincode::(( 0, @@ -6782,6 +6988,7 @@ pub mod tests { assert_eq!(rewards.unwrap(), rewards_vec); assert_eq!(loaded_addresses, test_loaded_addresses); assert_eq!(return_data.unwrap(), test_return_data); + assert_eq!(compute_units_consumed, compute_units_consumed_2); } #[test] @@ -6803,8 +7010,8 @@ pub mod tests { .write_transaction_status( slot0, Signature::new(&random_bytes), - vec![&Pubkey::new(&random_bytes[0..32])], - vec![&Pubkey::new(&random_bytes[32..])], + vec![&Pubkey::try_from(&random_bytes[..32]).unwrap()], + vec![&Pubkey::try_from(&random_bytes[32..]).unwrap()], TransactionStatusMeta::default(), ) .unwrap(); @@ -6869,8 +7076,8 @@ pub mod tests { .write_transaction_status( slot1, Signature::new(&random_bytes), - vec![&Pubkey::new(&random_bytes[0..32])], - vec![&Pubkey::new(&random_bytes[32..])], + vec![&Pubkey::try_from(&random_bytes[..32]).unwrap()], + vec![&Pubkey::try_from(&random_bytes[32..]).unwrap()], TransactionStatusMeta::default(), ) .unwrap(); @@ -7011,6 +7218,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed: Some(42u64), } .into(); @@ -7207,6 +7415,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed: Some(42u64), } .into(); @@ -7351,7 +7560,14 @@ pub mod tests { fn test_get_rooted_transaction() { let slot = 2; let entries = make_slot_entries_with_transactions(5); - let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0); + let shreds = entries_to_test_shreds( + &entries, + slot, + slot - 1, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); blockstore.insert_shreds(shreds, None, false).unwrap(); @@ -7394,6 +7610,7 @@ pub mod tests { rewards: rewards.clone(), loaded_addresses: LoadedAddresses::default(), return_data: return_data.clone(), + compute_units_consumed: Some(42), } .into(); blockstore @@ -7414,6 +7631,7 @@ pub mod tests { rewards, loaded_addresses: LoadedAddresses::default(), return_data, + compute_units_consumed: Some(42), }, } }) @@ -7462,7 +7680,14 @@ pub mod tests { let slot = 2; let entries = make_slot_entries_with_transactions(5); - let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0); + let shreds = entries_to_test_shreds( + &entries, + slot, + slot - 1, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); let expected_transactions: Vec = entries @@ -7502,6 +7727,7 @@ pub mod tests { rewards: rewards.clone(), loaded_addresses: LoadedAddresses::default(), return_data: return_data.clone(), + compute_units_consumed: Some(42u64), } .into(); blockstore @@ -7522,6 +7748,7 @@ pub mod tests { rewards, loaded_addresses: LoadedAddresses::default(), return_data, + compute_units_consumed: Some(42u64), }, } }) @@ -7796,7 +8023,7 @@ pub mod tests { let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); - let (shreds, _) = make_slot_entries(1, 0, 4); + let (shreds, _) = make_slot_entries(1, 0, 4, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); fn make_slot_entries_with_transaction_addresses(addresses: &[Pubkey]) -> Vec { @@ -7823,7 +8050,14 @@ pub mod tests { let entries = make_slot_entries_with_transaction_addresses(&[ address0, address1, address0, address1, ]); - let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0); + let shreds = entries_to_test_shreds( + &entries, + slot, + slot - 1, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); for entry in entries.into_iter() { @@ -7847,7 +8081,8 @@ pub mod tests { let entries = make_slot_entries_with_transaction_addresses(&[ address0, address1, address0, address1, ]); - let shreds = entries_to_test_shreds(&entries, slot, 8, true, 0); + let shreds = + entries_to_test_shreds(&entries, slot, 8, true, 0, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); for entry in entries.into_iter() { @@ -8282,6 +8517,7 @@ pub mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed: None, } .into(); transaction_status_cf @@ -8351,9 +8587,10 @@ pub mod tests { let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); - for i in 0..10 { - let slot = i; - let (shreds, _) = make_slot_entries(slot, 0, 1); + assert_eq!(blockstore.lowest_slot(), 0); + + for slot in 0..10 { + let (shreds, _) = make_slot_entries(slot, 0, 1, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); } assert_eq!(blockstore.lowest_slot(), 1); @@ -8361,6 +8598,27 @@ pub mod tests { assert_eq!(blockstore.lowest_slot(), 6); } + #[test] + fn test_highest_slot() { + let ledger_path = get_tmp_ledger_path_auto_delete!(); + let blockstore = Blockstore::open(ledger_path.path()).unwrap(); + + assert_eq!(blockstore.highest_slot().unwrap(), None); + + for slot in 0..10 { + let (shreds, _) = make_slot_entries(slot, 0, 1, /*merkle_variant:*/ true); + blockstore.insert_shreds(shreds, None, false).unwrap(); + assert_eq!(blockstore.highest_slot().unwrap(), Some(slot)); + } + blockstore + .run_purge(5, 10, PurgeType::PrimaryIndex) + .unwrap(); + assert_eq!(blockstore.highest_slot().unwrap(), Some(4)); + + blockstore.run_purge(0, 4, PurgeType::PrimaryIndex).unwrap(); + assert_eq!(blockstore.highest_slot().unwrap(), None); + } + #[test] fn test_recovery() { let ledger_path = get_tmp_ledger_path_auto_delete!(); @@ -8540,6 +8798,8 @@ pub mod tests { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); @@ -8594,6 +8854,7 @@ pub mod tests { let entries1 = make_slot_entries_with_transactions(1); let entries2 = make_slot_entries_with_transactions(1); let leader_keypair = Arc::new(Keypair::new()); + let reed_solomon_cache = ReedSolomonCache::default(); let shredder = Shredder::new(slot, 0, 0, 0).unwrap(); let (shreds, _) = shredder.entries_to_shreds( &leader_keypair, @@ -8601,6 +8862,8 @@ pub mod tests { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index, + true, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); let (duplicate_shreds, _) = shredder.entries_to_shreds( @@ -8609,6 +8872,8 @@ pub mod tests { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); let shred = shreds[0].clone(); @@ -8889,6 +9154,7 @@ pub mod tests { program_id: Pubkey::new_unique(), data: vec![1, 2, 3], }), + compute_units_consumed: Some(23456), }; let deprecated_status: StoredTransactionStatusMeta = status.clone().try_into().unwrap(); let protobuf_status: generated::TransactionStatusMeta = status.into(); @@ -8942,19 +9208,29 @@ pub mod tests { let parent = 0; let num_txs = 20; let entry = make_large_tx_entry(num_txs); - let shreds = entries_to_test_shreds(&[entry], slot, parent, true, 0); + let shreds = entries_to_test_shreds( + &[entry], + slot, + parent, + true, // is_full_slot + 0, // version + false, // merkle_variant + ); assert!(shreds.len() > 1); let ledger_path = get_tmp_ledger_path_auto_delete!(); let blockstore = Blockstore::open(ledger_path.path()).unwrap(); + let reed_solomon_cache = ReedSolomonCache::default(); let coding1 = Shredder::generate_coding_shreds( - &shreds, false, // is_last_in_slot - 0, // next_code_index + &shreds, + 0, // next_code_index + &reed_solomon_cache, ); let coding2 = Shredder::generate_coding_shreds( - &shreds, true, // is_last_in_slot - 0, // next_code_index + &shreds, + 1, // next_code_index + &reed_solomon_cache, ); for shred in &shreds { info!("shred {:?}", shred); @@ -8981,7 +9257,8 @@ pub mod tests { // Create enough entries to ensure there are at least two shreds created let num_unique_entries = max_ticks_per_n_shreds(1, None) + 1; - let (mut original_shreds, original_entries) = make_slot_entries(0, 0, num_unique_entries); + let (mut original_shreds, original_entries) = + make_slot_entries(0, 0, num_unique_entries, /*merkle_variant:*/ true); // Discard first shred, so that the slot is not full assert!(original_shreds.len() > 1); @@ -9004,7 +9281,14 @@ pub mod tests { assert!(!blockstore.is_full(0)); } - let duplicate_shreds = entries_to_test_shreds(&original_entries, 0, 0, true, 0); + let duplicate_shreds = entries_to_test_shreds( + &original_entries, + 0, // slot + 0, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); let num_shreds = duplicate_shreds.len() as u64; blockstore .insert_shreds(duplicate_shreds, None, false) @@ -9026,7 +9310,8 @@ pub mod tests { let num_shreds = 2; let num_entries = max_ticks_per_n_shreds(num_shreds, None); let slot = 1; - let (mut shreds, _) = make_slot_entries(slot, 0, num_entries); + let (mut shreds, _) = + make_slot_entries(slot, 0, num_entries, /*merkle_variant:*/ false); // Mark both as last shred shreds[0].set_last_in_slot(); @@ -9047,7 +9332,8 @@ pub mod tests { let setup_test_shreds = |slot: Slot| -> Vec { let num_entries = max_ticks_per_n_shreds(num_shreds, None); - let (mut shreds, _) = make_slot_entries(slot, 0, num_entries); + let (mut shreds, _) = + make_slot_entries(slot, 0, num_entries, /*merkle_variant:*/ false); shreds[smaller_last_shred_index].set_last_in_slot(); shreds[larger_last_shred_index].set_last_in_slot(); shreds @@ -9221,7 +9507,8 @@ pub mod tests { let num_shreds = 10; let middle_shred_index = 5; let num_entries = max_ticks_per_n_shreds(num_shreds, None); - let (shreds, _) = make_slot_entries(slot, 0, num_entries); + let (shreds, _) = + make_slot_entries(slot, 0, num_entries, /*merkle_variant:*/ false); // Reverse shreds so that last shred gets inserted first and sets meta.received let mut shreds: Vec = shreds.into_iter().rev().collect(); diff --git a/ledger/src/blockstore/blockstore_purge.rs b/ledger/src/blockstore/blockstore_purge.rs index 640b338f2c2cb2..3c492e7f17cc3f 100644 --- a/ledger/src/blockstore/blockstore_purge.rs +++ b/ledger/src/blockstore/blockstore_purge.rs @@ -4,6 +4,7 @@ use {super::*, solana_sdk::message::AccountKeys, std::time::Instant}; pub struct PurgeStats { delete_range: u64, write_batch: u64, + delete_files_in_range: u64, } #[derive(Clone, Copy)] @@ -46,7 +47,12 @@ impl Blockstore { ("from_slot", from_slot as i64, i64), ("to_slot", to_slot as i64, i64), ("delete_range_us", purge_stats.delete_range as i64, i64), - ("write_batch_us", purge_stats.write_batch as i64, i64) + ("write_batch_us", purge_stats.write_batch as i64, i64), + ( + "delete_files_in_range_us", + purge_stats.write_batch as i64, + i64 + ) ); if let Err(e) = purge_result { error!( @@ -141,6 +147,9 @@ impl Blockstore { /// A helper function to `purge_slots` that executes the ledger clean up /// from `from_slot` to `to_slot`. + /// + /// When `from_slot` is 0, any sst-file with a key-range completely older + /// than `to_slot` will also be deleted. pub(crate) fn run_purge_with_stats( &self, from_slot: Slot, @@ -239,6 +248,7 @@ impl Blockstore { } } delete_range_timer.stop(); + let mut write_timer = Measure::start("write_batch"); if let Err(e) = self.db.write(write_batch) { error!( @@ -248,8 +258,28 @@ impl Blockstore { return Err(e); } write_timer.stop(); + + let mut purge_files_in_range_timer = Measure::start("delete_file_in_range"); + // purge_files_in_range delete any files whose slot range is within + // [from_slot, to_slot]. When from_slot is 0, it is safe to run + // purge_files_in_range because if purge_files_in_range deletes any + // sst file that contains any range-deletion tombstone, the deletion + // range of that tombstone will be completely covered by the new + // range-delete tombstone (0, to_slot) issued above. + // + // On the other hand, purge_files_in_range is more effective and + // efficient than the compaction filter (which runs key-by-key) + // because all the sst files that have key range below to_slot + // can be deleted immediately. + if columns_purged && from_slot == 0 { + self.purge_files_in_range(from_slot, to_slot); + } + purge_files_in_range_timer.stop(); + purge_stats.delete_range += delete_range_timer.as_us(); purge_stats.write_batch += write_timer.as_us(); + purge_stats.delete_files_in_range += purge_files_in_range_timer.as_us(); + // only drop w_active_transaction_status_index after we do db.write(write_batch); // otherwise, readers might be confused with inconsistent state between // self.active_transaction_status_index and RockDb's TransactionStatusIndex contents @@ -257,6 +287,68 @@ impl Blockstore { Ok(columns_purged) } + fn purge_files_in_range(&self, from_slot: Slot, to_slot: Slot) -> bool { + self.db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + & self + .db + .delete_file_in_range_cf::(from_slot, to_slot) + .is_ok() + } + pub fn compact_storage(&self, from_slot: Slot, to_slot: Slot) -> Result { if self.no_compaction { info!("compact_storage: compaction disabled"); @@ -486,8 +578,8 @@ pub mod tests { .write_transaction_status( x, Signature::new(&random_bytes), - vec![&Pubkey::new(&random_bytes[0..32])], - vec![&Pubkey::new(&random_bytes[32..])], + vec![&Pubkey::try_from(&random_bytes[..32]).unwrap()], + vec![&Pubkey::try_from(&random_bytes[32..]).unwrap()], TransactionStatusMeta::default(), ) .unwrap(); @@ -501,8 +593,8 @@ pub mod tests { .write_transaction_status( x, Signature::new(&random_bytes), - vec![&Pubkey::new(&random_bytes[0..32])], - vec![&Pubkey::new(&random_bytes[32..])], + vec![&Pubkey::try_from(&random_bytes[..32]).unwrap()], + vec![&Pubkey::try_from(&random_bytes[32..]).unwrap()], TransactionStatusMeta::default(), ) .unwrap(); @@ -536,8 +628,8 @@ pub mod tests { .write_transaction_status( slot, Signature::new(&random_bytes), - vec![&Pubkey::new(&random_bytes[0..32])], - vec![&Pubkey::new(&random_bytes[32..])], + vec![&Pubkey::try_from(&random_bytes[..32]).unwrap()], + vec![&Pubkey::try_from(&random_bytes[32..]).unwrap()], TransactionStatusMeta::default(), ) .unwrap(); @@ -715,7 +807,14 @@ pub mod tests { for x in 0..index0_max_slot + 1 { let entries = make_slot_entries_with_transactions(1); - let shreds = entries_to_test_shreds(&entries, x, x.saturating_sub(1), true, 0); + let shreds = entries_to_test_shreds( + &entries, + x, // slot + x.saturating_sub(1), // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); let signature = entries .iter() @@ -729,8 +828,8 @@ pub mod tests { .write_transaction_status( x, signature, - vec![&Pubkey::new(&random_bytes[0..32])], - vec![&Pubkey::new(&random_bytes[32..])], + vec![&Pubkey::try_from(&random_bytes[..32]).unwrap()], + vec![&Pubkey::try_from(&random_bytes[32..]).unwrap()], TransactionStatusMeta::default(), ) .unwrap(); @@ -751,7 +850,14 @@ pub mod tests { for x in index0_max_slot + 1..index1_max_slot + 1 { let entries = make_slot_entries_with_transactions(1); - let shreds = entries_to_test_shreds(&entries, x, x.saturating_sub(1), true, 0); + let shreds = entries_to_test_shreds( + &entries, + x, // slot + x.saturating_sub(1), // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); let signature: Signature = entries .iter() @@ -765,8 +871,8 @@ pub mod tests { .write_transaction_status( x, signature, - vec![&Pubkey::new(&random_bytes[0..32])], - vec![&Pubkey::new(&random_bytes[32..])], + vec![&Pubkey::try_from(&random_bytes[..32]).unwrap()], + vec![&Pubkey::try_from(&random_bytes[32..]).unwrap()], TransactionStatusMeta::default(), ) .unwrap(); @@ -1151,7 +1257,14 @@ pub mod tests { let mut tick = create_ticks(1, 0, hash(&serialize(&x).unwrap())); entries.append(&mut tick); } - let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0); + let shreds = entries_to_test_shreds( + &entries, + slot, + slot - 1, // parent_slot + true, // is_full_slot + 0, // version + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); let mut write_batch = blockstore.db.batch().unwrap(); diff --git a/ledger/src/blockstore_db.rs b/ledger/src/blockstore_db.rs index 94064df7a179cd..5fb34ad1bddf11 100644 --- a/ledger/src/blockstore_db.rs +++ b/ledger/src/blockstore_db.rs @@ -259,7 +259,7 @@ impl Rocks { let access_type = options.access_type.clone(); let recovery_mode = options.recovery_mode.clone(); - fs::create_dir_all(&path)?; + fs::create_dir_all(path)?; // Use default database options if should_disable_auto_compactions(&access_type) { @@ -455,6 +455,16 @@ impl Rocks { Ok(()) } + fn delete_file_in_range_cf( + &self, + cf: &ColumnFamily, + from_key: &[u8], + to_key: &[u8], + ) -> Result<()> { + self.db.delete_file_in_range_cf(cf, from_key, to_key)?; + Ok(()) + } + fn iterator_cf(&self, cf: &ColumnFamily, iterator_mode: IteratorMode) -> DBIterator where C: Column, @@ -509,7 +519,7 @@ impl Rocks { /// /// Full list of properties that return int values could be found /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689). - fn get_int_property_cf(&self, cf: &ColumnFamily, name: &str) -> Result { + fn get_int_property_cf(&self, cf: &ColumnFamily, name: &'static std::ffi::CStr) -> Result { match self.db.property_int_value_cf(cf, name) { Ok(Some(value)) => Ok(value.try_into().unwrap()), Ok(None) => Ok(0), @@ -641,7 +651,7 @@ impl Column for columns::AddressSignatures { fn index(key: &[u8]) -> (u64, Pubkey, Slot, Signature) { let index = BigEndian::read_u64(&key[0..8]); - let pubkey = Pubkey::new(&key[8..40]); + let pubkey = Pubkey::try_from(&key[8..40]).unwrap(); let slot = BigEndian::read_u64(&key[40..48]); let signature = Signature::new(&key[48..112]); (index, pubkey, slot, signature) @@ -772,7 +782,7 @@ impl Column for columns::ProgramCosts { } fn index(key: &[u8]) -> Self::Index { - Pubkey::new(&key[0..32]) + Pubkey::try_from(&key[..32]).unwrap() } fn primary_index(_index: Self::Index) -> u64 { @@ -1059,7 +1069,10 @@ impl Database { { let cf = self.cf_handle::(); let iter = self.backend.iterator_cf::(cf, iterator_mode); - Ok(iter.map(|(key, value)| (C::index(&key), value))) + Ok(iter.map(|pair| { + let (key, value) = pair.unwrap(); + (C::index(&key), value) + })) } #[inline] @@ -1117,6 +1130,17 @@ impl Database { batch.delete_range_cf::(cf, from_index, to_index) } + pub fn delete_file_in_range_cf(&self, from: Slot, to: Slot) -> Result<()> + where + C: Column + ColumnName, + { + self.backend.delete_file_in_range_cf( + self.cf_handle::(), + &C::key(C::as_index(from)), + &C::key(C::as_index(to)), + ) + } + pub fn is_primary_access(&self) -> bool { self.backend.is_primary_access() } @@ -1153,7 +1177,10 @@ where ) -> Result)> + '_> { let cf = self.handle(); let iter = self.backend.iterator_cf::(cf, iterator_mode); - Ok(iter.map(|(key, value)| (C::index(&key), value))) + Ok(iter.map(|pair| { + let (key, value) = pair.unwrap(); + (C::index(&key), value) + })) } pub fn delete_slot( @@ -1235,7 +1262,7 @@ where /// /// Full list of properties that return int values could be found /// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689). - pub fn get_int_property(&self, name: &str) -> Result { + pub fn get_int_property(&self, name: &'static std::ffi::CStr) -> Result { self.backend.get_int_property_cf(self.handle(), name) } } diff --git a/ledger/src/blockstore_meta.rs b/ledger/src/blockstore_meta.rs index 65101fe98348ba..b19b627c520d4d 100644 --- a/ledger/src/blockstore_meta.rs +++ b/ledger/src/blockstore_meta.rs @@ -1,5 +1,6 @@ use { crate::shred::{Shred, ShredType}, + bitflags::bitflags, serde::{Deserialize, Deserializer, Serialize, Serializer}, solana_sdk::{ clock::{Slot, UnixTimestamp}, @@ -11,6 +12,43 @@ use { }, }; +bitflags! { + #[derive(Deserialize, Serialize)] + /// Flags to indicate whether a slot is a descendant of a slot on the main fork + pub struct ConnectedFlags:u8 { + // A slot S should be considered to be connected if: + // 1) S is a rooted slot itself OR + // 2) S's parent is connected AND S is full (S's complete block present) + // + // 1) is a straightfoward case, roots are finalized blocks on the main fork + // so by definition, they are connected. All roots are connected, but not + // all connected slots are (or will become) roots. + // + // Based on the criteria stated in 2), S is connected iff it has a series + // of ancestors (that are each connected) that form a chain back to + // some root slot. + // + // A ledger that is updating with a cluster will have either begun at + // genesis or at at some snapshot slot. + // - Genesis is obviously a special case, and slot 0's parent is deemed + // to be connected in order to kick off the induction + // - Snapshots are taken at rooted slots, and as such, the snapshot slot + // should be marked as connected so that a connected chain can start + // + // CONNECTED is explicitly the first bit to ensure backwards compatibility + // with the boolean field that ConnectedFlags replaced in SlotMeta. + const CONNECTED = 0b0000_0001; + // PARENT_CONNECTED IS INTENTIIONALLY UNUSED FOR NOW + const PARENT_CONNECTED = 0b1000_0000; + } +} + +impl Default for ConnectedFlags { + fn default() -> Self { + ConnectedFlags::empty() + } +} + #[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)] // The Meta column family pub struct SlotMeta { @@ -37,9 +75,8 @@ pub struct SlotMeta { // The list of slots, each of which contains a block that derives // from this one. pub next_slots: Vec, - // True if this slot is full (consumed == last_index + 1) and if every - // slot that is a parent of this slot is also connected. - pub is_connected: bool, + // Connected status flags of this slot + pub connected_flags: ConnectedFlags, // Shreds indices which are marked data complete. pub completed_data_indexes: BTreeSet, } @@ -214,6 +251,14 @@ impl SlotMeta { Some(self.consumed) == self.last_index.map(|ix| ix + 1) } + pub fn is_connected(&self) -> bool { + self.connected_flags.contains(ConnectedFlags::CONNECTED) + } + + pub fn set_connected(&mut self) { + self.connected_flags.set(ConnectedFlags::CONNECTED, true); + } + /// Dangerous. Currently only needed for a local-cluster test pub fn unset_parent(&mut self) { self.parent_slot = None; @@ -225,10 +270,17 @@ impl SlotMeta { } pub(crate) fn new(slot: Slot, parent_slot: Option) -> Self { + let connected_flags = if slot == 0 { + // Slot 0 is the start, mark it as having its' parent connected + // such that slot 0 becoming full will be updated as connected + ConnectedFlags::CONNECTED + } else { + ConnectedFlags::default() + }; SlotMeta { slot, parent_slot, - is_connected: slot == 0, + connected_flags, ..SlotMeta::default() } } @@ -426,6 +478,79 @@ mod test { } } + #[test] + fn test_connected_flags_compatibility() { + // Define a couple structs with bool and ConnectedFlags to illustrate + // that that ConnectedFlags can be deserialized into a bool if the + // PARENT_CONNECTED bit is NOT set + #[derive(Debug, Deserialize, PartialEq, Serialize)] + struct WithBool { + slot: Slot, + connected: bool, + } + #[derive(Debug, Deserialize, PartialEq, Serialize)] + struct WithFlags { + slot: Slot, + connected: ConnectedFlags, + } + + let slot = 3; + let mut with_bool = WithBool { + slot, + connected: false, + }; + let mut with_flags = WithFlags { + slot, + connected: ConnectedFlags::default(), + }; + + // Confirm that serialized byte arrays are same length + assert_eq!( + bincode::serialized_size(&with_bool).unwrap(), + bincode::serialized_size(&with_flags).unwrap() + ); + + // Confirm that connected=false equivalent to ConnectedFlags::default() + assert_eq!( + bincode::serialize(&with_bool).unwrap(), + bincode::serialize(&with_flags).unwrap() + ); + + // Set connected in WithBool and confirm inequality + with_bool.connected = true; + assert_ne!( + bincode::serialize(&with_bool).unwrap(), + bincode::serialize(&with_flags).unwrap() + ); + + // Set connected in WithFlags and confirm equality regained + with_flags.connected.set(ConnectedFlags::CONNECTED, true); + assert_eq!( + bincode::serialize(&with_bool).unwrap(), + bincode::serialize(&with_flags).unwrap() + ); + + // Dserializing WithBool into WithFlags succeeds + assert_eq!( + with_flags, + bincode::deserialize::(&bincode::serialize(&with_bool).unwrap()).unwrap() + ); + + // Deserializing WithFlags into WithBool succeeds + assert_eq!( + with_bool, + bincode::deserialize::(&bincode::serialize(&with_flags).unwrap()).unwrap() + ); + + // Deserializing WithFlags with extra bit set into WithBool fails + with_flags + .connected + .set(ConnectedFlags::PARENT_CONNECTED, true); + assert!( + bincode::deserialize::(&bincode::serialize(&with_flags).unwrap()).is_err() + ); + } + #[test] fn test_clear_unconfirmed_slot() { let mut slot_meta = SlotMeta::new_orphan(5); diff --git a/ledger/src/blockstore_processor.rs b/ledger/src/blockstore_processor.rs index 22b8b872b14c38..309f96a59d003b 100644 --- a/ledger/src/blockstore_processor.rs +++ b/ledger/src/blockstore_processor.rs @@ -2,6 +2,7 @@ use { crate::{ block_error::BlockError, blockstore::Blockstore, blockstore_db::BlockstoreError, blockstore_meta::SlotMeta, leader_schedule_cache::LeaderScheduleCache, + token_balances::collect_token_balances, }, chrono_humanize::{Accuracy, HumanTime, Tense}, crossbeam_channel::Sender, @@ -30,6 +31,7 @@ use { block_cost_limits::*, commitment::VOTE_THRESHOLD_SIZE, cost_model::CostModel, + prioritization_fee_cache::PrioritizationFeeCache, runtime_config::RuntimeConfig, transaction_batch::TransactionBatch, vote_account::VoteAccountsHashMap, @@ -49,9 +51,7 @@ use { VersionedTransaction, }, }, - solana_transaction_status::token_balances::{ - collect_token_balances, TransactionTokenBalancesSet, - }, + solana_transaction_status::token_balances::TransactionTokenBalancesSet, std::{ borrow::Cow, collections::{HashMap, HashSet}, @@ -63,35 +63,6 @@ use { thiserror::Error, }; -// it tracks the block cost available capacity - number of compute-units allowed -// by max block cost limit. -#[derive(Debug)] -pub struct BlockCostCapacityMeter { - pub capacity: u64, - pub accumulated_cost: u64, -} - -impl Default for BlockCostCapacityMeter { - fn default() -> Self { - BlockCostCapacityMeter::new(MAX_BLOCK_UNITS) - } -} - -impl BlockCostCapacityMeter { - pub fn new(capacity_limit: u64) -> Self { - Self { - capacity: capacity_limit, - accumulated_cost: 0_u64, - } - } - - // return the remaining capacity - pub fn accumulate(&mut self, cost: u64) -> u64 { - self.accumulated_cost += cost; - self.capacity.saturating_sub(self.accumulated_cost) - } -} - struct TransactionBatchWithIndexes<'a, 'b> { pub batch: TransactionBatch<'a, 'b>, pub transaction_indexes: Vec, @@ -107,33 +78,35 @@ struct ReplayEntry { lazy_static! { static ref PAR_THREAD_POOL: ThreadPool = rayon::ThreadPoolBuilder::new() .num_threads(get_max_thread_count()) - .thread_name(|ix| format!("blockstore_processor_{}", ix)) + .thread_name(|ix| format!("solBstoreProc{:02}", ix)) .build() .unwrap(); } -fn first_err(results: &[Result<()>]) -> Result<()> { +fn first_err(results: &[Result]) -> Result { for r in results { - if r.is_err() { - return r.clone(); + if let Err(e) = r { + return Err(e.clone()); } } - Ok(()) + Ok(T::default()) } // Includes transaction signature for unit-testing fn get_first_error( batch: &TransactionBatch, fee_collection_results: Vec>, -) -> Option<(Result<()>, Signature)> { + receipts: Vec<(Hash, Hash)>, +) -> Option<(Result>, Signature)> { let mut first_err = None; for (result, transaction) in fee_collection_results .iter() .zip(batch.sanitized_transactions()) + // .map(|(res,t| Ok()); { if let Err(ref err) = result { if first_err.is_none() { - first_err = Some((result.clone(), *transaction.signature())); + first_err = Some((Ok(receipts.clone()), *transaction.signature())); } warn!( "Unexpected validator error: {:?}, transaction: {:?}", @@ -152,29 +125,15 @@ fn get_first_error( first_err } -fn aggregate_total_execution_units(execute_timings: &ExecuteTimings) -> u64 { - let mut execute_cost_units: u64 = 0; - for (program_id, timing) in &execute_timings.details.per_program_timings { - if timing.count < 1 { - continue; - } - execute_cost_units = - execute_cost_units.saturating_add(timing.accumulated_units / timing.count as u64); - trace!("aggregated execution cost of {:?} {:?}", program_id, timing); - } - execute_cost_units -} - fn execute_batch( batch: &TransactionBatchWithIndexes, bank: &Arc, transaction_status_sender: Option<&TransactionStatusSender>, replay_vote_sender: Option<&ReplayVoteSender>, timings: &mut ExecuteTimings, - cost_capacity_meter: Arc>, - tx_cost: u64, log_messages_bytes_limit: Option, -) -> Result<()> { +) -> Result> { + // info!("chk1 enters execute_batch"); let TransactionBatchWithIndexes { batch, transaction_indexes, @@ -189,8 +148,6 @@ fn execute_batch( vec![] }; - let pre_process_units: u64 = aggregate_total_execution_units(timings); - let (tx_results, balances) = batch.bank().load_execute_and_commit_transactions( batch, MAX_PROCESSING_AGE, @@ -202,30 +159,6 @@ fn execute_batch( log_messages_bytes_limit, ); - if bank - .feature_set - .is_active(&feature_set::gate_large_block::id()) - { - let execution_cost_units = aggregate_total_execution_units(timings) - pre_process_units; - let remaining_block_cost_cap = cost_capacity_meter - .write() - .unwrap() - .accumulate(execution_cost_units + tx_cost); - - debug!( - "bank {} executed a batch, number of transactions {}, total execute cu {}, total additional cu {}, remaining block cost cap {}", - bank.slot(), - batch.sanitized_transactions().len(), - execution_cost_units, - tx_cost, - remaining_block_cost_cap, - ); - - if remaining_block_cost_cap == 0_u64 { - return Err(TransactionError::WouldExceedMaxBlockCostLimit); - } - } - bank_utils::find_and_send_votes( batch.sanitized_transactions(), &tx_results, @@ -236,11 +169,12 @@ fn execute_batch( fee_collection_results, execution_results, rent_debits, + receipts, .. - } = tx_results; - + } = tx_results.clone(); + // bank.append_receipts(receipts); check_accounts_data_size(bank, &execution_results)?; - + // info!("execrec: {:?} tx_results {:?}",receipts, tx_results); if let Some(transaction_status_sender) = transaction_status_sender { let transactions = batch.sanitized_transactions().to_vec(); let post_token_balances = if record_token_balances { @@ -263,10 +197,20 @@ fn execute_batch( ); } - let first_err = get_first_error(batch, fee_collection_results); - first_err.map(|(result, _)| result).unwrap_or(Ok(())) + let first_err = get_first_error(batch, fee_collection_results, receipts.clone()); + // ExecuteBatchResult{ + // result: + first_err.map(|(result, _)| result).unwrap_or(Ok(receipts)) + // ,receipts, + // } } +// #[derive(Default)] +// pub struct ExecuteBatchResult{ +// result: Result<()>, +// receipts: Vec<(Hash,Hash)> +// } + #[derive(Default)] struct ExecuteBatchesInternalMetrics { execution_timings_per_thread: HashMap, @@ -280,8 +224,6 @@ fn execute_batches_internal( entry_callback: Option<&ProcessCallback>, transaction_status_sender: Option<&TransactionStatusSender>, replay_vote_sender: Option<&ReplayVoteSender>, - cost_capacity_meter: Arc>, - tx_costs: &[u64], log_messages_bytes_limit: Option, ) -> Result { inc_new_counter_debug!("bank-par_execute_entries-count", batches.len()); @@ -289,26 +231,22 @@ fn execute_batches_internal( Mutex::new(HashMap::new()); let mut execute_batches_elapsed = Measure::start("execute_batches_elapsed"); - let results: Vec> = PAR_THREAD_POOL.install(|| { + info!("chk1 enters execute_batches_internal"); + let results: Vec>> = PAR_THREAD_POOL.install(|| { batches .into_par_iter() - .enumerate() - .map(|(index, transaction_batch_with_indexes)| { - let transaction_count = transaction_batch_with_indexes - .batch - .sanitized_transactions() - .len() as u64; + .map(|transaction_batch| { + let transaction_count = + transaction_batch.batch.sanitized_transactions().len() as u64; let mut timings = ExecuteTimings::default(); - let (result, execute_batches_time): (Result<()>, Measure) = measure!( + let (result, execute_batches_time): (Result>, Measure) = measure!( { let result = execute_batch( - transaction_batch_with_indexes, + transaction_batch, bank, transaction_status_sender, replay_vote_sender, &mut timings, - cost_capacity_meter.clone(), - tx_costs[index], log_messages_bytes_limit, ); if let Some(entry_callback) = entry_callback { @@ -347,7 +285,24 @@ fn execute_batches_internal( }); execute_batches_elapsed.stop(); + let receipts: Vec> = results + .clone() + .into_iter() + .filter(|res| res.is_ok()) + .map(|res| res.unwrap()) + .collect(); + info!("#64 done_executing batches {:?}", batches.len()); + info!("#64 append_receipts: {:?} results {:?}", receipts, results); + bank.append_receipts(receipts.clone()); + first_err(&results)?; + // let first_err = {for r in results{ + // if r.is_err(){ + // r + // } + // Ok(()) + // } + // }; Ok(ExecuteBatchesInternalMetrics { execution_timings_per_thread: execution_timings_per_thread.into_inner().unwrap(), @@ -383,7 +338,6 @@ fn execute_batches( transaction_status_sender: Option<&TransactionStatusSender>, replay_vote_sender: Option<&ReplayVoteSender>, confirmation_timing: &mut ConfirmationTiming, - cost_capacity_meter: Arc>, cost_model: &CostModel, log_messages_bytes_limit: Option, ) -> Result<()> { @@ -423,7 +377,6 @@ fn execute_batches( let target_batch_count = get_thread_count() as u64; let mut tx_batches: Vec = vec![]; - let mut tx_batch_costs: Vec = vec![]; let rebatched_txs = if total_cost > target_batch_count.saturating_mul(minimal_tx_cost) { let target_batch_cost = total_cost / target_batch_count; let mut batch_cost: u64 = 0; @@ -447,18 +400,12 @@ fn execute_batches( ); slice_start = next_index; tx_batches.push(tx_batch); - tx_batch_costs.push(batch_cost_without_bpf); batch_cost = 0; batch_cost_without_bpf = 0; } }); &tx_batches[..] } else { - match batches.len() { - // Ensure that the total cost attributed to this batch is essentially correct - 0 => tx_batch_costs = Vec::new(), - n => tx_batch_costs = vec![total_cost_without_bpf / (n as u64); n], - } batches }; @@ -468,8 +415,6 @@ fn execute_batches( entry_callback, transaction_status_sender, replay_vote_sender, - cost_capacity_meter, - &tx_batch_costs, log_messages_bytes_limit, )?; @@ -516,6 +461,7 @@ pub fn process_entries_for_tests( }) .collect(); + let _ignored_prioritization_fee_cache = PrioritizationFeeCache::new(0u64); let result = process_entries_with_callback( bank, &mut replay_entries, @@ -524,8 +470,8 @@ pub fn process_entries_for_tests( transaction_status_sender, replay_vote_sender, &mut confirmation_timing, - Arc::new(RwLock::new(BlockCostCapacityMeter::default())), None, + &_ignored_prioritization_fee_cache, ); debug!("process_entries: {:?}", confirmation_timing); @@ -542,20 +488,37 @@ fn process_entries_with_callback( transaction_status_sender: Option<&TransactionStatusSender>, replay_vote_sender: Option<&ReplayVoteSender>, confirmation_timing: &mut ConfirmationTiming, - cost_capacity_meter: Arc>, log_messages_bytes_limit: Option, + prioritization_fee_cache: &PrioritizationFeeCache, ) -> Result<()> { // accumulator for entries that can be processed in parallel let mut batches = vec![]; let mut tick_hashes = vec![]; let mut rng = thread_rng(); let cost_model = CostModel::new(); - + // let block_signatures = entries.iter().filter_map(|e| match &e.entry { + // EntryType::Transactions(txs) => Some(txs), + // _ => None, + // }).flatten() + // .map(|tx| *tx.signature()) + // .collect::>(); + info!("chk1 starts replay"); + let transaction_message_hashes: Vec = entries + .iter() + .filter_map(|e| match &e.entry { + EntryType::Transactions(txns) => Some(txns), + _ => None, + }) + .flatten() + .map(|txn| *txn.message_hash()) + .collect(); + bank.set_transaction_message_hashes(transaction_message_hashes); for ReplayEntry { entry, starting_index, } in entries { + // bank.set_transaction_indexes(entr, transaction_count) match entry { EntryType::Tick(hash) => { // If it's a tick, save it for later @@ -563,6 +526,7 @@ fn process_entries_with_callback( if bank.is_block_boundary(bank.tick_height() + tick_hashes.len() as u64) { // If it's a tick that will cause a new blockhash to be created, // execute the group and register the tick + // info!("chk1 batches before: {:?}",batches.len()); execute_batches( bank, &batches, @@ -570,10 +534,10 @@ fn process_entries_with_callback( transaction_status_sender, replay_vote_sender, confirmation_timing, - cost_capacity_meter.clone(), &cost_model, log_messages_bytes_limit, )?; + // info!("chk1 batches after: {:?}",batches.len()); batches.clear(); for hash in &tick_hashes { bank.register_tick(hash); @@ -606,6 +570,9 @@ fn process_entries_with_callback( batch, transaction_indexes, }); + // entry is scheduled to be processed, transactions in it can be used to + // update prioritization fee cache asynchronously. + prioritization_fee_cache.update(bank.clone(), transactions.iter()); // done with this entry break; } @@ -636,7 +603,6 @@ fn process_entries_with_callback( transaction_status_sender, replay_vote_sender, confirmation_timing, - cost_capacity_meter.clone(), &cost_model, log_messages_bytes_limit, )?; @@ -653,7 +619,6 @@ fn process_entries_with_callback( transaction_status_sender, replay_vote_sender, confirmation_timing, - cost_capacity_meter, &cost_model, log_messages_bytes_limit, )?; @@ -709,6 +674,9 @@ pub struct ProcessOptions { pub shrink_ratio: AccountShrinkThreshold, pub runtime_config: RuntimeConfig, pub on_halt_store_hash_raw_data_for_debug: bool, + /// true if after processing the contents of the blockstore at startup, we should run an accounts hash calc + /// This is useful for debugging. + pub run_final_accounts_hash_calc: bool, } pub fn test_process_blockstore( @@ -809,20 +777,19 @@ pub fn process_blockstore_from_root( ); } - if let Ok(metas) = blockstore.slot_meta_iterator(start_slot) { - if let Some((slot, _meta)) = metas.last() { - info!("ledger holds data through slot {}", slot); - } + if let Ok(Some(highest_slot)) = blockstore.highest_slot() { + info!("ledger holds data through slot {}", highest_slot); } let mut timing = ExecuteTimings::default(); // Iterate and replay slots from blockstore starting from `start_slot` + let mut num_slots_processed = 0; if let Some(start_slot_meta) = blockstore .meta(start_slot) .unwrap_or_else(|_| panic!("Failed to get meta for slot {}", start_slot)) { - load_frozen_forks( + num_slots_processed = load_frozen_forks( bank_forks, start_slot, &start_slot_meta, @@ -848,25 +815,6 @@ pub fn process_blockstore_from_root( let processing_time = now.elapsed(); - let debug_verify = opts.accounts_db_test_hash_calculation; - let mut time_cap = Measure::start("capitalization"); - // We might be promptly restarted after bad capitalization was detected while creating newer snapshot. - // In that case, we're most likely restored from the last good snapshot and replayed up to this root. - // So again check here for the bad capitalization to avoid to continue until the next snapshot creation. - if !bank_forks - .read() - .unwrap() - .root_bank() - .calculate_and_verify_capitalization(debug_verify) - { - return Err( - BlockstoreProcessorError::RootBankWithMismatchedCapitalization( - bank_forks.read().unwrap().root(), - ), - ); - } - time_cap.stop(); - datapoint_info!( "process_blockstore_from_root", ("total_time_us", processing_time.as_micros(), i64), @@ -876,8 +824,8 @@ pub fn process_blockstore_from_root( i64 ), ("slot", bank_forks.read().unwrap().root(), i64), + ("num_slots_processed", num_slots_processed, i64), ("forks", bank_forks.read().unwrap().banks().len(), i64), - ("calculate_capitalization_us", time_cap.as_us(), i64), ); info!("ledger processing timing: {:?}", timing); @@ -958,6 +906,8 @@ fn confirm_full_slot( ) -> result::Result<(), BlockstoreProcessorError> { let mut confirmation_timing = ConfirmationTiming::default(); let skip_verification = !opts.poh_verify; + let _ignored_prioritization_fee_cache = PrioritizationFeeCache::new(0u64); + info!("chk1 confirm_slot"); confirm_slot( blockstore, bank, @@ -970,6 +920,7 @@ fn confirm_full_slot( recyclers, opts.allow_dead_slots, opts.runtime_config.log_messages_bytes_limit, + &_ignored_prioritization_fee_cache, )?; timing.accumulate(&confirmation_timing.execute_timings); @@ -1096,6 +1047,7 @@ pub fn confirm_slot( recyclers: &VerifyRecyclers, allow_dead_slots: bool, log_messages_bytes_limit: Option, + prioritization_fee_cache: &PrioritizationFeeCache, ) -> result::Result<(), BlockstoreProcessorError> { let slot = bank.slot(); @@ -1112,7 +1064,7 @@ pub fn confirm_slot( } load_result }?; - + info!("chk1 confirm_slot_entries"); confirm_slot_entries( bank, slot_entries_load_result, @@ -1124,6 +1076,7 @@ pub fn confirm_slot( entry_callback, recyclers, log_messages_bytes_limit, + prioritization_fee_cache, ) } @@ -1139,6 +1092,7 @@ fn confirm_slot_entries( entry_callback: Option<&ProcessCallback>, recyclers: &VerifyRecyclers, log_messages_bytes_limit: Option, + prioritization_fee_cache: &PrioritizationFeeCache, ) -> result::Result<(), BlockstoreProcessorError> { let slot = bank.slot(); let (entries, num_shreds, slot_full) = slot_entries_load_result; @@ -1219,7 +1173,6 @@ fn confirm_slot_entries( assert!(entries.is_some()); let mut replay_elapsed = Measure::start("replay_elapsed"); - let cost_capacity_meter = Arc::new(RwLock::new(BlockCostCapacityMeter::default())); let mut replay_entries: Vec<_> = entries .unwrap() .into_iter() @@ -1230,6 +1183,7 @@ fn confirm_slot_entries( }) .collect(); // Note: This will shuffle entries' transactions in-place. + info!("chk1 process_entries_with_callback"); let process_result = process_entries_with_callback( bank, &mut replay_entries, @@ -1238,8 +1192,8 @@ fn confirm_slot_entries( transaction_status_sender, replay_vote_sender, timing, - cost_capacity_meter, log_messages_bytes_limit, + prioritization_fee_cache, ) .map_err(BlockstoreProcessorError::from); replay_elapsed.stop(); @@ -1322,6 +1276,7 @@ fn process_next_slots( blockstore: &Blockstore, leader_schedule_cache: &LeaderScheduleCache, pending_slots: &mut Vec<(SlotMeta, Bank, Hash)>, + halt_at_slot: Option, ) -> result::Result<(), BlockstoreProcessorError> { if meta.next_slots.is_empty() { return Ok(()); @@ -1329,6 +1284,13 @@ fn process_next_slots( // This is a fork point if there are multiple children, create a new child bank for each fork for next_slot in &meta.next_slots { + let skip_next_slot = halt_at_slot + .map(|halt_at_slot| *next_slot > halt_at_slot) + .unwrap_or(false); + if skip_next_slot { + continue; + } + let next_meta = blockstore .meta(*next_slot) .map_err(|err| { @@ -1375,11 +1337,14 @@ fn load_frozen_forks( cache_block_meta_sender: Option<&CacheBlockMetaSender>, timing: &mut ExecuteTimings, accounts_background_request_sender: &AbsRequestSender, -) -> result::Result<(), BlockstoreProcessorError> { +) -> result::Result { let recyclers = VerifyRecyclers::default(); let mut all_banks = HashMap::new(); let mut last_status_report = Instant::now(); let mut pending_slots = vec![]; + // The total number of slots processed + let mut total_slots_elapsed = 0; + // The number of slots processed between status report updates let mut slots_elapsed = 0; let mut txs = 0; let blockstore_max_root = blockstore.max_root(); @@ -1397,11 +1362,11 @@ fn load_frozen_forks( blockstore, leader_schedule_cache, &mut pending_slots, + opts.halt_at_slot, )?; - let halt_at_slot = opts.halt_at_slot.unwrap_or(std::u64::MAX); let on_halt_store_hash_raw_data_for_debug = opts.on_halt_store_hash_raw_data_for_debug; - if bank_forks.read().unwrap().root() != halt_at_slot { + if Some(bank_forks.read().unwrap().root()) != opts.halt_at_slot { while !pending_slots.is_empty() { timing.details.per_program_timings.clear(); let (meta, bank, last_entry_hash) = pending_slots.pop().unwrap(); @@ -1424,6 +1389,7 @@ fn load_frozen_forks( let mut progress = ConfirmationProgress::new(last_entry_hash); let bank = bank_forks.write().unwrap().insert(bank); + info!("chk1 process single slot"); if process_single_slot( blockstore, &bank, @@ -1517,6 +1483,7 @@ fn load_frozen_forks( } slots_elapsed += 1; + total_slots_elapsed += 1; trace!( "Bank for {}slot {} is complete", @@ -1524,18 +1491,25 @@ fn load_frozen_forks( slot, ); + let done_processing = opts + .halt_at_slot + .map(|halt_at_slot| slot >= halt_at_slot) + .unwrap_or(false); + if done_processing { + if opts.run_final_accounts_hash_calc { + run_final_hash_calc(&bank, on_halt_store_hash_raw_data_for_debug); + } + break; + } + process_next_slots( &bank, &meta, blockstore, leader_schedule_cache, &mut pending_slots, + opts.halt_at_slot, )?; - - if slot >= halt_at_slot { - run_final_hash_calc(&bank, on_halt_store_hash_raw_data_for_debug); - break; - } } } else if on_halt_store_hash_raw_data_for_debug { run_final_hash_calc( @@ -1544,7 +1518,7 @@ fn load_frozen_forks( ); } - Ok(()) + Ok(total_slots_elapsed) } fn run_final_hash_calc(bank: &Bank, on_halt_store_hash_raw_data_for_debug: bool) { @@ -1629,6 +1603,7 @@ fn process_single_slot( ) -> result::Result<(), BlockstoreProcessorError> { // Mark corrupt slots as dead so validators don't replay this slot and // see AlreadyProcessed errors later in ReplayStage + info!("chk1 confirm full slot"); confirm_full_slot( blockstore, bank, @@ -3740,6 +3715,7 @@ pub mod tests { let ( TransactionResults { fee_collection_results, + receipts, .. }, _balances, @@ -3753,7 +3729,7 @@ pub mod tests { &mut ExecuteTimings::default(), None, ); - let (err, signature) = get_first_error(&batch, fee_collection_results).unwrap(); + let (err, signature) = get_first_error(&batch, fee_collection_results, receipts).unwrap(); assert_eq!(err.unwrap_err(), TransactionError::AccountNotFound); assert_eq!(signature, account_not_found_sig); } @@ -3786,13 +3762,15 @@ pub mod tests { // Create an transaction that references the new blockhash, should still // be able to find the blockhash if we process transactions all in the same // batch - let mut expected_signatures = BTreeSet::new(); + let mut expected_successful_voter_pubkeys = BTreeSet::new(); let vote_txs: Vec<_> = validator_keypairs .iter() .enumerate() .map(|(i, validator_keypairs)| { - let vote_tx = if i % 3 == 0 { + if i % 3 == 0 { // These votes are correct + expected_successful_voter_pubkeys + .insert(validator_keypairs.vote_keypair.pubkey()); vote_transaction::new_vote_transaction( vec![0], bank0.hash(), @@ -3824,20 +3802,18 @@ pub mod tests { &validator_keypairs.vote_keypair, None, ) - }; - expected_signatures.insert(vote_tx.signatures[0]); - vote_tx + } }) .collect(); let entry = next_entry(&bank_1_blockhash, 1, vote_txs); let (replay_vote_sender, replay_vote_receiver) = crossbeam_channel::unbounded(); let _ = process_entries_for_tests(&bank1, vec![entry], true, None, Some(&replay_vote_sender)); - let signatures: BTreeSet<_> = replay_vote_receiver + let successes: BTreeSet = replay_vote_receiver .try_iter() - .map(|(.., signature)| signature) + .map(|(vote_pubkey, ..)| vote_pubkey) .collect(); - assert_eq!(signatures, expected_signatures); + assert_eq!(successes, expected_successful_voter_pubkeys); } fn make_slot_with_vote_tx( @@ -4130,6 +4106,7 @@ pub mod tests { None, &VerifyRecyclers::default(), None, + &PrioritizationFeeCache::new(0u64), ) } @@ -4273,6 +4250,7 @@ pub mod tests { None, &VerifyRecyclers::default(), None, + &PrioritizationFeeCache::new(0u64), ) .unwrap(); assert_eq!(progress.num_txs, 2); @@ -4318,6 +4296,7 @@ pub mod tests { None, &VerifyRecyclers::default(), None, + &PrioritizationFeeCache::new(0u64), ) .unwrap(); assert_eq!(progress.num_txs, 5); diff --git a/ledger/src/leader_schedule_cache.rs b/ledger/src/leader_schedule_cache.rs index 465ade38d3af11..a8200c8bade297 100644 --- a/ledger/src/leader_schedule_cache.rs +++ b/ledger/src/leader_schedule_cache.rs @@ -451,7 +451,7 @@ mod tests { // Write a shred into slot 2 that chains to slot 1, // but slot 1 is empty so should not be skipped - let (shreds, _) = make_slot_entries(2, 1, 1); + let (shreds, _) = make_slot_entries(2, 1, 1, /*merkle_variant:*/ true); blockstore.insert_shreds(shreds, None, false).unwrap(); assert_eq!( cache @@ -462,7 +462,7 @@ mod tests { ); // Write a shred into slot 1 - let (shreds, _) = make_slot_entries(1, 0, 1); + let (shreds, _) = make_slot_entries(1, 0, 1, /*merkle_variant:*/ true); // Check that slot 1 and 2 are skipped blockstore.insert_shreds(shreds, None, false).unwrap(); diff --git a/ledger/src/lib.rs b/ledger/src/lib.rs index e65a74cff257d8..f0aa3598dec7b1 100644 --- a/ledger/src/lib.rs +++ b/ledger/src/lib.rs @@ -28,6 +28,7 @@ mod shredder; pub mod sigverify_shreds; pub mod slot_stats; mod staking_utils; +pub mod token_balances; #[macro_use] extern crate solana_metrics; diff --git a/ledger/src/shred.rs b/ledger/src/shred.rs index dda68ff093a309..cee0e72cfe11d2 100644 --- a/ledger/src/shred.rs +++ b/ledger/src/shred.rs @@ -49,16 +49,15 @@ //! So, given a) - c), we must restrict data shred's payload length such that the entire coding //! payload can fit into one coding shred / packet. -pub(crate) use shred_data::ShredData; -pub use { - self::stats::{ProcessShredsStats, ShredFetchStats}, - crate::shredder::Shredder, -}; +#[cfg(test)] +pub(crate) use self::shred_code::MAX_CODE_SHREDS_PER_SLOT; use { self::{shred_code::ShredCode, traits::Shred as _}, crate::blockstore::{self, MAX_DATA_SHREDS_PER_SLOT}, bitflags::bitflags, num_enum::{IntoPrimitive, TryFromPrimitive}, + rayon::ThreadPool, + reed_solomon_erasure::Error::TooFewShardsPresent, serde::{Deserialize, Serialize}, solana_entry::entry::{create_ticks, Entry}, solana_perf::packet::Packet, @@ -66,23 +65,31 @@ use { clock::Slot, hash::{hashv, Hash}, pubkey::Pubkey, - signature::{Keypair, Signature, Signer}, + signature::{Keypair, Signature, Signer, SIGNATURE_BYTES}, }, static_assertions::const_assert_eq, - std::fmt::Debug, + std::{fmt::Debug, time::Instant}, thiserror::Error, }; +pub use { + self::{ + shred_data::ShredData, + stats::{ProcessShredsStats, ShredFetchStats}, + }, + crate::shredder::{ReedSolomonCache, Shredder}, +}; mod common; mod legacy; mod merkle; -mod shred_code; +pub mod shred_code; mod shred_data; mod stats; mod traits; pub type Nonce = u32; -pub const SIZE_OF_NONCE: usize = 4; +const_assert_eq!(SIZE_OF_NONCE, 4); +pub const SIZE_OF_NONCE: usize = std::mem::size_of::(); /// The following constants are computed by hand, and hardcoded. /// `test_shred_constants` ensures that the values are correct. @@ -90,16 +97,19 @@ pub const SIZE_OF_NONCE: usize = 4; const SIZE_OF_COMMON_SHRED_HEADER: usize = 83; const SIZE_OF_DATA_SHRED_HEADERS: usize = 88; const SIZE_OF_CODING_SHRED_HEADERS: usize = 89; -const SIZE_OF_SIGNATURE: usize = 64; +const SIZE_OF_SIGNATURE: usize = SIGNATURE_BYTES; const SIZE_OF_SHRED_VARIANT: usize = 1; const SIZE_OF_SHRED_SLOT: usize = 8; -const SIZE_OF_SHRED_INDEX: usize = 4; const OFFSET_OF_SHRED_VARIANT: usize = SIZE_OF_SIGNATURE; const OFFSET_OF_SHRED_SLOT: usize = SIZE_OF_SIGNATURE + SIZE_OF_SHRED_VARIANT; const OFFSET_OF_SHRED_INDEX: usize = OFFSET_OF_SHRED_SLOT + SIZE_OF_SHRED_SLOT; -pub const MAX_DATA_SHREDS_PER_FEC_BLOCK: u32 = 32; +// Shreds are uniformly split into erasure batches with a "target" number of +// data shreds per each batch as below. The actual number of data shreds in +// each erasure batch depends on the number of shreds obtained from serializing +// a &[Entry]. +pub const DATA_SHREDS_PER_FEC_BLOCK: usize = 32; // For legacy tests and benchmarks. const_assert_eq!(LEGACY_SHRED_DATA_CAPACITY, 1051); @@ -125,7 +135,7 @@ pub enum Error { #[error("Invalid data size: {size}, payload: {payload}")] InvalidDataSize { size: u16, payload: usize }, #[error("Invalid erasure shard index: {0:?}")] - InvalidErasureShardIndex(/*headers:*/ Box), + InvalidErasureShardIndex(/*headers:*/ Box), #[error("Invalid merkle proof")] InvalidMerkleProof, #[error("Invalid num coding shreds: {0}")] @@ -138,6 +148,10 @@ pub enum Error { InvalidPayloadSize(/*payload size:*/ usize), #[error("Invalid proof size: {0}")] InvalidProofSize(/*proof_size:*/ u8), + #[error("Invalid recovered shred")] + InvalidRecoveredShred, + #[error("Invalid shard size: {0}")] + InvalidShardSize(/*shard_size:*/ usize), #[error("Invalid shred flags: {0}")] InvalidShredFlags(u8), #[error("Invalid {0:?} shred index: {1}")] @@ -148,6 +162,8 @@ pub enum Error { InvalidShredVariant, #[error(transparent)] IoError(#[from] std::io::Error), + #[error("Unknown proof size")] + UnknownProofSize, } #[repr(u8)] @@ -176,7 +192,7 @@ pub enum ShredType { enum ShredVariant { LegacyCode, // 0b0101_1010 LegacyData, // 0b1010_0101 - // proof_size is the number of proof entries in the merkle tree branch. + // proof_size is the number of merkle proof entries. MerkleCode(/*proof_size:*/ u8), // 0b0100_???? MerkleData(/*proof_size:*/ u8), // 0b1000_???? } @@ -205,7 +221,7 @@ struct DataShredHeader { struct CodingShredHeader { num_data_shreds: u16, num_coding_shreds: u16, - position: u16, + position: u16, // [0..num_coding_shreds) } #[derive(Clone, Debug, PartialEq, Eq)] @@ -214,6 +230,21 @@ pub enum Shred { ShredData(ShredData), } +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum SignedData<'a> { + Chunk(&'a [u8]), // Chunk of payload past signature. + MerkleRoot(Hash), +} + +impl<'a> AsRef<[u8]> for SignedData<'a> { + fn as_ref(&self) -> &[u8] { + match self { + Self::Chunk(chunk) => chunk, + Self::MerkleRoot(root) => root.as_ref(), + } + } +} + /// Tuple which uniquely identifies a shred should it exists. #[derive(Clone, Copy, Eq, Debug, Hash, PartialEq)] pub struct ShredId(Slot, /*shred index:*/ u32, ShredType); @@ -227,7 +258,7 @@ impl ShredId { self.0 } - pub(crate) fn unwrap(&self) -> (Slot, /*shred index:*/ u32, ShredType) { + pub(crate) fn unpack(&self) -> (Slot, /*shred index:*/ u32, ShredType) { (self.0, self.1, self.2) } @@ -288,10 +319,12 @@ macro_rules! dispatch { } } +use dispatch; + impl Shred { dispatch!(fn common_header(&self) -> &ShredCommonHeader); dispatch!(fn set_signature(&mut self, signature: Signature)); - dispatch!(fn signed_message(&self) -> &[u8]); + dispatch!(fn signed_data(&self) -> Result); // Returns the portion of the shred's payload which is erasure coded. dispatch!(pub(crate) fn erasure_shard(self) -> Result, Error>); @@ -437,12 +470,13 @@ impl Shred { ErasureSetId(self.slot(), self.fec_set_index()) } - pub fn signature(&self) -> Signature { - self.common_header().signature + pub fn signature(&self) -> &Signature { + &self.common_header().signature } pub fn sign(&mut self, keypair: &Keypair) { - let signature = keypair.sign_message(self.signed_message()); + let data = self.signed_data().unwrap(); + let signature = keypair.sign_message(data.as_ref()); self.set_signature(signature); } @@ -488,9 +522,12 @@ impl Shred { } } + #[must_use] pub fn verify(&self, pubkey: &Pubkey) -> bool { - let message = self.signed_message(); - self.signature().verify(pubkey.as_ref(), message) + match self.signed_data() { + Ok(data) => self.signature().verify(pubkey.as_ref(), data.as_ref()), + Err(_) => false, + } } // Returns true if the erasure coding of the two shreds mismatch. @@ -520,6 +557,11 @@ impl Shred { // without deserializing the entire payload. pub mod layout { use {super::*, std::ops::Range}; + #[cfg(test)] + use { + rand::{seq::SliceRandom, Rng}, + std::collections::HashMap, + }; fn get_shred_size(packet: &Packet) -> Option { let size = packet.data(..)?.len(); @@ -572,17 +614,15 @@ pub mod layout { } pub fn get_version(shred: &[u8]) -> Option { - const OFFSET_OF_SHRED_VERSION: usize = OFFSET_OF_SHRED_INDEX + SIZE_OF_SHRED_INDEX; - <[u8; 2]>::try_from(shred.get(OFFSET_OF_SHRED_VERSION..)?.get(..2)?) + <[u8; 2]>::try_from(shred.get(77..79)?) .map(u16::from_le_bytes) .ok() } // The caller should verify first that the shred is data and not code! pub(super) fn get_parent_offset(shred: &[u8]) -> Option { - const OFFSET_OF_SHRED_PARENT: usize = SIZE_OF_COMMON_SHRED_HEADER; debug_assert_eq!(get_shred_type(shred).unwrap(), ShredType::Data); - <[u8; 2]>::try_from(shred.get(OFFSET_OF_SHRED_PARENT..)?.get(..2)?) + <[u8; 2]>::try_from(shred.get(83..85)?) .map(u16::from_le_bytes) .ok() } @@ -596,32 +636,118 @@ pub mod layout { )) } - // Returns slice range of the shred payload which is signed. - pub(crate) fn get_signed_message_range(shred: &[u8]) -> Option> { - let range = match get_shred_variant(shred).ok()? { - ShredVariant::LegacyCode | ShredVariant::LegacyData => legacy::SIGNED_MESSAGE_RANGE, + pub(crate) fn get_signed_data(shred: &[u8]) -> Option { + let data = match get_shred_variant(shred).ok()? { + ShredVariant::LegacyCode | ShredVariant::LegacyData => { + let chunk = shred.get(self::legacy::SIGNED_MESSAGE_OFFSETS)?; + SignedData::Chunk(chunk) + } ShredVariant::MerkleCode(proof_size) => { - merkle::ShredCode::get_signed_message_range(proof_size)? + let merkle_root = self::merkle::ShredCode::get_merkle_root(shred, proof_size)?; + SignedData::MerkleRoot(merkle_root) } ShredVariant::MerkleData(proof_size) => { - merkle::ShredData::get_signed_message_range(proof_size)? + let merkle_root = self::merkle::ShredData::get_merkle_root(shred, proof_size)?; + SignedData::MerkleRoot(merkle_root) } }; - (shred.len() <= range.end).then(|| range) + Some(data) + } + + // Returns offsets within the shred payload which is signed. + pub(crate) fn get_signed_data_offsets(shred: &[u8]) -> Option> { + match get_shred_variant(shred).ok()? { + ShredVariant::LegacyCode | ShredVariant::LegacyData => { + let offsets = self::legacy::SIGNED_MESSAGE_OFFSETS; + (offsets.end <= shred.len()).then(|| offsets) + } + // Merkle shreds sign merkle tree root which can be recovered from + // the merkle proof embedded in the payload but itself is not + // stored the payload. + ShredVariant::MerkleCode(_) => None, + ShredVariant::MerkleData(_) => None, + } } pub(crate) fn get_reference_tick(shred: &[u8]) -> Result { - const SIZE_OF_PARENT_OFFSET: usize = std::mem::size_of::(); - const OFFSET_OF_SHRED_FLAGS: usize = SIZE_OF_COMMON_SHRED_HEADER + SIZE_OF_PARENT_OFFSET; if get_shred_type(shred)? != ShredType::Data { return Err(Error::InvalidShredType); } - let flags = match shred.get(OFFSET_OF_SHRED_FLAGS) { + let flags = match shred.get(85) { None => return Err(Error::InvalidPayloadSize(shred.len())), Some(flags) => flags, }; Ok(flags & ShredFlags::SHRED_TICK_REFERENCE_MASK.bits()) } + + pub(crate) fn get_merkle_root(shred: &[u8]) -> Option { + match get_shred_variant(shred).ok()? { + ShredVariant::LegacyCode | ShredVariant::LegacyData => None, + ShredVariant::MerkleCode(proof_size) => { + merkle::ShredCode::get_merkle_root(shred, proof_size) + } + ShredVariant::MerkleData(proof_size) => { + merkle::ShredData::get_merkle_root(shred, proof_size) + } + } + } + + // Minimally corrupts the packet so that the signature no longer verifies. + #[cfg(test)] + pub(crate) fn corrupt_packet( + rng: &mut R, + packet: &mut Packet, + keypairs: &HashMap, + ) { + fn modify_packet(rng: &mut R, packet: &mut Packet, offsets: Range) { + let buffer = packet.buffer_mut(); + let byte = buffer[offsets].choose_mut(rng).unwrap(); + *byte = rng.gen::().max(1u8).wrapping_add(*byte); + } + let shred = get_shred(packet).unwrap(); + let merkle_proof_size = match get_shred_variant(shred).unwrap() { + ShredVariant::LegacyCode | ShredVariant::LegacyData => None, + ShredVariant::MerkleCode(proof_size) | ShredVariant::MerkleData(proof_size) => { + Some(proof_size) + } + }; + let coin_flip: bool = rng.gen(); + if coin_flip { + // Corrupt one byte within the signature offsets. + modify_packet(rng, packet, 0..SIGNATURE_BYTES); + } else { + // Corrupt one byte within the signed data offsets. + let offsets = merkle_proof_size + .map(|merkle_proof_size| { + // Need to corrupt the merkle proof. + // Proof entries are each 20 bytes at the end of shreds. + let offset = usize::from(merkle_proof_size) * 20; + shred.len() - offset..shred.len() + }) + .or_else(|| get_signed_data_offsets(shred)); + modify_packet(rng, packet, offsets.unwrap()); + } + // Assert that the signature no longer verifies. + let shred = get_shred(packet).unwrap(); + let slot = get_slot(shred).unwrap(); + let signature = get_signature(shred).unwrap(); + if coin_flip { + let pubkey = keypairs[&slot].pubkey(); + let data = get_signed_data(shred).unwrap(); + assert!(!signature.verify(pubkey.as_ref(), data.as_ref())); + if let Some(offsets) = get_signed_data_offsets(shred) { + assert!(!signature.verify(pubkey.as_ref(), &shred[offsets])); + } + } else { + // Slot may have been corrupted and no longer mapping to a keypair. + let pubkey = keypairs.get(&slot).map(Keypair::pubkey).unwrap_or_default(); + if let Some(data) = get_signed_data(shred) { + assert!(!signature.verify(pubkey.as_ref(), data.as_ref())); + } + let offsets = get_signed_data_offsets(shred).unwrap_or_default(); + assert!(!signature.verify(pubkey.as_ref(), &shred[offsets])); + } + } } impl From for Shred { @@ -636,6 +762,28 @@ impl From for Shred { } } +impl From for Shred { + fn from(shred: merkle::Shred) -> Self { + match shred { + merkle::Shred::ShredCode(shred) => Self::ShredCode(ShredCode::Merkle(shred)), + merkle::Shred::ShredData(shred) => Self::ShredData(ShredData::Merkle(shred)), + } + } +} + +impl TryFrom for merkle::Shred { + type Error = Error; + + fn try_from(shred: Shred) -> Result { + match shred { + Shred::ShredCode(ShredCode::Legacy(_)) => Err(Error::InvalidShredVariant), + Shred::ShredCode(ShredCode::Merkle(shred)) => Ok(Self::ShredCode(shred)), + Shred::ShredData(ShredData::Legacy(_)) => Err(Error::InvalidShredVariant), + Shred::ShredData(ShredData::Merkle(shred)) => Ok(Self::ShredData(shred)), + } + } +} + impl From for ShredType { #[inline] fn from(shred_variant: ShredVariant) -> Self { @@ -676,6 +824,67 @@ impl TryFrom for ShredVariant { } } +pub(crate) fn recover( + shreds: Vec, + reed_solomon_cache: &ReedSolomonCache, +) -> Result, Error> { + match shreds + .first() + .ok_or(TooFewShardsPresent)? + .common_header() + .shred_variant + { + ShredVariant::LegacyData | ShredVariant::LegacyCode => { + Shredder::try_recovery(shreds, reed_solomon_cache) + } + ShredVariant::MerkleCode(_) | ShredVariant::MerkleData(_) => { + let shreds = shreds + .into_iter() + .map(merkle::Shred::try_from) + .collect::>()?; + Ok(merkle::recover(shreds, reed_solomon_cache)? + .into_iter() + .map(Shred::from) + .collect()) + } + } +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn make_merkle_shreds_from_entries( + thread_pool: &ThreadPool, + keypair: &Keypair, + entries: &[Entry], + slot: Slot, + parent_slot: Slot, + shred_version: u16, + reference_tick: u8, + is_last_in_slot: bool, + next_shred_index: u32, + next_code_index: u32, + reed_solomon_cache: &ReedSolomonCache, + stats: &mut ProcessShredsStats, +) -> Result, Error> { + let now = Instant::now(); + let entries = bincode::serialize(entries)?; + stats.serialize_elapsed += now.elapsed().as_micros() as u64; + let shreds = merkle::make_shreds_from_data( + thread_pool, + keypair, + &entries[..], + slot, + parent_slot, + shred_version, + reference_tick, + is_last_in_slot, + next_shred_index, + next_code_index, + reed_solomon_cache, + stats, + )?; + Ok(shreds.into_iter().flatten().map(Shred::from).collect()) +} + // Accepts shreds in the slot range [root + 1, max_slot]. #[must_use] pub fn should_discard_shred( @@ -683,6 +892,7 @@ pub fn should_discard_shred( root: Slot, max_slot: Slot, shred_version: u16, + should_drop_merkle_shreds: impl Fn(Slot) -> bool, stats: &mut ShredFetchStats, ) -> bool { debug_assert!(root < max_slot); @@ -705,8 +915,8 @@ pub fn should_discard_shred( } } } - let shred_type = match layout::get_shred_type(shred) { - Ok(shred_type) => shred_type, + let shred_variant = match layout::get_shred_variant(shred) { + Ok(shred_variant) => shred_variant, Err(_) => { stats.bad_shred_type += 1; return true; @@ -732,7 +942,7 @@ pub fn should_discard_shred( return true; } }; - match shred_type { + match ShredType::from(shred_variant) { ShredType::Code => { if index >= shred_code::MAX_CODE_SHREDS_PER_SLOT as u32 { stats.index_out_of_bounds += 1; @@ -768,6 +978,21 @@ pub fn should_discard_shred( } } } + match shred_variant { + ShredVariant::LegacyCode | ShredVariant::LegacyData => (), + ShredVariant::MerkleCode(_) => { + if should_drop_merkle_shreds(slot) { + return true; + } + stats.num_shreds_merkle_code = stats.num_shreds_merkle_code.saturating_add(1); + } + ShredVariant::MerkleData(_) => { + if should_drop_merkle_shreds(slot) { + return true; + } + stats.num_shreds_merkle_data = stats.num_shreds_merkle_data.saturating_add(1); + } + } false } @@ -781,7 +1006,9 @@ pub fn max_entries_per_n_shred( num_shreds: u64, shred_data_size: Option, ) -> u64 { - let data_buffer_size = ShredData::capacity(/*merkle_proof_size:*/ None).unwrap(); + // Default 32:32 erasure batches yields 64 shreds; log2(64) = 6. + let merkle_proof_size = Some(6); + let data_buffer_size = ShredData::capacity(merkle_proof_size).unwrap(); let shred_data_size = shred_data_size.unwrap_or(data_buffer_size) as u64; let vec_size = bincode::serialized_size(&vec![entry]).unwrap(); let entry_size = bincode::serialized_size(entry).unwrap(); @@ -829,6 +1056,8 @@ mod tests { solana_sdk::{shred_version, signature::Signer}, }; + const SIZE_OF_SHRED_INDEX: usize = 4; + fn bs58_decode>(data: T) -> Vec { bs58::decode(data).into_vec().unwrap() } @@ -960,6 +1189,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); assert_eq!(stats, ShredFetchStats::default()); @@ -970,6 +1200,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); assert_eq!(stats.index_overrun, 1); @@ -980,6 +1211,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); assert_eq!(stats.index_overrun, 2); @@ -990,6 +1222,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); assert_eq!(stats.index_overrun, 3); @@ -1000,6 +1233,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); assert_eq!(stats.index_overrun, 4); @@ -1010,6 +1244,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); assert_eq!(stats.bad_parent_offset, 1); @@ -1030,6 +1265,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); @@ -1049,6 +1285,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); assert_eq!(1, stats.index_out_of_bounds); @@ -1069,6 +1306,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); packet.buffer_mut()[OFFSET_OF_SHRED_VARIANT] = u8::MAX; @@ -1078,6 +1316,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); assert_eq!(1, stats.bad_shred_type); @@ -1089,6 +1328,7 @@ mod tests { root, max_slot, shred_version, + |_| false, // should_drop_merkle_shreds &mut stats )); assert_eq!(1, stats.bad_shred_type); @@ -1261,7 +1501,7 @@ mod tests { assert_eq!(layout::get_index(data), Some(shred.index())); assert_eq!(layout::get_version(data), Some(shred.version())); assert_eq!(layout::get_shred_id(data), Some(shred.id())); - assert_eq!(layout::get_signature(data), Some(shred.signature())); + assert_eq!(layout::get_signature(data), Some(*shred.signature())); assert_eq!(layout::get_shred_type(data).unwrap(), shred.shred_type()); match shred.shred_type() { ShredType::Code => { diff --git a/ledger/src/shred/common.rs b/ledger/src/shred/common.rs index 3478001a0cbff5..330e4a8da9ff6d 100644 --- a/ledger/src/shred/common.rs +++ b/ledger/src/shred/common.rs @@ -51,14 +51,28 @@ macro_rules! impl_shred_common { // Only for tests. fn set_index(&mut self, index: u32) { - self.common_header.index = index; - bincode::serialize_into(&mut self.payload[..], &self.common_header).unwrap(); + match self.common_header.shred_variant { + ShredVariant::LegacyCode | ShredVariant::LegacyData => { + self.common_header.index = index; + bincode::serialize_into(&mut self.payload[..], &self.common_header).unwrap(); + } + ShredVariant::MerkleCode(_) | ShredVariant::MerkleData(_) => { + panic!("Not Implemented!"); + } + } } // Only for tests. fn set_slot(&mut self, slot: Slot) { - self.common_header.slot = slot; - bincode::serialize_into(&mut self.payload[..], &self.common_header).unwrap(); + match self.common_header.shred_variant { + ShredVariant::LegacyCode | ShredVariant::LegacyData => { + self.common_header.slot = slot; + bincode::serialize_into(&mut self.payload[..], &self.common_header).unwrap(); + } + ShredVariant::MerkleCode(_) | ShredVariant::MerkleData(_) => { + panic!("Not Implemented!"); + } + } } }; } diff --git a/ledger/src/shred/legacy.rs b/ledger/src/shred/legacy.rs index 71c4203785417f..06022da8833bbf 100644 --- a/ledger/src/shred/legacy.rs +++ b/ledger/src/shred/legacy.rs @@ -15,20 +15,21 @@ use { // All payload including any zero paddings are signed. // Code and data shreds have the same payload size. -pub(super) const SIGNED_MESSAGE_RANGE: Range = SIZE_OF_SIGNATURE..ShredData::SIZE_OF_PAYLOAD; +pub(super) const SIGNED_MESSAGE_OFFSETS: Range = + SIZE_OF_SIGNATURE..ShredData::SIZE_OF_PAYLOAD; const_assert_eq!(ShredData::SIZE_OF_PAYLOAD, ShredCode::SIZE_OF_PAYLOAD); const_assert_eq!(ShredData::SIZE_OF_PAYLOAD, 1228); const_assert_eq!(ShredData::CAPACITY, 1051); -// SIZE_OF_CODING_SHRED_HEADERS bytes at the end of data shreds +// ShredCode::SIZE_OF_HEADERS bytes at the end of data shreds // is never used and is not part of erasure coding. const_assert_eq!(SIZE_OF_ERASURE_ENCODED_SLICE, 1139); pub(super) const SIZE_OF_ERASURE_ENCODED_SLICE: usize = - ShredCode::SIZE_OF_PAYLOAD - SIZE_OF_CODING_SHRED_HEADERS; + ShredCode::SIZE_OF_PAYLOAD - ShredCode::SIZE_OF_HEADERS; // Layout: {common, data} headers | data | zero padding -// Everything up to SIZE_OF_CODING_SHRED_HEADERS bytes at the end (which is -// part of zero padding) is erasure coded. +// Everything up to ShredCode::SIZE_OF_HEADERS bytes at the end (which is part +// of zero padding) is erasure coded. // All payload past signature, including the entirety of zero paddings, is // signed. #[derive(Clone, Debug, Eq, PartialEq)] @@ -47,11 +48,14 @@ pub struct ShredCode { payload: Vec, } -impl Shred for ShredData { +impl<'a> Shred<'a> for ShredData { + type SignedData = &'a [u8]; + impl_shred_common!(); // Legacy data shreds are always zero padded and // the same size as coding shreds. const SIZE_OF_PAYLOAD: usize = shred_code::ShredCode::SIZE_OF_PAYLOAD; + const SIZE_OF_HEADERS: usize = SIZE_OF_DATA_SHRED_HEADERS; fn from_payload(mut payload: Vec) -> Result { let mut cursor = Cursor::new(&payload[..]); @@ -64,7 +68,7 @@ impl Shred for ShredData { // Repair packets have nonce at the end of packet payload; see: // https://github.com/solana-labs/solana/pull/10109 // https://github.com/solana-labs/solana/pull/16602 - if payload.len() < SIZE_OF_DATA_SHRED_HEADERS { + if payload.len() < Self::SIZE_OF_HEADERS { return Err(Error::InvalidPayloadSize(payload.len())); } payload.resize(Self::SIZE_OF_PAYLOAD, 0u8); @@ -107,15 +111,18 @@ impl Shred for ShredData { shred_data::sanitize(self) } - fn signed_message(&self) -> &[u8] { + fn signed_data(&'a self) -> Result { debug_assert_eq!(self.payload.len(), Self::SIZE_OF_PAYLOAD); - &self.payload[SIZE_OF_SIGNATURE..] + Ok(&self.payload[SIZE_OF_SIGNATURE..]) } } -impl Shred for ShredCode { +impl<'a> Shred<'a> for ShredCode { + type SignedData = &'a [u8]; + impl_shred_common!(); const SIZE_OF_PAYLOAD: usize = shred_code::ShredCode::SIZE_OF_PAYLOAD; + const SIZE_OF_HEADERS: usize = SIZE_OF_CODING_SHRED_HEADERS; fn from_payload(mut payload: Vec) -> Result { let mut cursor = Cursor::new(&payload[..]); @@ -147,10 +154,9 @@ impl Shred for ShredCode { return Err(Error::InvalidPayloadSize(self.payload.len())); } let mut shard = self.payload; - // SIZE_OF_CODING_SHRED_HEADERS bytes at the beginning of the - // coding shreds contains the header and is not part of erasure - // coding. - shard.drain(..SIZE_OF_CODING_SHRED_HEADERS); + // ShredCode::SIZE_OF_HEADERS bytes at the beginning of the coding + // shreds contains the header and is not part of erasure coding. + shard.drain(..Self::SIZE_OF_HEADERS); Ok(shard) } @@ -158,7 +164,7 @@ impl Shred for ShredCode { if self.payload.len() != Self::SIZE_OF_PAYLOAD { return Err(Error::InvalidPayloadSize(self.payload.len())); } - Ok(&self.payload[SIZE_OF_CODING_SHRED_HEADERS..]) + Ok(&self.payload[Self::SIZE_OF_HEADERS..]) } fn sanitize(&self) -> Result<(), Error> { @@ -169,9 +175,9 @@ impl Shred for ShredCode { shred_code::sanitize(self) } - fn signed_message(&self) -> &[u8] { + fn signed_data(&'a self) -> Result { debug_assert_eq!(self.payload.len(), Self::SIZE_OF_PAYLOAD); - &self.payload[SIZE_OF_SIGNATURE..] + Ok(&self.payload[SIZE_OF_SIGNATURE..]) } } @@ -185,22 +191,15 @@ impl ShredDataTrait for ShredData { let size = usize::from(self.data_header.size); #[allow(clippy::manual_range_contains)] if size > self.payload.len() - || size < SIZE_OF_DATA_SHRED_HEADERS - || size > SIZE_OF_DATA_SHRED_HEADERS + Self::CAPACITY + || size < Self::SIZE_OF_HEADERS + || size > Self::SIZE_OF_HEADERS + Self::CAPACITY { return Err(Error::InvalidDataSize { size: self.data_header.size, payload: self.payload.len(), }); } - Ok(&self.payload[SIZE_OF_DATA_SHRED_HEADERS..size]) - } - - // Only for tests. - fn set_last_in_slot(&mut self) { - self.data_header.flags |= ShredFlags::LAST_SHRED_IN_SLOT; - let buffer = &mut self.payload[SIZE_OF_COMMON_SHRED_HEADER..]; - bincode::serialize_into(buffer, &self.data_header).unwrap(); + Ok(&self.payload[Self::SIZE_OF_HEADERS..size]) } } @@ -214,7 +213,7 @@ impl ShredCodeTrait for ShredCode { impl ShredData { // Maximum size of ledger data that can be embedded in a data-shred. pub(super) const CAPACITY: usize = - Self::SIZE_OF_PAYLOAD - SIZE_OF_DATA_SHRED_HEADERS - SIZE_OF_CODING_SHRED_HEADERS; + Self::SIZE_OF_PAYLOAD - Self::SIZE_OF_HEADERS - ShredCode::SIZE_OF_HEADERS; pub(super) fn new_from_data( slot: Slot, @@ -235,7 +234,7 @@ impl ShredData { version, fec_set_index, }; - let size = (data.len() + SIZE_OF_DATA_SHRED_HEADERS) as u16; + let size = (data.len() + Self::SIZE_OF_HEADERS) as u16; let flags = flags | unsafe { ShredFlags::from_bits_unchecked( @@ -254,7 +253,7 @@ impl ShredData { bincode::serialize_into(&mut cursor, &data_header).unwrap(); // TODO: Need to check if data is too large! let offset = cursor.position() as usize; - debug_assert_eq!(offset, SIZE_OF_DATA_SHRED_HEADERS); + debug_assert_eq!(offset, Self::SIZE_OF_HEADERS); payload[offset..offset + data.len()].copy_from_slice(data); Self { common_header, @@ -271,12 +270,19 @@ impl ShredData { pub(super) fn resize_stored_shred(mut shred: Vec) -> Result, Error> { // Old shreds might have been extra zero padded. - if !(SIZE_OF_DATA_SHRED_HEADERS..=ShredCode::SIZE_OF_PAYLOAD).contains(&shred.len()) { + if !(Self::SIZE_OF_HEADERS..=Self::SIZE_OF_PAYLOAD).contains(&shred.len()) { return Err(Error::InvalidPayloadSize(shred.len())); } shred.resize(Self::SIZE_OF_PAYLOAD, 0u8); Ok(shred) } + + // Only for tests. + pub(crate) fn set_last_in_slot(&mut self) { + self.data_header.flags |= ShredFlags::LAST_SHRED_IN_SLOT; + let buffer = &mut self.payload[SIZE_OF_COMMON_SHRED_HEADER..]; + bincode::serialize_into(buffer, &self.data_header).unwrap(); + } } impl ShredCode { @@ -310,7 +316,7 @@ impl ShredCode { // Tests may have an empty parity_shard. if !parity_shard.is_empty() { let offset = cursor.position() as usize; - debug_assert_eq!(offset, SIZE_OF_CODING_SHRED_HEADERS); + debug_assert_eq!(offset, Self::SIZE_OF_HEADERS); payload[offset..].copy_from_slice(parity_shard); } Self { @@ -325,7 +331,7 @@ impl ShredCode { mod test { use { super::*, - crate::shred::{ShredType, MAX_DATA_SHREDS_PER_SLOT}, + crate::shred::{shred_code::MAX_CODE_SHREDS_PER_SLOT, ShredType, MAX_DATA_SHREDS_PER_SLOT}, matches::assert_matches, }; @@ -433,10 +439,10 @@ mod test { } { let mut shred = shred.clone(); - shred.common_header.index = MAX_DATA_SHREDS_PER_SLOT as u32; + shred.common_header.index = MAX_CODE_SHREDS_PER_SLOT as u32; assert_matches!( shred.sanitize(), - Err(Error::InvalidShredIndex(ShredType::Code, 32768)) + Err(Error::InvalidShredIndex(ShredType::Code, 32_768)) ); } // pos >= num_coding is invalid. @@ -454,7 +460,7 @@ mod test { { let mut shred = shred.clone(); shred.common_header.fec_set_index = MAX_DATA_SHREDS_PER_SLOT as u32 - 2; - shred.coding_header.num_data_shreds = 2; + shred.coding_header.num_data_shreds = 3; shred.coding_header.num_coding_shreds = 4; shred.coding_header.position = 1; shred.common_header.index = MAX_DATA_SHREDS_PER_SLOT as u32 - 2; @@ -463,6 +469,7 @@ mod test { Err(Error::InvalidErasureShardIndex { .. }) ); + shred.coding_header.num_data_shreds = 2; shred.coding_header.num_coding_shreds = 2000; assert_matches!(shred.sanitize(), Err(Error::InvalidNumCodingShreds(2000))); diff --git a/ledger/src/shred/merkle.rs b/ledger/src/shred/merkle.rs index 7eb3bf2d69fe16..f174aece65cbac 100644 --- a/ledger/src/shred/merkle.rs +++ b/ledger/src/shred/merkle.rs @@ -1,71 +1,134 @@ +#[cfg(test)] +use crate::shred::ShredType; use { - crate::shred::{ - common::impl_shred_common, - shred_code, shred_data, - traits::{Shred, ShredCode as ShredCodeTrait, ShredData as ShredDataTrait}, - CodingShredHeader, DataShredHeader, Error, ShredCommonHeader, ShredFlags, ShredVariant, - SIZE_OF_CODING_SHRED_HEADERS, SIZE_OF_COMMON_SHRED_HEADER, SIZE_OF_DATA_SHRED_HEADERS, - SIZE_OF_SIGNATURE, + crate::{ + shred::{ + self, + common::impl_shred_common, + dispatch, shred_code, shred_data, + traits::{ + Shred as ShredTrait, ShredCode as ShredCodeTrait, ShredData as ShredDataTrait, + }, + CodingShredHeader, DataShredHeader, Error, ProcessShredsStats, ShredCommonHeader, + ShredFlags, ShredVariant, DATA_SHREDS_PER_FEC_BLOCK, SIZE_OF_CODING_SHRED_HEADERS, + SIZE_OF_DATA_SHRED_HEADERS, SIZE_OF_SIGNATURE, + }, + shredder::{self, ReedSolomonCache}, }, + assert_matches::debug_assert_matches, + itertools::{Either, Itertools}, + rayon::{prelude::*, ThreadPool}, + reed_solomon_erasure::Error::{InvalidIndex, TooFewParityShards, TooFewShards}, solana_perf::packet::deserialize_from_with_limit, solana_sdk::{ clock::Slot, hash::{hashv, Hash}, - signature::Signature, + pubkey::Pubkey, + signature::{Signature, Signer}, + signer::keypair::Keypair, }, static_assertions::const_assert_eq, std::{ - io::{Cursor, Seek, SeekFrom}, - iter::repeat_with, + io::{Cursor, Write}, ops::Range, + time::Instant, }, }; -const_assert_eq!(SIZE_OF_MERKLE_ROOT, 20); -const SIZE_OF_MERKLE_ROOT: usize = std::mem::size_of::(); const_assert_eq!(SIZE_OF_MERKLE_PROOF_ENTRY, 20); const SIZE_OF_MERKLE_PROOF_ENTRY: usize = std::mem::size_of::(); const_assert_eq!(ShredData::SIZE_OF_PAYLOAD, 1203); // Defense against second preimage attack: // https://en.wikipedia.org/wiki/Merkle_tree#Second_preimage_attack -const MERKLE_HASH_PREFIX_LEAF: &[u8] = &[0x00]; -const MERKLE_HASH_PREFIX_NODE: &[u8] = &[0x01]; +// Following Certificate Transparency, 0x00 and 0x01 bytes are prepended to +// hash data when computing leaf and internal node hashes respectively. +const MERKLE_HASH_PREFIX_LEAF: &[u8] = b"\x00SOLANA_MERKLE_SHREDS_LEAF"; +const MERKLE_HASH_PREFIX_NODE: &[u8] = b"\x01SOLANA_MERKLE_SHREDS_NODE"; -type MerkleRoot = MerkleProofEntry; type MerkleProofEntry = [u8; 20]; -// Layout: {common, data} headers | data buffer | merkle branch -// The slice past signature and before merkle branch is erasure coded. +// Layout: {common, data} headers | data buffer | merkle proof +// The slice past signature and before the merkle proof is erasure coded. // Same slice is hashed to generate merkle tree. // The root of merkle tree is signed. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ShredData { common_header: ShredCommonHeader, data_header: DataShredHeader, - merkle_branch: MerkleBranch, payload: Vec, } -// Layout: {common, coding} headers | erasure coded shard | merkle branch -// The slice past signature and before merkle branch is hashed to generate +// Layout: {common, coding} headers | erasure coded shard | merkle proof +// The slice past signature and before the merkle proof is hashed to generate // merkle tree. The root of merkle tree is signed. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ShredCode { common_header: ShredCommonHeader, coding_header: CodingShredHeader, - merkle_branch: MerkleBranch, payload: Vec, } #[derive(Clone, Debug, Eq, PartialEq)] -struct MerkleBranch { - root: MerkleRoot, - proof: Vec, +pub(super) enum Shred { + ShredCode(ShredCode), + ShredData(ShredData), +} + +impl Shred { + dispatch!(fn common_header(&self) -> &ShredCommonHeader); + dispatch!(fn erasure_shard_as_slice(&self) -> Result<&[u8], Error>); + dispatch!(fn erasure_shard_index(&self) -> Result); + dispatch!(fn merkle_node(&self) -> Result); + dispatch!(fn payload(&self) -> &Vec); + dispatch!(fn sanitize(&self) -> Result<(), Error>); + dispatch!(fn set_merkle_proof(&mut self, proof: &[&MerkleProofEntry]) -> Result<(), Error>); + dispatch!(fn set_signature(&mut self, signature: Signature)); + dispatch!(fn signed_data(&self) -> Result); + + fn merkle_proof(&self) -> Result, Error> { + match self { + Self::ShredCode(shred) => shred.merkle_proof().map(Either::Left), + Self::ShredData(shred) => shred.merkle_proof().map(Either::Right), + } + } + + #[must_use] + fn verify(&self, pubkey: &Pubkey) -> bool { + match self.signed_data() { + Ok(data) => self.signature().verify(pubkey.as_ref(), data.as_ref()), + Err(_) => false, + } + } + + fn signature(&self) -> &Signature { + &self.common_header().signature + } + + fn from_payload(shred: Vec) -> Result { + match shred::layout::get_shred_variant(&shred)? { + ShredVariant::LegacyCode | ShredVariant::LegacyData => Err(Error::InvalidShredVariant), + ShredVariant::MerkleCode(_) => Ok(Self::ShredCode(ShredCode::from_payload(shred)?)), + ShredVariant::MerkleData(_) => Ok(Self::ShredData(ShredData::from_payload(shred)?)), + } + } +} + +#[cfg(test)] +impl Shred { + dispatch!(fn merkle_root(&self) -> Result); + + fn index(&self) -> u32 { + self.common_header().index + } + + fn shred_type(&self) -> ShredType { + ShredType::from(self.common_header().shred_variant) + } } impl ShredData { - // proof_size is the number of proof entries in the merkle tree branch. + // proof_size is the number of merkle proof entries. fn proof_size(&self) -> Result { match self.common_header.shred_variant { ShredVariant::MerkleData(proof_size) => Ok(proof_size), @@ -75,39 +138,112 @@ impl ShredData { // Maximum size of ledger data that can be embedded in a data-shred. // Also equal to: - // ShredCode::size_of_erasure_encoded_slice(proof_size).unwrap() - // - SIZE_OF_DATA_SHRED_HEADERS + // ShredCode::capacity(proof_size).unwrap() + // - ShredData::SIZE_OF_HEADERS // + SIZE_OF_SIGNATURE pub(super) fn capacity(proof_size: u8) -> Result { Self::SIZE_OF_PAYLOAD .checked_sub( - SIZE_OF_DATA_SHRED_HEADERS - + SIZE_OF_MERKLE_ROOT - + usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY, + Self::SIZE_OF_HEADERS + usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY, ) .ok_or(Error::InvalidProofSize(proof_size)) } - pub(super) fn get_signed_message_range(proof_size: u8) -> Option> { - let data_buffer_size = Self::capacity(proof_size).ok()?; - let offset = SIZE_OF_DATA_SHRED_HEADERS + data_buffer_size; - Some(offset..offset + SIZE_OF_MERKLE_ROOT) + // Where the merkle proof starts in the shred binary. + fn proof_offset(proof_size: u8) -> Result { + Ok(Self::SIZE_OF_HEADERS + Self::capacity(proof_size)?) } - fn merkle_tree_node(&self) -> Result { - let chunk = self.erasure_shard_as_slice()?; - Ok(hashv(&[MERKLE_HASH_PREFIX_LEAF, chunk])) + fn merkle_root(&self) -> Result { + let proof_size = self.proof_size()?; + let index = self.erasure_shard_index()?; + let proof_offset = Self::proof_offset(proof_size)?; + let proof = get_merkle_proof(&self.payload, proof_offset, proof_size)?; + let node = get_merkle_node(&self.payload, SIZE_OF_SIGNATURE..proof_offset)?; + get_merkle_root(index, node, proof) } - fn verify_merkle_proof(&self) -> Result { - let node = self.merkle_tree_node()?; - let index = self.erasure_shard_index()?; - Ok(verify_merkle_proof(index, node, &self.merkle_branch)) + fn merkle_proof(&self) -> Result, Error> { + let proof_size = self.proof_size()?; + let proof_offset = Self::proof_offset(proof_size)?; + get_merkle_proof(&self.payload, proof_offset, proof_size) + } + + fn merkle_node(&self) -> Result { + let proof_size = self.proof_size()?; + let proof_offset = Self::proof_offset(proof_size)?; + get_merkle_node(&self.payload, SIZE_OF_SIGNATURE..proof_offset) + } + + fn from_recovered_shard(signature: &Signature, mut shard: Vec) -> Result { + let shard_size = shard.len(); + if shard_size + SIZE_OF_SIGNATURE > Self::SIZE_OF_PAYLOAD { + return Err(Error::InvalidShardSize(shard_size)); + } + shard.resize(Self::SIZE_OF_PAYLOAD, 0u8); + shard.copy_within(0..shard_size, SIZE_OF_SIGNATURE); + shard[0..SIZE_OF_SIGNATURE].copy_from_slice(signature.as_ref()); + // Deserialize headers. + let mut cursor = Cursor::new(&shard[..]); + let common_header: ShredCommonHeader = deserialize_from_with_limit(&mut cursor)?; + let proof_size = match common_header.shred_variant { + ShredVariant::MerkleData(proof_size) => proof_size, + _ => return Err(Error::InvalidShredVariant), + }; + if ShredCode::capacity(proof_size)? != shard_size { + return Err(Error::InvalidShardSize(shard_size)); + } + let data_header = deserialize_from_with_limit(&mut cursor)?; + let shred = Self { + common_header, + data_header, + payload: shard, + }; + shred.sanitize()?; + Ok(shred) + } + + fn set_merkle_proof(&mut self, proof: &[&MerkleProofEntry]) -> Result<(), Error> { + let proof_size = self.proof_size()?; + if proof.len() != usize::from(proof_size) { + return Err(Error::InvalidMerkleProof); + } + let proof_offset = Self::proof_offset(proof_size)?; + let mut cursor = Cursor::new( + self.payload + .get_mut(proof_offset..) + .ok_or(Error::InvalidProofSize(proof_size))?, + ); + for entry in proof { + bincode::serialize_into(&mut cursor, entry)?; + } + Ok(()) + } + + pub(super) fn get_merkle_root(shred: &[u8], proof_size: u8) -> Option { + debug_assert_eq!( + shred::layout::get_shred_variant(shred).unwrap(), + ShredVariant::MerkleData(proof_size) + ); + // Shred index in the erasure batch. + let index = { + let fec_set_index = <[u8; 4]>::try_from(shred.get(79..83)?) + .map(u32::from_le_bytes) + .ok()?; + shred::layout::get_index(shred)? + .checked_sub(fec_set_index) + .map(usize::try_from)? + .ok()? + }; + let proof_offset = Self::proof_offset(proof_size).ok()?; + let proof = get_merkle_proof(shred, proof_offset, proof_size).ok()?; + let node = get_merkle_node(shred, SIZE_OF_SIGNATURE..proof_offset).ok()?; + get_merkle_root(index, node, proof).ok() } } impl ShredCode { - // proof_size is the number of proof entries in the merkle tree branch. + // proof_size is the number of merkle proof entries. fn proof_size(&self) -> Result { match self.common_header.shred_variant { ShredVariant::MerkleCode(proof_size) => Ok(proof_size), @@ -115,88 +251,146 @@ impl ShredCode { } } - // Size of the chunk of payload which will be erasure coded. - fn size_of_erasure_encoded_slice(proof_size: u8) -> Result { - // Merkle branch is generated and signed after coding shreds are + // Size of buffer embedding erasure codes. + fn capacity(proof_size: u8) -> Result { + // Merkle proof is generated and signed after coding shreds are // generated. Coding shred headers cannot be erasure coded either. Self::SIZE_OF_PAYLOAD .checked_sub( - SIZE_OF_CODING_SHRED_HEADERS - + SIZE_OF_MERKLE_ROOT - + SIZE_OF_MERKLE_PROOF_ENTRY * usize::from(proof_size), + Self::SIZE_OF_HEADERS + SIZE_OF_MERKLE_PROOF_ENTRY * usize::from(proof_size), ) .ok_or(Error::InvalidProofSize(proof_size)) } - fn merkle_tree_node(&self) -> Result { - let proof_size = self.proof_size()?; - let shard_size = Self::size_of_erasure_encoded_slice(proof_size)?; - let chunk = self - .payload - .get(SIZE_OF_SIGNATURE..SIZE_OF_CODING_SHRED_HEADERS + shard_size) - .ok_or(Error::InvalidPayloadSize(self.payload.len()))?; - Ok(hashv(&[MERKLE_HASH_PREFIX_LEAF, chunk])) + // Where the merkle proof starts in the shred binary. + fn proof_offset(proof_size: u8) -> Result { + Ok(Self::SIZE_OF_HEADERS + Self::capacity(proof_size)?) } - fn verify_merkle_proof(&self) -> Result { - let node = self.merkle_tree_node()?; + fn merkle_root(&self) -> Result { + let proof_size = self.proof_size()?; let index = self.erasure_shard_index()?; - Ok(verify_merkle_proof(index, node, &self.merkle_branch)) + let proof_offset = Self::proof_offset(proof_size)?; + let proof = get_merkle_proof(&self.payload, proof_offset, proof_size)?; + let node = get_merkle_node(&self.payload, SIZE_OF_SIGNATURE..proof_offset)?; + get_merkle_root(index, node, proof) + } + + fn merkle_proof(&self) -> Result, Error> { + let proof_size = self.proof_size()?; + let proof_offset = Self::proof_offset(proof_size)?; + get_merkle_proof(&self.payload, proof_offset, proof_size) } - pub(super) fn get_signed_message_range(proof_size: u8) -> Option> { - let offset = - SIZE_OF_CODING_SHRED_HEADERS + Self::size_of_erasure_encoded_slice(proof_size).ok()?; - Some(offset..offset + SIZE_OF_MERKLE_ROOT) + fn merkle_node(&self) -> Result { + let proof_size = self.proof_size()?; + let proof_offset = Self::proof_offset(proof_size)?; + get_merkle_node(&self.payload, SIZE_OF_SIGNATURE..proof_offset) } - pub(super) fn erasure_mismatch(&self, other: &ShredCode) -> bool { - shred_code::erasure_mismatch(self, other) - || self.merkle_branch.root != other.merkle_branch.root - || self.common_header.signature != other.common_header.signature + fn from_recovered_shard( + common_header: ShredCommonHeader, + coding_header: CodingShredHeader, + mut shard: Vec, + ) -> Result { + let proof_size = match common_header.shred_variant { + ShredVariant::MerkleCode(proof_size) => proof_size, + _ => return Err(Error::InvalidShredVariant), + }; + let shard_size = shard.len(); + if Self::capacity(proof_size)? != shard_size { + return Err(Error::InvalidShardSize(shard_size)); + } + if shard_size + Self::SIZE_OF_HEADERS > Self::SIZE_OF_PAYLOAD { + return Err(Error::InvalidShardSize(shard_size)); + } + shard.resize(Self::SIZE_OF_PAYLOAD, 0u8); + shard.copy_within(0..shard_size, Self::SIZE_OF_HEADERS); + let mut cursor = Cursor::new(&mut shard[..]); + bincode::serialize_into(&mut cursor, &common_header)?; + bincode::serialize_into(&mut cursor, &coding_header)?; + let shred = Self { + common_header, + coding_header, + payload: shard, + }; + shred.sanitize()?; + Ok(shred) + } + + fn set_merkle_proof(&mut self, proof: &[&MerkleProofEntry]) -> Result<(), Error> { + let proof_size = self.proof_size()?; + if proof.len() != usize::from(proof_size) { + return Err(Error::InvalidMerkleProof); + } + let proof_offset = Self::proof_offset(proof_size)?; + let mut cursor = Cursor::new( + self.payload + .get_mut(proof_offset..) + .ok_or(Error::InvalidProofSize(proof_size))?, + ); + for entry in proof { + bincode::serialize_into(&mut cursor, entry)?; + } + Ok(()) + } + + pub(super) fn get_merkle_root(shred: &[u8], proof_size: u8) -> Option { + debug_assert_eq!( + shred::layout::get_shred_variant(shred).unwrap(), + ShredVariant::MerkleCode(proof_size) + ); + // Shred index in the erasure batch. + let index = { + let num_data_shreds = <[u8; 2]>::try_from(shred.get(83..85)?) + .map(u16::from_le_bytes) + .map(usize::from) + .ok()?; + let position = <[u8; 2]>::try_from(shred.get(87..89)?) + .map(u16::from_le_bytes) + .map(usize::from) + .ok()?; + num_data_shreds.checked_add(position)? + }; + let proof_offset = Self::proof_offset(proof_size).ok()?; + let proof = get_merkle_proof(shred, proof_offset, proof_size).ok()?; + let node = get_merkle_node(shred, SIZE_OF_SIGNATURE..proof_offset).ok()?; + get_merkle_root(index, node, proof).ok() } } -impl Shred for ShredData { +impl<'a> ShredTrait<'a> for ShredData { + type SignedData = Hash; + impl_shred_common!(); // Also equal to: - // SIZE_OF_DATA_SHRED_HEADERS + // ShredData::SIZE_OF_HEADERS // + ShredData::capacity(proof_size).unwrap() - // + SIZE_OF_MERKLE_ROOT // + usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY const SIZE_OF_PAYLOAD: usize = - ShredCode::SIZE_OF_PAYLOAD - SIZE_OF_CODING_SHRED_HEADERS + SIZE_OF_SIGNATURE; + ShredCode::SIZE_OF_PAYLOAD - ShredCode::SIZE_OF_HEADERS + SIZE_OF_SIGNATURE; + const SIZE_OF_HEADERS: usize = SIZE_OF_DATA_SHRED_HEADERS; fn from_payload(mut payload: Vec) -> Result { + // see: https://github.com/solana-labs/solana/pull/10109 if payload.len() < Self::SIZE_OF_PAYLOAD { return Err(Error::InvalidPayloadSize(payload.len())); } payload.truncate(Self::SIZE_OF_PAYLOAD); let mut cursor = Cursor::new(&payload[..]); let common_header: ShredCommonHeader = deserialize_from_with_limit(&mut cursor)?; - let proof_size = match common_header.shred_variant { - ShredVariant::MerkleData(proof_size) => proof_size, - _ => return Err(Error::InvalidShredVariant), - }; + if !matches!(common_header.shred_variant, ShredVariant::MerkleData(_)) { + return Err(Error::InvalidShredVariant); + } let data_header = deserialize_from_with_limit(&mut cursor)?; - // Skip data buffer. - let data_buffer_size = Self::capacity(proof_size)?; - let data_buffer_size = i64::try_from(data_buffer_size).unwrap(); - cursor.seek(SeekFrom::Current(data_buffer_size))?; - // Deserialize merkle branch. - let root = deserialize_from_with_limit(&mut cursor)?; - let proof = repeat_with(|| deserialize_from_with_limit(&mut cursor)) - .take(usize::from(proof_size)) - .collect::>()?; - let merkle_branch = MerkleBranch { root, proof }; let shred = Self { common_header, data_header, - merkle_branch, payload, }; - shred.sanitize().map(|_| shred) + shred.sanitize()?; + Ok(shred) } fn erasure_shard_index(&self) -> Result { @@ -211,9 +405,9 @@ impl Shred for ShredData { return Err(Error::InvalidPayloadSize(self.payload.len())); } let proof_size = self.proof_size()?; - let data_buffer_size = Self::capacity(proof_size)?; + let proof_offset = Self::proof_offset(proof_size)?; let mut shard = self.payload; - shard.truncate(SIZE_OF_DATA_SHRED_HEADERS + data_buffer_size); + shard.truncate(proof_offset); shard.drain(0..SIZE_OF_SIGNATURE); Ok(shard) } @@ -223,63 +417,52 @@ impl Shred for ShredData { return Err(Error::InvalidPayloadSize(self.payload.len())); } let proof_size = self.proof_size()?; - let data_buffer_size = Self::capacity(proof_size)?; + let proof_offset = Self::proof_offset(proof_size)?; self.payload - .get(SIZE_OF_SIGNATURE..SIZE_OF_DATA_SHRED_HEADERS + data_buffer_size) + .get(SIZE_OF_SIGNATURE..proof_offset) .ok_or(Error::InvalidPayloadSize(self.payload.len())) } fn sanitize(&self) -> Result<(), Error> { - match self.common_header.shred_variant { - ShredVariant::MerkleData(proof_size) => { - if self.merkle_branch.proof.len() != usize::from(proof_size) { - return Err(Error::InvalidProofSize(proof_size)); - } - } - _ => return Err(Error::InvalidShredVariant), - } - if !self.verify_merkle_proof()? { - return Err(Error::InvalidMerkleProof); + let shred_variant = self.common_header.shred_variant; + if !matches!(shred_variant, ShredVariant::MerkleData(_)) { + return Err(Error::InvalidShredVariant); } + let _ = self.merkle_proof()?; shred_data::sanitize(self) } - fn signed_message(&self) -> &[u8] { - self.merkle_branch.root.as_ref() + fn signed_data(&'a self) -> Result { + self.merkle_root() } } -impl Shred for ShredCode { +impl<'a> ShredTrait<'a> for ShredCode { + type SignedData = Hash; + impl_shred_common!(); const SIZE_OF_PAYLOAD: usize = shred_code::ShredCode::SIZE_OF_PAYLOAD; + const SIZE_OF_HEADERS: usize = SIZE_OF_CODING_SHRED_HEADERS; fn from_payload(mut payload: Vec) -> Result { let mut cursor = Cursor::new(&payload[..]); let common_header: ShredCommonHeader = deserialize_from_with_limit(&mut cursor)?; - let proof_size = match common_header.shred_variant { - ShredVariant::MerkleCode(proof_size) => proof_size, - _ => return Err(Error::InvalidShredVariant), - }; + if !matches!(common_header.shred_variant, ShredVariant::MerkleCode(_)) { + return Err(Error::InvalidShredVariant); + } let coding_header = deserialize_from_with_limit(&mut cursor)?; - // Skip erasure code shard. - let shard_size = Self::size_of_erasure_encoded_slice(proof_size)?; - let shard_size = i64::try_from(shard_size).unwrap(); - cursor.seek(SeekFrom::Current(shard_size))?; - // Deserialize merkle branch. - let root = deserialize_from_with_limit(&mut cursor)?; - let proof = repeat_with(|| deserialize_from_with_limit(&mut cursor)) - .take(usize::from(proof_size)) - .collect::>()?; - let merkle_branch = MerkleBranch { root, proof }; // see: https://github.com/solana-labs/solana/pull/10109 + if payload.len() < Self::SIZE_OF_PAYLOAD { + return Err(Error::InvalidPayloadSize(payload.len())); + } payload.truncate(Self::SIZE_OF_PAYLOAD); let shred = Self { common_header, coding_header, - merkle_branch, payload, }; - shred.sanitize().map(|_| shred) + shred.sanitize()?; + Ok(shred) } fn erasure_shard_index(&self) -> Result { @@ -294,10 +477,10 @@ impl Shred for ShredCode { return Err(Error::InvalidPayloadSize(self.payload.len())); } let proof_size = self.proof_size()?; - let shard_size = Self::size_of_erasure_encoded_slice(proof_size)?; + let proof_offset = Self::proof_offset(proof_size)?; let mut shard = self.payload; - shard.drain(..SIZE_OF_CODING_SHRED_HEADERS); - shard.truncate(shard_size); + shard.truncate(proof_offset); + shard.drain(..Self::SIZE_OF_HEADERS); Ok(shard) } @@ -306,29 +489,23 @@ impl Shred for ShredCode { return Err(Error::InvalidPayloadSize(self.payload.len())); } let proof_size = self.proof_size()?; - let shard_size = Self::size_of_erasure_encoded_slice(proof_size)?; + let proof_offset = Self::proof_offset(proof_size)?; self.payload - .get(SIZE_OF_CODING_SHRED_HEADERS..SIZE_OF_CODING_SHRED_HEADERS + shard_size) + .get(Self::SIZE_OF_HEADERS..proof_offset) .ok_or(Error::InvalidPayloadSize(self.payload.len())) } fn sanitize(&self) -> Result<(), Error> { - match self.common_header.shred_variant { - ShredVariant::MerkleCode(proof_size) => { - if self.merkle_branch.proof.len() != usize::from(proof_size) { - return Err(Error::InvalidProofSize(proof_size)); - } - } - _ => return Err(Error::InvalidShredVariant), - } - if !self.verify_merkle_proof()? { - return Err(Error::InvalidMerkleProof); + let shred_variant = self.common_header.shred_variant; + if !matches!(shred_variant, ShredVariant::MerkleCode(_)) { + return Err(Error::InvalidShredVariant); } + let _ = self.merkle_proof()?; shred_code::sanitize(self) } - fn signed_message(&self) -> &[u8] { - self.merkle_branch.root.as_ref() + fn signed_data(&'a self) -> Result { + self.merkle_root() } } @@ -343,22 +520,15 @@ impl ShredDataTrait for ShredData { let data_buffer_size = Self::capacity(proof_size)?; let size = usize::from(self.data_header.size); if size > self.payload.len() - || size < SIZE_OF_DATA_SHRED_HEADERS - || size > SIZE_OF_DATA_SHRED_HEADERS + data_buffer_size + || size < Self::SIZE_OF_HEADERS + || size > Self::SIZE_OF_HEADERS + data_buffer_size { return Err(Error::InvalidDataSize { size: self.data_header.size, payload: self.payload.len(), }); } - Ok(&self.payload[SIZE_OF_DATA_SHRED_HEADERS..size]) - } - - // Only for tests. - fn set_last_in_slot(&mut self) { - self.data_header.flags |= ShredFlags::LAST_SHRED_IN_SLOT; - let buffer = &mut self.payload[SIZE_OF_COMMON_SHRED_HEADER..]; - bincode::serialize_into(buffer, &self.data_header).unwrap(); + Ok(&self.payload[Self::SIZE_OF_HEADERS..size]) } } @@ -376,21 +546,46 @@ fn join_nodes, T: AsRef<[u8]>>(node: S, other: T) -> Hash { hashv(&[MERKLE_HASH_PREFIX_NODE, node, other]) } -fn verify_merkle_proof(index: usize, node: Hash, merkle_branch: &MerkleBranch) -> bool { - let proof = merkle_branch.proof.iter(); - let (index, root) = proof.fold((index, node), |(index, node), other| { - let parent = if index % 2 == 0 { - join_nodes(node, other) - } else { - join_nodes(other, node) - }; - (index >> 1, parent) - }); - let root = &root.as_ref()[..SIZE_OF_MERKLE_ROOT]; - (index, root) == (0usize, &merkle_branch.root[..]) +// Recovers root of the merkle tree from a leaf node +// at the given index and the respective proof. +fn get_merkle_root<'a, I>(index: usize, node: Hash, proof: I) -> Result +where + I: IntoIterator, +{ + let (index, root) = proof + .into_iter() + .fold((index, node), |(index, node), other| { + let parent = if index % 2 == 0 { + join_nodes(node, other) + } else { + join_nodes(other, node) + }; + (index >> 1, parent) + }); + (index == 0).then(|| root).ok_or(Error::InvalidMerkleProof) +} + +fn get_merkle_proof( + shred: &[u8], + proof_offset: usize, // Where the merkle proof starts. + proof_size: u8, // Number of proof entries. +) -> Result, Error> { + let proof_size = usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY; + Ok(shred + .get(proof_offset..proof_offset + proof_size) + .ok_or(Error::InvalidPayloadSize(shred.len()))? + .chunks(SIZE_OF_MERKLE_PROOF_ENTRY) + .map(<&MerkleProofEntry>::try_from) + .map(Result::unwrap)) +} + +fn get_merkle_node(shred: &[u8], offsets: Range) -> Result { + let node = shred + .get(offsets) + .ok_or(Error::InvalidPayloadSize(shred.len()))?; + Ok(hashv(&[MERKLE_HASH_PREFIX_LEAF, node])) } -#[cfg(test)] fn make_merkle_tree(mut nodes: Vec) -> Vec { let mut size = nodes.len(); while size > 1 { @@ -406,60 +601,489 @@ fn make_merkle_tree(mut nodes: Vec) -> Vec { nodes } -#[cfg(test)] -fn make_merkle_branch( +fn make_merkle_proof( mut index: usize, // leaf index ~ shred's erasure shard index. mut size: usize, // number of leaves ~ erasure batch size. tree: &[Hash], -) -> Option { +) -> Option> { if index >= size { return None; } let mut offset = 0; - let mut proof = Vec::::new(); + let mut proof = Vec::<&MerkleProofEntry>::new(); while size > 1 { let node = tree.get(offset + (index ^ 1).min(size - 1))?; let entry = &node.as_ref()[..SIZE_OF_MERKLE_PROOF_ENTRY]; - proof.push(MerkleProofEntry::try_from(entry).unwrap()); + proof.push(<&MerkleProofEntry>::try_from(entry).unwrap()); offset += size; size = (size + 1) >> 1; index >>= 1; } - if offset + 1 != tree.len() { - return None; + (offset + 1 == tree.len()).then(|| proof) +} + +pub(super) fn recover( + mut shreds: Vec, + reed_solomon_cache: &ReedSolomonCache, +) -> Result, Error> { + // Grab {common, coding} headers from first coding shred. + let headers = shreds.iter().find_map(|shred| { + let shred = match shred { + Shred::ShredCode(shred) => shred, + Shred::ShredData(_) => return None, + }; + let position = u32::from(shred.coding_header.position); + let common_header = ShredCommonHeader { + index: shred.common_header.index.checked_sub(position)?, + ..shred.common_header + }; + let coding_header = CodingShredHeader { + position: 0u16, + ..shred.coding_header + }; + Some((common_header, coding_header)) + }); + let (common_header, coding_header) = headers.ok_or(TooFewParityShards)?; + debug_assert_matches!(common_header.shred_variant, ShredVariant::MerkleCode(_)); + let proof_size = match common_header.shred_variant { + ShredVariant::MerkleCode(proof_size) => proof_size, + ShredVariant::MerkleData(_) | ShredVariant::LegacyCode | ShredVariant::LegacyData => { + return Err(Error::InvalidShredVariant); + } + }; + // Verify that shreds belong to the same erasure batch + // and have consistent headers. + debug_assert!(shreds.iter().all(|shred| { + let ShredCommonHeader { + signature, + shred_variant, + slot, + index: _, + version, + fec_set_index, + } = shred.common_header(); + signature == &common_header.signature + && slot == &common_header.slot + && version == &common_header.version + && fec_set_index == &common_header.fec_set_index + && match shred { + Shred::ShredData(_) => shred_variant == &ShredVariant::MerkleData(proof_size), + Shred::ShredCode(shred) => { + let CodingShredHeader { + num_data_shreds, + num_coding_shreds, + position: _, + } = shred.coding_header; + shred_variant == &ShredVariant::MerkleCode(proof_size) + && num_data_shreds == coding_header.num_data_shreds + && num_coding_shreds == coding_header.num_coding_shreds + } + } + })); + let num_data_shreds = usize::from(coding_header.num_data_shreds); + let num_coding_shreds = usize::from(coding_header.num_coding_shreds); + let num_shards = num_data_shreds + num_coding_shreds; + // Obtain erasure encoded shards from shreds. + let shreds = { + let mut batch = vec![None; num_shards]; + while let Some(shred) = shreds.pop() { + let index = match shred.erasure_shard_index() { + Ok(index) if index < batch.len() => index, + _ => return Err(Error::from(InvalidIndex)), + }; + batch[index] = Some(shred); + } + batch + }; + let mut shards: Vec>> = shreds + .iter() + .map(|shred| Some(shred.as_ref()?.erasure_shard_as_slice().ok()?.to_vec())) + .collect(); + reed_solomon_cache + .get(num_data_shreds, num_coding_shreds)? + .reconstruct(&mut shards)?; + let mask: Vec<_> = shreds.iter().map(Option::is_some).collect(); + // Reconstruct code and data shreds from erasure encoded shards. + let mut shreds: Vec<_> = shreds + .into_iter() + .zip(shards) + .enumerate() + .map(|(index, (shred, shard))| { + if let Some(shred) = shred { + return Ok(shred); + } + let shard = shard.ok_or(TooFewShards)?; + if index < num_data_shreds { + let shred = ShredData::from_recovered_shard(&common_header.signature, shard)?; + let ShredCommonHeader { + signature: _, + shred_variant, + slot, + index: _, + version, + fec_set_index, + } = shred.common_header; + if shred_variant != ShredVariant::MerkleData(proof_size) + || common_header.slot != slot + || common_header.version != version + || common_header.fec_set_index != fec_set_index + { + return Err(Error::InvalidRecoveredShred); + } + Ok(Shred::ShredData(shred)) + } else { + let offset = index - num_data_shreds; + let coding_header = CodingShredHeader { + position: offset as u16, + ..coding_header + }; + let common_header = ShredCommonHeader { + index: common_header.index + offset as u32, + ..common_header + }; + let shred = ShredCode::from_recovered_shard(common_header, coding_header, shard)?; + Ok(Shred::ShredCode(shred)) + } + }) + .collect::>()?; + // Compute merkle tree and set the merkle proof on the recovered shreds. + let nodes: Vec<_> = shreds + .iter() + .map(Shred::merkle_node) + .collect::>()?; + let tree = make_merkle_tree(nodes); + for (index, (shred, mask)) in shreds.iter_mut().zip(&mask).enumerate() { + let proof = make_merkle_proof(index, num_shards, &tree).ok_or(Error::InvalidMerkleProof)?; + if proof.len() != usize::from(proof_size) { + return Err(Error::InvalidMerkleProof); + } + if *mask { + if shred.merkle_proof()?.ne(proof) { + return Err(Error::InvalidMerkleProof); + } + } else { + shred.set_merkle_proof(&proof)?; + // Already sanitized in Shred{Code,Data}::from_recovered_shard. + debug_assert_matches!(shred.sanitize(), Ok(())); + // Assert that shred payload is fully populated. + debug_assert_eq!(shred, { + let shred = shred.payload().clone(); + &Shred::from_payload(shred).unwrap() + }); + } + } + Ok(shreds + .into_iter() + .zip(mask) + .filter(|(_, mask)| !mask) + .map(|(shred, _)| shred) + .collect()) +} + +// Maps number of (code + data) shreds to merkle_proof.len(). +fn get_proof_size(num_shreds: usize) -> u8 { + let bits = usize::BITS - num_shreds.leading_zeros(); + let proof_size = if num_shreds.is_power_of_two() { + bits.checked_sub(1).unwrap() + } else { + bits + }; + u8::try_from(proof_size).unwrap() +} + +#[allow(clippy::too_many_arguments)] +pub(super) fn make_shreds_from_data( + thread_pool: &ThreadPool, + keypair: &Keypair, + mut data: &[u8], // Serialized &[Entry] + slot: Slot, + parent_slot: Slot, + shred_version: u16, + reference_tick: u8, + is_last_in_slot: bool, + next_shred_index: u32, + next_code_index: u32, + reed_solomon_cache: &ReedSolomonCache, + stats: &mut ProcessShredsStats, +) -> Result>, Error> { + fn new_shred_data( + common_header: ShredCommonHeader, + mut data_header: DataShredHeader, + data: &[u8], + ) -> ShredData { + let size = ShredData::SIZE_OF_HEADERS + data.len(); + let mut payload = vec![0u8; ShredData::SIZE_OF_PAYLOAD]; + payload[ShredData::SIZE_OF_HEADERS..size].copy_from_slice(data); + data_header.size = size as u16; + ShredData { + common_header, + data_header, + payload, + } + } + let now = Instant::now(); + let erasure_batch_size = shredder::get_erasure_batch_size(DATA_SHREDS_PER_FEC_BLOCK); + let proof_size = get_proof_size(erasure_batch_size); + let data_buffer_size = ShredData::capacity(proof_size)?; + let chunk_size = DATA_SHREDS_PER_FEC_BLOCK * data_buffer_size; + let mut common_header = ShredCommonHeader { + signature: Signature::default(), + shred_variant: ShredVariant::MerkleData(proof_size), + slot, + index: next_shred_index, + version: shred_version, + fec_set_index: next_shred_index, + }; + let data_header = { + let parent_offset = slot + .checked_sub(parent_slot) + .and_then(|offset| u16::try_from(offset).ok()) + .ok_or(Error::InvalidParentSlot { slot, parent_slot })?; + let flags = unsafe { + ShredFlags::from_bits_unchecked( + ShredFlags::SHRED_TICK_REFERENCE_MASK + .bits() + .min(reference_tick), + ) + }; + DataShredHeader { + parent_offset, + flags, + size: 0u16, + } + }; + // Split the data into erasure batches and initialize + // data shreds from chunks of each batch. + let mut shreds = Vec::::new(); + while data.len() >= 2 * chunk_size || data.len() == chunk_size { + let (chunk, rest) = data.split_at(chunk_size); + common_header.fec_set_index = common_header.index; + for shred in chunk.chunks(data_buffer_size) { + let shred = new_shred_data(common_header, data_header, shred); + shreds.push(shred); + common_header.index += 1; + } + data = rest; + } + // If shreds.is_empty() then the data argument was empty. In that case we + // want to generate one data shred with empty data. + if !data.is_empty() || shreds.is_empty() { + // Find the Merkle proof_size and data_buffer_size + // which can embed the remaining data. + let (proof_size, data_buffer_size) = (1u8..32) + .find_map(|proof_size| { + let data_buffer_size = ShredData::capacity(proof_size).ok()?; + let num_data_shreds = (data.len() + data_buffer_size - 1) / data_buffer_size; + let num_data_shreds = num_data_shreds.max(1); + let erasure_batch_size = shredder::get_erasure_batch_size(num_data_shreds); + (proof_size == get_proof_size(erasure_batch_size)) + .then(|| (proof_size, data_buffer_size)) + }) + .ok_or(Error::UnknownProofSize)?; + common_header.shred_variant = ShredVariant::MerkleData(proof_size); + common_header.fec_set_index = common_header.index; + let chunks = if data.is_empty() { + // Generate one data shred with empty data. + Either::Left(std::iter::once(data)) + } else { + Either::Right(data.chunks(data_buffer_size)) + }; + for shred in chunks { + let shred = new_shred_data(common_header, data_header, shred); + shreds.push(shred); + common_header.index += 1; + } + if let Some(shred) = shreds.last() { + stats.data_buffer_residual += data_buffer_size - shred.data()?.len(); + } + } + // Only the very last shred may have residual data buffer. + debug_assert!(shreds.iter().rev().skip(1).all(|shred| { + let proof_size = shred.proof_size().unwrap(); + let capacity = ShredData::capacity(proof_size).unwrap(); + shred.data().unwrap().len() == capacity + })); + // Adjust flags for the very last shred. + if let Some(shred) = shreds.last_mut() { + shred.data_header.flags |= if is_last_in_slot { + ShredFlags::LAST_SHRED_IN_SLOT // also implies DATA_COMPLETE_SHRED + } else { + ShredFlags::DATA_COMPLETE_SHRED + }; + } + // Write common and data headers into data shreds' payload buffer. + thread_pool.install(|| { + shreds.par_iter_mut().try_for_each(|shred| { + let mut cursor = Cursor::new(&mut shred.payload[..]); + bincode::serialize_into(&mut cursor, &shred.common_header)?; + bincode::serialize_into(&mut cursor, &shred.data_header) + }) + })?; + stats.gen_data_elapsed += now.elapsed().as_micros() as u64; + stats.record_num_data_shreds(shreds.len()); + let now = Instant::now(); + // Group shreds by their respective erasure-batch. + let shreds: Vec> = shreds + .into_iter() + .group_by(|shred| shred.common_header.fec_set_index) + .into_iter() + .map(|(_, shreds)| shreds.collect()) + .collect(); + // Obtain the shred index for the first coding shred of each batch. + let next_code_index: Vec<_> = shreds + .iter() + .scan(next_code_index, |next_code_index, chunk| { + let out = Some(*next_code_index); + let num_data_shreds = chunk.len(); + let erasure_batch_size = shredder::get_erasure_batch_size(num_data_shreds); + let num_coding_shreds = erasure_batch_size - num_data_shreds; + *next_code_index += num_coding_shreds as u32; + out + }) + .collect(); + // Generate coding shreds, populate merkle proof + // for all shreds and attach signature. + let shreds: Result, Error> = if shreds.len() <= 1 { + shreds + .into_iter() + .zip(next_code_index) + .map(|(shreds, next_code_index)| { + make_erasure_batch(keypair, shreds, next_code_index, reed_solomon_cache) + }) + .collect() + } else { + thread_pool.install(|| { + shreds + .into_par_iter() + .zip(next_code_index) + .map(|(shreds, next_code_index)| { + make_erasure_batch(keypair, shreds, next_code_index, reed_solomon_cache) + }) + .collect() + }) + }; + stats.gen_coding_elapsed += now.elapsed().as_micros() as u64; + shreds +} + +// Generates coding shreds from data shreds, populates merke proof for all +// shreds and attaches signature. +fn make_erasure_batch( + keypair: &Keypair, + shreds: Vec, + next_code_index: u32, + reed_solomon_cache: &ReedSolomonCache, +) -> Result, Error> { + let num_data_shreds = shreds.len(); + let erasure_batch_size = shredder::get_erasure_batch_size(num_data_shreds); + let num_coding_shreds = erasure_batch_size - num_data_shreds; + let proof_size = get_proof_size(erasure_batch_size); + debug_assert!(shreds + .iter() + .all(|shred| shred.common_header.shred_variant == ShredVariant::MerkleData(proof_size))); + let mut common_header = match shreds.first() { + None => return Ok(Vec::default()), + Some(shred) => shred.common_header, + }; + // Generate erasure codings for encoded shard of data shreds. + let data: Vec<_> = shreds + .iter() + .map(ShredData::erasure_shard_as_slice) + .collect::>()?; + // Shreds should have erasure encoded shard of the same length. + debug_assert_eq!(data.iter().map(|shard| shard.len()).dedup().count(), 1); + let mut parity = vec![vec![0u8; data[0].len()]; num_coding_shreds]; + reed_solomon_cache + .get(num_data_shreds, num_coding_shreds)? + .encode_sep(&data, &mut parity[..])?; + let mut shreds: Vec<_> = shreds.into_iter().map(Shred::ShredData).collect(); + // Initialize coding shreds from erasure coding shards. + common_header.index = next_code_index; + common_header.shred_variant = ShredVariant::MerkleCode(proof_size); + let mut coding_header = CodingShredHeader { + num_data_shreds: num_data_shreds as u16, + num_coding_shreds: num_coding_shreds as u16, + position: 0, + }; + for code in parity { + let mut payload = vec![0u8; ShredCode::SIZE_OF_PAYLOAD]; + let mut cursor = Cursor::new(&mut payload[..]); + bincode::serialize_into(&mut cursor, &common_header)?; + bincode::serialize_into(&mut cursor, &coding_header)?; + cursor.write_all(&code)?; + let shred = ShredCode { + common_header, + coding_header, + payload, + }; + shreds.push(Shred::ShredCode(shred)); + common_header.index += 1; + coding_header.position += 1; } - let root = &tree.last()?.as_ref()[..SIZE_OF_MERKLE_ROOT]; - let root = MerkleRoot::try_from(root).unwrap(); - Some(MerkleBranch { root, proof }) + // Compute Merkle tree for the erasure batch. + let tree = make_merkle_tree( + shreds + .iter() + .map(Shred::merkle_node) + .collect::>()?, + ); + // Sign root of Merkle tree. + let signature = { + let root = tree.last().ok_or(Error::InvalidMerkleProof)?; + keypair.sign_message(root.as_ref()) + }; + // Populate merkle proof for all shreds and attach signature. + for (index, shred) in shreds.iter_mut().enumerate() { + let proof = + make_merkle_proof(index, erasure_batch_size, &tree).ok_or(Error::InvalidMerkleProof)?; + debug_assert_eq!(proof.len(), usize::from(proof_size)); + shred.set_merkle_proof(&proof)?; + shred.set_signature(signature); + debug_assert!(shred.verify(&keypair.pubkey())); + debug_assert_matches!(shred.sanitize(), Ok(())); + // Assert that shred payload is fully populated. + debug_assert_eq!(shred, { + let shred = shred.payload().clone(); + &Shred::from_payload(shred).unwrap() + }); + } + Ok(shreds) } #[cfg(test)] mod test { - use {super::*, rand::Rng, std::iter::repeat_with}; + use { + super::*, + crate::shred::{ShredFlags, ShredId, SignedData}, + itertools::Itertools, + matches::assert_matches, + rand::{seq::SliceRandom, CryptoRng, Rng}, + rayon::ThreadPoolBuilder, + solana_sdk::signature::{Keypair, Signer}, + std::{cmp::Ordering, iter::repeat_with}, + test_case::test_case, + }; - // Total size of a data shred including headers and merkle branch. + // Total size of a data shred including headers and merkle proof. fn shred_data_size_of_payload(proof_size: u8) -> usize { - SIZE_OF_DATA_SHRED_HEADERS + ShredData::SIZE_OF_HEADERS + ShredData::capacity(proof_size).unwrap() - + SIZE_OF_MERKLE_ROOT + usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY } - // Merkle branch is generated and signed after coding shreds are generated. - // All payload excluding merkle branch and the signature are erasure coded. + // Merkle proof is generated and signed after coding shreds are generated. + // All payload excluding merkle proof and the signature are erasure coded. // Therefore the data capacity is equal to erasure encoded shard size minus // size of erasure encoded header. fn shred_data_capacity(proof_size: u8) -> usize { const SIZE_OF_ERASURE_ENCODED_HEADER: usize = - SIZE_OF_DATA_SHRED_HEADERS - SIZE_OF_SIGNATURE; - ShredCode::size_of_erasure_encoded_slice(proof_size).unwrap() - - SIZE_OF_ERASURE_ENCODED_HEADER + ShredData::SIZE_OF_HEADERS - SIZE_OF_SIGNATURE; + ShredCode::capacity(proof_size).unwrap() - SIZE_OF_ERASURE_ENCODED_HEADER } fn shred_data_size_of_erasure_encoded_slice(proof_size: u8) -> usize { ShredData::SIZE_OF_PAYLOAD - SIZE_OF_SIGNATURE - - SIZE_OF_MERKLE_ROOT - usize::from(proof_size) * SIZE_OF_MERKLE_PROOF_ENTRY } @@ -484,10 +1108,10 @@ mod test { } #[test] - fn test_size_of_erasure_encoded_slice() { + fn test_shred_code_capacity() { for proof_size in 0..0x15 { assert_eq!( - ShredCode::size_of_erasure_encoded_slice(proof_size).unwrap(), + ShredCode::capacity(proof_size).unwrap(), shred_data_size_of_erasure_encoded_slice(proof_size), ); } @@ -508,13 +1132,16 @@ mod test { let nodes = repeat_with(|| rng.gen::<[u8; 32]>()).map(Hash::from); let nodes: Vec<_> = nodes.take(size).collect(); let tree = make_merkle_tree(nodes.clone()); + let root = tree.last().copied().unwrap(); for index in 0..size { - let branch = make_merkle_branch(index, size, &tree).unwrap(); - let root = &tree.last().unwrap().as_ref()[..SIZE_OF_MERKLE_ROOT]; - assert_eq!(&branch.root, root); - assert!(verify_merkle_proof(index, nodes[index], &branch)); - for i in (0..size).filter(|&i| i != index) { - assert!(!verify_merkle_proof(i, nodes[i], &branch)); + let proof = make_merkle_proof(index, size, &tree).unwrap(); + for (k, &node) in nodes.iter().enumerate() { + let proof = proof.iter().copied(); + if k == index { + assert_eq!(root, get_merkle_root(k, node, proof).unwrap()); + } else { + assert_ne!(root, get_merkle_root(k, node, proof).unwrap()); + } } } } @@ -525,4 +1152,362 @@ mod test { run_merkle_tree_round_trip(size); } } + + #[test_case(37)] + #[test_case(64)] + #[test_case(73)] + fn test_recover_merkle_shreds(num_shreds: usize) { + let mut rng = rand::thread_rng(); + let reed_solomon_cache = ReedSolomonCache::default(); + for num_data_shreds in 1..num_shreds { + let num_coding_shreds = num_shreds - num_data_shreds; + run_recover_merkle_shreds( + &mut rng, + num_data_shreds, + num_coding_shreds, + &reed_solomon_cache, + ); + } + } + + fn run_recover_merkle_shreds( + rng: &mut R, + num_data_shreds: usize, + num_coding_shreds: usize, + reed_solomon_cache: &ReedSolomonCache, + ) { + let keypair = Keypair::generate(rng); + let num_shreds = num_data_shreds + num_coding_shreds; + let proof_size = get_proof_size(num_shreds); + let capacity = ShredData::capacity(proof_size).unwrap(); + let common_header = ShredCommonHeader { + signature: Signature::default(), + shred_variant: ShredVariant::MerkleData(proof_size), + slot: 145_865_705, + index: 1835, + version: rng.gen(), + fec_set_index: 1835, + }; + let data_header = { + let reference_tick = rng.gen_range(0, 0x40); + DataShredHeader { + parent_offset: rng.gen::().max(1), + flags: unsafe { ShredFlags::from_bits_unchecked(reference_tick) }, + size: 0, + } + }; + let coding_header = CodingShredHeader { + num_data_shreds: num_data_shreds as u16, + num_coding_shreds: num_coding_shreds as u16, + position: 0, + }; + let mut shreds = Vec::with_capacity(num_shreds); + for i in 0..num_data_shreds { + let common_header = ShredCommonHeader { + index: common_header.index + i as u32, + ..common_header + }; + let size = ShredData::SIZE_OF_HEADERS + rng.gen_range(0, capacity); + let data_header = DataShredHeader { + size: size as u16, + ..data_header + }; + let mut payload = vec![0u8; ShredData::SIZE_OF_PAYLOAD]; + let mut cursor = Cursor::new(&mut payload[..]); + bincode::serialize_into(&mut cursor, &common_header).unwrap(); + bincode::serialize_into(&mut cursor, &data_header).unwrap(); + rng.fill(&mut payload[ShredData::SIZE_OF_HEADERS..size]); + let shred = ShredData { + common_header, + data_header, + payload, + }; + shreds.push(Shred::ShredData(shred)); + } + let data: Vec<_> = shreds + .iter() + .map(Shred::erasure_shard_as_slice) + .collect::>() + .unwrap(); + let mut parity = vec![vec![0u8; data[0].len()]; num_coding_shreds]; + reed_solomon_cache + .get(num_data_shreds, num_coding_shreds) + .unwrap() + .encode_sep(&data, &mut parity[..]) + .unwrap(); + for (i, code) in parity.into_iter().enumerate() { + let common_header = ShredCommonHeader { + shred_variant: ShredVariant::MerkleCode(proof_size), + index: common_header.index + i as u32 + 7, + ..common_header + }; + let coding_header = CodingShredHeader { + position: i as u16, + ..coding_header + }; + let mut payload = vec![0u8; ShredCode::SIZE_OF_PAYLOAD]; + let mut cursor = Cursor::new(&mut payload[..]); + bincode::serialize_into(&mut cursor, &common_header).unwrap(); + bincode::serialize_into(&mut cursor, &coding_header).unwrap(); + payload[ShredCode::SIZE_OF_HEADERS..ShredCode::SIZE_OF_HEADERS + code.len()] + .copy_from_slice(&code); + let shred = ShredCode { + common_header, + coding_header, + payload, + }; + shreds.push(Shred::ShredCode(shred)); + } + let nodes: Vec<_> = shreds + .iter() + .map(Shred::merkle_node) + .collect::>() + .unwrap(); + let tree = make_merkle_tree(nodes); + for (index, shred) in shreds.iter_mut().enumerate() { + let proof = make_merkle_proof(index, num_shreds, &tree).unwrap(); + assert_eq!(proof.len(), usize::from(proof_size)); + shred.set_merkle_proof(&proof).unwrap(); + let data = shred.signed_data().unwrap(); + let signature = keypair.sign_message(data.as_ref()); + shred.set_signature(signature); + assert!(shred.verify(&keypair.pubkey())); + assert_matches!(shred.sanitize(), Ok(())); + } + assert_eq!(shreds.iter().map(Shred::signature).dedup().count(), 1); + for size in num_data_shreds..num_shreds { + let mut shreds = shreds.clone(); + shreds.shuffle(rng); + let mut removed_shreds = shreds.split_off(size); + // Should at least contain one coding shred. + if shreds.iter().all(|shred| { + matches!( + shred.common_header().shred_variant, + ShredVariant::MerkleData(_) + ) + }) { + assert_matches!( + recover(shreds, reed_solomon_cache), + Err(Error::ErasureError(TooFewParityShards)) + ); + continue; + } + let recovered_shreds = recover(shreds, reed_solomon_cache).unwrap(); + assert_eq!(size + recovered_shreds.len(), num_shreds); + assert_eq!(recovered_shreds.len(), removed_shreds.len()); + removed_shreds.sort_by(|a, b| { + if a.shred_type() == b.shred_type() { + a.index().cmp(&b.index()) + } else if a.shred_type() == ShredType::Data { + Ordering::Less + } else { + Ordering::Greater + } + }); + assert_eq!(recovered_shreds, removed_shreds); + } + } + + #[test] + fn test_get_proof_size() { + assert_eq!(get_proof_size(0), 0); + assert_eq!(get_proof_size(1), 0); + assert_eq!(get_proof_size(2), 1); + assert_eq!(get_proof_size(3), 2); + assert_eq!(get_proof_size(4), 2); + assert_eq!(get_proof_size(5), 3); + assert_eq!(get_proof_size(63), 6); + assert_eq!(get_proof_size(64), 6); + assert_eq!(get_proof_size(65), 7); + assert_eq!(get_proof_size(usize::MAX - 1), 64); + assert_eq!(get_proof_size(usize::MAX), 64); + for proof_size in 1u8..9 { + let max_num_shreds = 1usize << u32::from(proof_size); + let min_num_shreds = (max_num_shreds >> 1) + 1; + for num_shreds in min_num_shreds..=max_num_shreds { + assert_eq!(get_proof_size(num_shreds), proof_size); + } + } + } + + #[test_case(0)] + #[test_case(15600)] + #[test_case(31200)] + #[test_case(46800)] + fn test_make_shreds_from_data(data_size: usize) { + let mut rng = rand::thread_rng(); + let data_size = data_size.saturating_sub(16); + let reed_solomon_cache = ReedSolomonCache::default(); + for data_size in data_size..data_size + 32 { + run_make_shreds_from_data(&mut rng, data_size, &reed_solomon_cache); + } + } + + #[test] + fn test_make_shreds_from_data_rand() { + let mut rng = rand::thread_rng(); + let reed_solomon_cache = ReedSolomonCache::default(); + for _ in 0..32 { + let data_size = rng.gen_range(0, 31200 * 7); + run_make_shreds_from_data(&mut rng, data_size, &reed_solomon_cache); + } + } + + fn run_make_shreds_from_data( + rng: &mut R, + data_size: usize, + reed_solomon_cache: &ReedSolomonCache, + ) { + let thread_pool = ThreadPoolBuilder::new().num_threads(2).build().unwrap(); + let keypair = Keypair::new(); + let slot = 149_745_689; + let parent_slot = slot - rng.gen_range(1, 65536); + let shred_version = rng.gen(); + let reference_tick = rng.gen_range(1, 64); + let next_shred_index = rng.gen_range(0, 671); + let next_code_index = rng.gen_range(0, 781); + let mut data = vec![0u8; data_size]; + rng.fill(&mut data[..]); + let shreds = make_shreds_from_data( + &thread_pool, + &keypair, + &data[..], + slot, + parent_slot, + shred_version, + reference_tick, + true, // is_last_in_slot + next_shred_index, + next_code_index, + reed_solomon_cache, + &mut ProcessShredsStats::default(), + ) + .unwrap(); + let shreds: Vec<_> = shreds.into_iter().flatten().collect(); + let data_shreds: Vec<_> = shreds + .iter() + .filter_map(|shred| match shred { + Shred::ShredCode(_) => None, + Shred::ShredData(shred) => Some(shred), + }) + .collect(); + // Assert that the input data can be recovered from data shreds. + assert_eq!( + data, + data_shreds + .iter() + .flat_map(|shred| shred.data().unwrap()) + .copied() + .collect::>() + ); + // Assert that shreds sanitize and verify. + let pubkey = keypair.pubkey(); + for shred in &shreds { + assert!(shred.verify(&pubkey)); + assert_matches!(shred.sanitize(), Ok(())); + let ShredCommonHeader { + signature, + shred_variant, + slot, + index, + version, + fec_set_index: _, + } = *shred.common_header(); + let shred_type = ShredType::from(shred_variant); + let key = ShredId::new(slot, index, shred_type); + let merkle_root = shred.merkle_root().unwrap(); + assert!(signature.verify(pubkey.as_ref(), merkle_root.as_ref())); + // Verify shred::layout api. + let shred = shred.payload(); + assert_eq!(shred::layout::get_signature(shred), Some(signature)); + assert_eq!( + shred::layout::get_shred_variant(shred).unwrap(), + shred_variant + ); + assert_eq!(shred::layout::get_shred_type(shred).unwrap(), shred_type); + assert_eq!(shred::layout::get_slot(shred), Some(slot)); + assert_eq!(shred::layout::get_index(shred), Some(index)); + assert_eq!(shred::layout::get_version(shred), Some(version)); + assert_eq!(shred::layout::get_shred_id(shred), Some(key)); + assert_eq!(shred::layout::get_merkle_root(shred), Some(merkle_root)); + assert_eq!(shred::layout::get_signed_data_offsets(shred), None); + let data = shred::layout::get_signed_data(shred).unwrap(); + assert_eq!(data, SignedData::MerkleRoot(merkle_root)); + assert!(signature.verify(pubkey.as_ref(), data.as_ref())); + } + // Verify common, data and coding headers. + let mut num_data_shreds = 0; + let mut num_coding_shreds = 0; + for shred in &shreds { + let common_header = shred.common_header(); + assert_eq!(common_header.slot, slot); + assert_eq!(common_header.version, shred_version); + match shred { + Shred::ShredCode(_) => { + assert_eq!(common_header.index, next_code_index + num_coding_shreds); + assert_matches!(common_header.shred_variant, ShredVariant::MerkleCode(_)); + num_coding_shreds += 1; + } + Shred::ShredData(shred) => { + assert_eq!(common_header.index, next_shred_index + num_data_shreds); + assert_matches!(common_header.shred_variant, ShredVariant::MerkleData(_)); + assert!(common_header.fec_set_index <= common_header.index); + assert_eq!( + Slot::from(shred.data_header.parent_offset), + slot - parent_slot + ); + assert_eq!( + (shred.data_header.flags & ShredFlags::SHRED_TICK_REFERENCE_MASK).bits(), + reference_tick, + ); + let shred = shred.payload(); + assert_eq!( + shred::layout::get_parent_offset(shred), + Some(u16::try_from(slot - parent_slot).unwrap()), + ); + assert_eq!( + shred::layout::get_reference_tick(shred).unwrap(), + reference_tick + ); + num_data_shreds += 1; + } + } + } + assert!(num_coding_shreds >= num_data_shreds); + // Assert that only the last shred is LAST_SHRED_IN_SLOT. + assert_eq!( + data_shreds + .iter() + .filter(|shred| shred + .data_header + .flags + .contains(ShredFlags::LAST_SHRED_IN_SLOT)) + .count(), + 1 + ); + assert!(data_shreds + .last() + .unwrap() + .data_header + .flags + .contains(ShredFlags::LAST_SHRED_IN_SLOT)); + // Assert that data shreds can be recovered from coding shreds. + let recovered_data_shreds: Vec<_> = shreds + .iter() + .filter_map(|shred| match shred { + Shred::ShredCode(_) => Some(shred.clone()), + Shred::ShredData(_) => None, + }) + .group_by(|shred| shred.common_header().fec_set_index) + .into_iter() + .flat_map(|(_, shreds)| recover(shreds.collect(), reed_solomon_cache).unwrap()) + .collect(); + assert_eq!(recovered_data_shreds.len(), data_shreds.len()); + for (shred, other) in recovered_data_shreds.into_iter().zip(data_shreds) { + match shred { + Shred::ShredCode(_) => panic!("Invalid shred type!"), + Shred::ShredData(shred) => assert_eq!(shred, *other), + } + } + } } diff --git a/ledger/src/shred/shred_code.rs b/ledger/src/shred/shred_code.rs index 25ce8a2385916d..d6d1f543c3748f 100644 --- a/ledger/src/shred/shred_code.rs +++ b/ledger/src/shred/shred_code.rs @@ -3,14 +3,15 @@ use { common::dispatch, legacy, merkle, traits::{Shred, ShredCode as ShredCodeTrait}, - CodingShredHeader, Error, ShredCommonHeader, ShredType, MAX_DATA_SHREDS_PER_FEC_BLOCK, - MAX_DATA_SHREDS_PER_SLOT, SIZE_OF_NONCE, + CodingShredHeader, Error, ShredCommonHeader, ShredType, SignedData, + DATA_SHREDS_PER_FEC_BLOCK, MAX_DATA_SHREDS_PER_SLOT, SIZE_OF_NONCE, }, solana_sdk::{clock::Slot, packet::PACKET_DATA_SIZE, signature::Signature}, static_assertions::const_assert_eq, }; -pub(super) const MAX_CODE_SHREDS_PER_SLOT: usize = MAX_DATA_SHREDS_PER_SLOT; +const_assert_eq!(MAX_CODE_SHREDS_PER_SLOT, 32_768); +pub const MAX_CODE_SHREDS_PER_SLOT: usize = MAX_DATA_SHREDS_PER_SLOT; const_assert_eq!(ShredCode::SIZE_OF_PAYLOAD, 1228); @@ -34,12 +35,18 @@ impl ShredCode { dispatch!(pub(super) fn payload(&self) -> &Vec); dispatch!(pub(super) fn sanitize(&self) -> Result<(), Error>); dispatch!(pub(super) fn set_signature(&mut self, signature: Signature)); - dispatch!(pub(super) fn signed_message(&self) -> &[u8]); // Only for tests. dispatch!(pub(super) fn set_index(&mut self, index: u32)); dispatch!(pub(super) fn set_slot(&mut self, slot: Slot)); + pub(super) fn signed_data(&self) -> Result { + match self { + Self::Legacy(shred) => Ok(SignedData::Chunk(shred.signed_data()?)), + Self::Merkle(shred) => Ok(SignedData::MerkleRoot(shred.signed_data()?)), + } + } + pub(super) fn new_from_parity_shard( slot: Slot, index: u32, @@ -74,8 +81,15 @@ impl ShredCode { pub(super) fn erasure_mismatch(&self, other: &ShredCode) -> bool { match (self, other) { (Self::Legacy(shred), Self::Legacy(other)) => erasure_mismatch(shred, other), - (Self::Merkle(shred), Self::Merkle(other)) => shred.erasure_mismatch(other), - _ => true, + (Self::Legacy(_), Self::Merkle(_)) => true, + (Self::Merkle(_), Self::Legacy(_)) => true, + (Self::Merkle(shred), Self::Merkle(other)) => { + // Merkle shreds within the same erasure batch have the same + // merkle root. The root of the merkle tree is signed. So + // either the signatures match or one fails sigverify. + erasure_mismatch(shred, other) + || shred.common_header().signature != other.common_header().signature + } } } } @@ -132,8 +146,8 @@ pub(super) fn sanitize(shred: &T) -> Result<(), Error> { common_header.index, )); } - let num_coding_shreds = u32::from(coding_header.num_coding_shreds); - if num_coding_shreds > 8 * MAX_DATA_SHREDS_PER_FEC_BLOCK { + let num_coding_shreds = usize::from(coding_header.num_coding_shreds); + if num_coding_shreds > 8 * DATA_SHREDS_PER_FEC_BLOCK { return Err(Error::InvalidNumCodingShreds( coding_header.num_coding_shreds, )); diff --git a/ledger/src/shred/shred_data.rs b/ledger/src/shred/shred_data.rs index 777ef6feffb8cb..9bf2c0bf05f79e 100644 --- a/ledger/src/shred/shred_data.rs +++ b/ledger/src/shred/shred_data.rs @@ -4,7 +4,7 @@ use { common::dispatch, legacy, merkle, traits::{Shred as _, ShredData as ShredDataTrait}, - DataShredHeader, Error, ShredCommonHeader, ShredFlags, ShredType, ShredVariant, + DataShredHeader, Error, ShredCommonHeader, ShredFlags, ShredType, ShredVariant, SignedData, MAX_DATA_SHREDS_PER_SLOT, }, solana_sdk::{clock::Slot, signature::Signature}, @@ -28,14 +28,19 @@ impl ShredData { dispatch!(pub(super) fn parent(&self) -> Result); dispatch!(pub(super) fn payload(&self) -> &Vec); dispatch!(pub(super) fn sanitize(&self) -> Result<(), Error>); - dispatch!(pub(super) fn set_last_in_slot(&mut self)); dispatch!(pub(super) fn set_signature(&mut self, signature: Signature)); - dispatch!(pub(super) fn signed_message(&self) -> &[u8]); // Only for tests. dispatch!(pub(super) fn set_index(&mut self, index: u32)); dispatch!(pub(super) fn set_slot(&mut self, slot: Slot)); + pub(super) fn signed_data(&self) -> Result { + match self { + Self::Legacy(shred) => Ok(SignedData::Chunk(shred.signed_data()?)), + Self::Merkle(shred) => Ok(SignedData::MerkleRoot(shred.signed_data()?)), + } + } + pub(super) fn new_from_data( slot: Slot, index: u32, @@ -97,14 +102,22 @@ impl ShredData { } // Maximum size of ledger data that can be embedded in a data-shred. - // merkle_proof_size is the number of proof entries in the merkle tree - // branch. None indicates a legacy data-shred. - pub(crate) fn capacity(merkle_proof_size: Option) -> Result { + // merkle_proof_size is the number of merkle proof entries. + // None indicates a legacy data-shred. + pub fn capacity(merkle_proof_size: Option) -> Result { match merkle_proof_size { None => Ok(legacy::ShredData::CAPACITY), Some(proof_size) => merkle::ShredData::capacity(proof_size), } } + + // Only for tests. + pub(super) fn set_last_in_slot(&mut self) { + match self { + Self::Legacy(shred) => shred.set_last_in_slot(), + Self::Merkle(_) => panic!("Not Implemented!"), + } + } } impl From for ShredData { diff --git a/ledger/src/shred/stats.rs b/ledger/src/shred/stats.rs index 1f261f5c8d842d..b1c4769d5f876c 100644 --- a/ledger/src/shred/stats.rs +++ b/ledger/src/shred/stats.rs @@ -17,24 +17,28 @@ pub struct ProcessShredsStats { pub sign_coding_elapsed: u64, pub coding_send_elapsed: u64, pub get_leader_schedule_elapsed: u64, + pub coalesce_elapsed: u64, // Histogram count of num_data_shreds obtained from serializing entries // counted in 5 buckets. num_data_shreds_hist: [usize; 5], // If the blockstore already has shreds for the broadcast slot. pub num_extant_slots: u64, pub(crate) data_buffer_residual: usize, + pub num_merkle_data_shreds: usize, + pub num_merkle_coding_shreds: usize, } #[derive(Default, Debug, Eq, PartialEq)] pub struct ShredFetchStats { pub index_overrun: usize, pub shred_count: usize, + pub(crate) num_shreds_merkle_code: usize, + pub(crate) num_shreds_merkle_data: usize, pub ping_count: usize, pub ping_err_verify_count: usize, pub(crate) index_bad_deserialize: usize, pub(crate) index_out_of_bounds: usize, pub(crate) slot_bad_deserialize: usize, - pub duplicate_shred: usize, pub slot_out_of_range: usize, pub(crate) bad_shred_type: usize, pub shred_version_mismatch: usize, @@ -48,6 +52,7 @@ impl ProcessShredsStats { name: &'static str, slot: Slot, num_data_shreds: u32, + num_coding_shreds: u32, slot_broadcast_time: Option, ) { let slot_broadcast_time = slot_broadcast_time @@ -63,6 +68,13 @@ impl ProcessShredsStats { ("shredding_time", self.shredding_elapsed, i64), ("receive_time", self.receive_elapsed, i64), ("num_data_shreds", num_data_shreds, i64), + ("num_coding_shreds", num_coding_shreds, i64), + ("num_merkle_data_shreds", self.num_merkle_data_shreds, i64), + ( + "num_merkle_coding_shreds", + self.num_merkle_coding_shreds, + i64 + ), ("slot_broadcast_time", slot_broadcast_time, i64), ( "get_leader_schedule_time", @@ -81,6 +93,7 @@ impl ProcessShredsStats { ("num_data_shreds_31", self.num_data_shreds_hist[2], i64), ("num_data_shreds_63", self.num_data_shreds_hist[3], i64), ("num_data_shreds_64", self.num_data_shreds_hist[4], i64), + ("coalesce_elapsed", self.coalesce_elapsed, i64), ); *self = Self::default(); } @@ -103,13 +116,14 @@ impl ShredFetchStats { name, ("index_overrun", self.index_overrun, i64), ("shred_count", self.shred_count, i64), + ("num_shreds_merkle_code", self.num_shreds_merkle_code, i64), + ("num_shreds_merkle_data", self.num_shreds_merkle_data, i64), ("ping_count", self.ping_count, i64), ("ping_err_verify_count", self.ping_err_verify_count, i64), ("slot_bad_deserialize", self.slot_bad_deserialize, i64), ("index_bad_deserialize", self.index_bad_deserialize, i64), ("index_out_of_bounds", self.index_out_of_bounds, i64), ("slot_out_of_range", self.slot_out_of_range, i64), - ("duplicate_shred", self.duplicate_shred, i64), ("bad_shred_type", self.bad_shred_type, i64), ("shred_version_mismatch", self.shred_version_mismatch, i64), ("bad_parent_offset", self.bad_parent_offset, i64), @@ -132,9 +146,12 @@ impl AddAssign for ProcessShredsStats { sign_coding_elapsed, coding_send_elapsed, get_leader_schedule_elapsed, + coalesce_elapsed, num_data_shreds_hist, num_extant_slots, data_buffer_residual, + num_merkle_data_shreds, + num_merkle_coding_shreds, } = rhs; self.shredding_elapsed += shredding_elapsed; self.receive_elapsed += receive_elapsed; @@ -144,8 +161,11 @@ impl AddAssign for ProcessShredsStats { self.sign_coding_elapsed += sign_coding_elapsed; self.coding_send_elapsed += coding_send_elapsed; self.get_leader_schedule_elapsed += get_leader_schedule_elapsed; + self.coalesce_elapsed += coalesce_elapsed; self.num_extant_slots += num_extant_slots; self.data_buffer_residual += data_buffer_residual; + self.num_merkle_data_shreds += num_merkle_data_shreds; + self.num_merkle_coding_shreds += num_merkle_coding_shreds; for (i, bucket) in self.num_data_shreds_hist.iter_mut().enumerate() { *bucket += num_data_shreds_hist[i]; } diff --git a/ledger/src/shred/traits.rs b/ledger/src/shred/traits.rs index 70e049113d8670..35a6c0a617af64 100644 --- a/ledger/src/shred/traits.rs +++ b/ledger/src/shred/traits.rs @@ -3,10 +3,14 @@ use { solana_sdk::{clock::Slot, signature::Signature}, }; -pub(super) trait Shred: Sized { +pub(super) trait Shred<'a>: Sized { // Total size of payload including headers, merkle // branches (if any), zero paddings, etc. const SIZE_OF_PAYLOAD: usize; + // Size of common and code/data headers. + const SIZE_OF_HEADERS: usize; + + type SignedData: AsRef<[u8]>; fn from_payload(shred: Vec) -> Result; fn common_header(&self) -> &ShredCommonHeader; @@ -25,14 +29,14 @@ pub(super) trait Shred: Sized { fn erasure_shard_as_slice(&self) -> Result<&[u8], Error>; // Portion of the payload which is signed. - fn signed_message(&self) -> &[u8]; + fn signed_data(&'a self) -> Result; // Only for tests. fn set_index(&mut self, index: u32); fn set_slot(&mut self, slot: Slot); } -pub(super) trait ShredData: Shred { +pub(super) trait ShredData: for<'a> Shred<'a> { fn data_header(&self) -> &DataShredHeader; fn parent(&self) -> Result { @@ -52,12 +56,9 @@ pub(super) trait ShredData: Shred { } fn data(&self) -> Result<&[u8], Error>; - - // Only for tests. - fn set_last_in_slot(&mut self); } -pub(super) trait ShredCode: Shred { +pub(super) trait ShredCode: for<'a> Shred<'a> { fn coding_header(&self) -> &CodingShredHeader; fn first_coding_index(&self) -> Option { diff --git a/ledger/src/shredder.rs b/ledger/src/shredder.rs index 22df6b87d7a063..48573207da8f12 100644 --- a/ledger/src/shredder.rs +++ b/ledger/src/shredder.rs @@ -1,30 +1,46 @@ use { crate::shred::{ - Error, ProcessShredsStats, Shred, ShredData, ShredFlags, MAX_DATA_SHREDS_PER_FEC_BLOCK, + self, Error, ProcessShredsStats, Shred, ShredData, ShredFlags, DATA_SHREDS_PER_FEC_BLOCK, }, itertools::Itertools, lazy_static::lazy_static, + lru::LruCache, rayon::{prelude::*, ThreadPool}, reed_solomon_erasure::{ - galois_8::Field, + galois_8::ReedSolomon, Error::{InvalidIndex, TooFewDataShards, TooFewShardsPresent}, }, solana_entry::entry::Entry, solana_measure::measure::Measure, solana_rayon_threadlimit::get_thread_count, solana_sdk::{clock::Slot, signature::Keypair}, - std::{borrow::Borrow, fmt::Debug}, + std::{ + borrow::Borrow, + fmt::Debug, + sync::{Arc, Mutex}, + }, }; lazy_static! { static ref PAR_THREAD_POOL: ThreadPool = rayon::ThreadPoolBuilder::new() .num_threads(get_thread_count()) - .thread_name(|ix| format!("shredder_{}", ix)) + .thread_name(|ix| format!("solShredder{:02}", ix)) .build() .unwrap(); } -type ReedSolomon = reed_solomon_erasure::ReedSolomon; +// Maps number of data shreds to the optimal erasure batch size which has the +// same recovery probabilities as a 32:32 erasure batch. +pub(crate) const ERASURE_BATCH_SIZE: [usize; 33] = [ + 0, 18, 20, 22, 23, 25, 27, 28, 30, // 8 + 32, 33, 35, 36, 38, 39, 41, 42, // 16 + 43, 45, 46, 48, 49, 51, 52, 53, // 24 + 55, 56, 58, 59, 60, 62, 63, 64, // 32 +]; + +pub struct ReedSolomonCache( + Mutex>>, +); #[derive(Debug)] pub struct Shredder { @@ -60,50 +76,51 @@ impl Shredder { is_last_in_slot: bool, next_shred_index: u32, next_code_index: u32, + merkle_variant: bool, + reed_solomon_cache: &ReedSolomonCache, stats: &mut ProcessShredsStats, ) -> ( Vec, // data shreds Vec, // coding shreds ) { - let data_shreds = self.entries_to_data_shreds( - keypair, - entries, - is_last_in_slot, - next_shred_index, - next_shred_index, // fec_set_offset - stats, - ); + if merkle_variant { + return shred::make_merkle_shreds_from_entries( + &PAR_THREAD_POOL, + keypair, + entries, + self.slot, + self.parent_slot, + self.version, + self.reference_tick, + is_last_in_slot, + next_shred_index, + next_code_index, + reed_solomon_cache, + stats, + ) + .unwrap() + .into_iter() + .partition(Shred::is_data); + } + let data_shreds = + self.entries_to_data_shreds(keypair, entries, is_last_in_slot, next_shred_index, stats); let coding_shreds = Self::data_shreds_to_coding_shreds( keypair, &data_shreds, - is_last_in_slot, next_code_index, + reed_solomon_cache, stats, ) .unwrap(); (data_shreds, coding_shreds) } - /// Each FEC block has maximum MAX_DATA_SHREDS_PER_FEC_BLOCK shreds. - /// "FEC set index" is the index of first data shred in that FEC block. - /// **Data** shreds with the same value of: - /// (data_shred.index() - fec_set_offset) / MAX_DATA_SHREDS_PER_FEC_BLOCK - /// belong to the same FEC set. - /// Coding shreds inherit their fec_set_index from the data shreds that - /// they are generated from. - pub fn fec_set_index(data_shred_index: u32, fec_set_offset: u32) -> Option { - let diff = data_shred_index.checked_sub(fec_set_offset)?; - Some(data_shred_index - diff % MAX_DATA_SHREDS_PER_FEC_BLOCK) - } - - pub fn entries_to_data_shreds( + fn entries_to_data_shreds( &self, keypair: &Keypair, entries: &[Entry], is_last_in_slot: bool, next_shred_index: u32, - // Shred index offset at which FEC sets are generated. - fec_set_offset: u32, process_stats: &mut ProcessShredsStats, ) -> Vec { let mut serialize_time = Measure::start("shred_serialize"); @@ -119,7 +136,7 @@ impl Shredder { let num_shreds = (serialized_shreds.len() + data_buffer_size - 1) / data_buffer_size; let last_shred_index = next_shred_index + num_shreds as u32 - 1; // 1) Generate data shreds - let make_data_shred = |shred_index: u32, data| { + let make_data_shred = |data, shred_index: u32, fec_set_index: u32| { let flags = if shred_index != last_shred_index { ShredFlags::empty() } else if is_last_in_slot { @@ -129,7 +146,6 @@ impl Shredder { ShredFlags::DATA_COMPLETE_SHRED }; let parent_offset = self.slot - self.parent_slot; - let fec_set_index = Self::fec_set_index(shred_index, fec_set_offset); let mut shred = Shred::new_from_data( self.slot, shred_index, @@ -138,18 +154,24 @@ impl Shredder { flags, self.reference_tick, self.version, - fec_set_index.unwrap(), + fec_set_index, ); shred.sign(keypair); shred }; - let data_shreds: Vec = PAR_THREAD_POOL.install(|| { - serialized_shreds - .par_chunks(data_buffer_size) + let shreds: Vec<&[u8]> = serialized_shreds.chunks(data_buffer_size).collect(); + let fec_set_offsets: Vec = + get_fec_set_offsets(shreds.len(), DATA_SHREDS_PER_FEC_BLOCK).collect(); + assert_eq!(shreds.len(), fec_set_offsets.len()); + let shreds: Vec = PAR_THREAD_POOL.install(|| { + shreds + .into_par_iter() + .zip(fec_set_offsets) .enumerate() - .map(|(i, shred_data)| { + .map(|(i, (shred, offset))| { let shred_index = next_shred_index + i as u32; - make_data_shred(shred_index, shred_data) + let fec_set_index = next_shred_index + offset as u32; + make_data_shred(shred, shred_index, fec_set_index) }) .collect() }); @@ -157,16 +179,16 @@ impl Shredder { process_stats.serialize_elapsed += serialize_time.as_us(); process_stats.gen_data_elapsed += gen_data_time.as_us(); - process_stats.record_num_data_shreds(data_shreds.len()); + process_stats.record_num_data_shreds(shreds.len()); - data_shreds + shreds } - pub fn data_shreds_to_coding_shreds( + fn data_shreds_to_coding_shreds( keypair: &Keypair, data_shreds: &[Shred], - is_last_in_slot: bool, next_code_index: u32, + reed_solomon_cache: &ReedSolomonCache, process_stats: &mut ProcessShredsStats, ) -> Result, Error> { if data_shreds.is_empty() { @@ -185,23 +207,36 @@ impl Shredder { .iter() .scan(next_code_index, |next_code_index, chunk| { let num_data_shreds = chunk.len(); - let erasure_batch_size = - get_erasure_batch_size(num_data_shreds, is_last_in_slot); + let erasure_batch_size = get_erasure_batch_size(num_data_shreds); *next_code_index += (erasure_batch_size - num_data_shreds) as u32; Some(*next_code_index) }), ) .collect(); // 1) Generate coding shreds - let mut coding_shreds: Vec<_> = PAR_THREAD_POOL.install(|| { + let mut coding_shreds: Vec<_> = if chunks.len() <= 1 { chunks - .into_par_iter() + .into_iter() .zip(next_code_index) .flat_map(|(shreds, next_code_index)| { - Shredder::generate_coding_shreds(&shreds, is_last_in_slot, next_code_index) + Shredder::generate_coding_shreds(&shreds, next_code_index, reed_solomon_cache) }) .collect() - }); + } else { + PAR_THREAD_POOL.install(|| { + chunks + .into_par_iter() + .zip(next_code_index) + .flat_map(|(shreds, next_code_index)| { + Shredder::generate_coding_shreds( + &shreds, + next_code_index, + reed_solomon_cache, + ) + }) + .collect() + }) + }; gen_coding_time.stop(); let mut sign_coding_time = Measure::start("sign_coding_shreds"); @@ -221,8 +256,8 @@ impl Shredder { /// Generates coding shreds for the data shreds in the current FEC set pub fn generate_coding_shreds>( data: &[T], - is_last_in_slot: bool, next_code_index: u32, + reed_solomon_cache: &ReedSolomonCache, ) -> Vec { let (slot, index, version, fec_set_index) = { let shred = data.first().unwrap().borrow(); @@ -241,9 +276,10 @@ impl Shredder { && shred.version() == version && shred.fec_set_index() == fec_set_index)); let num_data = data.len(); - let num_coding = get_erasure_batch_size(num_data, is_last_in_slot) + let num_coding = get_erasure_batch_size(num_data) .checked_sub(num_data) .unwrap(); + assert!(num_coding > 0); let data: Vec<_> = data .iter() .map(Borrow::borrow) @@ -251,7 +287,8 @@ impl Shredder { .collect::>() .unwrap(); let mut parity = vec![vec![0u8; data[0].len()]; num_coding]; - ReedSolomon::new(num_data, num_coding) + reed_solomon_cache + .get(num_data, num_coding) .unwrap() .encode_sep(&data, &mut parity[..]) .unwrap(); @@ -276,7 +313,10 @@ impl Shredder { .collect() } - pub fn try_recovery(shreds: Vec) -> Result, Error> { + pub fn try_recovery( + shreds: Vec, + reed_solomon_cache: &ReedSolomonCache, + ) -> Result, Error> { let (slot, fec_set_index) = match shreds.first() { None => return Err(Error::from(TooFewShardsPresent)), Some(shred) => (shred.slot(), shred.fec_set_index()), @@ -316,7 +356,9 @@ impl Shredder { mask[index] = true; } } - ReedSolomon::new(num_data_shreds, num_coding_shreds)?.reconstruct_data(&mut shards)?; + reed_solomon_cache + .get(num_data_shreds, num_coding_shreds)? + .reconstruct_data(&mut shards)?; let recovered_data = mask .into_iter() .zip(shards) @@ -359,22 +401,76 @@ impl Shredder { } } -/// Maps number of data shreds in each batch to the erasure batch size. -fn get_erasure_batch_size(num_data_shreds: usize, is_last_in_slot: bool) -> usize { - if is_last_in_slot { - 2 * num_data_shreds.max(MAX_DATA_SHREDS_PER_FEC_BLOCK as usize) - } else { - 2 * num_data_shreds +impl ReedSolomonCache { + const CAPACITY: usize = 4 * DATA_SHREDS_PER_FEC_BLOCK; + + pub(crate) fn get( + &self, + data_shards: usize, + parity_shards: usize, + ) -> Result, reed_solomon_erasure::Error> { + let key = (data_shards, parity_shards); + { + let mut cache = self.0.lock().unwrap(); + if let Some(entry) = cache.get(&key) { + return Ok(entry.clone()); + } + } + let entry = ReedSolomon::new(data_shards, parity_shards)?; + let entry = Arc::new(entry); + { + let entry = entry.clone(); + let mut cache = self.0.lock().unwrap(); + cache.put(key, entry); + } + Ok(entry) + } +} + +impl Default for ReedSolomonCache { + fn default() -> Self { + Self(Mutex::new(LruCache::new(Self::CAPACITY))) } } +/// Maps number of data shreds in each batch to the erasure batch size. +pub(crate) fn get_erasure_batch_size(num_data_shreds: usize) -> usize { + ERASURE_BATCH_SIZE + .get(num_data_shreds) + .copied() + .unwrap_or(2 * num_data_shreds) +} + +// Returns offsets to fec_set_index when spliting shreds into erasure batches. +fn get_fec_set_offsets( + mut num_shreds: usize, + min_chunk_size: usize, +) -> impl Iterator { + let mut offset = 0; + std::iter::from_fn(move || { + if num_shreds == 0 { + return None; + } + let num_chunks = (num_shreds / min_chunk_size).max(1); + let chunk_size = (num_shreds + num_chunks - 1) / num_chunks; + let offsets = std::iter::repeat(offset).take(chunk_size); + num_shreds -= chunk_size; + offset += chunk_size; + Some(offsets) + }) + .flatten() +} + #[cfg(test)] mod tests { use { super::*, - crate::shred::{ - self, max_entries_per_n_shred, max_ticks_per_n_shreds, verify_test_data_shred, - ShredType, + crate::{ + blockstore::MAX_DATA_SHREDS_PER_SLOT, + shred::{ + self, max_entries_per_n_shred, max_ticks_per_n_shreds, verify_test_data_shred, + ShredType, MAX_CODE_SHREDS_PER_SLOT, + }, }, bincode::serialized_size, matches::assert_matches, @@ -427,8 +523,7 @@ mod tests { let data_buffer_size = ShredData::capacity(/*merkle_proof_size:*/ None).unwrap(); let num_expected_data_shreds = (size + data_buffer_size - 1) / data_buffer_size; let num_expected_coding_shreds = - get_erasure_batch_size(num_expected_data_shreds, /*is_last_in_slot:*/ true) - - num_expected_data_shreds; + get_erasure_batch_size(num_expected_data_shreds) - num_expected_data_shreds; let start_index = 0; let (data_shreds, coding_shreds) = shredder.entries_to_shreds( &keypair, @@ -436,6 +531,8 @@ mod tests { true, // is_last_in_slot start_index, // next_shred_index start_index, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); let next_index = data_shreds.last().unwrap().index() + 1; @@ -513,6 +610,8 @@ mod tests { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); let deserialized_shred = @@ -543,6 +642,8 @@ mod tests { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); data_shreds.iter().for_each(|s| { @@ -578,6 +679,8 @@ mod tests { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); data_shreds.iter().for_each(|s| { @@ -622,6 +725,8 @@ mod tests { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); for (i, s) in data_shreds.iter().enumerate() { @@ -669,13 +774,16 @@ mod tests { }) .collect(); + let reed_solomon_cache = ReedSolomonCache::default(); let serialized_entries = bincode::serialize(&entries).unwrap(); let (data_shreds, coding_shreds) = shredder.entries_to_shreds( &keypair, &entries, is_last_in_slot, - 0, // next_shred_index - 0, // next_code_index + 0, // next_shred_index + 0, // next_code_index + false, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); let num_coding_shreds = coding_shreds.len(); @@ -684,7 +792,7 @@ mod tests { assert_eq!(data_shreds.len(), num_data_shreds); assert_eq!( num_coding_shreds, - get_erasure_batch_size(num_data_shreds, is_last_in_slot) - num_data_shreds + get_erasure_batch_size(num_data_shreds) - num_data_shreds ); let all_shreds = data_shreds @@ -695,12 +803,17 @@ mod tests { // Test0: Try recovery/reassembly with only data shreds, but not all data shreds. Hint: should fail assert_eq!( - Shredder::try_recovery(data_shreds[..data_shreds.len() - 1].to_vec()).unwrap(), + Shredder::try_recovery( + data_shreds[..data_shreds.len() - 1].to_vec(), + &reed_solomon_cache + ) + .unwrap(), Vec::default() ); // Test1: Try recovery/reassembly with only data shreds. Hint: should work - let recovered_data = Shredder::try_recovery(data_shreds[..].to_vec()).unwrap(); + let recovered_data = + Shredder::try_recovery(data_shreds[..].to_vec(), &reed_solomon_cache).unwrap(); assert!(recovered_data.is_empty()); // Test2: Try recovery/reassembly with missing data shreds + coding shreds. Hint: should work @@ -710,7 +823,8 @@ mod tests { .filter_map(|(i, b)| if i % 2 == 0 { Some(b.clone()) } else { None }) .collect(); - let mut recovered_data = Shredder::try_recovery(shred_info.clone()).unwrap(); + let mut recovered_data = + Shredder::try_recovery(shred_info.clone(), &reed_solomon_cache).unwrap(); assert_eq!(recovered_data.len(), 2); // Data shreds 1 and 3 were missing let recovered_shred = recovered_data.remove(0); @@ -750,7 +864,8 @@ mod tests { .filter_map(|(i, b)| if i % 2 != 0 { Some(b.clone()) } else { None }) .collect(); - let recovered_data = Shredder::try_recovery(shred_info.clone()).unwrap(); + let recovered_data = + Shredder::try_recovery(shred_info.clone(), &reed_solomon_cache).unwrap(); assert_eq!(recovered_data.len(), 3); // Data shreds 0, 2, 4 were missing for (i, recovered_shred) in recovered_data.into_iter().enumerate() { @@ -801,9 +916,11 @@ mod tests { let (data_shreds, coding_shreds) = shredder.entries_to_shreds( &keypair, &entries, - true, // is_last_in_slot - 25, // next_shred_index, - 25, // next_code_index + true, // is_last_in_slot + 25, // next_shred_index, + 25, // next_code_index + false, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); // We should have 10 shreds now @@ -821,7 +938,8 @@ mod tests { .filter_map(|(i, b)| if i % 2 != 0 { Some(b.clone()) } else { None }) .collect(); - let recovered_data = Shredder::try_recovery(shred_info.clone()).unwrap(); + let recovered_data = + Shredder::try_recovery(shred_info.clone(), &reed_solomon_cache).unwrap(); assert_eq!(recovered_data.len(), 3); // Data shreds 25, 27, 29 were missing for (i, recovered_shred) in recovered_data.into_iter().enumerate() { @@ -845,7 +963,8 @@ mod tests { assert_eq!(serialized_entries[..], result[..serialized_entries.len()]); // Test6: Try recovery/reassembly with incorrect slot. Hint: does not recover any shreds - let recovered_data = Shredder::try_recovery(shred_info.clone()).unwrap(); + let recovered_data = + Shredder::try_recovery(shred_info.clone(), &reed_solomon_cache).unwrap(); assert!(recovered_data.is_empty()); } @@ -889,12 +1008,15 @@ mod tests { ) .unwrap(); let next_shred_index = rng.gen_range(1, 1024); + let reed_solomon_cache = ReedSolomonCache::default(); let (data_shreds, coding_shreds) = shredder.entries_to_shreds( &keypair, &[entry], is_last_in_slot, next_shred_index, next_shred_index, // next_code_index + false, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); let num_data_shreds = data_shreds.len(); @@ -914,7 +1036,7 @@ mod tests { .filter(|shred| shred.is_data()) .map(|shred| shred.index()) .collect(); - let recovered_shreds = Shredder::try_recovery(shreds).unwrap(); + let recovered_shreds = Shredder::try_recovery(shreds, &reed_solomon_cache).unwrap(); assert_eq!( recovered_shreds, data_shreds @@ -955,6 +1077,8 @@ mod tests { true, // is_last_in_slot 0, // next_shred_index 0, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); assert!(!data_shreds @@ -987,21 +1111,38 @@ mod tests { true, // is_last_in_slot start_index, // next_shred_index start_index, // next_code_index + true, // merkle_variant + &ReedSolomonCache::default(), &mut ProcessShredsStats::default(), ); - let max_per_block = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize; - data_shreds.iter().enumerate().for_each(|(i, s)| { - let expected_fec_set_index = start_index + (i - i % max_per_block) as u32; - assert_eq!(s.fec_set_index(), expected_fec_set_index); - }); - - coding_shreds.iter().enumerate().for_each(|(i, s)| { - let mut expected_fec_set_index = start_index + (i - i % max_per_block) as u32; - while expected_fec_set_index as usize - start_index as usize > data_shreds.len() { - expected_fec_set_index -= max_per_block as u32; - } - assert_eq!(s.fec_set_index(), expected_fec_set_index); - }); + const MIN_CHUNK_SIZE: usize = DATA_SHREDS_PER_FEC_BLOCK; + let chunks: Vec<_> = data_shreds + .iter() + .group_by(|shred| shred.fec_set_index()) + .into_iter() + .map(|(fec_set_index, chunk)| (fec_set_index, chunk.count())) + .collect(); + assert!(chunks + .iter() + .all(|(_, chunk_size)| *chunk_size >= MIN_CHUNK_SIZE)); + assert!(chunks + .iter() + .all(|(_, chunk_size)| *chunk_size < 2 * MIN_CHUNK_SIZE)); + assert_eq!(chunks[0].0, start_index); + assert!(chunks.iter().tuple_windows().all( + |((fec_set_index, chunk_size), (next_fec_set_index, _chunk_size))| fec_set_index + + *chunk_size as u32 + == *next_fec_set_index + )); + assert!(coding_shreds.len() >= data_shreds.len()); + assert!(coding_shreds + .iter() + .zip(&data_shreds) + .all(|(code, data)| code.fec_set_index() == data.fec_set_index())); + assert_eq!( + coding_shreds.last().unwrap().fec_set_index(), + data_shreds.last().unwrap().fec_set_index() + ); } #[test] @@ -1028,42 +1169,75 @@ mod tests { &entries, true, // is_last_in_slot start_index, - start_index, // fec_set_offset &mut stats, ); - assert!(data_shreds.len() > MAX_DATA_SHREDS_PER_FEC_BLOCK as usize); let next_code_index = data_shreds[0].index(); + let reed_solomon_cache = ReedSolomonCache::default(); - (1..=MAX_DATA_SHREDS_PER_FEC_BLOCK as usize).for_each(|count| { - for is_last_in_slot in [false, true] { - let coding_shreds = Shredder::data_shreds_to_coding_shreds( - &keypair, - &data_shreds[..count], - is_last_in_slot, - next_code_index, - &mut stats, - ) - .unwrap(); - let num_coding_shreds = get_erasure_batch_size(count, is_last_in_slot) - count; - assert_eq!(coding_shreds.len(), num_coding_shreds); - } - }); - for is_last_in_slot in [false, true] { + for size in (1..data_shreds.len()).step_by(5) { + let data_shreds = &data_shreds[..size]; let coding_shreds = Shredder::data_shreds_to_coding_shreds( &keypair, - &data_shreds[..MAX_DATA_SHREDS_PER_FEC_BLOCK as usize + 1], - is_last_in_slot, + data_shreds, next_code_index, + &reed_solomon_cache, &mut stats, ) .unwrap(); - let num_shreds = - get_erasure_batch_size(MAX_DATA_SHREDS_PER_FEC_BLOCK as usize, is_last_in_slot) - + get_erasure_batch_size(1, is_last_in_slot); + let num_shreds: usize = data_shreds + .iter() + .group_by(|shred| shred.fec_set_index()) + .into_iter() + .map(|(_, chunk)| get_erasure_batch_size(chunk.count())) + .sum(); + assert_eq!(coding_shreds.len(), num_shreds - data_shreds.len()); + } + } + + #[test] + fn test_get_fec_set_offsets() { + const MIN_CHUNK_SIZE: usize = 32usize; + for num_shreds in 0usize..MIN_CHUNK_SIZE { + let offsets: Vec<_> = get_fec_set_offsets(num_shreds, MIN_CHUNK_SIZE).collect(); + assert_eq!(offsets, vec![0usize; num_shreds]); + } + for num_shreds in MIN_CHUNK_SIZE..MIN_CHUNK_SIZE * 8 { + let chunks: Vec<_> = get_fec_set_offsets(num_shreds, MIN_CHUNK_SIZE) + .group_by(|offset| *offset) + .into_iter() + .map(|(offset, chunk)| (offset, chunk.count())) + .collect(); assert_eq!( - coding_shreds.len(), - num_shreds - MAX_DATA_SHREDS_PER_FEC_BLOCK as usize - 1 + chunks + .iter() + .map(|(_offset, chunk_size)| chunk_size) + .sum::(), + num_shreds + ); + assert!(chunks + .iter() + .all(|(_offset, chunk_size)| *chunk_size >= MIN_CHUNK_SIZE)); + assert!(chunks + .iter() + .all(|(_offset, chunk_size)| *chunk_size < 2 * MIN_CHUNK_SIZE)); + assert_eq!(chunks[0].0, 0); + assert!(chunks.iter().tuple_windows().all( + |((offset, chunk_size), (next_offset, _chunk_size))| offset + chunk_size + == *next_offset + )); + } + } + + #[test] + fn test_max_shreds_per_slot() { + for num_data_shreds in 32..128 { + let num_coding_shreds = get_erasure_batch_size(num_data_shreds) + .checked_sub(num_data_shreds) + .unwrap(); + assert!( + MAX_DATA_SHREDS_PER_SLOT * num_coding_shreds + <= MAX_CODE_SHREDS_PER_SLOT * num_data_shreds ); } } diff --git a/ledger/src/sigverify_shreds.rs b/ledger/src/sigverify_shreds.rs index aecad26aa6c311..5dc91ac9bded6a 100644 --- a/ledger/src/sigverify_shreds.rs +++ b/ledger/src/sigverify_shreds.rs @@ -1,7 +1,7 @@ #![allow(clippy::implicit_hasher)] use { crate::shred, - itertools::Itertools, + itertools::{izip, Itertools}, rayon::{prelude::*, ThreadPool}, sha2::{Digest, Sha512}, solana_metrics::inc_new_counter_debug, @@ -12,25 +12,21 @@ use { recycler_cache::RecyclerCache, sigverify::{self, count_packets_in_batches, TxOffset}, }, - solana_rayon_threadlimit::get_thread_count, solana_sdk::{ clock::Slot, + hash::Hash, pubkey::Pubkey, signature::{Keypair, Signature, Signer}, }, + static_assertions::const_assert_eq, std::{collections::HashMap, fmt::Debug, iter::repeat, mem::size_of, ops::Range, sync::Arc}, }; const SIGN_SHRED_GPU_MIN: usize = 256; +const_assert_eq!(SIZE_OF_MERKLE_ROOT, 32); +const SIZE_OF_MERKLE_ROOT: usize = std::mem::size_of::(); -lazy_static! { - static ref SIGVERIFY_THREAD_POOL: ThreadPool = rayon::ThreadPoolBuilder::new() - .num_threads(get_thread_count()) - .thread_name(|ix| format!("sigverify_shreds_{}", ix)) - .build() - .unwrap(); -} - +#[must_use] pub fn verify_shred_cpu( packet: &Packet, slot_leaders: &HashMap, @@ -56,21 +52,21 @@ pub fn verify_shred_cpu( Some(signature) => signature, }; trace!("signature {}", signature); - let message = - match shred::layout::get_signed_message_range(shred).and_then(|slice| shred.get(slice)) { - None => return false, - Some(message) => message, - }; - signature.verify(pubkey, message) + let data = match shred::layout::get_signed_data(shred) { + None => return false, + Some(data) => data, + }; + signature.verify(pubkey, data.as_ref()) } fn verify_shreds_cpu( + thread_pool: &ThreadPool, batches: &[PacketBatch], slot_leaders: &HashMap, ) -> Vec> { let packet_count = count_packets_in_batches(batches); debug!("CPU SHRED ECDSA for {}", packet_count); - let rv = SIGVERIFY_THREAD_POOL.install(|| { + let rv = thread_pool.install(|| { batches .into_par_iter() .map(|batch| { @@ -86,17 +82,17 @@ fn verify_shreds_cpu( } fn slot_key_data_for_gpu( - offset_start: usize, + thread_pool: &ThreadPool, batches: &[PacketBatch], slot_keys: &HashMap, recycler_cache: &RecyclerCache, -) -> (PinnedVec, TxOffset, usize) +) -> (/*pubkeys:*/ PinnedVec, TxOffset) where T: AsRef<[u8]> + Copy + Debug + Default + Eq + std::hash::Hash + Sync, { //TODO: mark Pubkey::default shreds as failed after the GPU returns assert_eq!(slot_keys.get(&Slot::MAX), Some(&T::default())); - let slots: Vec = SIGVERIFY_THREAD_POOL.install(|| { + let slots: Vec = thread_pool.install(|| { batches .into_par_iter() .flat_map_iter(|batch| { @@ -115,56 +111,108 @@ where }); let keys_to_slots: HashMap> = slots .iter() - .map(|slot| (*slot_keys.get(slot).unwrap(), *slot)) + .map(|slot| (slot_keys[slot], *slot)) .into_group_map(); let mut keyvec = recycler_cache.buffer().allocate("shred_gpu_pubkeys"); keyvec.set_pinnable(); let keyvec_size = keys_to_slots.len() * size_of::(); - keyvec.resize(keyvec_size, 0); - - let slot_to_key_ix: HashMap = keys_to_slots - .into_iter() - .enumerate() - .flat_map(|(i, (k, slots))| { - let start = i * size_of::(); - let end = start + size_of::(); - keyvec[start..end].copy_from_slice(k.as_ref()); - slots.into_iter().zip(repeat(i)) - }) - .collect(); - + resize_buffer(&mut keyvec, keyvec_size); + + let key_offsets: HashMap = { + let mut next_offset = 0; + keys_to_slots + .into_iter() + .flat_map(|(key, slots)| { + let offset = next_offset; + next_offset += std::mem::size_of::(); + keyvec[offset..next_offset].copy_from_slice(key.as_ref()); + slots.into_iter().zip(repeat(offset)) + }) + .collect() + }; let mut offsets = recycler_cache.offsets().allocate("shred_offsets"); offsets.set_pinnable(); for slot in slots { - let key_offset = slot_to_key_ix.get(&slot).unwrap() * size_of::(); - offsets.push((offset_start + key_offset) as u32); + offsets.push(key_offsets[&slot] as u32); } - let num_in_packets = resize_vec(&mut keyvec); trace!("keyvec.len: {}", keyvec.len()); trace!("keyvec: {:?}", keyvec); trace!("offsets: {:?}", offsets); - (keyvec, offsets, num_in_packets) + (keyvec, offsets) } -fn vec_size_in_packets(keyvec: &PinnedVec) -> usize { - (keyvec.len() + (size_of::() - 1)) / size_of::() +// Recovers merkle roots from shreds binary. +fn get_merkle_roots( + thread_pool: &ThreadPool, + packets: &[PacketBatch], + recycler_cache: &RecyclerCache, +) -> ( + PinnedVec, // Merkle roots + Vec>, // Offsets +) { + let merkle_roots: Vec> = thread_pool.install(|| { + packets + .par_iter() + .flat_map(|packets| { + packets.par_iter().map(|packet| { + if packet.meta.discard() { + return None; + } + let shred = shred::layout::get_shred(packet)?; + shred::layout::get_merkle_root(shred) + }) + }) + .collect() + }); + let num_merkle_roots = merkle_roots.iter().flatten().count(); + let mut buffer = recycler_cache.buffer().allocate("shred_gpu_merkle_roots"); + buffer.set_pinnable(); + resize_buffer(&mut buffer, num_merkle_roots * SIZE_OF_MERKLE_ROOT); + let offsets = { + let mut next_offset = 0; + merkle_roots + .into_iter() + .map(|root| { + let root = root?; + let offset = next_offset; + next_offset += SIZE_OF_MERKLE_ROOT; + buffer[offset..next_offset].copy_from_slice(root.as_ref()); + Some(offset) + }) + .collect() + }; + (buffer, offsets) } -fn resize_vec(keyvec: &mut PinnedVec) -> usize { +// Resizes the buffer to >= size and a multiple of +// std::mem::size_of::(). +fn resize_buffer(buffer: &mut PinnedVec, size: usize) { //HACK: Pubkeys vector is passed along as a `PacketBatch` buffer to the GPU //TODO: GPU needs a more opaque interface, which can handle variable sized structures for data //Pad the Pubkeys buffer such that it is bigger than a buffer of Packet sized elems - let num_in_packets = (keyvec.len() + (size_of::() - 1)) / size_of::(); - keyvec.resize(num_in_packets * size_of::(), 0u8); - num_in_packets + let num_packets = (size + std::mem::size_of::() - 1) / std::mem::size_of::(); + let size = num_packets * std::mem::size_of::(); + buffer.resize(size, 0u8); +} + +fn elems_from_buffer(buffer: &PinnedVec) -> perf_libs::Elems { + // resize_buffer ensures that buffer size is a multiple of Packet size. + debug_assert_eq!(buffer.len() % std::mem::size_of::(), 0); + let num_packets = buffer.len() / std::mem::size_of::(); + perf_libs::Elems { + #[allow(clippy::cast_ptr_alignment)] + elems: buffer.as_ptr() as *const solana_sdk::packet::Packet, + num: num_packets as u32, + } } fn shred_gpu_offsets( - mut pubkeys_end: usize, + offset: usize, batches: &[PacketBatch], + merkle_roots_offsets: impl IntoIterator>, recycler_cache: &RecyclerCache, -) -> (TxOffset, TxOffset, TxOffset, Vec>) { +) -> (TxOffset, TxOffset, TxOffset) { fn add_offset(range: Range, offset: usize) -> Range { range.start + offset..range.end + offset } @@ -174,73 +222,72 @@ fn shred_gpu_offsets( msg_start_offsets.set_pinnable(); let mut msg_sizes = recycler_cache.offsets().allocate("shred_msg_sizes"); msg_sizes.set_pinnable(); - let mut v_sig_lens = vec![]; - for batch in batches.iter() { - let mut sig_lens = Vec::new(); - for packet in batch.iter() { - let sig = shred::layout::get_signature_range(); - let sig = add_offset(sig, pubkeys_end); - debug_assert_eq!(sig.end - sig.start, std::mem::size_of::()); - let shred = shred::layout::get_shred(packet); - // Signature may verify for an empty message but the packet will be - // discarded during deserialization. - let msg = shred.and_then(shred::layout::get_signed_message_range); - let msg = add_offset(msg.unwrap_or_default(), pubkeys_end); - signature_offsets.push(sig.start as u32); - msg_start_offsets.push(msg.start as u32); - let msg_size = msg.end.saturating_sub(msg.start); - msg_sizes.push(msg_size as u32); - sig_lens.push(1); - pubkeys_end += size_of::(); - } - v_sig_lens.push(sig_lens); + let offsets = std::iter::successors(Some(offset), |offset| { + offset.checked_add(std::mem::size_of::()) + }); + let packets = batches.iter().flatten(); + for (offset, packet, merkle_root_offset) in izip!(offsets, packets, merkle_roots_offsets) { + let sig = shred::layout::get_signature_range(); + let sig = add_offset(sig, offset); + debug_assert_eq!(sig.end - sig.start, std::mem::size_of::()); + // Signature may verify for an empty message but the packet will be + // discarded during deserialization. + let msg: Range = match merkle_root_offset { + None => { + let shred = shred::layout::get_shred(packet); + let msg = shred.and_then(shred::layout::get_signed_data_offsets); + add_offset(msg.unwrap_or_default(), offset) + } + Some(merkle_root_offset) => { + merkle_root_offset..merkle_root_offset + SIZE_OF_MERKLE_ROOT + } + }; + signature_offsets.push(sig.start as u32); + msg_start_offsets.push(msg.start as u32); + let msg_size = msg.end.saturating_sub(msg.start); + msg_sizes.push(msg_size as u32); } - (signature_offsets, msg_start_offsets, msg_sizes, v_sig_lens) + (signature_offsets, msg_start_offsets, msg_sizes) } pub fn verify_shreds_gpu( + thread_pool: &ThreadPool, batches: &[PacketBatch], slot_leaders: &HashMap, recycler_cache: &RecyclerCache, ) -> Vec> { - let api = perf_libs::api(); - if api.is_none() { - return verify_shreds_cpu(batches, slot_leaders); - } - let api = api.unwrap(); - - let mut elems = Vec::new(); - let mut rvs = Vec::new(); - let packet_count = count_packets_in_batches(batches); - let (pubkeys, pubkey_offsets, mut num_packets) = - slot_key_data_for_gpu(0, batches, slot_leaders, recycler_cache); + let api = match perf_libs::api() { + None => return verify_shreds_cpu(thread_pool, batches, slot_leaders), + Some(api) => api, + }; + let (pubkeys, pubkey_offsets) = + slot_key_data_for_gpu(thread_pool, batches, slot_leaders, recycler_cache); //HACK: Pubkeys vector is passed along as a `PacketBatch` buffer to the GPU //TODO: GPU needs a more opaque interface, which can handle variable sized structures for data - let pubkeys_len = num_packets * size_of::(); - trace!("num_packets: {}", num_packets); - trace!("pubkeys_len: {}", pubkeys_len); - let (signature_offsets, msg_start_offsets, msg_sizes, v_sig_lens) = - shred_gpu_offsets(pubkeys_len, batches, recycler_cache); + let (merkle_roots, merkle_roots_offsets) = + get_merkle_roots(thread_pool, batches, recycler_cache); + // Merkle roots are placed after pubkeys; adjust offsets accordingly. + let merkle_roots_offsets = { + let shift = pubkeys.len(); + merkle_roots_offsets + .into_iter() + .map(move |offset| Some(offset? + shift)) + }; + let offset = pubkeys.len() + merkle_roots.len(); + let (signature_offsets, msg_start_offsets, msg_sizes) = + shred_gpu_offsets(offset, batches, merkle_roots_offsets, recycler_cache); let mut out = recycler_cache.buffer().allocate("out_buffer"); out.set_pinnable(); - elems.push(perf_libs::Elems { - #[allow(clippy::cast_ptr_alignment)] - elems: pubkeys.as_ptr() as *const solana_sdk::packet::Packet, - num: num_packets as u32, - }); - - for batch in batches { - elems.push(perf_libs::Elems { - elems: batch.as_ptr(), - num: batch.len() as u32, - }); - let mut v = Vec::new(); - v.resize(batch.len(), 0); - rvs.push(v); - num_packets += batch.len(); - } - out.resize(signature_offsets.len(), 0); - + out.resize(signature_offsets.len(), 0u8); + let mut elems = vec![ + elems_from_buffer(&pubkeys), + elems_from_buffer(&merkle_roots), + ]; + elems.extend(batches.iter().map(|batch| perf_libs::Elems { + elems: batch.as_ptr(), + num: batch.len() as u32, + })); + let num_packets = elems.iter().map(|elem| elem.num).sum(); trace!("Starting verify num packets: {}", num_packets); trace!("elem len: {}", elems.len() as u32); trace!("packet sizeof: {}", size_of::() as u32); @@ -250,7 +297,7 @@ pub fn verify_shreds_gpu( elems.as_ptr(), elems.len() as u32, size_of::() as u32, - num_packets as u32, + num_packets, signature_offsets.len() as u32, msg_sizes.as_ptr(), pubkey_offsets.as_ptr(), @@ -266,37 +313,40 @@ pub fn verify_shreds_gpu( trace!("done verify"); trace!("out buf {:?}", out); - sigverify::copy_return_values(&v_sig_lens, &out, &mut rvs); + // Each shred has exactly one signature. + let v_sig_lens = batches.iter().map(|batch| repeat(1u32).take(batch.len())); + let mut rvs: Vec<_> = batches.iter().map(|batch| vec![0u8; batch.len()]).collect(); + sigverify::copy_return_values(v_sig_lens, &out, &mut rvs); - inc_new_counter_debug!("ed25519_shred_verify_gpu", packet_count); + inc_new_counter_debug!("ed25519_shred_verify_gpu", out.len()); rvs } fn sign_shred_cpu(keypair: &Keypair, packet: &mut Packet) { let sig = shred::layout::get_signature_range(); let msg = shred::layout::get_shred(packet) - .and_then(shred::layout::get_signed_message_range) + .and_then(shred::layout::get_signed_data) .unwrap(); assert!( packet.meta.size >= sig.end, "packet is not large enough for a signature" ); - let signature = keypair.sign_message(packet.data(msg).unwrap()); + let signature = keypair.sign_message(msg.as_ref()); trace!("signature {:?}", signature); packet.buffer_mut()[sig].copy_from_slice(signature.as_ref()); } -pub fn sign_shreds_cpu(keypair: &Keypair, batches: &mut [PacketBatch]) { +pub fn sign_shreds_cpu(thread_pool: &ThreadPool, keypair: &Keypair, batches: &mut [PacketBatch]) { let packet_count = count_packets_in_batches(batches); debug!("CPU SHRED ECDSA for {}", packet_count); - SIGVERIFY_THREAD_POOL.install(|| { + thread_pool.install(|| { batches.par_iter_mut().for_each(|batch| { batch[..] .par_iter_mut() .for_each(|p| sign_shred_cpu(keypair, p)); }); }); - inc_new_counter_debug!("ed25519_shred_verify_cpu", packet_count); + inc_new_counter_debug!("ed25519_shred_sign_cpu", packet_count); } pub fn sign_shreds_gpu_pinned_keypair(keypair: &Keypair, cache: &RecyclerCache) -> PinnedVec { @@ -304,19 +354,20 @@ pub fn sign_shreds_gpu_pinned_keypair(keypair: &Keypair, cache: &RecyclerCache) let pubkey = keypair.pubkey().to_bytes(); let secret = keypair.secret().to_bytes(); let mut hasher = Sha512::default(); - hasher.update(&secret); + hasher.update(secret); let mut result = hasher.finalize(); result[0] &= 248; result[31] &= 63; result[31] |= 64; - vec.resize(pubkey.len() + result.len(), 0); + let size = pubkey.len() + result.len(); + resize_buffer(&mut vec, size); vec[0..pubkey.len()].copy_from_slice(&pubkey); - vec[pubkey.len()..].copy_from_slice(&result); - resize_vec(&mut vec); + vec[pubkey.len()..size].copy_from_slice(&result); vec } pub fn sign_shreds_gpu( + thread_pool: &ThreadPool, keypair: &Keypair, pinned_keypair: &Option>>, batches: &mut [PacketBatch], @@ -324,19 +375,16 @@ pub fn sign_shreds_gpu( ) { let sig_size = size_of::(); let pubkey_size = size_of::(); - let api = perf_libs::api(); let packet_count = count_packets_in_batches(batches); - if api.is_none() || packet_count < SIGN_SHRED_GPU_MIN || pinned_keypair.is_none() { - return sign_shreds_cpu(keypair, batches); + if packet_count < SIGN_SHRED_GPU_MIN || pinned_keypair.is_none() { + return sign_shreds_cpu(thread_pool, keypair, batches); } - let api = api.unwrap(); + let api = match perf_libs::api() { + None => return sign_shreds_cpu(thread_pool, keypair, batches), + Some(api) => api, + }; let pinned_keypair = pinned_keypair.as_ref().unwrap(); - let mut elems = Vec::new(); - let offset: usize = pinned_keypair.len(); - let num_keypair_packets = vec_size_in_packets(pinned_keypair); - let mut num_packets = num_keypair_packets; - //should be zero let mut pubkey_offsets = recycler_cache.offsets().allocate("pubkey offsets"); pubkey_offsets.resize(packet_count, 0); @@ -344,29 +392,33 @@ pub fn sign_shreds_gpu( let mut secret_offsets = recycler_cache.offsets().allocate("secret_offsets"); secret_offsets.resize(packet_count, pubkey_size as u32); + let (merkle_roots, merkle_roots_offsets) = + get_merkle_roots(thread_pool, batches, recycler_cache); + // Merkle roots are placed after the keypair; adjust offsets accordingly. + let merkle_roots_offsets = { + let shift = pinned_keypair.len(); + merkle_roots_offsets + .into_iter() + .map(move |offset| Some(offset? + shift)) + }; + let offset = pinned_keypair.len() + merkle_roots.len(); trace!("offset: {}", offset); - let (signature_offsets, msg_start_offsets, msg_sizes, _v_sig_lens) = - shred_gpu_offsets(offset, batches, recycler_cache); + let (signature_offsets, msg_start_offsets, msg_sizes) = + shred_gpu_offsets(offset, batches, merkle_roots_offsets, recycler_cache); let total_sigs = signature_offsets.len(); let mut signatures_out = recycler_cache.buffer().allocate("ed25519 signatures"); signatures_out.set_pinnable(); signatures_out.resize(total_sigs * sig_size, 0); - elems.push(perf_libs::Elems { - #[allow(clippy::cast_ptr_alignment)] - elems: pinned_keypair.as_ptr() as *const solana_sdk::packet::Packet, - num: num_keypair_packets as u32, - }); - - for batch in batches.iter() { - elems.push(perf_libs::Elems { - elems: batch.as_ptr(), - num: batch.len() as u32, - }); - let mut v = Vec::new(); - v.resize(batch.len(), 0); - num_packets += batch.len(); - } + let mut elems = vec![ + elems_from_buffer(pinned_keypair), + elems_from_buffer(&merkle_roots), + ]; + elems.extend(batches.iter().map(|batch| perf_libs::Elems { + elems: batch.as_ptr(), + num: batch.len() as u32, + })); + let num_packets = elems.iter().map(|elem| elem.num).sum(); trace!("Starting verify num packets: {}", num_packets); trace!("elem len: {}", elems.len() as u32); trace!("packet sizeof: {}", size_of::() as u32); @@ -376,7 +428,7 @@ pub fn sign_shreds_gpu( elems.as_mut_ptr(), elems.len() as u32, size_of::() as u32, - num_packets as u32, + num_packets, total_sigs as u32, msg_sizes.as_ptr(), pubkey_offsets.as_ptr(), @@ -390,20 +442,20 @@ pub fn sign_shreds_gpu( } } trace!("done sign"); - let mut sizes: Vec = vec![0]; - sizes.extend(batches.iter().map(|b| b.len())); - for i in 0..sizes.len() { - if i == 0 { - continue; - } - sizes[i] += sizes[i - 1]; - } - SIGVERIFY_THREAD_POOL.install(|| { + // Cumulative number of packets within batches. + let num_packets: Vec<_> = batches + .iter() + .scan(0, |num_packets, batch| { + let out = *num_packets; + *num_packets += batch.len(); + Some(out) + }) + .collect(); + thread_pool.install(|| { batches .par_iter_mut() - .enumerate() - .for_each(|(batch_ix, batch)| { - let num_packets = sizes[batch_ix]; + .zip(num_packets) + .for_each(|(batch, num_packets)| { batch[..] .par_iter_mut() .enumerate() @@ -423,8 +475,22 @@ pub fn sign_shreds_gpu( mod tests { use { super::*, - crate::shred::{Shred, ShredFlags, LEGACY_SHRED_DATA_CAPACITY}, - solana_sdk::signature::{Keypair, Signer}, + crate::{ + shred::{ProcessShredsStats, Shred, ShredFlags, LEGACY_SHRED_DATA_CAPACITY}, + shredder::{ReedSolomonCache, Shredder}, + }, + matches::assert_matches, + rand::{seq::SliceRandom, Rng}, + rayon::ThreadPoolBuilder, + solana_entry::entry::Entry, + solana_sdk::{ + hash, + hash::Hash, + signature::{Keypair, Signer}, + system_transaction, + transaction::Transaction, + }, + std::iter::{once, repeat_with}, }; fn run_test_sigverify_shred_cpu(slot: Slot) { @@ -469,7 +535,7 @@ mod tests { run_test_sigverify_shred_cpu(0xdead_c0de); } - fn run_test_sigverify_shreds_cpu(slot: Slot) { + fn run_test_sigverify_shreds_cpu(thread_pool: &ThreadPool, slot: Slot) { solana_logger::setup(); let mut batches = [PacketBatch::default()]; let mut shred = Shred::new_from_data( @@ -492,7 +558,7 @@ mod tests { .iter() .cloned() .collect(); - let rv = verify_shreds_cpu(&batches, &leader_slots); + let rv = verify_shreds_cpu(thread_pool, &batches, &leader_slots); assert_eq!(rv, vec![vec![1]]); let wrong_keypair = Keypair::new(); @@ -500,11 +566,11 @@ mod tests { .iter() .cloned() .collect(); - let rv = verify_shreds_cpu(&batches, &leader_slots); + let rv = verify_shreds_cpu(thread_pool, &batches, &leader_slots); assert_eq!(rv, vec![vec![0]]); let leader_slots = HashMap::new(); - let rv = verify_shreds_cpu(&batches, &leader_slots); + let rv = verify_shreds_cpu(thread_pool, &batches, &leader_slots); assert_eq!(rv, vec![vec![0]]); let leader_slots = [(slot, keypair.pubkey().to_bytes())] @@ -512,16 +578,17 @@ mod tests { .cloned() .collect(); batches[0][0].meta.size = 0; - let rv = verify_shreds_cpu(&batches, &leader_slots); + let rv = verify_shreds_cpu(thread_pool, &batches, &leader_slots); assert_eq!(rv, vec![vec![0]]); } #[test] fn test_sigverify_shreds_cpu() { - run_test_sigverify_shreds_cpu(0xdead_c0de); + let thread_pool = ThreadPoolBuilder::new().num_threads(3).build().unwrap(); + run_test_sigverify_shreds_cpu(&thread_pool, 0xdead_c0de); } - fn run_test_sigverify_shreds_gpu(slot: Slot) { + fn run_test_sigverify_shreds_gpu(thread_pool: &ThreadPool, slot: Slot) { solana_logger::setup(); let recycler_cache = RecyclerCache::default(); @@ -549,7 +616,7 @@ mod tests { .iter() .cloned() .collect(); - let rv = verify_shreds_gpu(&batches, &leader_slots, &recycler_cache); + let rv = verify_shreds_gpu(thread_pool, &batches, &leader_slots, &recycler_cache); assert_eq!(rv, vec![vec![1]]); let wrong_keypair = Keypair::new(); @@ -560,11 +627,11 @@ mod tests { .iter() .cloned() .collect(); - let rv = verify_shreds_gpu(&batches, &leader_slots, &recycler_cache); + let rv = verify_shreds_gpu(thread_pool, &batches, &leader_slots, &recycler_cache); assert_eq!(rv, vec![vec![0]]); let leader_slots = [(std::u64::MAX, [0u8; 32])].iter().cloned().collect(); - let rv = verify_shreds_gpu(&batches, &leader_slots, &recycler_cache); + let rv = verify_shreds_gpu(thread_pool, &batches, &leader_slots, &recycler_cache); assert_eq!(rv, vec![vec![0]]); batches[0][0].meta.size = 0; @@ -575,16 +642,17 @@ mod tests { .iter() .cloned() .collect(); - let rv = verify_shreds_gpu(&batches, &leader_slots, &recycler_cache); + let rv = verify_shreds_gpu(thread_pool, &batches, &leader_slots, &recycler_cache); assert_eq!(rv, vec![vec![0]]); } #[test] fn test_sigverify_shreds_gpu() { - run_test_sigverify_shreds_gpu(0xdead_c0de); + let thread_pool = ThreadPoolBuilder::new().num_threads(3).build().unwrap(); + run_test_sigverify_shreds_gpu(&thread_pool, 0xdead_c0de); } - fn run_test_sigverify_shreds_sign_gpu(slot: Slot) { + fn run_test_sigverify_shreds_sign_gpu(thread_pool: &ThreadPool, slot: Slot) { solana_logger::setup(); let recycler_cache = RecyclerCache::default(); @@ -618,23 +686,30 @@ mod tests { .cloned() .collect(); //unsigned - let rv = verify_shreds_gpu(&batches, &pubkeys, &recycler_cache); + let rv = verify_shreds_gpu(thread_pool, &batches, &pubkeys, &recycler_cache); assert_eq!(rv, vec![vec![0; num_packets]; num_batches]); //signed - sign_shreds_gpu(&keypair, &pinned_keypair, &mut batches, &recycler_cache); - let rv = verify_shreds_cpu(&batches, &pubkeys); + sign_shreds_gpu( + thread_pool, + &keypair, + &pinned_keypair, + &mut batches, + &recycler_cache, + ); + let rv = verify_shreds_cpu(thread_pool, &batches, &pubkeys); assert_eq!(rv, vec![vec![1; num_packets]; num_batches]); - let rv = verify_shreds_gpu(&batches, &pubkeys, &recycler_cache); + let rv = verify_shreds_gpu(thread_pool, &batches, &pubkeys, &recycler_cache); assert_eq!(rv, vec![vec![1; num_packets]; num_batches]); } #[test] fn test_sigverify_shreds_sign_gpu() { - run_test_sigverify_shreds_sign_gpu(0xdead_c0de); + let thread_pool = ThreadPoolBuilder::new().num_threads(3).build().unwrap(); + run_test_sigverify_shreds_sign_gpu(&thread_pool, 0xdead_c0de); } - fn run_test_sigverify_shreds_sign_cpu(slot: Slot) { + fn run_test_sigverify_shreds_sign_cpu(thread_pool: &ThreadPool, slot: Slot) { solana_logger::setup(); let mut batches = [PacketBatch::default()]; @@ -661,16 +736,213 @@ mod tests { .cloned() .collect(); //unsigned - let rv = verify_shreds_cpu(&batches, &pubkeys); + let rv = verify_shreds_cpu(thread_pool, &batches, &pubkeys); assert_eq!(rv, vec![vec![0]]); //signed - sign_shreds_cpu(&keypair, &mut batches); - let rv = verify_shreds_cpu(&batches, &pubkeys); + sign_shreds_cpu(thread_pool, &keypair, &mut batches); + let rv = verify_shreds_cpu(thread_pool, &batches, &pubkeys); assert_eq!(rv, vec![vec![1]]); } #[test] fn test_sigverify_shreds_sign_cpu() { - run_test_sigverify_shreds_sign_cpu(0xdead_c0de); + let thread_pool = ThreadPoolBuilder::new().num_threads(3).build().unwrap(); + run_test_sigverify_shreds_sign_cpu(&thread_pool, 0xdead_c0de); + } + + fn make_transaction(rng: &mut R) -> Transaction { + let block = rng.gen::<[u8; 32]>(); + let recent_blockhash = hash::hashv(&[&block]); + system_transaction::transfer( + &Keypair::new(), // from + &Pubkey::new_unique(), // to + rng.gen(), // lamports + recent_blockhash, + ) + } + + fn make_entry(rng: &mut R, prev_hash: &Hash) -> Entry { + let size = rng.gen_range(16, 32); + let txs = repeat_with(|| make_transaction(rng)).take(size).collect(); + Entry::new( + prev_hash, + rng.gen_range(1, 64), // num_hashes + txs, + ) + } + + fn make_entries(rng: &mut R, num_entries: usize) -> Vec { + let prev_hash = hash::hashv(&[&rng.gen::<[u8; 32]>()]); + let entry = make_entry(rng, &prev_hash); + std::iter::successors(Some(entry), |entry| Some(make_entry(rng, &entry.hash))) + .take(num_entries) + .collect() + } + + fn make_shreds(rng: &mut R, keypairs: &HashMap) -> Vec { + let reed_solomon_cache = ReedSolomonCache::default(); + let mut shreds: Vec<_> = keypairs + .iter() + .flat_map(|(&slot, keypair)| { + let parent_slot = slot - rng.gen::().max(1) as Slot; + let num_entries = rng.gen_range(64, 128); + let (data_shreds, coding_shreds) = Shredder::new( + slot, + parent_slot, + rng.gen_range(0, 0x40), // reference_tick + rng.gen(), // version + ) + .unwrap() + .entries_to_shreds( + keypair, + &make_entries(rng, num_entries), + rng.gen(), // is_last_in_slot + rng.gen_range(0, 2671), // next_shred_index + rng.gen_range(0, 2781), // next_code_index + rng.gen(), // merkle_variant, + &reed_solomon_cache, + &mut ProcessShredsStats::default(), + ); + [data_shreds, coding_shreds] + }) + .flatten() + .collect(); + shreds.shuffle(rng); + // Assert that all shreds verfiy and sanitize. + for shred in &shreds { + let pubkey = keypairs[&shred.slot()].pubkey(); + assert!(shred.verify(&pubkey)); + assert_matches!(shred.sanitize(), Ok(())); + } + // Verfiy using layout api. + for shred in &shreds { + let shred = shred.payload(); + let slot = shred::layout::get_slot(shred).unwrap(); + let signature = shred::layout::get_signature(shred).unwrap(); + let pubkey = keypairs[&slot].pubkey(); + if let Some(offsets) = shred::layout::get_signed_data_offsets(shred) { + assert!(signature.verify(pubkey.as_ref(), &shred[offsets])); + } + let data = shred::layout::get_signed_data(shred).unwrap(); + assert!(signature.verify(pubkey.as_ref(), data.as_ref())); + } + shreds + } + + fn make_packets(rng: &mut R, shreds: &[Shred]) -> Vec { + let mut packets = shreds.iter().map(|shred| { + let mut packet = Packet::default(); + shred.copy_to_packet(&mut packet); + packet + }); + let packets: Vec<_> = repeat_with(|| { + let size = rng.gen_range(0, 16); + let packets = packets.by_ref().take(size).collect(); + let batch = PacketBatch::new(packets); + (size == 0 || !batch.is_empty()).then(|| batch) + }) + .while_some() + .collect(); + assert_eq!( + shreds.len(), + packets.iter().map(PacketBatch::len).sum::() + ); + assert!(count_packets_in_batches(&packets) > SIGN_SHRED_GPU_MIN); + packets + } + + #[test] + fn test_verify_shreds_fuzz() { + let mut rng = rand::thread_rng(); + let thread_pool = ThreadPoolBuilder::new().num_threads(3).build().unwrap(); + let recycler_cache = RecyclerCache::default(); + let keypairs = repeat_with(|| rng.gen_range(169_367_809, 169_906_789)) + .map(|slot| (slot, Keypair::new())) + .take(3) + .collect(); + let shreds = make_shreds(&mut rng, &keypairs); + let pubkeys: HashMap = keypairs + .iter() + .map(|(&slot, keypair)| (slot, keypair.pubkey().to_bytes())) + .chain(once((Slot::MAX, Pubkey::default().to_bytes()))) + .collect(); + let mut packets = make_packets(&mut rng, &shreds); + assert_eq!( + verify_shreds_gpu(&thread_pool, &packets, &pubkeys, &recycler_cache), + packets + .iter() + .map(|batch| vec![1u8; batch.len()]) + .collect::>() + ); + // Invalidate signatures for a random number of packets. + let out: Vec<_> = packets + .iter_mut() + .map(|packets| { + packets + .iter_mut() + .map(|packet| { + let coin_flip: bool = rng.gen(); + if !coin_flip { + shred::layout::corrupt_packet(&mut rng, packet, &keypairs); + } + u8::from(coin_flip) + }) + .collect::>() + }) + .collect(); + assert_eq!( + verify_shreds_gpu(&thread_pool, &packets, &pubkeys, &recycler_cache), + out + ); + } + + #[test] + fn test_sign_shreds_gpu() { + let mut rng = rand::thread_rng(); + let thread_pool = ThreadPoolBuilder::new().num_threads(3).build().unwrap(); + let recycler_cache = RecyclerCache::default(); + let shreds = { + let keypairs = repeat_with(|| rng.gen_range(169_367_809, 169_906_789)) + .map(|slot| (slot, Keypair::new())) + .take(3) + .collect(); + make_shreds(&mut rng, &keypairs) + }; + let keypair = Keypair::new(); + let pubkeys: HashMap = { + let pubkey = keypair.pubkey().to_bytes(); + shreds + .iter() + .map(Shred::slot) + .map(|slot| (slot, pubkey)) + .chain(once((Slot::MAX, Pubkey::default().to_bytes()))) + .collect() + }; + let mut packets = make_packets(&mut rng, &shreds); + // Assert that initially all signatrues are invalid. + assert_eq!( + verify_shreds_gpu(&thread_pool, &packets, &pubkeys, &recycler_cache), + packets + .iter() + .map(|batch| vec![0u8; batch.len()]) + .collect::>() + ); + let pinned_keypair = sign_shreds_gpu_pinned_keypair(&keypair, &recycler_cache); + let pinned_keypair = Some(Arc::new(pinned_keypair)); + // Sign and verify shreds signatures. + sign_shreds_gpu( + &thread_pool, + &keypair, + &pinned_keypair, + &mut packets, + &recycler_cache, + ); + assert_eq!( + verify_shreds_gpu(&thread_pool, &packets, &pubkeys, &recycler_cache), + packets + .iter() + .map(|batch| vec![1u8; batch.len()]) + .collect::>() + ); } } diff --git a/ledger/src/slot_stats.rs b/ledger/src/slot_stats.rs index 0f3a72defd8e43..f144eb245d4de6 100644 --- a/ledger/src/slot_stats.rs +++ b/ledger/src/slot_stats.rs @@ -39,10 +39,10 @@ pub struct SlotStats { impl SlotStats { pub fn get_min_index_count(&self) -> usize { self.turbine_fec_set_index_counts - .iter() - .map(|(_, cnt)| *cnt) + .values() .min() - .unwrap_or(0) + .copied() + .unwrap_or_default() } fn report(&self, slot: Slot) { diff --git a/ledger/src/token_balances.rs b/ledger/src/token_balances.rs new file mode 100644 index 00000000000000..673cc5556b1bac --- /dev/null +++ b/ledger/src/token_balances.rs @@ -0,0 +1,477 @@ +use { + solana_account_decoder::parse_token::{ + is_known_spl_token_id, pubkey_from_spl_token, spl_token_native_mint, + token_amount_to_ui_amount, UiTokenAmount, + }, + solana_measure::measure::Measure, + solana_metrics::datapoint_debug, + solana_runtime::{bank::Bank, transaction_batch::TransactionBatch}, + solana_sdk::{account::ReadableAccount, pubkey::Pubkey}, + solana_transaction_status::{ + token_balances::TransactionTokenBalances, TransactionTokenBalance, + }, + spl_token_2022::{ + extension::StateWithExtensions, + state::{Account as TokenAccount, Mint}, + }, + std::collections::HashMap, +}; + +fn get_mint_decimals(bank: &Bank, mint: &Pubkey) -> Option { + if mint == &spl_token_native_mint() { + Some(spl_token::native_mint::DECIMALS) + } else { + let mint_account = bank.get_account(mint)?; + + if !is_known_spl_token_id(mint_account.owner()) { + return None; + } + + let decimals = StateWithExtensions::::unpack(mint_account.data()) + .map(|mint| mint.base.decimals) + .ok()?; + + Some(decimals) + } +} + +pub fn collect_token_balances( + bank: &Bank, + batch: &TransactionBatch, + mint_decimals: &mut HashMap, +) -> TransactionTokenBalances { + let mut balances: TransactionTokenBalances = vec![]; + let mut collect_time = Measure::start("collect_token_balances"); + + for transaction in batch.sanitized_transactions() { + let account_keys = transaction.message().account_keys(); + let has_token_program = account_keys.iter().any(is_known_spl_token_id); + + let mut transaction_balances: Vec = vec![]; + if has_token_program { + for (index, account_id) in account_keys.iter().enumerate() { + if transaction.message().is_invoked(index) || is_known_spl_token_id(account_id) { + continue; + } + + if let Some(TokenBalanceData { + mint, + ui_token_amount, + owner, + program_id, + }) = collect_token_balance_from_account(bank, account_id, mint_decimals) + { + transaction_balances.push(TransactionTokenBalance { + account_index: index as u8, + mint, + ui_token_amount, + owner, + program_id, + }); + } + } + } + balances.push(transaction_balances); + } + collect_time.stop(); + datapoint_debug!( + "collect_token_balances", + ("collect_time_us", collect_time.as_us(), i64), + ); + balances +} + +#[derive(Debug, PartialEq)] +struct TokenBalanceData { + mint: String, + owner: String, + ui_token_amount: UiTokenAmount, + program_id: String, +} + +fn collect_token_balance_from_account( + bank: &Bank, + account_id: &Pubkey, + mint_decimals: &mut HashMap, +) -> Option { + let account = bank.get_account(account_id)?; + + if !is_known_spl_token_id(account.owner()) { + return None; + } + + let token_account = StateWithExtensions::::unpack(account.data()).ok()?; + let mint = pubkey_from_spl_token(&token_account.base.mint); + + let decimals = mint_decimals.get(&mint).cloned().or_else(|| { + let decimals = get_mint_decimals(bank, &mint)?; + mint_decimals.insert(mint, decimals); + Some(decimals) + })?; + + Some(TokenBalanceData { + mint: token_account.base.mint.to_string(), + owner: token_account.base.owner.to_string(), + ui_token_amount: token_amount_to_ui_amount(token_account.base.amount, decimals), + program_id: account.owner().to_string(), + }) +} + +#[cfg(test)] +mod test { + use { + super::*, + solana_account_decoder::parse_token::{pubkey_from_spl_token, spl_token_pubkey}, + solana_sdk::{account::Account, genesis_config::create_genesis_config}, + spl_token_2022::{ + extension::{ + immutable_owner::ImmutableOwner, memo_transfer::MemoTransfer, + mint_close_authority::MintCloseAuthority, ExtensionType, StateWithExtensionsMut, + }, + pod::OptionalNonZeroPubkey, + solana_program::{program_option::COption, program_pack::Pack}, + }, + std::collections::BTreeMap, + }; + + #[test] + fn test_collect_token_balance_from_account() { + let (mut genesis_config, _mint_keypair) = create_genesis_config(500); + + // Add a variety of accounts, token and not + let account = Account::new(42, 55, &Pubkey::new_unique()); + + let mint_data = Mint { + mint_authority: COption::None, + supply: 4242, + decimals: 2, + is_initialized: true, + freeze_authority: COption::None, + }; + let mut data = [0; Mint::LEN]; + Mint::pack(mint_data, &mut data).unwrap(); + let mint_pubkey = Pubkey::new_unique(); + let mint = Account { + lamports: 100, + data: data.to_vec(), + owner: pubkey_from_spl_token(&spl_token::id()), + executable: false, + rent_epoch: 0, + }; + let other_mint_pubkey = Pubkey::new_unique(); + let other_mint = Account { + lamports: 100, + data: data.to_vec(), + owner: Pubkey::new_unique(), // !is_known_spl_token_id + executable: false, + rent_epoch: 0, + }; + + let token_owner = Pubkey::new_unique(); + let token_data = TokenAccount { + mint: spl_token_pubkey(&mint_pubkey), + owner: spl_token_pubkey(&token_owner), + amount: 42, + delegate: COption::None, + state: spl_token_2022::state::AccountState::Initialized, + is_native: COption::Some(100), + delegated_amount: 0, + close_authority: COption::None, + }; + let mut data = [0; TokenAccount::LEN]; + TokenAccount::pack(token_data, &mut data).unwrap(); + + let spl_token_account = Account { + lamports: 100, + data: data.to_vec(), + owner: pubkey_from_spl_token(&spl_token::id()), + executable: false, + rent_epoch: 0, + }; + let other_account = Account { + lamports: 100, + data: data.to_vec(), + owner: Pubkey::new_unique(), // !is_known_spl_token_id + executable: false, + rent_epoch: 0, + }; + + let other_mint_data = TokenAccount { + mint: spl_token_pubkey(&other_mint_pubkey), + owner: spl_token_pubkey(&token_owner), + amount: 42, + delegate: COption::None, + state: spl_token_2022::state::AccountState::Initialized, + is_native: COption::Some(100), + delegated_amount: 0, + close_authority: COption::None, + }; + let mut data = [0; TokenAccount::LEN]; + TokenAccount::pack(other_mint_data, &mut data).unwrap(); + + let other_mint_token_account = Account { + lamports: 100, + data: data.to_vec(), + owner: pubkey_from_spl_token(&spl_token::id()), + executable: false, + rent_epoch: 0, + }; + + let mut accounts = BTreeMap::new(); + + let account_pubkey = Pubkey::new_unique(); + accounts.insert(account_pubkey, account); + accounts.insert(mint_pubkey, mint); + accounts.insert(other_mint_pubkey, other_mint); + let spl_token_account_pubkey = Pubkey::new_unique(); + accounts.insert(spl_token_account_pubkey, spl_token_account); + let other_account_pubkey = Pubkey::new_unique(); + accounts.insert(other_account_pubkey, other_account); + let other_mint_account_pubkey = Pubkey::new_unique(); + accounts.insert(other_mint_account_pubkey, other_mint_token_account); + + genesis_config.accounts = accounts; + + let bank = Bank::new_for_tests(&genesis_config); + let mut mint_decimals = HashMap::new(); + + // Account is not owned by spl_token (nor does it have TokenAccount state) + assert_eq!( + collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals), + None + ); + + // Mint does not have TokenAccount state + assert_eq!( + collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals), + None + ); + + // TokenAccount owned by spl_token::id() works + assert_eq!( + collect_token_balance_from_account( + &bank, + &spl_token_account_pubkey, + &mut mint_decimals + ), + Some(TokenBalanceData { + mint: mint_pubkey.to_string(), + owner: token_owner.to_string(), + ui_token_amount: UiTokenAmount { + ui_amount: Some(0.42), + decimals: 2, + amount: "42".to_string(), + ui_amount_string: "0.42".to_string(), + }, + program_id: spl_token::id().to_string(), + }) + ); + + // TokenAccount is not owned by known spl-token program_id + assert_eq!( + collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals), + None + ); + + // TokenAccount's mint is not owned by known spl-token program_id + assert_eq!( + collect_token_balance_from_account( + &bank, + &other_mint_account_pubkey, + &mut mint_decimals + ), + None + ); + } + + #[test] + fn test_collect_token_balance_from_spl_token_2022_account() { + let (mut genesis_config, _mint_keypair) = create_genesis_config(500); + + // Add a variety of accounts, token and not + let account = Account::new(42, 55, &Pubkey::new_unique()); + + let mint_authority = Pubkey::new_unique(); + let mint_size = + ExtensionType::get_account_len::(&[ExtensionType::MintCloseAuthority]); + let mint_base = Mint { + mint_authority: COption::None, + supply: 4242, + decimals: 2, + is_initialized: true, + freeze_authority: COption::None, + }; + let mut mint_data = vec![0; mint_size]; + let mut mint_state = + StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data).unwrap(); + mint_state.base = mint_base; + mint_state.pack_base(); + mint_state.init_account_type().unwrap(); + let mut mint_close_authority = mint_state + .init_extension::(true) + .unwrap(); + mint_close_authority.close_authority = + OptionalNonZeroPubkey::try_from(Some(spl_token_pubkey(&mint_authority))).unwrap(); + + let mint_pubkey = Pubkey::new_unique(); + let mint = Account { + lamports: 100, + data: mint_data.to_vec(), + owner: pubkey_from_spl_token(&spl_token_2022::id()), + executable: false, + rent_epoch: 0, + }; + let other_mint_pubkey = Pubkey::new_unique(); + let other_mint = Account { + lamports: 100, + data: mint_data.to_vec(), + owner: Pubkey::new_unique(), + executable: false, + rent_epoch: 0, + }; + + let token_owner = Pubkey::new_unique(); + let token_base = TokenAccount { + mint: spl_token_pubkey(&mint_pubkey), + owner: spl_token_pubkey(&token_owner), + amount: 42, + delegate: COption::None, + state: spl_token_2022::state::AccountState::Initialized, + is_native: COption::Some(100), + delegated_amount: 0, + close_authority: COption::None, + }; + let account_size = ExtensionType::get_account_len::(&[ + ExtensionType::ImmutableOwner, + ExtensionType::MemoTransfer, + ]); + let mut account_data = vec![0; account_size]; + let mut account_state = + StateWithExtensionsMut::::unpack_uninitialized(&mut account_data) + .unwrap(); + account_state.base = token_base; + account_state.pack_base(); + account_state.init_account_type().unwrap(); + account_state + .init_extension::(true) + .unwrap(); + let mut memo_transfer = account_state.init_extension::(true).unwrap(); + memo_transfer.require_incoming_transfer_memos = true.into(); + + let spl_token_account = Account { + lamports: 100, + data: account_data.to_vec(), + owner: pubkey_from_spl_token(&spl_token_2022::id()), + executable: false, + rent_epoch: 0, + }; + let other_account = Account { + lamports: 100, + data: account_data.to_vec(), + owner: Pubkey::new_unique(), + executable: false, + rent_epoch: 0, + }; + + let other_mint_token_base = TokenAccount { + mint: spl_token_pubkey(&other_mint_pubkey), + owner: spl_token_pubkey(&token_owner), + amount: 42, + delegate: COption::None, + state: spl_token_2022::state::AccountState::Initialized, + is_native: COption::Some(100), + delegated_amount: 0, + close_authority: COption::None, + }; + let account_size = ExtensionType::get_account_len::(&[ + ExtensionType::ImmutableOwner, + ExtensionType::MemoTransfer, + ]); + let mut account_data = vec![0; account_size]; + let mut account_state = + StateWithExtensionsMut::::unpack_uninitialized(&mut account_data) + .unwrap(); + account_state.base = other_mint_token_base; + account_state.pack_base(); + account_state.init_account_type().unwrap(); + account_state + .init_extension::(true) + .unwrap(); + let mut memo_transfer = account_state.init_extension::(true).unwrap(); + memo_transfer.require_incoming_transfer_memos = true.into(); + + let other_mint_token_account = Account { + lamports: 100, + data: account_data.to_vec(), + owner: pubkey_from_spl_token(&spl_token_2022::id()), + executable: false, + rent_epoch: 0, + }; + + let mut accounts = BTreeMap::new(); + + let account_pubkey = Pubkey::new_unique(); + accounts.insert(account_pubkey, account); + accounts.insert(mint_pubkey, mint); + accounts.insert(other_mint_pubkey, other_mint); + let spl_token_account_pubkey = Pubkey::new_unique(); + accounts.insert(spl_token_account_pubkey, spl_token_account); + let other_account_pubkey = Pubkey::new_unique(); + accounts.insert(other_account_pubkey, other_account); + let other_mint_account_pubkey = Pubkey::new_unique(); + accounts.insert(other_mint_account_pubkey, other_mint_token_account); + + genesis_config.accounts = accounts; + + let bank = Bank::new_for_tests(&genesis_config); + let mut mint_decimals = HashMap::new(); + + // Account is not owned by spl_token (nor does it have TokenAccount state) + assert_eq!( + collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals), + None + ); + + // Mint does not have TokenAccount state + assert_eq!( + collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals), + None + ); + + // TokenAccount owned by spl_token_2022::id() works + assert_eq!( + collect_token_balance_from_account( + &bank, + &spl_token_account_pubkey, + &mut mint_decimals + ), + Some(TokenBalanceData { + mint: mint_pubkey.to_string(), + owner: token_owner.to_string(), + ui_token_amount: UiTokenAmount { + ui_amount: Some(0.42), + decimals: 2, + amount: "42".to_string(), + ui_amount_string: "0.42".to_string(), + }, + program_id: spl_token_2022::id().to_string(), + }) + ); + + // TokenAccount is not owned by known spl-token program_id + assert_eq!( + collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals), + None + ); + + // TokenAccount's mint is not owned by known spl-token program_id + assert_eq!( + collect_token_balance_from_account( + &bank, + &other_mint_account_pubkey, + &mut mint_decimals + ), + None + ); + } +} diff --git a/ledger/tests/blockstore.rs b/ledger/tests/blockstore.rs index b2268ee3a2d84e..d5af08671fcdbe 100644 --- a/ledger/tests/blockstore.rs +++ b/ledger/tests/blockstore.rs @@ -21,7 +21,14 @@ fn test_multiple_threads_insert_shred() { let threads: Vec<_> = (0..num_threads) .map(|i| { let entries = entry::create_ticks(1, 0, Hash::default()); - let shreds = blockstore::entries_to_test_shreds(&entries, i + 1, 0, false, 0); + let shreds = blockstore::entries_to_test_shreds( + &entries, + i + 1, + 0, + false, + 0, + true, // merkle_variant + ); let blockstore_ = blockstore.clone(); Builder::new() .name("blockstore-writer".to_string()) diff --git a/ledger/tests/shred.rs b/ledger/tests/shred.rs index 192e36ecf0bc5e..ef873bd8f585d8 100644 --- a/ledger/tests/shred.rs +++ b/ledger/tests/shred.rs @@ -2,8 +2,8 @@ use { solana_entry::entry::Entry, solana_ledger::shred::{ - max_entries_per_n_shred, verify_test_data_shred, ProcessShredsStats, Shred, Shredder, - LEGACY_SHRED_DATA_CAPACITY, MAX_DATA_SHREDS_PER_FEC_BLOCK, + max_entries_per_n_shred, verify_test_data_shred, ProcessShredsStats, ReedSolomonCache, + Shred, Shredder, DATA_SHREDS_PER_FEC_BLOCK, LEGACY_SHRED_DATA_CAPACITY, }, solana_sdk::{ clock::Slot, @@ -26,7 +26,7 @@ fn test_multi_fec_block_coding() { let slot = 0x1234_5678_9abc_def0; let shredder = Shredder::new(slot, slot - 5, 0, 0).unwrap(); let num_fec_sets = 100; - let num_data_shreds = (MAX_DATA_SHREDS_PER_FEC_BLOCK * num_fec_sets) as usize; + let num_data_shreds = DATA_SHREDS_PER_FEC_BLOCK * num_fec_sets; let keypair0 = Keypair::new(); let keypair1 = Keypair::new(); let tx0 = system_transaction::transfer(&keypair0, &keypair1.pubkey(), 1, Hash::default()); @@ -47,13 +47,16 @@ fn test_multi_fec_block_coding() { }) .collect(); + let reed_solomon_cache = ReedSolomonCache::default(); let serialized_entries = bincode::serialize(&entries).unwrap(); let (data_shreds, coding_shreds) = shredder.entries_to_shreds( &keypair, &entries, - true, // is_last_in_slot - 0, // next_shred_index - 0, // next_code_index + true, // is_last_in_slot + 0, // next_shred_index + 0, // next_code_index + false, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); let next_index = data_shreds.last().unwrap().index() + 1; @@ -67,8 +70,8 @@ fn test_multi_fec_block_coding() { let mut all_shreds = vec![]; for i in 0..num_fec_sets { - let shred_start_index = (MAX_DATA_SHREDS_PER_FEC_BLOCK * i) as usize; - let end_index = shred_start_index + MAX_DATA_SHREDS_PER_FEC_BLOCK as usize - 1; + let shred_start_index = DATA_SHREDS_PER_FEC_BLOCK * i; + let end_index = shred_start_index + DATA_SHREDS_PER_FEC_BLOCK - 1; let fec_set_shreds = data_shreds[shred_start_index..=end_index] .iter() .cloned() @@ -81,7 +84,8 @@ fn test_multi_fec_block_coding() { .filter_map(|(i, b)| if i % 2 != 0 { Some(b.clone()) } else { None }) .collect(); - let recovered_data = Shredder::try_recovery(shred_info.clone()).unwrap(); + let recovered_data = + Shredder::try_recovery(shred_info.clone(), &reed_solomon_cache).unwrap(); for (i, recovered_shred) in recovered_data.into_iter().enumerate() { let index = shred_start_index + (i * 2); @@ -99,11 +103,7 @@ fn test_multi_fec_block_coding() { shred_info.insert(i * 2, recovered_shred); } - all_shreds.extend( - shred_info - .into_iter() - .take(MAX_DATA_SHREDS_PER_FEC_BLOCK as usize), - ); + all_shreds.extend(shred_info.into_iter().take(DATA_SHREDS_PER_FEC_BLOCK)); } let result = Shredder::deshred(&all_shreds[..]).unwrap(); @@ -119,6 +119,7 @@ fn test_multi_fec_block_different_size_coding() { setup_different_sized_fec_blocks(slot, parent_slot, keypair.clone()); let total_num_data_shreds: usize = fec_data.values().map(|x| x.len()).sum(); + let reed_solomon_cache = ReedSolomonCache::default(); // Test recovery for (fec_data_shreds, fec_coding_shreds) in fec_data.values().zip(fec_coding.values()) { let first_data_index = fec_data_shreds.first().unwrap().index() as usize; @@ -128,7 +129,7 @@ fn test_multi_fec_block_different_size_coding() { .chain(fec_coding_shreds.iter().step_by(2)) .cloned() .collect(); - let recovered_data = Shredder::try_recovery(all_shreds).unwrap(); + let recovered_data = Shredder::try_recovery(all_shreds, &reed_solomon_cache).unwrap(); // Necessary in order to ensure the last shred in the slot // is part of the recovered set, and that the below `index` // calcuation in the loop is correct @@ -193,11 +194,11 @@ fn setup_different_sized_fec_blocks( let tx0 = system_transaction::transfer(&keypair0, &keypair1.pubkey(), 1, Hash::default()); let entry = Entry::new(&Hash::default(), 1, vec![tx0]); - // Make enough entries for `MAX_DATA_SHREDS_PER_FEC_BLOCK + 2` shreds so one - // fec set will have `MAX_DATA_SHREDS_PER_FEC_BLOCK` shreds and the next + // Make enough entries for `DATA_SHREDS_PER_FEC_BLOCK + 2` shreds so one + // fec set will have `DATA_SHREDS_PER_FEC_BLOCK` shreds and the next // will have 2 shreds. - assert!(MAX_DATA_SHREDS_PER_FEC_BLOCK > 2); - let num_shreds_per_iter = MAX_DATA_SHREDS_PER_FEC_BLOCK as usize + 2; + assert!(DATA_SHREDS_PER_FEC_BLOCK > 2); + let num_shreds_per_iter = DATA_SHREDS_PER_FEC_BLOCK + 2; let num_entries = max_entries_per_n_shred( &entry, num_shreds_per_iter as u64, @@ -222,6 +223,7 @@ fn setup_different_sized_fec_blocks( let mut coding_slot_and_index = HashSet::new(); let total_num_data_shreds: usize = 2 * num_shreds_per_iter; + let reed_solomon_cache = ReedSolomonCache::default(); for i in 0..2 { let is_last = i == 1; let (data_shreds, coding_shreds) = shredder.entries_to_shreds( @@ -230,6 +232,8 @@ fn setup_different_sized_fec_blocks( is_last, next_shred_index, next_code_index, + false, // merkle_variant + &reed_solomon_cache, &mut ProcessShredsStats::default(), ); for shred in &data_shreds { diff --git a/local-cluster/Cargo.toml b/local-cluster/Cargo.toml index 0d6893bb5013bf..a62fcd2bcf19e8 100644 --- a/local-cluster/Cargo.toml +++ b/local-cluster/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-local-cluster" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -16,25 +16,25 @@ itertools = "0.10.3" log = "0.4.17" rand = "0.7.0" rayon = "1.5.3" -solana-client = { path = "../client", version = "=1.11.6" } -solana-config-program = { path = "../programs/config", version = "=1.11.6" } -solana-core = { path = "../core", version = "=1.11.6" } -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-stake-program = { path = "../programs/stake", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } -tempfile = "3.3.0" +solana-client = { path = "../client", version = "=1.14.24" } +solana-config-program = { path = "../programs/config", version = "=1.14.24" } +solana-core = { path = "../core", version = "=1.14.24" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } +tempfile = "3.4.0" [dev-dependencies] assert_matches = "1.5.0" gag = "1.0.0" serial_test = "0.8.0" -solana-download-utils = { path = "../download-utils", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-download-utils = { path = "../download-utils", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/local-cluster/src/cluster.rs b/local-cluster/src/cluster.rs index b5bcf658fb38e9..0cee27e6c99e79 100644 --- a/local-cluster/src/cluster.rs +++ b/local-cluster/src/cluster.rs @@ -1,7 +1,7 @@ use { solana_client::thin_client::ThinClient, solana_core::validator::{Validator, ValidatorConfig}, - solana_gossip::{cluster_info::Node, contact_info::ContactInfo}, + solana_gossip::{cluster_info::Node, legacy_contact_info::LegacyContactInfo as ContactInfo}, solana_sdk::{pubkey::Pubkey, signature::Keypair}, solana_streamer::socket::SocketAddrSpace, std::{path::PathBuf, sync::Arc}, diff --git a/local-cluster/src/cluster_tests.rs b/local-cluster/src/cluster_tests.rs index e0ad36f3009549..25edf397905ea6 100644 --- a/local-cluster/src/cluster_tests.rs +++ b/local-cluster/src/cluster_tests.rs @@ -11,10 +11,10 @@ use { solana_entry::entry::{Entry, EntrySlice}, solana_gossip::{ cluster_info, - contact_info::ContactInfo, crds_value::{self, CrdsData, CrdsValue}, gossip_error::GossipError, gossip_service::discover_cluster, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_ledger::blockstore::Blockstore, solana_sdk::{ @@ -35,7 +35,7 @@ use { solana_vote_program::vote_transaction, std::{ collections::{HashMap, HashSet}, - net::SocketAddr, + net::{IpAddr, Ipv4Addr, SocketAddr}, path::Path, sync::{Arc, RwLock}, thread::sleep, @@ -43,6 +43,14 @@ use { }, }; +pub fn get_client_facing_addr(contact_info: &ContactInfo) -> (SocketAddr, SocketAddr) { + let (rpc, mut tpu) = contact_info.client_facing_addr(); + // QUIC certificate authentication requires the IP Address to match. ContactInfo might have + // 0.0.0.0 as the IP instead of 127.0.0.1. + tpu.set_ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); + (rpc, tpu) +} + /// Spend and verify from every node in the network pub fn spend_and_verify_all_nodes( entry_point_info: &ContactInfo, @@ -61,7 +69,7 @@ pub fn spend_and_verify_all_nodes( return; } let random_keypair = Keypair::new(); - let (rpc, tpu) = ingress_node.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(ingress_node); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); let bal = client .poll_get_balance_with_commitment( @@ -83,7 +91,7 @@ pub fn spend_and_verify_all_nodes( if ignore_nodes.contains(&validator.id) { continue; } - let (rpc, tpu) = validator.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(validator); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); client.poll_for_signature_confirmation(&sig, confs).unwrap(); } @@ -95,7 +103,7 @@ pub fn verify_balances( node: &ContactInfo, connection_cache: Arc, ) { - let (rpc, tpu) = node.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(node); let client = ThinClient::new(rpc, tpu, connection_cache); for (pk, b) in expected_balances { let bal = client @@ -112,7 +120,7 @@ pub fn send_many_transactions( max_tokens_per_transfer: u64, num_txs: u64, ) -> HashMap { - let (rpc, tpu) = node.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(node); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); let mut expected_balances = HashMap::new(); for _ in 0..num_txs { @@ -205,7 +213,7 @@ pub fn kill_entry_and_spend_and_verify_rest( let cluster_nodes = discover_cluster(&entry_point_info.gossip, nodes, socket_addr_space).unwrap(); assert!(cluster_nodes.len() >= nodes); - let (rpc, tpu) = entry_point_info.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(entry_point_info); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); // sleep long enough to make sure we are in epoch 3 @@ -235,7 +243,7 @@ pub fn kill_entry_and_spend_and_verify_rest( continue; } - let (rpc, tpu) = ingress_node.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(ingress_node); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); let balance = client .poll_get_balance_with_commitment( @@ -318,7 +326,7 @@ pub fn check_for_new_roots( assert!(loop_start.elapsed() < loop_timeout); for (i, ingress_node) in contact_infos.iter().enumerate() { - let (rpc, tpu) = ingress_node.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(ingress_node); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); let root_slot = client .get_slot_with_commitment(CommitmentConfig::finalized()) @@ -351,7 +359,7 @@ pub fn check_no_new_roots( .iter() .enumerate() .map(|(i, ingress_node)| { - let (rpc, tpu) = ingress_node.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(ingress_node); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); let initial_root = client .get_slot() @@ -370,7 +378,7 @@ pub fn check_no_new_roots( let mut reached_end_slot = false; loop { for contact_info in contact_infos { - let (rpc, tpu) = contact_info.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(contact_info); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); current_slot = client .get_slot_with_commitment(CommitmentConfig::processed()) @@ -393,7 +401,7 @@ pub fn check_no_new_roots( } for (i, ingress_node) in contact_infos.iter().enumerate() { - let (rpc, tpu) = ingress_node.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(ingress_node); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); assert_eq!( client @@ -415,7 +423,7 @@ fn poll_all_nodes_for_signature( if validator.id == entry_point_info.id { continue; } - let (rpc, tpu) = validator.client_facing_addr(); + let (rpc, tpu) = get_client_facing_addr(validator); let client = ThinClient::new(rpc, tpu, connection_cache.clone()); client.poll_for_signature_confirmation(sig, confs)?; } diff --git a/local-cluster/src/local_cluster.rs b/local-cluster/src/local_cluster.rs index 6f3b13e5e0aed5..533921b5fec77b 100644 --- a/local-cluster/src/local_cluster.rs +++ b/local-cluster/src/local_cluster.rs @@ -8,7 +8,8 @@ use { log::*, solana_client::{ connection_cache::{ - ConnectionCache, DEFAULT_TPU_CONNECTION_POOL_SIZE, DEFAULT_TPU_USE_QUIC, + ConnectionCache, DEFAULT_TPU_CONNECTION_POOL_SIZE, DEFAULT_TPU_ENABLE_UDP, + DEFAULT_TPU_USE_QUIC, }, thin_client::ThinClient, }, @@ -17,7 +18,8 @@ use { validator::{Validator, ValidatorConfig, ValidatorStartProgress}, }, solana_gossip::{ - cluster_info::Node, contact_info::ContactInfo, gossip_service::discover_cluster, + cluster_info::Node, gossip_service::discover_cluster, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_ledger::create_new_tmp_ledger, solana_runtime::{ @@ -282,6 +284,8 @@ impl LocalCluster { socket_addr_space, DEFAULT_TPU_USE_QUIC, DEFAULT_TPU_CONNECTION_POOL_SIZE, + DEFAULT_TPU_ENABLE_UDP, + Arc::new(RwLock::new(None)), ); let mut validators = HashMap::new(); @@ -426,7 +430,7 @@ impl LocalCluster { mut voting_keypair: Option>, socket_addr_space: SocketAddrSpace, ) -> Pubkey { - let (rpc, tpu) = self.entry_point_info.client_facing_addr(); + let (rpc, tpu) = cluster_tests::get_client_facing_addr(&self.entry_point_info); let client = ThinClient::new(rpc, tpu, self.connection_cache.clone()); // Must have enough tokens to fund vote account and set delegate @@ -480,6 +484,8 @@ impl LocalCluster { socket_addr_space, DEFAULT_TPU_USE_QUIC, DEFAULT_TPU_CONNECTION_POOL_SIZE, + DEFAULT_TPU_ENABLE_UDP, + Arc::new(RwLock::new(None)), ); let validator_pubkey = validator_keypair.pubkey(); @@ -512,7 +518,7 @@ impl LocalCluster { } pub fn transfer(&self, source_keypair: &Keypair, dest_pubkey: &Pubkey, lamports: u64) -> u64 { - let (rpc, tpu) = self.entry_point_info.client_facing_addr(); + let (rpc, tpu) = cluster_tests::get_client_facing_addr(&self.entry_point_info); let client = ThinClient::new(rpc, tpu, self.connection_cache.clone()); Self::transfer_with_client(&client, source_keypair, dest_pubkey, lamports) } @@ -759,7 +765,7 @@ impl Cluster for LocalCluster { fn get_validator_client(&self, pubkey: &Pubkey) -> Option { self.validators.get(pubkey).map(|f| { - let (rpc, tpu) = f.info.contact_info.client_facing_addr(); + let (rpc, tpu) = cluster_tests::get_client_facing_addr(&f.info.contact_info); ThinClient::new(rpc, tpu, self.connection_cache.clone()) }) } @@ -841,6 +847,8 @@ impl Cluster for LocalCluster { socket_addr_space, DEFAULT_TPU_USE_QUIC, DEFAULT_TPU_CONNECTION_POOL_SIZE, + DEFAULT_TPU_ENABLE_UDP, + Arc::new(RwLock::new(None)), ); cluster_validator_info.validator = Some(restarted_node); cluster_validator_info diff --git a/local-cluster/src/validator_configs.rs b/local-cluster/src/validator_configs.rs index e717b468152dfa..7ad91f47c27575 100644 --- a/local-cluster/src/validator_configs.rs +++ b/local-cluster/src/validator_configs.rs @@ -58,13 +58,13 @@ pub fn safe_clone_config(config: &ValidatorConfig) -> ValidatorConfig { tpu_coalesce_ms: config.tpu_coalesce_ms, validator_exit: Arc::new(RwLock::new(Exit::default())), poh_hashes_per_batch: config.poh_hashes_per_batch, + process_ledger_before_services: config.process_ledger_before_services, no_wait_for_vote_to_start_leader: config.no_wait_for_vote_to_start_leader, accounts_shrink_ratio: config.accounts_shrink_ratio, accounts_db_config: config.accounts_db_config.clone(), wait_to_vote_slot: config.wait_to_vote_slot, ledger_column_options: config.ledger_column_options.clone(), runtime_config: config.runtime_config.clone(), - enable_quic_servers: config.enable_quic_servers, } } diff --git a/local-cluster/tests/local_cluster.rs b/local-cluster/tests/local_cluster.rs index d3956d7adc7fc1..aaf312ddd8d5e4 100644 --- a/local-cluster/tests/local_cluster.rs +++ b/local-cluster/tests/local_cluster.rs @@ -173,6 +173,7 @@ fn test_spend_and_verify_all_nodes_3() { #[test] #[serial] +#[ignore] fn test_local_cluster_signature_subscribe() { solana_logger::setup_with_default(RUST_LOG_FILTER); let num_nodes = 2; @@ -191,7 +192,7 @@ fn test_local_cluster_signature_subscribe() { .unwrap(); let non_bootstrap_info = cluster.get_contact_info(&non_bootstrap_id).unwrap(); - let (rpc, tpu) = non_bootstrap_info.client_facing_addr(); + let (rpc, tpu) = cluster_tests::get_client_facing_addr(non_bootstrap_info); let tx_client = ThinClient::new(rpc, tpu, cluster.connection_cache.clone()); let (blockhash, _) = tx_client @@ -418,7 +419,7 @@ fn test_mainnet_beta_cluster_type() { .unwrap(); assert_eq!(cluster_nodes.len(), 1); - let (rpc, tpu) = cluster.entry_point_info.client_facing_addr(); + let (rpc, tpu) = cluster_tests::get_client_facing_addr(&cluster.entry_point_info); let client = ThinClient::new(rpc, tpu, cluster.connection_cache.clone()); // Programs that are available at epoch 0 @@ -1336,7 +1337,7 @@ fn test_snapshots_blockstore_floor() { let archive_info = loop { let archive = - snapshot_utils::get_highest_full_snapshot_archive_info(&full_snapshot_archives_dir); + snapshot_utils::get_highest_full_snapshot_archive_info(full_snapshot_archives_dir); if archive.is_some() { trace!("snapshot exists"); break archive.unwrap(); @@ -1593,7 +1594,7 @@ fn test_optimistic_confirmation_violation_detection() { solana_logger::setup_with_default(RUST_LOG_FILTER); // First set up the cluster with 2 nodes let slots_per_epoch = 2048; - let node_stakes = vec![51 * DEFAULT_NODE_STAKE, 50 * DEFAULT_NODE_STAKE]; + let node_stakes = vec![50 * DEFAULT_NODE_STAKE, 51 * DEFAULT_NODE_STAKE]; let validator_keys: Vec<_> = vec![ "4qhhXNTbKD1a5vxDDLZcHKj7ELNeiivtUBxn3wUK1F5VRsQVP89VUhfXqSfgiFB14GfuBgtrQ96n9NvWQADVkcCg", "3kHBzVwie5vTEaY6nFCPeFT8qDpoXzn7dCEioGRNBTnUDpvwnG85w8Wq63gVWpVTP8k2a8cgcWRjSXyUkEygpXWS", @@ -1602,6 +1603,12 @@ fn test_optimistic_confirmation_violation_detection() { .map(|s| (Arc::new(Keypair::from_base58_string(s)), true)) .take(node_stakes.len()) .collect(); + + // Do not restart the validator which is the cluster entrypoint because its gossip port + // might be changed after restart resulting in the two nodes not being able to + // to form a cluster. The heavier validator is the second node. + let node_to_restart = validator_keys[1].0.pubkey(); + let mut config = ClusterConfig { cluster_lamports: DEFAULT_CLUSTER_LAMPORTS + node_stakes.iter().sum::(), node_stakes: node_stakes.clone(), @@ -1616,12 +1623,11 @@ fn test_optimistic_confirmation_violation_detection() { ..ClusterConfig::default() }; let mut cluster = LocalCluster::new(&mut config, SocketAddrSpace::Unspecified); - let entry_point_id = cluster.entry_point_info.id; // Let the nodes run for a while. Wait for validators to vote on slot `S` // so that the vote on `S-1` is definitely in gossip and optimistic confirmation is // detected on slot `S-1` for sure, then stop the heavier of the two // validators - let client = cluster.get_validator_client(&entry_point_id).unwrap(); + let client = cluster.get_validator_client(&node_to_restart).unwrap(); let mut prev_voted_slot = 0; loop { let last_voted_slot = client @@ -1637,7 +1643,7 @@ fn test_optimistic_confirmation_violation_detection() { sleep(Duration::from_millis(100)); } - let exited_validator_info = cluster.exit_node(&entry_point_id); + let exited_validator_info = cluster.exit_node(&node_to_restart); // Mark fork as dead on the heavier validator, this should make the fork effectively // dead, even though it was optimistically confirmed. The smaller validator should @@ -1657,7 +1663,7 @@ fn test_optimistic_confirmation_violation_detection() { // on ancestors of last vote) // 2) Won't reset to this earlier ancestor becasue reset can only happen on same voted fork if // it's for the last vote slot or later - remove_tower(&exited_validator_info.info.ledger_path, &entry_point_id); + remove_tower(&exited_validator_info.info.ledger_path, &node_to_restart); blockstore.set_dead_slot(prev_voted_slot).unwrap(); } @@ -1667,7 +1673,7 @@ fn test_optimistic_confirmation_violation_detection() { .err() .map(|_| BufferRedirect::stderr().unwrap()); cluster.restart_node( - &entry_point_id, + &node_to_restart, exited_validator_info, SocketAddrSpace::Unspecified, ); @@ -1675,7 +1681,7 @@ fn test_optimistic_confirmation_violation_detection() { // Wait for a root > prev_voted_slot to be set. Because the root is on a // different fork than `prev_voted_slot`, then optimistic confirmation is // violated - let client = cluster.get_validator_client(&entry_point_id).unwrap(); + let client = cluster.get_validator_client(&node_to_restart).unwrap(); loop { let last_root = client .get_slot_with_commitment(CommitmentConfig::finalized()) @@ -1715,7 +1721,7 @@ fn test_optimistic_confirmation_violation_detection() { // Make sure validator still makes progress cluster_tests::check_for_new_roots( 16, - &[cluster.get_contact_info(&entry_point_id).unwrap().clone()], + &[cluster.get_contact_info(&node_to_restart).unwrap().clone()], &cluster.connection_cache, "test_optimistic_confirmation_violation", ); @@ -2520,6 +2526,7 @@ fn run_test_load_program_accounts_partition(scan_commitment: CommitmentConfig) { #[test] #[serial] +#[ignore] fn test_votes_land_in_fork_during_long_partition() { let total_stake = 3 * DEFAULT_NODE_STAKE; // Make `lighter_stake` insufficient for switching threshold diff --git a/local-cluster/tests/local_cluster_slow_1.rs b/local-cluster/tests/local_cluster_slow_1.rs index dab4f7807e677e..582d02ff15b4fd 100644 --- a/local-cluster/tests/local_cluster_slow_1.rs +++ b/local-cluster/tests/local_cluster_slow_1.rs @@ -49,6 +49,7 @@ mod common; #[test] #[serial] +#[ignore] // Steps in this test: // We want to create a situation like: /* @@ -587,6 +588,7 @@ fn test_duplicate_shreds_broadcast_leader() { #[test] #[serial] +#[ignore] fn test_switch_threshold_uses_gossip_votes() { solana_logger::setup_with_default(RUST_LOG_FILTER); let total_stake = 100 * DEFAULT_NODE_STAKE; diff --git a/log-analyzer/Cargo.toml b/log-analyzer/Cargo.toml index caf6268bba4717..89c77eda032d99 100644 --- a/log-analyzer/Cargo.toml +++ b/log-analyzer/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-log-analyzer" description = "The solana cluster network analysis tool" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -14,8 +14,8 @@ byte-unit = "4.0.14" clap = { version = "3.1.5", features = ["cargo"] } serde = "1.0.138" serde_json = "1.0.81" -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [[bin]] name = "solana-log-analyzer" diff --git a/log-analyzer/src/main.rs b/log-analyzer/src/main.rs index c2bd11aa072803..d28bdf597324f4 100644 --- a/log-analyzer/src/main.rs +++ b/log-analyzer/src/main.rs @@ -129,8 +129,8 @@ fn process_iftop_logs(matches: &ArgMatches) { } }); let output: Vec = unique_latest_logs - .into_iter() - .map(|(_, l)| { + .into_values() + .map(|l| { if map_list.is_empty() { l } else { diff --git a/logger/Cargo.toml b/logger/Cargo.toml index 769849aff60943..ba4fba3be43e54 100644 --- a/logger/Cargo.toml +++ b/logger/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-logger" -version = "1.11.6" +version = "1.14.24" description = "Solana Logger" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" diff --git a/measure/Cargo.toml b/measure/Cargo.toml index 071d83a3e7f652..233337e49cfb07 100644 --- a/measure/Cargo.toml +++ b/measure/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "solana-measure" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" homepage = "https://solana.com/" documentation = "https://docs.rs/solana-measure" readme = "../README.md" @@ -12,7 +12,7 @@ edition = "2021" [dependencies] log = "0.4.17" -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/merkle-root-bench/Cargo.toml b/merkle-root-bench/Cargo.toml index 3d20de8ad55bbb..e0271885f74adb 100644 --- a/merkle-root-bench/Cargo.toml +++ b/merkle-root-bench/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-merkle-root-bench" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -11,11 +11,11 @@ publish = false [dependencies] clap = "2.33.1" log = "0.4.17" -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/merkle-tree/Cargo.toml b/merkle-tree/Cargo.toml index 6413f108e39b2e..c4dd5235f93a1b 100644 --- a/merkle-tree/Cargo.toml +++ b/merkle-tree/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-merkle-tree" -version = "1.11.6" +version = "1.14.24" description = "Solana Merkle Tree" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -11,7 +11,7 @@ edition = "2021" [dependencies] fast-math = "0.1" -solana-program = { path = "../sdk/program", version = "=1.11.6" } +solana-program = { path = "../sdk/program", version = "=1.14.24" } # This can go once the BPF toolchain target Rust 1.42.0+ [target.bpfel-unknown-unknown.dependencies] diff --git a/merkle-tree/src/merkle_tree.rs b/merkle-tree/src/merkle_tree.rs index 70e09c27ecb9c2..74be06cf25955d 100644 --- a/merkle-tree/src/merkle_tree.rs +++ b/merkle-tree/src/merkle_tree.rs @@ -135,6 +135,42 @@ impl MerkleTree { mt } + pub fn new_with_leaves(mut items: Vec) -> Self { + let cap = MerkleTree::calculate_vec_capacity(items.len()); + let mut mt = MerkleTree { + leaf_count: items.len(), + nodes: Vec::with_capacity(cap), + }; + + mt.nodes.append(&mut items); + + let mut level_len = MerkleTree::next_level_len(items.len()); + let mut level_start = items.len(); + let mut prev_level_len = items.len(); + let mut prev_level_start = 0; + while level_len > 0 { + for i in 0..level_len { + let prev_level_idx = 2 * i; + let lsib = &mt.nodes[prev_level_start + prev_level_idx]; + let rsib = if prev_level_idx + 1 < prev_level_len { + &mt.nodes[prev_level_start + prev_level_idx + 1] + } else { + // Duplicate last entry if the level length is odd + &mt.nodes[prev_level_start + prev_level_idx] + }; + + let hash = hash_intermediate!(lsib, rsib); + mt.nodes.push(hash); + } + prev_level_start = level_start; + prev_level_len = level_len; + level_start += level_len; + level_len = MerkleTree::next_level_len(level_len); + } + + mt + } + pub fn get_root(&self) -> Option<&Hash> { self.nodes.iter().last() } diff --git a/metrics/Cargo.toml b/metrics/Cargo.toml index 9348a0c1df8fc3..bfed337175b23d 100644 --- a/metrics/Cargo.toml +++ b/metrics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-metrics" -version = "1.11.6" +version = "1.14.24" description = "Solana Metrics" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -15,7 +15,7 @@ gethostname = "0.2.3" lazy_static = "1.4.0" log = "0.4.17" reqwest = { version = "0.11.11", default-features = false, features = ["blocking", "brotli", "deflate", "gzip", "rustls-tls", "json"] } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } [dev-dependencies] env_logger = "0.9.0" diff --git a/metrics/src/metrics.rs b/metrics/src/metrics.rs index 99f1183648b147..4b9b183de98ee4 100644 --- a/metrics/src/metrics.rs +++ b/metrics/src/metrics.rs @@ -173,7 +173,11 @@ impl MetricsAgent { max_points_per_sec: usize, ) -> Self { let (sender, receiver) = unbounded::(); - thread::spawn(move || Self::run(&receiver, &writer, write_frequency, max_points_per_sec)); + + thread::Builder::new() + .name("solMetricsAgent".into()) + .spawn(move || Self::run(&receiver, &writer, write_frequency, max_points_per_sec)) + .unwrap(); Self { sender } } diff --git a/multinode-demo/bootstrap-validator.sh b/multinode-demo/bootstrap-validator.sh index 9245f507c394e2..fb6353f76bc9d7 100755 --- a/multinode-demo/bootstrap-validator.sh +++ b/multinode-demo/bootstrap-validator.sh @@ -61,7 +61,10 @@ while [[ -n $1 ]]; do elif [[ $1 = --enable-rpc-bigtable-ledger-storage ]]; then args+=("$1") shift - elif [[ $1 = --tpu-use-quic ]]; then + elif [[ $1 = --tpu-disable-quic ]]; then + args+=("$1") + shift + elif [[ $1 = --tpu-enable-udp ]]; then args+=("$1") shift elif [[ $1 = --rpc-send-batch-ms ]]; then diff --git a/multinode-demo/validator.sh b/multinode-demo/validator.sh index 84bfe2caae0313..1ccfc72f9d0b97 100755 --- a/multinode-demo/validator.sh +++ b/multinode-demo/validator.sh @@ -150,6 +150,9 @@ while [[ -n $1 ]]; do elif [[ $1 = --tpu-use-quic ]]; then args+=("$1") shift + elif [[ $1 = --tpu-enable-udp ]]; then + args+=("$1") + shift elif [[ $1 = --rpc-send-batch-ms ]]; then args+=("$1" "$2") shift 2 diff --git a/net-shaper/Cargo.toml b/net-shaper/Cargo.toml index 284c290c6207c4..1dfe07cd85fe2b 100644 --- a/net-shaper/Cargo.toml +++ b/net-shaper/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-net-shaper" description = "The solana cluster network shaping tool" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -14,7 +14,7 @@ clap = { version = "3.1.5", features = ["cargo"] } rand = "0.7.0" serde = { version = "1.0.138", features = ["derive"] } serde_json = "1.0.81" -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [[bin]] name = "solana-net-shaper" diff --git a/net-utils/Cargo.toml b/net-utils/Cargo.toml index 6d37c010d8cb3f..3c13cc7c750f7b 100644 --- a/net-utils/Cargo.toml +++ b/net-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-net-utils" -version = "1.11.6" +version = "1.14.24" description = "Solana Network Utilities" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -19,10 +19,10 @@ rand = "0.7.0" serde = "1.0.138" serde_derive = "1.0.103" socket2 = "0.4.4" -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -tokio = { version = "~1.14.1", features = ["full"] } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +tokio = { version = "1", features = ["full"] } url = "2.2.2" [lib] diff --git a/net-utils/src/lib.rs b/net-utils/src/lib.rs index 9f60bb9b5749b2..47e296be51ad21 100644 --- a/net-utils/src/lib.rs +++ b/net-utils/src/lib.rs @@ -144,15 +144,18 @@ fn do_verify_reachable_ports( for (port, tcp_listener) in tcp_listeners { let (sender, receiver) = unbounded(); let listening_addr = tcp_listener.local_addr().unwrap(); - let thread_handle = std::thread::spawn(move || { - debug!("Waiting for incoming connection on tcp/{}", port); - match tcp_listener.incoming().next() { - Some(_) => sender - .send(()) - .unwrap_or_else(|err| warn!("send failure: {}", err)), - None => warn!("tcp incoming failed"), - } - }); + let thread_handle = std::thread::Builder::new() + .name(format!("solVrfyTcp{:05}", port)) + .spawn(move || { + debug!("Waiting for incoming connection on tcp/{}", port); + match tcp_listener.incoming().next() { + Some(_) => sender + .send(()) + .unwrap_or_else(|err| warn!("send failure: {}", err)), + None => warn!("tcp incoming failed"), + } + }) + .unwrap(); match receiver.recv_timeout(timeout) { Ok(_) => { info!("tcp/{} is reachable", port); @@ -222,33 +225,37 @@ fn do_verify_reachable_ports( let port = udp_socket.local_addr().unwrap().port(); let udp_socket = udp_socket.try_clone().expect("Unable to clone udp socket"); let reachable_ports = reachable_ports.clone(); - std::thread::spawn(move || { - let start = Instant::now(); - - let original_read_timeout = udp_socket.read_timeout().unwrap(); - udp_socket - .set_read_timeout(Some(Duration::from_millis(250))) - .unwrap(); - loop { - if reachable_ports.read().unwrap().contains(&port) - || Instant::now().duration_since(start) >= timeout - { - break; - } - let recv_result = udp_socket.recv(&mut [0; 1]); - debug!( - "Waited for incoming datagram on udp/{}: {:?}", - port, recv_result - ); - - if recv_result.is_ok() { - reachable_ports.write().unwrap().insert(port); - break; + std::thread::Builder::new() + .name(format!("solVrfyUdp{:05}", port)) + .spawn(move || { + let start = Instant::now(); + + let original_read_timeout = udp_socket.read_timeout().unwrap(); + udp_socket + .set_read_timeout(Some(Duration::from_millis(250))) + .unwrap(); + loop { + if reachable_ports.read().unwrap().contains(&port) + || Instant::now().duration_since(start) >= timeout + { + break; + } + + let recv_result = udp_socket.recv(&mut [0; 1]); + debug!( + "Waited for incoming datagram on udp/{}: {:?}", + port, recv_result + ); + + if recv_result.is_ok() { + reachable_ports.write().unwrap().insert(port); + break; + } } - } - udp_socket.set_read_timeout(original_read_timeout).unwrap(); - }) + udp_socket.set_read_timeout(original_read_timeout).unwrap(); + }) + .unwrap() }) .collect(); @@ -516,34 +523,37 @@ pub fn bind_common( let addr = SocketAddr::new(ip_addr, port); let sock_addr = SockAddr::from(addr); sock.bind(&sock_addr) - .and_then(|_| TcpListener::bind(&addr).map(|listener| (sock.into(), listener))) + .and_then(|_| TcpListener::bind(addr).map(|listener| (sock.into(), listener))) } -pub fn bind_two_consecutive_in_range( +pub fn bind_two_in_range_with_offset( ip_addr: IpAddr, range: PortRange, + offset: u16, ) -> io::Result<((u16, UdpSocket), (u16, UdpSocket))> { - let mut first: Option = None; + if range.1.saturating_sub(range.0) < offset { + return Err(io::Error::new( + io::ErrorKind::Other, + "range too small to find two ports with the correct offset".to_string(), + )); + } for port in range.0..range.1 { - if let Ok(bind) = bind_to(ip_addr, port, false) { - match first { - Some(first_bind) => { + if let Ok(first_bind) = bind_to(ip_addr, port, false) { + if range.1.saturating_sub(port) >= offset { + if let Ok(second_bind) = bind_to(ip_addr, port + offset, false) { return Ok(( (first_bind.local_addr().unwrap().port(), first_bind), - (bind.local_addr().unwrap().port(), bind), + (second_bind.local_addr().unwrap().port(), second_bind), )); } - None => { - first = Some(bind); - } + } else { + break; } - } else { - first = None; } } Err(io::Error::new( io::ErrorKind::Other, - "couldn't find two consecutive ports in range".to_string(), + "couldn't find two ports with the correct offset in range".to_string(), )) } @@ -818,12 +828,21 @@ mod tests { } #[test] - fn test_bind_two_consecutive_in_range() { + fn test_bind_two_in_range_with_offset() { solana_logger::setup(); let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)); - if let Ok(((port1, _), (port2, _))) = bind_two_consecutive_in_range(ip_addr, (1024, 65535)) + let offset = 6; + if let Ok(((port1, _), (port2, _))) = + bind_two_in_range_with_offset(ip_addr, (1024, 65535), offset) + { + assert!(port2 == port1 + offset); + } + let offset = 42; + if let Ok(((port1, _), (port2, _))) = + bind_two_in_range_with_offset(ip_addr, (1024, 65535), offset) { - assert!(port2 == port1 + 1); + assert!(port2 == port1 + offset); } + assert!(bind_two_in_range_with_offset(ip_addr, (1024, 1044), offset).is_err()); } } diff --git a/net/gce.sh b/net/gce.sh index 6c9e9b357f6e18..1a4eb775e220c1 100755 --- a/net/gce.sh +++ b/net/gce.sh @@ -397,6 +397,7 @@ cloud_ForEachInstance() { declare name publicIp privateIp IFS=: read -r name publicIp privateIp zone < <(echo "$info") + # shellcheck disable=SC2294 eval "$cmd" "$name" "$publicIp" "$privateIp" "$zone" "$count" "$@" count=$((count + 1)) done diff --git a/net/net.sh b/net/net.sh index 418716c47b00ff..ade427e767c103 100755 --- a/net/net.sh +++ b/net/net.sh @@ -107,6 +107,10 @@ Operate a configured testnet - Boot from a snapshot that has warped ahead to WARP_SLOT rather than a slot 0 genesis. --full-rpc - Support full RPC services on all nodes + + --tpu-enable-udp + - Enable UDP for tpu transactions + sanity/start-specific options: -F - Discard validator nodes that didn't bootup successfully -o noInstallCheck - Skip solana-install sanity @@ -320,6 +324,7 @@ startBootstrapLeader() { \"$waitForNodeInit\" \ \"$extraPrimordialStakes\" \ \"$TMPFS_ACCOUNTS\" \ + \"$enableUdp\" \ " ) >> "$logFile" 2>&1 || { @@ -392,6 +397,7 @@ startNode() { \"$waitForNodeInit\" \ \"$extraPrimordialStakes\" \ \"$TMPFS_ACCOUNTS\" \ + \"$enableUdp\" \ " ) >> "$logFile" 2>&1 & declare pid=$! @@ -595,7 +601,7 @@ deploy() { if $bootstrapLeader; then SECONDS=0 declare bootstrapNodeDeployTime= - startBootstrapLeader "$nodeAddress" $nodeIndex "$netLogDir/bootstrap-validator-$ipAddress.log" + startBootstrapLeader "$nodeAddress" "$nodeIndex" "$netLogDir/bootstrap-validator-$ipAddress.log" bootstrapNodeDeployTime=$SECONDS $metricsWriteDatapoint "testnet-deploy net-bootnode-leader-started=1" @@ -603,7 +609,7 @@ deploy() { SECONDS=0 pids=() else - startNode "$ipAddress" $nodeType $nodeIndex + startNode "$ipAddress" "$nodeType" "$nodeIndex" # Stagger additional node start time. If too many nodes start simultaneously # the bootstrap node gets more rsync requests from the additional nodes than @@ -800,6 +806,7 @@ maybeWarpSlot= maybeFullRpc=false waitForNodeInit=true extraPrimordialStakes=0 +enableUdp=false command=$1 [[ -n $command ]] || usage @@ -912,6 +919,9 @@ while [[ -n $1 ]]; do elif [[ $1 == --full-rpc ]]; then maybeFullRpc=true shift 1 + elif [[ $1 == --tpu-enable-udp ]]; then + enableUdp=true + shift 1 elif [[ $1 == --async-node-init ]]; then waitForNodeInit=false shift 1 @@ -1114,7 +1124,7 @@ startnode) nodeType= nodeIndex= getNodeType - startNode "$nodeAddress" $nodeType $nodeIndex + startNode "$nodeAddress" "$nodeType" "$nodeIndex" ;; startclients) startClients diff --git a/net/remote/remote-node.sh b/net/remote/remote-node.sh index 2b93ffed145a66..7c5a931ac3a5fa 100755 --- a/net/remote/remote-node.sh +++ b/net/remote/remote-node.sh @@ -28,6 +28,8 @@ maybeFullRpc="${19}" waitForNodeInit="${20}" extraPrimordialStakes="${21:=0}" tmpfsAccounts="${22:false}" +enableUdp="${23}" + set +x missing() { @@ -283,6 +285,10 @@ EOF args+=(--enable-extended-tx-metadata-storage) fi + if $enableUdp; then + args+=(--tpu-enable-udp) + fi + if [[ $airdropsEnabled = true ]]; then cat >> ~/solana/on-reboot < faucet.log 2>&1 & @@ -411,6 +417,10 @@ EOF args+=(--enable-extended-tx-metadata-storage) fi + if $enableUdp; then + args+=(--tpu-enable-udp) + fi + cat >> ~/solana/on-reboot < validator.log.\$now 2>&1 & diff --git a/net/scripts/colo-node-onfree.sh b/net/scripts/colo-node-onfree.sh index c541b210d1adb4..e5af6a4df172a6 100644 --- a/net/scripts/colo-node-onfree.sh +++ b/net/scripts/colo-node-onfree.sh @@ -4,7 +4,7 @@ SOLANA_LOCK_FILE="${SOLANA_LOCK_FILE:?}" SECONDARY_DISK_MOUNT_POINT="${SECONDARY_DISK_MOUNT_POINT:?}" SSH_AUTHORIZED_KEYS="${SSH_AUTHORIZED_KEYS:?}" -FORCE_DELETE="${FORCE_DELETE}" +FORCE_DELETE="${FORCE_DELETE:?}" RC=false if [[ -f "${SOLANA_LOCK_FILE}" ]]; then diff --git a/notifier/Cargo.toml b/notifier/Cargo.toml index 858589a2331425..e8bffb2151145a 100644 --- a/notifier/Cargo.toml +++ b/notifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-notifier" -version = "1.11.6" +version = "1.14.24" description = "Solana Notifier" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" diff --git a/perf/Cargo.toml b/perf/Cargo.toml index 135865d89f27b2..fb04b226904605 100644 --- a/perf/Cargo.toml +++ b/perf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-perf" -version = "1.11.6" +version = "1.14.24" description = "Solana Performance APIs" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -22,10 +22,10 @@ log = "0.4.17" rand = "0.7.0" rayon = "1.5.3" serde = "1.0.138" -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } [target."cfg(target_os = \"linux\")".dependencies] caps = "0.5.3" @@ -37,7 +37,9 @@ name = "solana_perf" [dev-dependencies] matches = "0.1.9" -solana-logger = { path = "../logger", version = "=1.11.6" } +rand_chacha = "0.2.2" +solana-logger = { path = "../logger", version = "=1.14.24" } +test-case = "2.1.0" [[bench]] name = "sigverify" diff --git a/perf/benches/dedup.rs b/perf/benches/dedup.rs index c4f3169979cf52..842d3ad071e0d9 100644 --- a/perf/benches/dedup.rs +++ b/perf/benches/dedup.rs @@ -7,7 +7,7 @@ use { rand::prelude::*, solana_perf::{ packet::{to_packet_batches, PacketBatch}, - sigverify, + sigverify::{self, Deduper}, }, std::time::Duration, test::Bencher, @@ -24,10 +24,16 @@ fn test_packet_with_size(size: usize, rng: &mut ThreadRng) -> Vec { fn do_bench_dedup_packets(bencher: &mut Bencher, mut batches: Vec) { // verify packets - let mut deduper = sigverify::Deduper::new(1_000_000, Duration::from_millis(2_000)); + let mut rng = rand::thread_rng(); + let mut deduper = Deduper::<2, [u8]>::new(&mut rng, /*num_bits:*/ 63_999_979); bencher.iter(|| { - let _ans = deduper.dedup_packets_and_count_discards(&mut batches, |_, _, _| ()); - deduper.reset(); + let _ans = + sigverify::dedup_packets_and_count_discards(&deduper, &mut batches, |_, _, _| ()); + deduper.maybe_reset( + &mut rng, + 0.001, // false_positive_rate + Duration::from_secs(2), // reset_cycle + ); batches .iter_mut() .for_each(|b| b.iter_mut().for_each(|p| p.meta.set_discard(false))); @@ -112,8 +118,13 @@ fn bench_dedup_baseline(bencher: &mut Bencher) { #[bench] #[ignore] fn bench_dedup_reset(bencher: &mut Bencher) { - let mut deduper = sigverify::Deduper::new(1_000_000, Duration::from_millis(0)); + let mut rng = rand::thread_rng(); + let mut deduper = Deduper::<2, [u8]>::new(&mut rng, /*num_bits:*/ 63_999_979); bencher.iter(|| { - deduper.reset(); + deduper.maybe_reset( + &mut rng, + 0.001, // false_positive_rate + Duration::from_millis(0), // reset_cycle + ); }); } diff --git a/perf/src/perf_libs.rs b/perf/src/perf_libs.rs index 1f0505b4a4e7dd..6644a1eabd1cef 100644 --- a/perf/src/perf_libs.rs +++ b/perf/src/perf_libs.rs @@ -121,7 +121,7 @@ fn find_cuda_home(perf_libs_path: &Path) -> Option { } // Search /usr/local for a `cuda-` directory that matches a perf-libs subdirectory - for entry in fs::read_dir(&perf_libs_path).unwrap().flatten() { + for entry in fs::read_dir(perf_libs_path).unwrap().flatten() { let path = entry.path(); if !path.is_dir() { continue; diff --git a/perf/src/sigverify.rs b/perf/src/sigverify.rs index aee1b310dd59d9..b50ef21d166965 100644 --- a/perf/src/sigverify.rs +++ b/perf/src/sigverify.rs @@ -6,12 +6,12 @@ use { crate::{ cuda_runtime::PinnedVec, - packet::{Packet, PacketBatch, PacketFlags}, + packet::{Packet, PacketBatch, PacketFlags, PACKET_DATA_SIZE}, perf_libs, recycler::Recycler, }, ahash::AHasher, - rand::{thread_rng, Rng}, + rand::Rng, rayon::{prelude::*, ThreadPool}, solana_metrics::inc_new_counter_debug, solana_rayon_threadlimit::get_thread_count, @@ -19,15 +19,16 @@ use { hash::Hash, message::{MESSAGE_HEADER_LENGTH, MESSAGE_VERSION_PREFIX}, pubkey::Pubkey, - saturating_add_assign, short_vec::decode_shortu16_len, signature::Signature, }, std::{ convert::TryFrom, hash::Hasher, + iter::repeat_with, + marker::PhantomData, mem::size_of, - sync::atomic::{AtomicBool, AtomicU64, Ordering}, + sync::atomic::{AtomicU64, Ordering}, time::{Duration, Instant}, }, }; @@ -45,7 +46,7 @@ pub const VERIFY_MIN_PACKETS_PER_THREAD: usize = 128; lazy_static! { static ref PAR_THREAD_POOL: ThreadPool = rayon::ThreadPoolBuilder::new() .num_threads(get_thread_count()) - .thread_name(|ix| format!("sigverify_{}", ix)) + .thread_name(|ix| format!("solSigVerify{:02}", ix)) .build() .unwrap(); } @@ -367,6 +368,15 @@ fn check_for_simple_vote_transaction( return Err(PacketError::InvalidSignatureLen); } + // simple vote should only be legacy message + let msg_start = (packet_offsets.msg_start as usize) + .checked_sub(current_offset) + .ok_or(PacketError::InvalidLen)?; + let message_prefix = *packet.data(msg_start).ok_or(PacketError::InvalidLen)?; + if message_prefix & MESSAGE_VERSION_PREFIX != 0 { + return Ok(()); + } + let pubkey_start = (packet_offsets.pubkey_start as usize) .checked_sub(current_offset) .ok_or(PacketError::InvalidLen)?; @@ -486,92 +496,100 @@ pub fn generate_offsets( ) } -pub struct Deduper { - filter: Vec, - seed: (u128, u128), - age: Instant, - max_age: Duration, - pub saturated: AtomicBool, +pub struct Deduper { + num_bits: u64, + bits: Vec, + seeds: [(u128, u128); K], + clock: Instant, + popcount: AtomicU64, // Number of one bits in self.bits. + _phantom: PhantomData, } -impl Deduper { - pub fn new(size: u32, max_age: Duration) -> Self { - let mut filter: Vec = Vec::with_capacity(size as usize); - filter.resize_with(size as usize, Default::default); - let seed = thread_rng().gen(); +impl Deduper { + pub fn new(rng: &mut R, num_bits: u64) -> Self { + let size = num_bits.checked_add(63).unwrap() / 64; + let size = usize::try_from(size).unwrap(); Self { - filter, - seed, - age: Instant::now(), - max_age, - saturated: AtomicBool::new(false), + num_bits, + seeds: [(); K].map(|_| rng.gen()), + clock: Instant::now(), + bits: repeat_with(AtomicU64::default).take(size).collect(), + popcount: AtomicU64::default(), + _phantom: PhantomData::::default(), } } - pub fn reset(&mut self) { - let now = Instant::now(); - //this should reset every 500k unique packets per 1m sized deduper - //false positive rate is 1/1000 at that point - let saturated = self.saturated.load(Ordering::Relaxed); - if saturated || now.duration_since(self.age) > self.max_age { - let len = self.filter.len(); - self.filter.clear(); - self.filter.resize_with(len, AtomicU64::default); - self.seed = thread_rng().gen(); - self.age = now; - self.saturated.store(false, Ordering::Relaxed); + fn false_positive_rate(&self) -> f64 { + let popcount = self.popcount.load(Ordering::Relaxed); + let ones_ratio = popcount.min(self.num_bits) as f64 / self.num_bits as f64; + ones_ratio.powi(K as i32) + } + + /// Resets the Deduper if either it is older than the reset_cycle or it is + /// saturated enough that false positive rate exceeds specified threshold. + /// Returns true if the deduper was saturated. + pub fn maybe_reset( + &mut self, + rng: &mut R, + false_positive_rate: f64, + reset_cycle: Duration, + ) -> bool { + assert!(0.0 < false_positive_rate && false_positive_rate < 1.0); + let saturated = self.false_positive_rate() >= false_positive_rate; + if saturated || self.clock.elapsed() >= reset_cycle { + self.seeds = [(); K].map(|_| rng.gen()); + self.clock = Instant::now(); + self.bits.fill_with(AtomicU64::default); + self.popcount = AtomicU64::default(); } - } - - /// Compute hash from packet data, returns (hash, bin_pos). - fn compute_hash(&self, packet: &Packet) -> (u64, usize) { - let mut hasher = AHasher::new_with_keys(self.seed.0, self.seed.1); - hasher.write(packet.data(..).unwrap_or_default()); - let h = hasher.finish(); - let len = self.filter.len(); - let pos = (usize::try_from(h).unwrap()).wrapping_rem(len); - (h, pos) - } - - // Deduplicates packets and returns 1 if packet is to be discarded. Else, 0. - fn dedup_packet(&self, packet: &mut Packet) -> u64 { - // If this packet was already marked as discard, drop it - if packet.meta.discard() { - return 1; - } - let (hash, pos) = self.compute_hash(packet); - // saturate each position with or - let prev = self.filter[pos].fetch_or(hash, Ordering::Relaxed); - if prev == u64::MAX { - self.saturated.store(true, Ordering::Relaxed); - //reset this value - self.filter[pos].store(hash, Ordering::Relaxed); - } - if hash == prev & hash { - packet.meta.set_discard(true); - return 1; + saturated + } + + // Returns true if the data is duplicate. + #[must_use] + #[allow(clippy::integer_arithmetic)] + pub fn dedup(&self, data: &T) -> bool { + let mut out = true; + for seed in self.seeds { + let mut hasher = AHasher::new_with_keys(seed.0, seed.1); + data.hash(&mut hasher); + let hash: u64 = hasher.finish() % self.num_bits; + let index = (hash >> 6) as usize; + let mask: u64 = 1u64 << (hash & 63); + let old = self.bits[index].fetch_or(mask, Ordering::Relaxed); + if old & mask == 0u64 { + self.popcount.fetch_add(1, Ordering::Relaxed); + out = false; + } } - 0 + out } +} - pub fn dedup_packets_and_count_discards( - &self, - batches: &mut [PacketBatch], - mut process_received_packet: impl FnMut(&mut Packet, bool, bool), - ) -> u64 { - let mut num_removed: u64 = 0; - batches.iter_mut().for_each(|batch| { - batch.iter_mut().for_each(|p| { - let removed_before_sigverify = p.meta.discard(); - let is_duplicate = self.dedup_packet(p); - if is_duplicate == 1 { - saturating_add_assign!(num_removed, 1); - } - process_received_packet(p, removed_before_sigverify, is_duplicate == 1); - }) - }); - num_removed - } +pub fn dedup_packets_and_count_discards( + deduper: &Deduper, + batches: &mut [PacketBatch], + mut process_received_packet: impl FnMut(&mut Packet, bool, bool), +) -> u64 { + batches + .iter_mut() + .flat_map(PacketBatch::iter_mut) + .map(|packet| { + if packet.meta.discard() { + process_received_packet(packet, true, false); + } else if packet + .data(..) + .map(|data| deduper.dedup(data)) + .unwrap_or(true) + { + packet.meta.set_discard(true); + process_received_packet(packet, false, true); + } else { + process_received_packet(packet, false, false); + } + u64::from(packet.meta.discard()) + }) + .sum() } //inplace shrink a batch of packets @@ -662,26 +680,18 @@ pub fn ed25519_verify_disabled(batches: &mut [PacketBatch]) { inc_new_counter_debug!("ed25519_verify_disabled", packet_count); } -pub fn copy_return_values(sig_lens: &[Vec], out: &PinnedVec, rvs: &mut [Vec]) { - let mut num = 0; - for (vs, sig_vs) in rvs.iter_mut().zip(sig_lens.iter()) { - for (v, sig_v) in vs.iter_mut().zip(sig_vs.iter()) { - if *sig_v == 0 { - *v = 0; - } else { - let mut vout = 1; - for _ in 0..*sig_v { - if 0 == out[num] { - vout = 0; - } - num = num.saturating_add(1); - } - *v = vout; - } - if *v != 0 { - trace!("VERIFIED PACKET!!!!!"); - } - } +pub fn copy_return_values(sig_lens: I, out: &PinnedVec, rvs: &mut [Vec]) +where + I: IntoIterator, + T: IntoIterator, +{ + debug_assert!(rvs.iter().flatten().all(|&rv| rv == 0u8)); + let mut offset = 0usize; + let rvs = rvs.iter_mut().flatten(); + for (k, rv) in sig_lens.into_iter().flatten().zip(rvs) { + let out = out[offset..].iter().take(k as usize).all(|&x| x == 1u8); + *rv = u8::from(k != 0u32 && out); + offset = offset.saturating_add(k as usize); } } @@ -730,14 +740,10 @@ pub fn ed25519_verify( reject_non_vote: bool, valid_packet_count: usize, ) { - let api = perf_libs::api(); - if api.is_none() { - return ed25519_verify_cpu(batches, reject_non_vote, valid_packet_count); - } - let api = api.unwrap(); - - use crate::packet::PACKET_DATA_SIZE; - + let api = match perf_libs::api() { + None => return ed25519_verify_cpu(batches, reject_non_vote, valid_packet_count), + Some(api) => api, + }; let total_packet_count = count_packets_in_batches(batches); // micro-benchmarks show GPU time for smallest batch around 15-20ms // and CPU speed for 64-128 sigverifies around 10-20ms. 64 is a nice @@ -798,7 +804,7 @@ pub fn ed25519_verify( } } trace!("done verify"); - copy_return_values(&sig_lens, &out, &mut rvs); + copy_return_values(sig_lens, &out, &mut rvs); mark_disabled(batches, &rvs); inc_new_counter_debug!("ed25519_verify_gpu", valid_packet_count); } @@ -809,20 +815,26 @@ mod tests { use { super::*, crate::{ - packet::{to_packet_batches, Packet, PacketBatch, PACKETS_PER_BATCH, PACKET_DATA_SIZE}, + packet::{to_packet_batches, Packet, PacketBatch, PACKETS_PER_BATCH}, sigverify::{self, PacketOffsets}, test_tx::{new_test_vote_tx, test_multisig_tx, test_tx}, }, bincode::{deserialize, serialize}, curve25519_dalek::{edwards::CompressedEdwardsY, scalar::Scalar}, - rand::{thread_rng, Rng}, + rand::{thread_rng, Rng, SeedableRng}, + rand_chacha::ChaChaRng, solana_sdk::{ instruction::CompiledInstruction, message::{Message, MessageHeader}, + packet::Meta, signature::{Keypair, Signature, Signer}, transaction::Transaction, }, - std::sync::atomic::{AtomicU64, Ordering}, + std::{ + iter::repeat_with, + sync::atomic::{AtomicU64, Ordering}, + }, + test_case::test_case, }; const SIG_OFFSET: usize = 1; @@ -838,6 +850,45 @@ mod tests { None } + #[test] + fn test_copy_return_values() { + let mut rng = rand::thread_rng(); + let sig_lens: Vec> = { + let size = rng.gen_range(0, 64); + repeat_with(|| { + let size = rng.gen_range(0, 16); + repeat_with(|| rng.gen_range(0, 5)).take(size).collect() + }) + .take(size) + .collect() + }; + let out: Vec>> = sig_lens + .iter() + .map(|sig_lens| { + sig_lens + .iter() + .map(|&size| repeat_with(|| rng.gen()).take(size as usize).collect()) + .collect() + }) + .collect(); + let expected: Vec> = out + .iter() + .map(|out| { + out.iter() + .map(|out| u8::from(!out.is_empty() && out.iter().all(|&k| k))) + .collect() + }) + .collect(); + let out = + PinnedVec::::from_vec(out.into_iter().flatten().flatten().map(u8::from).collect()); + let mut rvs: Vec> = sig_lens + .iter() + .map(|sig_lens| vec![0u8; sig_lens.len()]) + .collect(); + copy_return_values(sig_lens, &out, &mut rvs); + assert_eq!(rvs, expected); + } + #[test] fn test_mark_disabled() { let batch_size = 1; @@ -1447,7 +1498,7 @@ mod tests { assert!(!packet.meta.is_simple_vote_tx()); } - // single vote tx is + // single legacy vote tx is { let mut tx = new_test_vote_tx(&mut rng); tx.message.instructions[0].data = vec![1, 2, 3]; @@ -1457,6 +1508,26 @@ mod tests { assert!(packet.meta.is_simple_vote_tx()); } + // single versioned vote tx is not + { + let mut tx = new_test_vote_tx(&mut rng); + tx.message.instructions[0].data = vec![1, 2, 3]; + let mut packet = Packet::from_data(None, tx).unwrap(); + + // set messager version to v0 + let mut packet_offsets = do_get_packet_offsets(&packet, 0).unwrap(); + let msg_start = packet_offsets.msg_start as usize; + let msg_bytes = packet.data(msg_start..).unwrap().to_vec(); + packet.buffer_mut()[msg_start] = MESSAGE_VERSION_PREFIX; + packet.meta.size += 1; + let msg_end = packet.meta.size; + packet.buffer_mut()[msg_start + 1..msg_end].copy_from_slice(&msg_bytes); + + packet_offsets = do_get_packet_offsets(&packet, 0).unwrap(); + check_for_simple_vote_transaction(&mut packet, &packet_offsets, 0).ok(); + assert!(!packet.meta.is_simple_vote_tx()); + } + // multiple mixed tx is not { let key = Keypair::new(); @@ -1484,22 +1555,52 @@ mod tests { solana_logger::setup(); let mut rng = rand::thread_rng(); - let mut current_offset = 0usize; - let mut batch = PacketBatch::default(); - batch.push(Packet::from_data(None, test_tx()).unwrap()); - let tx = new_test_vote_tx(&mut rng); - batch.push(Packet::from_data(None, tx).unwrap()); - batch.iter_mut().enumerate().for_each(|(index, packet)| { - let packet_offsets = do_get_packet_offsets(packet, current_offset).unwrap(); - check_for_simple_vote_transaction(packet, &packet_offsets, current_offset).ok(); - if index == 1 { - assert!(packet.meta.is_simple_vote_tx()); - } else { + // batch of legacy messages + { + let mut current_offset = 0usize; + let mut batch = PacketBatch::default(); + batch.push(Packet::from_data(None, test_tx()).unwrap()); + let tx = new_test_vote_tx(&mut rng); + batch.push(Packet::from_data(None, tx).unwrap()); + batch.iter_mut().enumerate().for_each(|(index, packet)| { + let packet_offsets = do_get_packet_offsets(packet, current_offset).unwrap(); + check_for_simple_vote_transaction(packet, &packet_offsets, current_offset).ok(); + if index == 1 { + assert!(packet.meta.is_simple_vote_tx()); + } else { + assert!(!packet.meta.is_simple_vote_tx()); + } + + current_offset = current_offset.saturating_add(size_of::()); + }); + } + + // batch of mixed legacy messages and versioned vote tx, which won't be flagged as + // simple_vote_tx + { + let mut current_offset = 0usize; + let mut batch = PacketBatch::default(); + batch.push(Packet::from_data(None, test_tx()).unwrap()); + // versioned vote tx + let tx = new_test_vote_tx(&mut rng); + let mut packet = Packet::from_data(None, tx).unwrap(); + let packet_offsets = do_get_packet_offsets(&packet, 0).unwrap(); + let msg_start = packet_offsets.msg_start as usize; + let msg_bytes = packet.data(msg_start..).unwrap().to_vec(); + packet.buffer_mut()[msg_start] = MESSAGE_VERSION_PREFIX; + packet.meta.size += 1; + let msg_end = packet.meta.size; + packet.buffer_mut()[msg_start + 1..msg_end].copy_from_slice(&msg_bytes); + batch.push(packet); + + batch.iter_mut().for_each(|packet| { + let packet_offsets = do_get_packet_offsets(packet, current_offset).unwrap(); + check_for_simple_vote_transaction(packet, &packet_offsets, current_offset).ok(); assert!(!packet.meta.is_simple_vote_tx()); - } - current_offset = current_offset.saturating_add(size_of::()); - }); + current_offset = current_offset.saturating_add(size_of::()); + }); + } } #[test] @@ -1509,9 +1610,11 @@ mod tests { let mut batches = to_packet_batches(&std::iter::repeat(tx).take(1024).collect::>(), 128); let packet_count = sigverify::count_packets_in_batches(&batches); - let filter = Deduper::new(1_000_000, Duration::from_millis(0)); + let mut rng = rand::thread_rng(); + let filter = Deduper::<2, [u8]>::new(&mut rng, /*num_bits:*/ 63_999_979); let mut num_deduped = 0; - let discard = filter.dedup_packets_and_count_discards( + let discard = dedup_packets_and_count_discards( + &filter, &mut batches, |_deduped_packet, _removed_before_sigverify_stage, _is_dup| { num_deduped += 1; @@ -1523,86 +1626,136 @@ mod tests { #[test] fn test_dedup_diff() { - let mut filter = Deduper::new(1_000_000, Duration::from_millis(0)); + let mut rng = rand::thread_rng(); + let mut filter = Deduper::<2, [u8]>::new(&mut rng, /*num_bits:*/ 63_999_979); let mut batches = to_packet_batches(&(0..1024).map(|_| test_tx()).collect::>(), 128); - let discard = filter.dedup_packets_and_count_discards(&mut batches, |_, _, _| ()) as usize; + let discard = + dedup_packets_and_count_discards(&filter, &mut batches, |_, _, _| ()) as usize; // because dedup uses a threadpool, there maybe up to N threads of txs that go through assert_eq!(discard, 0); - filter.reset(); - for i in filter.filter { + assert!(!filter.maybe_reset( + &mut rng, + 0.001, // false_positive_rate + Duration::from_millis(0), // reset_cycle + )); + for i in filter.bits { assert_eq!(i.load(Ordering::Relaxed), 0); } } + fn get_capacity(num_bits: u64, false_positive_rate: f64) -> u64 { + (num_bits as f64 * false_positive_rate.powf(1f64 / K as f64)) as u64 + } + #[test] #[ignore] fn test_dedup_saturated() { - let filter = Deduper::new(1_000_000, Duration::from_millis(0)); + const NUM_BITS: u64 = 63_999_979; + const FALSE_POSITIVE_RATE: f64 = 0.001; + let mut rng = rand::thread_rng(); + let mut filter = Deduper::<2, [u8]>::new(&mut rng, NUM_BITS); + let capacity = get_capacity::<2>(NUM_BITS, FALSE_POSITIVE_RATE); let mut discard = 0; - assert!(!filter.saturated.load(Ordering::Relaxed)); + assert!(filter.popcount.load(Ordering::Relaxed) < capacity); for i in 0..1000 { let mut batches = to_packet_batches(&(0..1000).map(|_| test_tx()).collect::>(), 128); - discard += filter.dedup_packets_and_count_discards(&mut batches, |_, _, _| ()) as usize; + discard += + dedup_packets_and_count_discards(&filter, &mut batches, |_, _, _| ()) as usize; trace!("{} {}", i, discard); - if filter.saturated.load(Ordering::Relaxed) { + if filter.popcount.load(Ordering::Relaxed) > capacity { break; } } - assert!(filter.saturated.load(Ordering::Relaxed)); + assert!(filter.popcount.load(Ordering::Relaxed) > capacity); + assert!(filter.false_positive_rate() >= FALSE_POSITIVE_RATE); + assert!(filter.maybe_reset( + &mut rng, + FALSE_POSITIVE_RATE, + Duration::from_millis(0), // reset_cycle + )); } #[test] fn test_dedup_false_positive() { - let filter = Deduper::new(1_000_000, Duration::from_millis(0)); + let mut rng = rand::thread_rng(); + let filter = Deduper::<2, [u8]>::new(&mut rng, /*num_bits:*/ 63_999_979); let mut discard = 0; for i in 0..10 { let mut batches = to_packet_batches(&(0..1024).map(|_| test_tx()).collect::>(), 128); - discard += filter.dedup_packets_and_count_discards(&mut batches, |_, _, _| ()) as usize; + discard += + dedup_packets_and_count_discards(&filter, &mut batches, |_, _, _| ()) as usize; debug!("false positive rate: {}/{}", discard, i * 1024); } //allow for 1 false positive even if extremely unlikely assert!(discard < 2); } - #[test] - fn test_shrink_fuzz() { - for _ in 0..5 { - let mut batches = to_packet_batches( - &(0..PACKETS_PER_BATCH * 3) - .map(|_| test_tx()) - .collect::>(), - PACKETS_PER_BATCH, - ); - batches.iter_mut().for_each(|b| { - b.iter_mut() - .for_each(|p| p.meta.set_discard(thread_rng().gen())) - }); - //find all the non discarded packets - let mut start = vec![]; - batches.iter_mut().for_each(|b| { - b.iter_mut() - .filter(|p| !p.meta.discard()) - .for_each(|p| start.push(p.clone())) - }); - start.sort_by(|a, b| a.data(..).cmp(&b.data(..))); - - let packet_count = count_valid_packets(&batches, |_| ()); - shrink_batches(&mut batches); - - //make sure all the non discarded packets are the same - let mut end = vec![]; - batches.iter_mut().for_each(|b| { - b.iter_mut() - .filter(|p| !p.meta.discard()) - .for_each(|p| end.push(p.clone())) - }); - end.sort_by(|a, b| a.data(..).cmp(&b.data(..))); - let packet_count2 = count_valid_packets(&batches, |_| ()); - assert_eq!(packet_count, packet_count2); - assert_eq!(start, end); + #[test_case(63_999_979, 0.001, 2_023_857)] + #[test_case(622_401_961, 0.001, 19_682_078)] + #[test_case(622_401_979, 0.001, 19_682_078)] + #[test_case(629_145_593, 0.001, 19_895_330)] + #[test_case(632_455_543, 0.001, 20_000_000)] + #[test_case(637_534_199, 0.001, 20_160_601)] + #[test_case(622_401_961, 0.0001, 6_224_019)] + #[test_case(622_401_979, 0.0001, 6_224_019)] + #[test_case(629_145_593, 0.0001, 6_291_455)] + #[test_case(632_455_543, 0.0001, 6_324_555)] + #[test_case(637_534_199, 0.0001, 6_375_341)] + fn test_dedup_capacity(num_bits: u64, false_positive_rate: f64, capacity: u64) { + let mut rng = rand::thread_rng(); + assert_eq!(get_capacity::<2>(num_bits, false_positive_rate), capacity); + let mut deduper = Deduper::<2, [u8]>::new(&mut rng, num_bits); + assert_eq!(deduper.false_positive_rate(), 0.0); + deduper.popcount.store(capacity, Ordering::Relaxed); + assert!(deduper.false_positive_rate() < false_positive_rate); + deduper.popcount.store(capacity + 1, Ordering::Relaxed); + assert!(deduper.false_positive_rate() >= false_positive_rate); + assert!(deduper.maybe_reset( + &mut rng, + false_positive_rate, + Duration::from_millis(0), // reset_cycle + )); + } + + #[test_case([0xf9; 32], 3_199_997, 101_192, 51_414, 70, 101_125)] + #[test_case([0xdc; 32], 3_200_003, 101_192, 51_414, 71, 101_132)] + #[test_case([0xa5; 32], 6_399_971, 202_384, 102_828, 127, 202_157)] + #[test_case([0xdb; 32], 6_400_013, 202_386, 102_828, 145, 202_277)] + #[test_case([0xcd; 32], 12_799_987, 404_771, 205_655, 289, 404_434)] + #[test_case([0xc3; 32], 12_800_009, 404_771, 205_656, 309, 404_278)] + fn test_dedup_seeded( + seed: [u8; 32], + num_bits: u64, + capacity: u64, + num_packets: usize, + num_dups: usize, + popcount: u64, + ) { + const FALSE_POSITIVE_RATE: f64 = 0.001; + let mut rng = ChaChaRng::from_seed(seed); + let mut deduper = Deduper::<2, [u8]>::new(&mut rng, num_bits); + assert_eq!(get_capacity::<2>(num_bits, FALSE_POSITIVE_RATE), capacity); + let mut packet = Packet::new([0u8; PACKET_DATA_SIZE], Meta::default()); + let mut dup_count = 0usize; + for _ in 0..num_packets { + let size = rng.gen_range(0, PACKET_DATA_SIZE); + packet.meta.size = size; + rng.fill(&mut packet.buffer_mut()[0..size]); + if deduper.dedup(packet.data(..).unwrap()) { + dup_count += 1; + } + assert!(deduper.dedup(packet.data(..).unwrap())); } + assert_eq!(dup_count, num_dups); + assert_eq!(deduper.popcount.load(Ordering::Relaxed), popcount); + assert!(deduper.false_positive_rate() < FALSE_POSITIVE_RATE); + assert!(!deduper.maybe_reset( + &mut rng, + FALSE_POSITIVE_RATE, + Duration::from_millis(0), // reset_cycle + )); } #[test] diff --git a/poh-bench/Cargo.toml b/poh-bench/Cargo.toml index 2288f02794d56b..2cfd97308150b7 100644 --- a/poh-bench/Cargo.toml +++ b/poh-bench/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-poh-bench" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -14,12 +14,12 @@ clap = { version = "3.1.5", features = ["cargo"] } log = "0.4.17" rand = "0.7.0" rayon = "1.5.3" -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/poh/Cargo.toml b/poh/Cargo.toml index ea028d51a27812..52c025e827e98a 100644 --- a/poh/Cargo.toml +++ b/poh/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-poh" -version = "1.11.6" +version = "1.14.24" description = "Solana PoH" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -13,21 +13,21 @@ edition = "2021" core_affinity = "0.5.10" crossbeam-channel = "0.5" log = "0.4.17" -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-sys-tuner = { path = "../sys-tuner", version = "=1.11.6" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-sys-tuner = { path = "../sys-tuner", version = "=1.14.24" } thiserror = "1.0" [dev-dependencies] bincode = "1.3.3" matches = "0.1.9" rand = "0.7.0" -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/poh/src/poh_service.rs b/poh/src/poh_service.rs index 70f4d2f1ed47ac..2b71c6ab6140f4 100644 --- a/poh/src/poh_service.rs +++ b/poh/src/poh_service.rs @@ -106,7 +106,7 @@ impl PohService { let poh_exit_ = poh_exit.clone(); let poh_config = poh_config.clone(); let tick_producer = Builder::new() - .name("solana-poh-service-tick_producer".to_string()) + .name("solPohTickProd".to_string()) .spawn(move || { solana_sys_tuner::request_realtime_poh(); if poh_config.hashes_per_tick.is_none() { @@ -452,7 +452,7 @@ mod tests { let exit = exit.clone(); Builder::new() - .name("solana-poh-service-entry_producer".to_string()) + .name("solPohEntryProd".to_string()) .spawn(move || { let now = Instant::now(); let mut total_us = 0; diff --git a/program-runtime/Cargo.toml b/program-runtime/Cargo.toml index e822a2d2bce6d1..4f86ae748d6f7d 100644 --- a/program-runtime/Cargo.toml +++ b/program-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-program-runtime" -version = "1.11.6" +version = "1.14.24" description = "Solana program runtime" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -19,17 +19,18 @@ libloading = "0.7.0" log = "0.4.14" num-derive = { version = "0.3" } num-traits = { version = "0.2" } +rand = "0.7.0" serde = { version = "1.0.129", features = ["derive", "rc"] } -solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-frozen-abi = { path = "../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } thiserror = "1.0" enum-iterator = "0.8.1" [dev-dependencies] -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/program-runtime/src/compute_budget.rs b/program-runtime/src/compute_budget.rs index 31432f387f2fc5..0fc87b4b19487e 100644 --- a/program-runtime/src/compute_budget.rs +++ b/program-runtime/src/compute_budget.rs @@ -67,6 +67,12 @@ pub struct ComputeBudget { pub curve25519_edwards_subtract_cost: u64, /// Number of compute units consumed to multiply a curve25519 edwards point pub curve25519_edwards_multiply_cost: u64, + /// Number of compute units consumed for a multiscalar multiplication (msm) of edwards points. + /// The total cost is calculated as `msm_base_cost + (length - 1) * msm_incremental_cost`. + pub curve25519_edwards_msm_base_cost: u64, + /// Number of compute units consumed for a multiscalar multiplication (msm) of edwards points. + /// The total cost is calculated as `msm_base_cost + (length - 1) * msm_incremental_cost`. + pub curve25519_edwards_msm_incremental_cost: u64, /// Number of compute units consumed to validate a curve25519 ristretto point pub curve25519_ristretto_validate_point_cost: u64, /// Number of compute units consumed to add two curve25519 ristretto points @@ -75,6 +81,12 @@ pub struct ComputeBudget { pub curve25519_ristretto_subtract_cost: u64, /// Number of compute units consumed to multiply a curve25519 ristretto point pub curve25519_ristretto_multiply_cost: u64, + /// Number of compute units consumed for a multiscalar multiplication (msm) of ristretto points. + /// The total cost is calculated as `msm_base_cost + (length - 1) * msm_incremental_cost`. + pub curve25519_ristretto_msm_base_cost: u64, + /// Number of compute units consumed for a multiscalar multiplication (msm) of ristretto points. + /// The total cost is calculated as `msm_base_cost + (length - 1) * msm_incremental_cost`. + pub curve25519_ristretto_msm_incremental_cost: u64, /// Optional program heap region size, if `None` then loader default pub heap_size: Option, /// Number of compute units per additional 32k heap above the default (~.5 @@ -109,14 +121,18 @@ impl ComputeBudget { sysvar_base_cost: 100, secp256k1_recover_cost: 25_000, syscall_base_cost: 100, - curve25519_edwards_validate_point_cost: 5_000, // TODO: precisely determine curve25519 costs - curve25519_edwards_add_cost: 5_000, - curve25519_edwards_subtract_cost: 5_000, - curve25519_edwards_multiply_cost: 10_000, - curve25519_ristretto_validate_point_cost: 5_000, - curve25519_ristretto_add_cost: 5_000, - curve25519_ristretto_subtract_cost: 5_000, - curve25519_ristretto_multiply_cost: 10_000, + curve25519_edwards_validate_point_cost: 159, + curve25519_edwards_add_cost: 473, + curve25519_edwards_subtract_cost: 475, + curve25519_edwards_multiply_cost: 2_177, + curve25519_edwards_msm_base_cost: 2_273, + curve25519_edwards_msm_incremental_cost: 758, + curve25519_ristretto_validate_point_cost: 169, + curve25519_ristretto_add_cost: 521, + curve25519_ristretto_subtract_cost: 519, + curve25519_ristretto_multiply_cost: 2_208, + curve25519_ristretto_msm_base_cost: 2303, + curve25519_ristretto_msm_incremental_cost: 788, heap_size: None, heap_cost: 8, mem_op_base_cost: 10, @@ -128,6 +144,7 @@ impl ComputeBudget { instructions: impl Iterator, default_units_per_instruction: bool, support_set_compute_unit_price_ix: bool, + enable_request_heap_frame_ix: bool, ) -> Result { let mut num_non_compute_budget_instructions: usize = 0; let mut updated_compute_unit_limit = None; @@ -209,7 +226,8 @@ impl ComputeBudget { } if let Some((bytes, i)) = requested_heap_size { - if bytes > MAX_HEAP_FRAME_BYTES + if !enable_request_heap_frame_ix + || bytes > MAX_HEAP_FRAME_BYTES || bytes < MIN_HEAP_FRAME_BYTES as u32 || bytes % 1024 != 0 { @@ -267,7 +285,7 @@ mod tests { } macro_rules! test { - ( $instructions: expr, $expected_result: expr, $expected_budget: expr, $type_change: expr ) => { + ( $instructions: expr, $expected_result: expr, $expected_budget: expr, $type_change: expr, $enable_request_heap_frame_ix: expr) => { let payer_keypair = Keypair::new(); let tx = SanitizedTransaction::from_transaction_for_tests(Transaction::new( &[&payer_keypair], @@ -279,12 +297,19 @@ mod tests { tx.message().program_instructions_iter(), true, $type_change, + $enable_request_heap_frame_ix, ); assert_eq!($expected_result, result); assert_eq!(compute_budget, $expected_budget); }; ( $instructions: expr, $expected_result: expr, $expected_budget: expr) => { - test!($instructions, $expected_result, $expected_budget, true); + test!( + $instructions, + $expected_result, + $expected_budget, + true, + true + ); }; } @@ -358,7 +383,8 @@ mod tests { compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64 * 3, ..ComputeBudget::default() }, - false + false, + true ); // Prioritization fee @@ -372,7 +398,8 @@ mod tests { compute_unit_limit: 1, ..ComputeBudget::default() }, - false + false, + true ); test!( @@ -400,7 +427,8 @@ mod tests { compute_unit_limit: 1, ..ComputeBudget::default() }, - false + false, + true ); // HeapFrame @@ -532,7 +560,8 @@ mod tests { InstructionError::InvalidInstructionData, )), ComputeBudget::default(), - false + false, + true ); test!( @@ -568,7 +597,8 @@ mod tests { heap_size: Some(MIN_HEAP_FRAME_BYTES as usize), ..ComputeBudget::default() }, - false + false, + true ); // Duplicates @@ -602,4 +632,98 @@ mod tests { ComputeBudget::default() ); } + + #[test] + fn test_process_instructions_disable_request_heap_frame() { + // assert empty message results default compute budget and fee + test!( + &[], + Ok(PrioritizationFeeDetails::default()), + ComputeBudget { + compute_unit_limit: 0, + ..ComputeBudget::default() + }, + true, + false + ); + + // assert requesting heap frame when feature is disable will result instruction error + test!( + &[ + ComputeBudgetInstruction::request_heap_frame(40 * 1024), + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + ], + Err(TransactionError::InstructionError( + 0, + InstructionError::InvalidInstructionData + )), + ComputeBudget::default(), + true, + false + ); + test!( + &[ + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES), + ], + Err(TransactionError::InstructionError( + 1, + InstructionError::InvalidInstructionData, + )), + ComputeBudget::default(), + true, + false + ); + test!( + &[ + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES), + ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT), + ComputeBudgetInstruction::set_compute_unit_price(u64::MAX), + ], + Err(TransactionError::InstructionError( + 1, + InstructionError::InvalidInstructionData, + )), + ComputeBudget::default(), + true, + false + ); + test!( + &[ + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + ComputeBudgetInstruction::set_compute_unit_limit(1), + ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES), + ComputeBudgetInstruction::set_compute_unit_price(u64::MAX), + ], + Err(TransactionError::InstructionError( + 2, + InstructionError::InvalidInstructionData, + )), + ComputeBudget::default(), + true, + false + ); + + // assert normal results when not requesting heap frame when the feature is disabled + test!( + &[ + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + Instruction::new_with_bincode(Pubkey::new_unique(), &0_u8, vec![]), + ], + Ok(PrioritizationFeeDetails::default()), + ComputeBudget { + compute_unit_limit: DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64 * 7, + ..ComputeBudget::default() + }, + true, + false + ); + } } diff --git a/program-runtime/src/executor_cache.rs b/program-runtime/src/executor_cache.rs new file mode 100644 index 00000000000000..fd459cb55c40bd --- /dev/null +++ b/program-runtime/src/executor_cache.rs @@ -0,0 +1,637 @@ +use { + crate::invoke_context::InvokeContext, + log::*, + rand::Rng, + solana_sdk::{ + instruction::InstructionError, pubkey::Pubkey, saturating_add_assign, slot_history::Slot, + stake_history::Epoch, + }, + std::{ + collections::HashMap, + fmt::Debug, + ops::Div, + sync::{ + atomic::{AtomicU64, Ordering::Relaxed}, + Arc, + }, + }, +}; + +/// Program executor +pub trait Executor: Debug + Send + Sync { + /// Execute the program + fn execute( + &self, + first_instruction_account: usize, + invoke_context: &mut InvokeContext, + ) -> Result<(), InstructionError>; +} + +pub type Executors = HashMap; + +#[repr(u8)] +#[derive(PartialEq, Debug)] +enum TransactionExecutorStatus { + /// Executor was already in the cache, no update needed + Cached, + /// Executor was missing from the cache, but not updated + Missing, + /// Executor is for an updated program + Updated, +} + +/// Tracks whether a given executor is "dirty" and needs to updated in the +/// executors cache +#[derive(Debug)] +pub struct TransactionExecutor { + pub(crate) executor: Arc, + status: TransactionExecutorStatus, +} + +impl TransactionExecutor { + /// Wraps an executor and tracks that it doesn't need to be updated in the + /// executors cache. + pub fn new_cached(executor: Arc) -> Self { + Self { + executor, + status: TransactionExecutorStatus::Cached, + } + } + + /// Wraps an executor and tracks that it needs to be updated in the + /// executors cache. + pub fn new_miss(executor: Arc) -> Self { + Self { + executor, + status: TransactionExecutorStatus::Missing, + } + } + + /// Wraps an executor and tracks that it needs to be updated in the + /// executors cache only if the transaction succeeded. + pub fn new_updated(executor: Arc) -> Self { + Self { + executor, + status: TransactionExecutorStatus::Updated, + } + } + + pub fn is_missing(&self) -> bool { + self.status == TransactionExecutorStatus::Missing + } + + pub fn is_updated(&self) -> bool { + self.status == TransactionExecutorStatus::Updated + } + + pub fn get(&self) -> Arc { + self.executor.clone() + } +} + +/// Capacity of `CachedExecutors` +pub const MAX_CACHED_EXECUTORS: usize = 256; + +/// An `Executor` and its statistics tracked in `CachedExecutors` +#[derive(Debug)] +pub struct CachedExecutorsEntry { + prev_epoch_count: u64, + epoch_count: AtomicU64, + executor: Arc, + pub hit_count: AtomicU64, +} + +impl Clone for CachedExecutorsEntry { + fn clone(&self) -> Self { + Self { + prev_epoch_count: self.prev_epoch_count, + epoch_count: AtomicU64::new(self.epoch_count.load(Relaxed)), + executor: self.executor.clone(), + hit_count: AtomicU64::new(self.hit_count.load(Relaxed)), + } + } +} + +/// LFU Cache of executors with single-epoch memory of usage counts +#[derive(Debug)] +pub struct CachedExecutors { + capacity: usize, + current_epoch: Epoch, + pub executors: HashMap, + pub stats: Stats, +} + +impl Default for CachedExecutors { + fn default() -> Self { + Self { + capacity: MAX_CACHED_EXECUTORS, + current_epoch: Epoch::default(), + executors: HashMap::default(), + stats: Stats::default(), + } + } +} + +#[cfg(RUSTC_WITH_SPECIALIZATION)] +impl solana_frozen_abi::abi_example::AbiExample for CachedExecutors { + fn example() -> Self { + // Delegate AbiExample impl to Default before going deep and stuck with + // not easily impl-able Arc due to rust's coherence issue + // This is safe because CachedExecutors isn't serializable by definition. + Self::default() + } +} + +impl CachedExecutors { + pub fn new(max_capacity: usize, current_epoch: Epoch) -> Self { + Self { + capacity: max_capacity, + current_epoch, + executors: HashMap::new(), + stats: Stats::default(), + } + } + + pub fn new_from_parent_bank_executors( + parent_bank_executors: &CachedExecutors, + current_epoch: Epoch, + ) -> Self { + let executors = if parent_bank_executors.current_epoch == current_epoch { + parent_bank_executors.executors.clone() + } else { + parent_bank_executors + .executors + .iter() + .map(|(&key, entry)| { + let entry = CachedExecutorsEntry { + prev_epoch_count: entry.epoch_count.load(Relaxed), + epoch_count: AtomicU64::default(), + executor: entry.executor.clone(), + hit_count: AtomicU64::new(entry.hit_count.load(Relaxed)), + }; + (key, entry) + }) + .collect() + }; + + Self { + capacity: parent_bank_executors.capacity, + current_epoch, + executors, + stats: Stats::default(), + } + } + + pub fn get(&self, pubkey: &Pubkey) -> Option> { + if let Some(entry) = self.executors.get(pubkey) { + self.stats.hits.fetch_add(1, Relaxed); + entry.epoch_count.fetch_add(1, Relaxed); + entry.hit_count.fetch_add(1, Relaxed); + Some(entry.executor.clone()) + } else { + self.stats.misses.fetch_add(1, Relaxed); + None + } + } + + pub fn put(&mut self, executors: &[(&Pubkey, Arc)]) { + let mut new_executors: Vec<_> = executors + .iter() + .filter_map(|(key, executor)| { + if let Some(mut entry) = self.remove(key) { + self.stats.replacements.fetch_add(1, Relaxed); + entry.executor = executor.clone(); + let _ = self.executors.insert(**key, entry); + None + } else { + self.stats.insertions.fetch_add(1, Relaxed); + Some((*key, executor)) + } + }) + .collect(); + + if !new_executors.is_empty() { + let mut counts = self + .executors + .iter() + .map(|(key, entry)| { + let count = entry + .prev_epoch_count + .saturating_add(entry.epoch_count.load(Relaxed)); + (key, count) + }) + .collect::>(); + counts.sort_unstable_by_key(|(_, count)| *count); + + let primer_counts = Self::get_primer_counts(counts.as_slice(), new_executors.len()); + + if self.executors.len() >= self.capacity { + let mut least_keys = counts + .iter() + .take(new_executors.len()) + .map(|least| *least.0) + .collect::>(); + for least_key in least_keys.drain(..) { + let _ = self.remove(&least_key); + self.stats + .evictions + .entry(least_key) + .and_modify(|c| saturating_add_assign!(*c, 1)) + .or_insert(1); + } + } + + for ((key, executor), primer_count) in new_executors.drain(..).zip(primer_counts) { + let entry = CachedExecutorsEntry { + prev_epoch_count: 0, + epoch_count: AtomicU64::new(primer_count), + executor: executor.clone(), + hit_count: AtomicU64::new(1), + }; + let _ = self.executors.insert(*key, entry); + } + } + } + + pub fn remove(&mut self, pubkey: &Pubkey) -> Option { + let maybe_entry = self.executors.remove(pubkey); + if let Some(entry) = maybe_entry.as_ref() { + if entry.hit_count.load(Relaxed) == 1 { + self.stats.one_hit_wonders.fetch_add(1, Relaxed); + } + } + maybe_entry + } + + pub fn clear(&mut self) { + *self = CachedExecutors::default(); + } + + pub fn get_primer_count_upper_bound_inclusive(counts: &[(&Pubkey, u64)]) -> u64 { + const PRIMER_COUNT_TARGET_PERCENTILE: u64 = 85; + #[allow(clippy::assertions_on_constants)] + { + assert!(PRIMER_COUNT_TARGET_PERCENTILE <= 100); + } + // Executor use-frequencies are assumed to fit a Pareto distribution. Choose an + // upper-bound for our primer count as the actual count at the target rank to avoid + // an upward bias + + let target_index = u64::try_from(counts.len().saturating_sub(1)) + .ok() + .and_then(|counts| { + let index = counts + .saturating_mul(PRIMER_COUNT_TARGET_PERCENTILE) + .div(100); // switch to u64::saturating_div once stable + usize::try_from(index).ok() + }) + .unwrap_or(0); + + counts + .get(target_index) + .map(|(_, count)| *count) + .unwrap_or(0) + } + + pub fn get_primer_counts(counts: &[(&Pubkey, u64)], num_counts: usize) -> Vec { + let max_primer_count = Self::get_primer_count_upper_bound_inclusive(counts); + let mut rng = rand::thread_rng(); + + (0..num_counts) + .map(|_| rng.gen_range(0, max_primer_count.saturating_add(1))) + .collect::>() + } +} + +/// Statistics of the entrie `CachedExecutors` +#[derive(Debug, Default)] +pub struct Stats { + pub hits: AtomicU64, + pub misses: AtomicU64, + pub evictions: HashMap, + pub insertions: AtomicU64, + pub replacements: AtomicU64, + pub one_hit_wonders: AtomicU64, +} + +impl Stats { + /// Logs the measurement values + pub fn submit(&self, slot: Slot) { + let hits = self.hits.load(Relaxed); + let misses = self.misses.load(Relaxed); + let insertions = self.insertions.load(Relaxed); + let replacements = self.replacements.load(Relaxed); + let one_hit_wonders = self.one_hit_wonders.load(Relaxed); + let evictions: u64 = self.evictions.values().sum(); + datapoint_info!( + "bank-executor-cache-stats", + ("slot", slot, i64), + ("hits", hits, i64), + ("misses", misses, i64), + ("evictions", evictions, i64), + ("insertions", insertions, i64), + ("replacements", replacements, i64), + ("one_hit_wonders", one_hit_wonders, i64), + ); + debug!( + "Executor Cache Stats -- Hits: {}, Misses: {}, Evictions: {}, Insertions: {}, Replacements: {}, One-Hit-Wonders: {}", + hits, misses, evictions, insertions, replacements, one_hit_wonders, + ); + if log_enabled!(log::Level::Trace) && !self.evictions.is_empty() { + let mut evictions = self.evictions.iter().collect::>(); + evictions.sort_by_key(|e| e.1); + let evictions = evictions + .into_iter() + .rev() + .map(|(program_id, evictions)| { + format!(" {:<44} {}", program_id.to_string(), evictions) + }) + .collect::>(); + let evictions = evictions.join("\n"); + trace!( + "Eviction Details:\n {:<44} {}\n{}", + "Program", + "Count", + evictions + ); + } + } +} + +#[allow(clippy::indexing_slicing)] +#[cfg(test)] +mod tests { + use { + super::*, crate::invoke_context::InvokeContext, solana_sdk::instruction::InstructionError, + }; + + #[derive(Debug)] + struct TestExecutor {} + impl Executor for TestExecutor { + fn execute( + &self, + _first_instruction_account: usize, + _invoke_context: &mut InvokeContext, + ) -> std::result::Result<(), InstructionError> { + Ok(()) + } + } + + #[test] + fn test_cached_executors() { + let key1 = solana_sdk::pubkey::new_rand(); + let key2 = solana_sdk::pubkey::new_rand(); + let key3 = solana_sdk::pubkey::new_rand(); + let key4 = solana_sdk::pubkey::new_rand(); + let executor: Arc = Arc::new(TestExecutor {}); + let mut cache = CachedExecutors::new(3, 0); + + cache.put(&[(&key1, executor.clone())]); + cache.put(&[(&key2, executor.clone())]); + cache.put(&[(&key3, executor.clone())]); + assert!(cache.get(&key1).is_some()); + assert!(cache.get(&key2).is_some()); + assert!(cache.get(&key3).is_some()); + + assert!(cache.get(&key1).is_some()); + assert!(cache.get(&key1).is_some()); + assert!(cache.get(&key2).is_some()); + cache.put(&[(&key4, executor.clone())]); + assert!(cache.get(&key4).is_some()); + let num_retained = [&key1, &key2, &key3] + .iter() + .filter_map(|key| cache.get(key)) + .count(); + assert_eq!(num_retained, 2); + + assert!(cache.get(&key4).is_some()); + assert!(cache.get(&key4).is_some()); + assert!(cache.get(&key4).is_some()); + cache.put(&[(&key3, executor.clone())]); + assert!(cache.get(&key3).is_some()); + let num_retained = [&key1, &key2, &key4] + .iter() + .filter_map(|key| cache.get(key)) + .count(); + assert_eq!(num_retained, 2); + } + + #[test] + fn test_cached_executor_eviction() { + let key1 = solana_sdk::pubkey::new_rand(); + let key2 = solana_sdk::pubkey::new_rand(); + let key3 = solana_sdk::pubkey::new_rand(); + let key4 = solana_sdk::pubkey::new_rand(); + let executor: Arc = Arc::new(TestExecutor {}); + let mut cache = CachedExecutors::new(3, 0); + assert!(cache.current_epoch == 0); + + cache.put(&[(&key1, executor.clone())]); + cache.put(&[(&key2, executor.clone())]); + cache.put(&[(&key3, executor.clone())]); + assert!(cache.get(&key1).is_some()); + assert!(cache.get(&key1).is_some()); + assert!(cache.get(&key1).is_some()); + + let mut cache = CachedExecutors::new_from_parent_bank_executors(&cache, 1); + assert!(cache.current_epoch == 1); + + assert!(cache.get(&key2).is_some()); + assert!(cache.get(&key2).is_some()); + assert!(cache.get(&key3).is_some()); + cache.put(&[(&key4, executor.clone())]); + + assert!(cache.get(&key4).is_some()); + let num_retained = [&key1, &key2, &key3] + .iter() + .filter_map(|key| cache.get(key)) + .count(); + assert_eq!(num_retained, 2); + + cache.put(&[(&key1, executor.clone())]); + cache.put(&[(&key3, executor.clone())]); + assert!(cache.get(&key1).is_some()); + assert!(cache.get(&key3).is_some()); + let num_retained = [&key2, &key4] + .iter() + .filter_map(|key| cache.get(key)) + .count(); + assert_eq!(num_retained, 1); + + cache = CachedExecutors::new_from_parent_bank_executors(&cache, 2); + assert!(cache.current_epoch == 2); + + cache.put(&[(&key3, executor.clone())]); + assert!(cache.get(&key3).is_some()); + } + + #[test] + fn test_cached_executors_evicts_smallest() { + let key1 = solana_sdk::pubkey::new_rand(); + let key2 = solana_sdk::pubkey::new_rand(); + let key3 = solana_sdk::pubkey::new_rand(); + let executor: Arc = Arc::new(TestExecutor {}); + let mut cache = CachedExecutors::new(2, 0); + + cache.put(&[(&key1, executor.clone())]); + for _ in 0..5 { + let _ = cache.get(&key1); + } + cache.put(&[(&key2, executor.clone())]); + // make key1's use-count for sure greater than key2's + let _ = cache.get(&key1); + + let mut entries = cache + .executors + .iter() + .map(|(k, v)| (*k, v.epoch_count.load(Relaxed))) + .collect::>(); + entries.sort_by_key(|(_, v)| *v); + assert!(entries[0].1 < entries[1].1); + + cache.put(&[(&key3, executor.clone())]); + assert!(cache.get(&entries[0].0).is_none()); + assert!(cache.get(&entries[1].0).is_some()); + } + + #[test] + fn test_cached_executors_one_hit_wonder_counter() { + let mut cache = CachedExecutors::new(1, 0); + + let one_hit_wonder = Pubkey::new_unique(); + let popular = Pubkey::new_unique(); + let executor: Arc = Arc::new(TestExecutor {}); + + // make sure we're starting from where we think we are + assert_eq!(cache.stats.one_hit_wonders.load(Relaxed), 0); + + // add our one-hit-wonder + cache.put(&[(&one_hit_wonder, executor.clone())]); + assert_eq!(cache.executors[&one_hit_wonder].hit_count.load(Relaxed), 1); + // displace the one-hit-wonder with "popular program" + cache.put(&[(&popular, executor.clone())]); + assert_eq!(cache.executors[&popular].hit_count.load(Relaxed), 1); + + // one-hit-wonder counter incremented + assert_eq!(cache.stats.one_hit_wonders.load(Relaxed), 1); + + // make "popular program" popular + cache.get(&popular).unwrap(); + assert_eq!(cache.executors[&popular].hit_count.load(Relaxed), 2); + + // evict "popular program" + cache.put(&[(&one_hit_wonder, executor.clone())]); + assert_eq!(cache.executors[&one_hit_wonder].hit_count.load(Relaxed), 1); + + // one-hit-wonder counter not incremented + assert_eq!(cache.stats.one_hit_wonders.load(Relaxed), 1); + } + + #[test] + fn test_executor_cache_get_primer_count_upper_bound_inclusive() { + let pubkey = Pubkey::default(); + let v = []; + assert_eq!( + CachedExecutors::get_primer_count_upper_bound_inclusive(&v), + 0 + ); + let v = [(&pubkey, 1)]; + assert_eq!( + CachedExecutors::get_primer_count_upper_bound_inclusive(&v), + 1 + ); + let v = (0u64..10).map(|i| (&pubkey, i)).collect::>(); + assert_eq!( + CachedExecutors::get_primer_count_upper_bound_inclusive(v.as_slice()), + 7 + ); + } + + #[test] + fn test_cached_executors_stats() { + #[derive(Debug, Default, PartialEq)] + struct ComparableStats { + hits: u64, + misses: u64, + evictions: HashMap, + insertions: u64, + replacements: u64, + one_hit_wonders: u64, + } + impl From<&Stats> for ComparableStats { + fn from(stats: &Stats) -> Self { + let Stats { + hits, + misses, + evictions, + insertions, + replacements, + one_hit_wonders, + } = stats; + ComparableStats { + hits: hits.load(Relaxed), + misses: misses.load(Relaxed), + evictions: evictions.clone(), + insertions: insertions.load(Relaxed), + replacements: replacements.load(Relaxed), + one_hit_wonders: one_hit_wonders.load(Relaxed), + } + } + } + + const CURRENT_EPOCH: Epoch = 0; + let mut cache = CachedExecutors::new(2, CURRENT_EPOCH); + let mut expected_stats = ComparableStats::default(); + + let program_id1 = Pubkey::new_unique(); + let program_id2 = Pubkey::new_unique(); + let executor: Arc = Arc::new(TestExecutor {}); + + // make sure we're starting from where we think we are + assert_eq!(ComparableStats::from(&cache.stats), expected_stats,); + + // insert some executors + cache.put(&[(&program_id1, executor.clone())]); + cache.put(&[(&program_id2, executor.clone())]); + expected_stats.insertions += 2; + assert_eq!(ComparableStats::from(&cache.stats), expected_stats); + + // replace a one-hit-wonder executor + cache.put(&[(&program_id1, executor.clone())]); + expected_stats.replacements += 1; + expected_stats.one_hit_wonders += 1; + assert_eq!(ComparableStats::from(&cache.stats), expected_stats); + + // hit some executors + cache.get(&program_id1); + cache.get(&program_id1); + cache.get(&program_id2); + expected_stats.hits += 3; + assert_eq!(ComparableStats::from(&cache.stats), expected_stats); + + // miss an executor + cache.get(&Pubkey::new_unique()); + expected_stats.misses += 1; + assert_eq!(ComparableStats::from(&cache.stats), expected_stats); + + // evict an executor + cache.put(&[(&Pubkey::new_unique(), executor.clone())]); + expected_stats.insertions += 1; + expected_stats.evictions.insert(program_id2, 1); + assert_eq!(ComparableStats::from(&cache.stats), expected_stats); + + // make sure stats are cleared in new_from_parent + assert_eq!( + ComparableStats::from( + &CachedExecutors::new_from_parent_bank_executors(&cache, CURRENT_EPOCH).stats + ), + ComparableStats::default() + ); + assert_eq!( + ComparableStats::from( + &CachedExecutors::new_from_parent_bank_executors(&cache, CURRENT_EPOCH + 1).stats + ), + ComparableStats::default() + ); + } +} diff --git a/program-runtime/src/invoke_context.rs b/program-runtime/src/invoke_context.rs index 52c860bb63dcc7..86c740585ba22a 100644 --- a/program-runtime/src/invoke_context.rs +++ b/program-runtime/src/invoke_context.rs @@ -4,6 +4,7 @@ use { crate::{ accounts_data_meter::AccountsDataMeter, compute_budget::ComputeBudget, + executor_cache::{Executor, Executors, TransactionExecutor}, ic_logger_msg, ic_msg, log_collector::LogCollector, pre_account::PreAccount, @@ -30,7 +31,6 @@ use { alloc::Layout, borrow::Cow, cell::RefCell, - collections::HashMap, fmt::{self, Debug}, rc::Rc, sync::Arc, @@ -60,78 +60,6 @@ impl std::fmt::Debug for BuiltinProgram { } } -/// Program executor -pub trait Executor: Debug + Send + Sync { - /// Execute the program - fn execute( - &self, - first_instruction_account: usize, - invoke_context: &mut InvokeContext, - ) -> Result<(), InstructionError>; -} - -pub type Executors = HashMap; - -#[repr(u8)] -#[derive(PartialEq, Debug)] -enum TransactionExecutorStatus { - /// Executor was already in the cache, no update needed - Cached, - /// Executor was missing from the cache, but not updated - Missing, - /// Executor is for an updated program - Updated, -} - -/// Tracks whether a given executor is "dirty" and needs to updated in the -/// executors cache -#[derive(Debug)] -pub struct TransactionExecutor { - executor: Arc, - status: TransactionExecutorStatus, -} - -impl TransactionExecutor { - /// Wraps an executor and tracks that it doesn't need to be updated in the - /// executors cache. - pub fn new_cached(executor: Arc) -> Self { - Self { - executor, - status: TransactionExecutorStatus::Cached, - } - } - - /// Wraps an executor and tracks that it needs to be updated in the - /// executors cache. - pub fn new_miss(executor: Arc) -> Self { - Self { - executor, - status: TransactionExecutorStatus::Missing, - } - } - - /// Wraps an executor and tracks that it needs to be updated in the - /// executors cache only if the transaction succeeded. - pub fn new_updated(executor: Arc) -> Self { - Self { - executor, - status: TransactionExecutorStatus::Updated, - } - } - - pub fn is_missing(&self) -> bool { - self.status == TransactionExecutorStatus::Missing - } - - pub fn is_updated(&self) -> bool { - self.status == TransactionExecutorStatus::Updated - } - - pub fn get(&self) -> Arc { - self.executor.clone() - } -} - /// Compute meter pub struct ComputeMeter { remaining: u64, diff --git a/program-runtime/src/lib.rs b/program-runtime/src/lib.rs index 2a9be8d8c4e3bd..88bfe95834a30e 100644 --- a/program-runtime/src/lib.rs +++ b/program-runtime/src/lib.rs @@ -11,6 +11,7 @@ extern crate solana_metrics; pub mod accounts_data_meter; pub mod compute_budget; +pub mod executor_cache; pub mod invoke_context; pub mod log_collector; pub mod pre_account; diff --git a/program-test/Cargo.toml b/program-test/Cargo.toml index 1d84c0d5aa9218..19a0b75fe0f8b6 100644 --- a/program-test/Cargo.toml +++ b/program-test/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" license = "Apache-2.0" name = "solana-program-test" repository = "https://github.com/solana-labs/solana" -version = "1.11.6" +version = "1.14.24" [dependencies] assert_matches = "1.5.0" @@ -15,13 +15,16 @@ bincode = "1.3.3" chrono-humanize = "0.2.1" log = "0.4.17" serde = "1.0.138" -solana-banks-client = { path = "../banks-client", version = "=1.11.6" } -solana-banks-server = { path = "../banks-server", version = "=1.11.6" } -solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-program-runtime = { path = "../program-runtime", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } +solana-banks-client = { path = "../banks-client", version = "=1.14.24" } +solana-banks-server = { path = "../banks-server", version = "=1.14.24" } +solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-program-runtime = { path = "../program-runtime", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } thiserror = "1.0" -tokio = { version = "~1.14.1", features = ["full"] } +tokio = { version = "1", features = ["full"] } + +[dev-dependencies] +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index d7c3e260f8d2d6..fe9eba15a84df5 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -405,7 +405,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { pub fn find_file(filename: &str) -> Option { for dir in default_shared_object_dirs() { - let candidate = dir.join(&filename); + let candidate = dir.join(filename); if candidate.exists() { return Some(candidate); } @@ -606,7 +606,7 @@ impl ProgramTest { }) .ok() .flatten() - .unwrap_or_else(|| "".to_string()) + .unwrap_or_default() ); this.add_account( diff --git a/program-test/src/programs.rs b/program-test/src/programs.rs index f2cfd0e05f07af..2224252da77d19 100644 --- a/program-test/src/programs.rs +++ b/program-test/src/programs.rs @@ -1,5 +1,6 @@ use solana_sdk::{ account::{Account, AccountSharedData}, + bpf_loader_upgradeable::UpgradeableLoaderState, pubkey::Pubkey, rent::Rent, }; @@ -7,6 +8,9 @@ use solana_sdk::{ mod spl_token { solana_sdk::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); } +mod spl_token_2022 { + solana_sdk::declare_id!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"); +} mod spl_memo_1_0 { solana_sdk::declare_id!("Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"); } @@ -17,36 +21,76 @@ mod spl_associated_token_account { solana_sdk::declare_id!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); } -static SPL_PROGRAMS: &[(Pubkey, &[u8])] = &[ - (spl_token::ID, include_bytes!("programs/spl_token-3.3.0.so")), +static SPL_PROGRAMS: &[(Pubkey, Pubkey, &[u8])] = &[ + ( + spl_token::ID, + solana_sdk::bpf_loader::ID, + include_bytes!("programs/spl_token-3.5.0.so"), + ), + ( + spl_token_2022::ID, + solana_sdk::bpf_loader_upgradeable::ID, + include_bytes!("programs/spl_token_2022-0.6.0.so"), + ), ( spl_memo_1_0::ID, + solana_sdk::bpf_loader::ID, include_bytes!("programs/spl_memo-1.0.0.so"), ), ( spl_memo_3_0::ID, + solana_sdk::bpf_loader::ID, include_bytes!("programs/spl_memo-3.0.0.so"), ), ( spl_associated_token_account::ID, - include_bytes!("programs/spl_associated_token_account-1.0.5.so"), + solana_sdk::bpf_loader::ID, + include_bytes!("programs/spl_associated_token_account-1.1.1.so"), ), ]; pub fn spl_programs(rent: &Rent) -> Vec<(Pubkey, AccountSharedData)> { SPL_PROGRAMS .iter() - .map(|(program_id, elf)| { - ( + .flat_map(|(program_id, loader_id, elf)| { + let mut accounts = vec![]; + let data = if *loader_id == solana_sdk::bpf_loader_upgradeable::ID { + let (programdata_address, _) = + Pubkey::find_program_address(&[program_id.as_ref()], loader_id); + let mut program_data = bincode::serialize(&UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::default()), + }) + .unwrap(); + program_data.extend_from_slice(elf); + accounts.push(( + programdata_address, + AccountSharedData::from(Account { + lamports: rent.minimum_balance(program_data.len()).max(1), + data: program_data, + owner: *loader_id, + executable: false, + rent_epoch: 0, + }), + )); + bincode::serialize(&UpgradeableLoaderState::Program { + programdata_address, + }) + .unwrap() + } else { + elf.to_vec() + }; + accounts.push(( *program_id, AccountSharedData::from(Account { - lamports: rent.minimum_balance(elf.len()).min(1), - data: elf.to_vec(), - owner: solana_sdk::bpf_loader::id(), + lamports: rent.minimum_balance(data.len()).max(1), + data, + owner: *loader_id, executable: true, rent_epoch: 0, }), - ) + )); + accounts.into_iter() }) .collect() } diff --git a/program-test/src/programs/spl_associated_token_account-1.0.5.so b/program-test/src/programs/spl_associated_token_account-1.0.5.so deleted file mode 100755 index 7b360e4cadc854..00000000000000 Binary files a/program-test/src/programs/spl_associated_token_account-1.0.5.so and /dev/null differ diff --git a/program-test/src/programs/spl_associated_token_account-1.1.1.so b/program-test/src/programs/spl_associated_token_account-1.1.1.so new file mode 100644 index 00000000000000..63c3958b2eb60b Binary files /dev/null and b/program-test/src/programs/spl_associated_token_account-1.1.1.so differ diff --git a/program-test/src/programs/spl_token-3.3.0.so b/program-test/src/programs/spl_token-3.3.0.so deleted file mode 100644 index 84098a6a6ac901..00000000000000 Binary files a/program-test/src/programs/spl_token-3.3.0.so and /dev/null differ diff --git a/program-test/src/programs/spl_token-3.5.0.so b/program-test/src/programs/spl_token-3.5.0.so new file mode 100644 index 00000000000000..60cbbeaf6b5542 Binary files /dev/null and b/program-test/src/programs/spl_token-3.5.0.so differ diff --git a/program-test/src/programs/spl_token_2022-0.6.0.so b/program-test/src/programs/spl_token_2022-0.6.0.so new file mode 100644 index 00000000000000..0638fee19500c9 Binary files /dev/null and b/program-test/src/programs/spl_token_2022-0.6.0.so differ diff --git a/program-test/tests/spl.rs b/program-test/tests/spl.rs new file mode 100644 index 00000000000000..a97cdc51bd2656 --- /dev/null +++ b/program-test/tests/spl.rs @@ -0,0 +1,65 @@ +use { + solana_program_test::{programs::spl_programs, ProgramTest}, + solana_sdk::{ + bpf_loader, bpf_loader_upgradeable, + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + signature::Signer, + signer::keypair::Keypair, + system_instruction, + sysvar::rent, + transaction::Transaction, + }, +}; + +#[tokio::test] +async fn programs_present() { + let (mut banks_client, _, _) = ProgramTest::default().start().await; + let rent = banks_client.get_rent().await.unwrap(); + let token_2022_id = Pubkey::try_from("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap(); + let (token_2022_programdata_id, _) = + Pubkey::find_program_address(&[token_2022_id.as_ref()], &bpf_loader_upgradeable::id()); + + for (program_id, _) in spl_programs(&rent) { + let program_account = banks_client.get_account(program_id).await.unwrap().unwrap(); + if program_id == token_2022_id || program_id == token_2022_programdata_id { + assert_eq!(program_account.owner, bpf_loader_upgradeable::id()); + } else { + assert_eq!(program_account.owner, bpf_loader::id()); + } + } +} + +#[tokio::test] +async fn token_2022() { + let (mut banks_client, payer, recent_blockhash) = ProgramTest::default().start().await; + + let token_2022_id = Pubkey::try_from("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap(); + let mint = Keypair::new(); + let rent = banks_client.get_rent().await.unwrap(); + let space = 82; + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &payer.pubkey(), + &mint.pubkey(), + rent.minimum_balance(space), + space as u64, + &token_2022_id, + ), + Instruction::new_with_bytes( + token_2022_id, + &[0; 35], // initialize mint + vec![ + AccountMeta::new(mint.pubkey(), false), + AccountMeta::new_readonly(rent::id(), false), + ], + ), + ], + Some(&payer.pubkey()), + &[&payer, &mint], + recent_blockhash, + ); + + banks_client.process_transaction(transaction).await.unwrap(); +} diff --git a/program-test/tests/warp.rs b/program-test/tests/warp.rs index 6e29fa3ad79aeb..aab7101c6acede 100644 --- a/program-test/tests/warp.rs +++ b/program-test/tests/warp.rs @@ -1,11 +1,13 @@ #![allow(clippy::integer_arithmetic)] use { bincode::deserialize, + log::debug, solana_banks_client::BanksClient, solana_program_test::{ processor, ProgramTest, ProgramTestBanksClientExt, ProgramTestContext, ProgramTestError, }, solana_sdk::{ + account::Account, account_info::{next_account_info, AccountInfo}, clock::Clock, entrypoint::ProgramResult, @@ -26,9 +28,10 @@ use { }, transaction::{Transaction, TransactionError}, }, + solana_stake_program::stake_state, solana_vote_program::{ vote_instruction, - vote_state::{VoteInit, VoteState}, + vote_state::{self, VoteInit, VoteState}, }, std::convert::TryInto, }; @@ -276,6 +279,122 @@ async fn stake_rewards_from_warp() { ); } +#[tokio::test] +async fn stake_rewards_filter_bench_100() { + stake_rewards_filter_bench_core(100).await; +} + +async fn stake_rewards_filter_bench_core(num_stake_accounts: u64) { + // Initialize and start the test network + let mut program_test = ProgramTest::default(); + + // create vote account + let vote_address = Pubkey::new_unique(); + let node_address = Pubkey::new_unique(); + + let vote_account = vote_state::create_account(&vote_address, &node_address, 0, 1_000_000_000); + program_test.add_account(vote_address, vote_account.clone().into()); + + // create stake accounts with 0.9 sol to test min-stake filtering + const TEST_FILTER_STAKE: u64 = 900_000_000; // 0.9 sol + let mut to_filter = vec![]; + for i in 0..num_stake_accounts { + let stake_pubkey = Pubkey::new_unique(); + let stake_account = Account::from(stake_state::create_account( + &stake_pubkey, + &vote_address, + &vote_account, + &Rent::default(), + TEST_FILTER_STAKE, + )); + program_test.add_account(stake_pubkey, stake_account); + to_filter.push(stake_pubkey); + if i % 100 == 0 { + debug!("create stake account {} {}", i, stake_pubkey); + } + } + + let mut context = program_test.start_with_context().await; + + let stake_lamports = 2_000_000_000_000; + + let user_keypair = Keypair::new(); + let stake_address = + setup_stake(&mut context, &user_keypair, &vote_address, stake_lamports).await; + + let account = context + .banks_client + .get_account(stake_address) + .await + .expect("account exists") + .unwrap(); + assert_eq!(account.lamports, stake_lamports); + + // warp one epoch forward for normal inflation, no rewards collected + let first_normal_slot = context.genesis_config().epoch_schedule.first_normal_slot; + context.warp_to_slot(first_normal_slot).unwrap(); + let account = context + .banks_client + .get_account(stake_address) + .await + .expect("account exists") + .unwrap(); + assert_eq!(account.lamports, stake_lamports); + + context.increment_vote_account_credits(&vote_address, 100); + + // go forward and see that rewards have been distributed + let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; + context + .warp_to_slot(first_normal_slot + slots_per_epoch) + .unwrap(); + + let account = context + .banks_client + .get_account(stake_address) + .await + .expect("account exists") + .unwrap(); + assert!(account.lamports > stake_lamports); + + // check that filtered stake accounts are excluded from receiving epoch rewards + for stake_address in to_filter { + let account = context + .banks_client + .get_account(stake_address) + .await + .expect("account exists") + .unwrap(); + assert_eq!(account.lamports, TEST_FILTER_STAKE); + } + + // check that stake is fully active + let stake_history_account = context + .banks_client + .get_account(stake_history::id()) + .await + .expect("account exists") + .unwrap(); + + let clock_account = context + .banks_client + .get_account(clock::id()) + .await + .expect("account exists") + .unwrap(); + + let stake_state: StakeState = deserialize(&account.data).unwrap(); + let stake_history: StakeHistory = deserialize(&stake_history_account.data).unwrap(); + let clock: Clock = deserialize(&clock_account.data).unwrap(); + let stake = stake_state.stake().unwrap(); + assert_eq!( + stake + .delegation + .stake_activating_and_deactivating(clock.epoch, Some(&stake_history)), + StakeActivationStatus::with_effective(stake.delegation.stake), + ); +} + async fn check_credits_observed( banks_client: &mut BanksClient, stake_address: Pubkey, diff --git a/programs/address-lookup-table-tests/Cargo.toml b/programs/address-lookup-table-tests/Cargo.toml index befe777e4f8846..c255fde9548175 100644 --- a/programs/address-lookup-table-tests/Cargo.toml +++ b/programs/address-lookup-table-tests/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "solana-address-lookup-table-program-tests" -version = "1.11.6" +version = "1.14.24" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" @@ -14,9 +14,9 @@ publish = false [dev-dependencies] assert_matches = "1.5.0" bincode = "1.3.3" -solana-address-lookup-table-program = { path = "../address-lookup-table", version = "=1.11.6" } -solana-program-test = { path = "../../program-test", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } +solana-address-lookup-table-program = { path = "../address-lookup-table", version = "=1.14.24" } +solana-program-test = { path = "../../program-test", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/address-lookup-table/Cargo.toml b/programs/address-lookup-table/Cargo.toml index aeb5e5d5309072..08f76003c01a45 100644 --- a/programs/address-lookup-table/Cargo.toml +++ b/programs/address-lookup-table/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-address-lookup-table-program" -version = "1.11.6" +version = "1.14.24" description = "Solana address lookup table program" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -16,14 +16,14 @@ log = "0.4.17" num-derive = "0.3" num-traits = "0.2" serde = { version = "1.0.138", features = ["derive"] } -solana-frozen-abi = { path = "../../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "=1.11.6" } -solana-program = { path = "../../sdk/program", version = "=1.11.6" } +solana-frozen-abi = { path = "../../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "=1.14.24" } +solana-program = { path = "../../sdk/program", version = "=1.14.24" } thiserror = "1.0" [target.'cfg(not(target_os = "solana"))'.dependencies] -solana-program-runtime = { path = "../../program-runtime", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } [build-dependencies] rustc_version = "0.4" diff --git a/programs/address-lookup-table/src/error.rs b/programs/address-lookup-table/src/error.rs index 6fb67c703a65b0..b427067afc386c 100644 --- a/programs/address-lookup-table/src/error.rs +++ b/programs/address-lookup-table/src/error.rs @@ -1,5 +1,5 @@ #[cfg(not(target_os = "solana"))] -use solana_sdk::transaction::TransactionError; +use solana_program::message::AddressLoaderError; use thiserror::Error; #[derive(Debug, Error, PartialEq, Eq, Clone)] @@ -22,13 +22,13 @@ pub enum AddressLookupError { } #[cfg(not(target_os = "solana"))] -impl From for TransactionError { +impl From for AddressLoaderError { fn from(err: AddressLookupError) -> Self { match err { - AddressLookupError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound, - AddressLookupError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner, - AddressLookupError::InvalidAccountData => Self::InvalidAddressLookupTableData, - AddressLookupError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex, + AddressLookupError::LookupTableAccountNotFound => Self::LookupTableAccountNotFound, + AddressLookupError::InvalidAccountOwner => Self::InvalidAccountOwner, + AddressLookupError::InvalidAccountData => Self::InvalidAccountData, + AddressLookupError::InvalidLookupIndex => Self::InvalidLookupIndex, } } } diff --git a/programs/bpf-loader-tests/Cargo.toml b/programs/bpf-loader-tests/Cargo.toml index 5b67b2ad23b6db..48f47d39b451bc 100644 --- a/programs/bpf-loader-tests/Cargo.toml +++ b/programs/bpf-loader-tests/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "solana-bpf-loader-program-tests" -version = "1.11.6" +version = "1.14.24" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" @@ -14,9 +14,9 @@ publish = false [dev-dependencies] assert_matches = "1.5.0" bincode = "1.3.3" -solana-bpf-loader-program = { path = "../bpf_loader", version = "=1.11.6" } -solana-program-test = { path = "../../program-test", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } +solana-bpf-loader-program = { path = "../bpf_loader", version = "=1.14.24" } +solana-program-test = { path = "../../program-test", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf-loader-tests/tests/extend_program_data_ix.rs b/programs/bpf-loader-tests/tests/extend_program_data_ix.rs deleted file mode 100644 index d5c79c577e1cb5..00000000000000 --- a/programs/bpf-loader-tests/tests/extend_program_data_ix.rs +++ /dev/null @@ -1,422 +0,0 @@ -use { - assert_matches::assert_matches, - common::{add_upgradeable_loader_account, assert_ix_error, setup_test_context}, - solana_program_test::*, - solana_sdk::{ - account::{AccountSharedData, ReadableAccount}, - account_utils::StateMut, - bpf_loader_upgradeable::{extend_program_data, id, UpgradeableLoaderState}, - instruction::InstructionError, - pubkey::Pubkey, - signature::{Keypair, Signer}, - system_instruction::{self, SystemError, MAX_PERMITTED_DATA_LENGTH}, - system_program, - transaction::Transaction, - }, -}; - -mod common; - -#[tokio::test] -async fn test_extend_program_data() { - let mut context = setup_test_context().await; - - let program_data_address = Pubkey::new_unique(); - let program_data_len = 100; - add_upgradeable_loader_account( - &mut context, - &program_data_address, - &UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: Some(Pubkey::new_unique()), - }, - program_data_len, - ) - .await; - - let client = &mut context.banks_client; - let payer = &context.payer; - let recent_blockhash = context.last_blockhash; - const ADDITIONAL_BYTES: u32 = 42; - let transaction = Transaction::new_signed_with_payer( - &[extend_program_data( - &program_data_address, - Some(&payer.pubkey()), - ADDITIONAL_BYTES, - )], - Some(&payer.pubkey()), - &[payer], - recent_blockhash, - ); - - assert_matches!(client.process_transaction(transaction).await, Ok(())); - let updated_program_data_account = client - .get_account(program_data_address) - .await - .unwrap() - .unwrap(); - assert_eq!( - updated_program_data_account.data().len(), - program_data_len + ADDITIONAL_BYTES as usize - ); -} - -#[tokio::test] -async fn test_extend_program_data_not_upgradeable() { - let mut context = setup_test_context().await; - - let program_data_address = Pubkey::new_unique(); - add_upgradeable_loader_account( - &mut context, - &program_data_address, - &UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: None, - }, - 100, - ) - .await; - - let payer_address = context.payer.pubkey(); - assert_ix_error( - &mut context, - extend_program_data(&program_data_address, Some(&payer_address), 42), - None, - InstructionError::Immutable, - "should fail because the program data account isn't upgradeable", - ) - .await; -} - -#[tokio::test] -async fn test_extend_program_data_by_zero_bytes() { - let mut context = setup_test_context().await; - - let program_data_address = Pubkey::new_unique(); - add_upgradeable_loader_account( - &mut context, - &program_data_address, - &UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: Some(Pubkey::new_unique()), - }, - 100, - ) - .await; - - let payer_address = context.payer.pubkey(); - assert_ix_error( - &mut context, - extend_program_data(&program_data_address, Some(&payer_address), 0), - None, - InstructionError::InvalidInstructionData, - "should fail because the program data account must be extended by more than 0 bytes", - ) - .await; -} - -#[tokio::test] -async fn test_extend_program_data_past_max_size() { - let mut context = setup_test_context().await; - - let program_data_address = Pubkey::new_unique(); - add_upgradeable_loader_account( - &mut context, - &program_data_address, - &UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: Some(Pubkey::new_unique()), - }, - MAX_PERMITTED_DATA_LENGTH as usize, - ) - .await; - - let payer_address = context.payer.pubkey(); - assert_ix_error( - &mut context, - extend_program_data(&program_data_address, Some(&payer_address), 1), - None, - InstructionError::InvalidRealloc, - "should fail because the program data account cannot be extended past the max data size", - ) - .await; -} - -#[tokio::test] -async fn test_extend_program_data_with_invalid_payer() { - let mut context = setup_test_context().await; - let rent = context.banks_client.get_rent().await.unwrap(); - - let program_data_address = Pubkey::new_unique(); - add_upgradeable_loader_account( - &mut context, - &program_data_address, - &UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: Some(Pubkey::new_unique()), - }, - 100, - ) - .await; - - let payer_with_sufficient_funds = Keypair::new(); - context.set_account( - &payer_with_sufficient_funds.pubkey(), - &AccountSharedData::new(10_000_000_000, 0, &system_program::id()), - ); - - let payer_with_insufficient_funds = Keypair::new(); - context.set_account( - &payer_with_insufficient_funds.pubkey(), - &AccountSharedData::new(rent.minimum_balance(0), 0, &system_program::id()), - ); - - let payer_with_invalid_owner = Keypair::new(); - context.set_account( - &payer_with_invalid_owner.pubkey(), - &AccountSharedData::new(rent.minimum_balance(0), 0, &id()), - ); - - assert_ix_error( - &mut context, - extend_program_data( - &program_data_address, - Some(&payer_with_insufficient_funds.pubkey()), - 1024, - ), - Some(&payer_with_insufficient_funds), - InstructionError::from(SystemError::ResultWithNegativeLamports), - "should fail because the payer has insufficient funds to cover program data account rent", - ) - .await; - - assert_ix_error( - &mut context, - extend_program_data( - &program_data_address, - Some(&payer_with_invalid_owner.pubkey()), - 1, - ), - Some(&payer_with_invalid_owner), - InstructionError::ExternalAccountLamportSpend, - "should fail because the payer is not a system account", - ) - .await; - - let mut ix = extend_program_data( - &program_data_address, - Some(&payer_with_sufficient_funds.pubkey()), - 1, - ); - - // Demote payer account meta to non-signer so that transaction signing succeeds - { - let payer_meta = ix - .accounts - .iter_mut() - .find(|meta| meta.pubkey == payer_with_sufficient_funds.pubkey()) - .expect("expected to find payer account meta"); - payer_meta.is_signer = false; - } - - assert_ix_error( - &mut context, - ix, - None, - InstructionError::PrivilegeEscalation, - "should fail because the payer did not sign", - ) - .await; -} - -#[tokio::test] -async fn test_extend_program_data_without_payer() { - let mut context = setup_test_context().await; - let rent = context.banks_client.get_rent().await.unwrap(); - - let program_data_address = Pubkey::new_unique(); - let program_data_len = 100; - add_upgradeable_loader_account( - &mut context, - &program_data_address, - &UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: Some(Pubkey::new_unique()), - }, - program_data_len, - ) - .await; - - assert_ix_error( - &mut context, - extend_program_data(&program_data_address, None, 1024), - None, - InstructionError::NotEnoughAccountKeys, - "should fail because program data has insufficient funds to cover rent", - ) - .await; - - let client = &mut context.banks_client; - let payer = &context.payer; - let recent_blockhash = context.last_blockhash; - - const ADDITIONAL_BYTES: u32 = 42; - let min_balance_increase_for_extend = rent - .minimum_balance(ADDITIONAL_BYTES as usize) - .saturating_sub(rent.minimum_balance(0)); - - let transaction = Transaction::new_signed_with_payer( - &[ - system_instruction::transfer( - &payer.pubkey(), - &program_data_address, - min_balance_increase_for_extend, - ), - extend_program_data(&program_data_address, None, ADDITIONAL_BYTES), - ], - Some(&payer.pubkey()), - &[payer], - recent_blockhash, - ); - - assert_matches!(client.process_transaction(transaction).await, Ok(())); - let updated_program_data_account = client - .get_account(program_data_address) - .await - .unwrap() - .unwrap(); - assert_eq!( - updated_program_data_account.data().len(), - program_data_len + ADDITIONAL_BYTES as usize - ); -} - -#[tokio::test] -async fn test_extend_program_data_with_invalid_system_program() { - let mut context = setup_test_context().await; - - let program_data_address = Pubkey::new_unique(); - let program_data_len = 100; - add_upgradeable_loader_account( - &mut context, - &program_data_address, - &UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: Some(Pubkey::new_unique()), - }, - program_data_len, - ) - .await; - - let payer_address = context.payer.pubkey(); - let mut ix = extend_program_data(&program_data_address, Some(&payer_address), 1); - - // Change system program to an invalid key - { - let system_program_meta = ix - .accounts - .iter_mut() - .find(|meta| meta.pubkey == crate::system_program::ID) - .expect("expected to find system program account meta"); - system_program_meta.pubkey = Pubkey::new_unique(); - } - - assert_ix_error( - &mut context, - ix, - None, - InstructionError::MissingAccount, - "should fail because the system program is missing", - ) - .await; -} - -#[tokio::test] -async fn test_extend_program_data_with_invalid_program_data() { - let mut context = setup_test_context().await; - let rent = context.banks_client.get_rent().await.unwrap(); - let payer_address = context.payer.pubkey(); - - let program_data_address = Pubkey::new_unique(); - add_upgradeable_loader_account( - &mut context, - &program_data_address, - &UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: Some(Pubkey::new_unique()), - }, - 100, - ) - .await; - - let program_data_address_with_invalid_state = Pubkey::new_unique(); - { - let mut account = AccountSharedData::new(rent.minimum_balance(100), 100, &id()); - account - .set_state(&UpgradeableLoaderState::Buffer { - authority_address: Some(payer_address), - }) - .expect("serialization failed"); - context.set_account(&program_data_address_with_invalid_state, &account); - } - - let program_data_address_with_invalid_owner = Pubkey::new_unique(); - { - let invalid_owner = Pubkey::new_unique(); - let mut account = AccountSharedData::new(rent.minimum_balance(100), 100, &invalid_owner); - account - .set_state(&UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: Some(payer_address), - }) - .expect("serialization failed"); - context.set_account(&program_data_address_with_invalid_owner, &account); - } - - assert_ix_error( - &mut context, - extend_program_data( - &program_data_address_with_invalid_state, - Some(&payer_address), - 1024, - ), - None, - InstructionError::InvalidAccountData, - "should fail because the program data account state isn't valid", - ) - .await; - - assert_ix_error( - &mut context, - extend_program_data( - &program_data_address_with_invalid_owner, - Some(&payer_address), - 1024, - ), - None, - InstructionError::InvalidAccountOwner, - "should fail because the program data account owner isn't valid", - ) - .await; - - let mut ix = extend_program_data(&program_data_address, Some(&payer_address), 1); - - // Demote ProgramData account meta to read-only - { - let program_data_meta = ix - .accounts - .iter_mut() - .find(|meta| meta.pubkey == program_data_address) - .expect("expected to find program data account meta"); - program_data_meta.is_writable = false; - } - - assert_ix_error( - &mut context, - ix, - None, - InstructionError::InvalidArgument, - "should fail because the program data account is not writable", - ) - .await; -} diff --git a/programs/bpf-loader-tests/tests/extend_program_ix.rs b/programs/bpf-loader-tests/tests/extend_program_ix.rs new file mode 100644 index 00000000000000..7c928446e54579 --- /dev/null +++ b/programs/bpf-loader-tests/tests/extend_program_ix.rs @@ -0,0 +1,715 @@ +use { + assert_matches::assert_matches, + common::{add_upgradeable_loader_account, assert_ix_error, setup_test_context}, + solana_program_test::*, + solana_sdk::{ + account::{AccountSharedData, ReadableAccount}, + account_utils::StateMut, + bpf_loader_upgradeable::{extend_program, id, UpgradeableLoaderState}, + instruction::InstructionError, + pubkey::Pubkey, + signature::{Keypair, Signer}, + system_instruction::{self, SystemError, MAX_PERMITTED_DATA_LENGTH}, + system_program, + transaction::Transaction, + }, +}; + +mod common; + +#[tokio::test] +async fn test_extend_program() { + let mut context = setup_test_context().await; + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + let program_data_len = 100; + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + program_data_len, + ) + .await; + + let client = &mut context.banks_client; + let payer = &context.payer; + let recent_blockhash = context.last_blockhash; + const ADDITIONAL_BYTES: u32 = 42; + let transaction = Transaction::new_signed_with_payer( + &[extend_program( + &program_address, + Some(&payer.pubkey()), + ADDITIONAL_BYTES, + )], + Some(&payer.pubkey()), + &[payer], + recent_blockhash, + ); + + assert_matches!(client.process_transaction(transaction).await, Ok(())); + let updated_program_data_account = client + .get_account(programdata_address) + .await + .unwrap() + .unwrap(); + assert_eq!( + updated_program_data_account.data().len(), + program_data_len + ADDITIONAL_BYTES as usize + ); +} + +#[tokio::test] +async fn test_extend_program_not_upgradeable() { + let mut context = setup_test_context().await; + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: None, + }, + 100, + ) + .await; + + let payer_address = context.payer.pubkey(); + assert_ix_error( + &mut context, + extend_program(&program_address, Some(&payer_address), 42), + None, + InstructionError::Immutable, + "should fail because the program data account isn't upgradeable", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_by_zero_bytes() { + let mut context = setup_test_context().await; + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + 100, + ) + .await; + + let payer_address = context.payer.pubkey(); + assert_ix_error( + &mut context, + extend_program(&program_address, Some(&payer_address), 0), + None, + InstructionError::InvalidInstructionData, + "should fail because the program data account must be extended by more than 0 bytes", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_past_max_size() { + let mut context = setup_test_context().await; + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + MAX_PERMITTED_DATA_LENGTH as usize, + ) + .await; + + let payer_address = context.payer.pubkey(); + assert_ix_error( + &mut context, + extend_program(&program_address, Some(&payer_address), 1), + None, + InstructionError::InvalidRealloc, + "should fail because the program data account cannot be extended past the max data size", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_with_invalid_payer() { + let mut context = setup_test_context().await; + let rent = context.banks_client.get_rent().await.unwrap(); + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + 100, + ) + .await; + + let payer_with_sufficient_funds = Keypair::new(); + context.set_account( + &payer_with_sufficient_funds.pubkey(), + &AccountSharedData::new(10_000_000_000, 0, &system_program::id()), + ); + + let payer_with_insufficient_funds = Keypair::new(); + context.set_account( + &payer_with_insufficient_funds.pubkey(), + &AccountSharedData::new(rent.minimum_balance(0), 0, &system_program::id()), + ); + + let payer_with_invalid_owner = Keypair::new(); + context.set_account( + &payer_with_invalid_owner.pubkey(), + &AccountSharedData::new(rent.minimum_balance(0), 0, &id()), + ); + + assert_ix_error( + &mut context, + extend_program( + &program_address, + Some(&payer_with_insufficient_funds.pubkey()), + 1024, + ), + Some(&payer_with_insufficient_funds), + InstructionError::from(SystemError::ResultWithNegativeLamports), + "should fail because the payer has insufficient funds to cover program data account rent", + ) + .await; + + assert_ix_error( + &mut context, + extend_program( + &program_address, + Some(&payer_with_invalid_owner.pubkey()), + 1, + ), + Some(&payer_with_invalid_owner), + InstructionError::ExternalAccountLamportSpend, + "should fail because the payer is not a system account", + ) + .await; + + let mut ix = extend_program( + &program_address, + Some(&payer_with_sufficient_funds.pubkey()), + 1, + ); + + // Demote payer account meta to non-signer so that transaction signing succeeds + { + let payer_meta = ix + .accounts + .iter_mut() + .find(|meta| meta.pubkey == payer_with_sufficient_funds.pubkey()) + .expect("expected to find payer account meta"); + payer_meta.is_signer = false; + } + + assert_ix_error( + &mut context, + ix, + None, + InstructionError::PrivilegeEscalation, + "should fail because the payer did not sign", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_without_payer() { + let mut context = setup_test_context().await; + let rent = context.banks_client.get_rent().await.unwrap(); + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + let program_data_len = 100; + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + program_data_len, + ) + .await; + + assert_ix_error( + &mut context, + extend_program(&program_address, None, 1024), + None, + InstructionError::NotEnoughAccountKeys, + "should fail because program data has insufficient funds to cover rent", + ) + .await; + + let client = &mut context.banks_client; + let payer = &context.payer; + let recent_blockhash = context.last_blockhash; + + const ADDITIONAL_BYTES: u32 = 42; + let min_balance_increase_for_extend = rent + .minimum_balance(ADDITIONAL_BYTES as usize) + .saturating_sub(rent.minimum_balance(0)); + + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::transfer( + &payer.pubkey(), + &programdata_address, + min_balance_increase_for_extend, + ), + extend_program(&program_address, None, ADDITIONAL_BYTES), + ], + Some(&payer.pubkey()), + &[payer], + recent_blockhash, + ); + + assert_matches!(client.process_transaction(transaction).await, Ok(())); + let updated_program_data_account = client + .get_account(programdata_address) + .await + .unwrap() + .unwrap(); + assert_eq!( + updated_program_data_account.data().len(), + program_data_len + ADDITIONAL_BYTES as usize + ); +} + +#[tokio::test] +async fn test_extend_program_with_invalid_system_program() { + let mut context = setup_test_context().await; + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + let program_data_len = 100; + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + program_data_len, + ) + .await; + + let payer_address = context.payer.pubkey(); + let mut ix = extend_program(&program_address, Some(&payer_address), 1); + + // Change system program to an invalid key + { + let system_program_meta = ix + .accounts + .iter_mut() + .find(|meta| meta.pubkey == crate::system_program::ID) + .expect("expected to find system program account meta"); + system_program_meta.pubkey = Pubkey::new_unique(); + } + + assert_ix_error( + &mut context, + ix, + None, + InstructionError::MissingAccount, + "should fail because the system program is missing", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_with_mismatch_program_data() { + let mut context = setup_test_context().await; + let payer_address = context.payer.pubkey(); + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + + let mismatch_programdata_address = Pubkey::new_unique(); + add_upgradeable_loader_account( + &mut context, + &mismatch_programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + 100, + ) + .await; + + let mut ix = extend_program(&program_address, Some(&payer_address), 1); + + // Replace ProgramData account meta with invalid account + { + let program_data_meta = ix + .accounts + .iter_mut() + .find(|meta| meta.pubkey == programdata_address) + .expect("expected to find program data account meta"); + program_data_meta.pubkey = mismatch_programdata_address; + } + + assert_ix_error( + &mut context, + ix, + None, + InstructionError::InvalidArgument, + "should fail because the program data account doesn't match the program", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_with_readonly_program_data() { + let mut context = setup_test_context().await; + let payer_address = context.payer.pubkey(); + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + 100, + ) + .await; + + let mut ix = extend_program(&program_address, Some(&payer_address), 1); + + // Demote ProgramData account meta to read-only + { + let program_data_meta = ix + .accounts + .iter_mut() + .find(|meta| meta.pubkey == programdata_address) + .expect("expected to find program data account meta"); + program_data_meta.is_writable = false; + } + + assert_ix_error( + &mut context, + ix, + None, + InstructionError::InvalidArgument, + "should fail because the program data account is not writable", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_with_invalid_program_data_state() { + let mut context = setup_test_context().await; + let rent = context.banks_client.get_rent().await.unwrap(); + let payer_address = context.payer.pubkey(); + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + + { + let mut account = AccountSharedData::new(rent.minimum_balance(100), 100, &id()); + account + .set_state(&UpgradeableLoaderState::Buffer { + authority_address: Some(payer_address), + }) + .expect("serialization failed"); + context.set_account(&programdata_address, &account); + } + + assert_ix_error( + &mut context, + extend_program(&program_address, Some(&payer_address), 1024), + None, + InstructionError::InvalidAccountData, + "should fail because the program data account state isn't valid", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_with_invalid_program_data_owner() { + let mut context = setup_test_context().await; + let rent = context.banks_client.get_rent().await.unwrap(); + let payer_address = context.payer.pubkey(); + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + + { + let invalid_owner = Pubkey::new_unique(); + let mut account = AccountSharedData::new(rent.minimum_balance(100), 100, &invalid_owner); + account + .set_state(&UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(payer_address), + }) + .expect("serialization failed"); + context.set_account(&programdata_address, &account); + } + + assert_ix_error( + &mut context, + extend_program(&program_address, Some(&payer_address), 1024), + None, + InstructionError::InvalidAccountOwner, + "should fail because the program data account owner isn't valid", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_with_readonly_program() { + let mut context = setup_test_context().await; + let payer_address = context.payer.pubkey(); + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + add_upgradeable_loader_account( + &mut context, + &program_address, + &UpgradeableLoaderState::Program { + programdata_address, + }, + UpgradeableLoaderState::size_of_program(), + ) + .await; + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + 100, + ) + .await; + + let mut ix = extend_program(&program_address, Some(&payer_address), 1); + + // Demote Program account meta to read-only + { + let program_meta = ix + .accounts + .iter_mut() + .find(|meta| meta.pubkey == program_address) + .expect("expected to find program account meta"); + program_meta.is_writable = false; + } + + assert_ix_error( + &mut context, + ix, + None, + InstructionError::InvalidArgument, + "should fail because the program account is not writable", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_with_invalid_program_owner() { + let mut context = setup_test_context().await; + let rent = context.banks_client.get_rent().await.unwrap(); + let payer_address = context.payer.pubkey(); + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + + { + let invalid_owner = Pubkey::new_unique(); + let program_len = UpgradeableLoaderState::size_of_program(); + let mut account = AccountSharedData::new( + rent.minimum_balance(program_len), + program_len, + &invalid_owner, + ); + account + .set_state(&UpgradeableLoaderState::Program { + programdata_address, + }) + .expect("serialization failed"); + context.set_account(&program_address, &account); + } + + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + 100, + ) + .await; + + assert_ix_error( + &mut context, + extend_program(&program_address, Some(&payer_address), 1024), + None, + InstructionError::InvalidAccountOwner, + "should fail because the program account owner isn't valid", + ) + .await; +} + +#[tokio::test] +async fn test_extend_program_with_invalid_program_state() { + let mut context = setup_test_context().await; + let rent = context.banks_client.get_rent().await.unwrap(); + let payer_address = context.payer.pubkey(); + + let program_address = Pubkey::new_unique(); + let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id()); + + { + let mut account = AccountSharedData::new(rent.minimum_balance(100), 100, &id()); + account + .set_state(&UpgradeableLoaderState::Buffer { + authority_address: Some(payer_address), + }) + .expect("serialization failed"); + context.set_account(&program_address, &account); + } + + add_upgradeable_loader_account( + &mut context, + &programdata_address, + &UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: Some(Pubkey::new_unique()), + }, + 100, + ) + .await; + + assert_ix_error( + &mut context, + extend_program(&program_address, Some(&payer_address), 1024), + None, + InstructionError::InvalidAccountData, + "should fail because the program account state isn't valid", + ) + .await; +} diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index d95ae7849c394d..61ed30f4f86bb2 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -156,9 +156,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", "synstructure", ] @@ -168,9 +168,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -218,9 +218,9 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -229,9 +229,9 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -240,7 +240,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.13", "libc", "winapi 0.3.9", ] @@ -291,9 +291,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.2" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3ddbd16eabff8b45f21b98671fddcc93daaa7ac4c84f8473693437226040de5" +checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" dependencies = [ "async-trait", "bytes", @@ -301,6 +301,8 @@ dependencies = [ "http", "http-body", "mime", + "tower-layer", + "tower-service", ] [[package]] @@ -346,9 +348,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.59.2" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" dependencies = [ "bitflags", "cexpr", @@ -356,7 +358,7 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", "regex", "rustc-hash", @@ -457,8 +459,8 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.41", - "syn 1.0.98", + "proc-macro2 1.0.51", + "syn 1.0.109", ] [[package]] @@ -467,9 +469,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -478,9 +480,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -543,22 +545,22 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e" +checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -581,9 +583,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] name = "bzip2" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" dependencies = [ "bzip2-sys", "libc", @@ -606,7 +608,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61bf7211aad104ce2769ec05efcdfabf85ee84ac92461d142f22cf8badd0e54c" dependencies = [ - "errno", + "errno 0.2.8", "libc", "thiserror", ] @@ -983,10 +985,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", "rustc_version", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -1065,9 +1067,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -1141,9 +1143,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86b50932a01e7ec5c06160492ab660fb19b6bb2a7878030dd6cd68d21df9d4d" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -1191,9 +1193,9 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -1204,9 +1206,9 @@ checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef" dependencies = [ "num-bigint 0.4.3", "num-traits", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -1216,9 +1218,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb" dependencies = [ "once_cell", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -1245,6 +1247,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -1307,7 +1320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46e245f4c8ec30c6415c56cb132c07e69e74f1942f6b4a4061da748b49f486ca" dependencies = [ "cfg-if 1.0.0", - "rustix", + "rustix 0.34.4", "windows-sys 0.30.0", ] @@ -1443,9 +1456,9 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -1594,9 +1607,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.11" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -1607,7 +1620,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.7.1", "tracing", ] @@ -1687,6 +1700,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "histogram" version = "0.6.9" @@ -1736,9 +1755,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -1946,6 +1965,17 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504" +[[package]] +name = "io-lifetimes" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "ipnet" version = "2.3.0" @@ -2047,9 +2077,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" dependencies = [ "proc-macro-crate 0.1.5", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -2146,9 +2176,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libloading" @@ -2168,9 +2198,9 @@ checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" [[package]] name = "librocksdb-sys" -version = "0.6.1+6.28.2" +version = "0.8.0+7.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc587013734dadb7cf23468e531aa120788b87243648be42e2d3a072186291" +checksum = "611804e4666a25136fcc5f8cf425ab4d26c7f74ea245ffe92ea23b85b6420b5d" dependencies = [ "bindgen", "bzip2-sys", @@ -2190,15 +2220,31 @@ dependencies = [ "base64 0.12.3", "digest 0.9.0", "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", + "libsecp256k1-core 0.2.2", + "libsecp256k1-gen-ecmult 0.2.1", + "libsecp256k1-gen-genmult 0.2.1", "rand 0.7.3", "serde", "sha2 0.9.9", "typenum", ] +[[package]] +name = "libsecp256k1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0452aac8bab02242429380e9b2f94ea20cea2b37e2c1777a1358799bbe97f37" +dependencies = [ + "arrayref", + "base64 0.13.0", + "digest 0.9.0", + "libsecp256k1-core 0.3.0", + "libsecp256k1-gen-ecmult 0.3.0", + "libsecp256k1-gen-genmult 0.3.0", + "rand 0.8.5", + "serde", +] + [[package]] name = "libsecp256k1-core" version = "0.2.2" @@ -2210,13 +2256,33 @@ dependencies = [ "subtle", ] +[[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.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.2.2", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core 0.3.0", ] [[package]] @@ -2225,7 +2291,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" dependencies = [ - "libsecp256k1-core", + "libsecp256k1-core 0.2.2", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core 0.3.0", ] [[package]] @@ -2251,6 +2326,12 @@ version = "0.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7" +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + [[package]] name = "lock_api" version = "0.4.6" @@ -2271,18 +2352,18 @@ dependencies = [ [[package]] name = "lru" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84e6fe5655adc6ce00787cf7dcaf8dc4f998a0565d23eafc207a8b08ca3349a" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" dependencies = [ - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] name = "lz4" -version = "1.23.3" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edcb94251b1c375c459e5abe9fb0168c1c826c3370172684844f8f3f8d1a885" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" dependencies = [ "libc", "lz4-sys", @@ -2290,9 +2371,9 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7be8908e2ed6f31c02db8a9fa962f03e36c53fbfde437363eae3306b85d7e17" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" dependencies = [ "cc", "libc", @@ -2417,9 +2498,9 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -2540,9 +2621,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -2593,29 +2674,29 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.13", "libc", ] [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" dependencies = [ "proc-macro-crate 1.1.3", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -2662,9 +2743,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.41" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -2681,9 +2762,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -2694,20 +2775,19 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.22.0+1.1.1q" +version = "111.25.0+1.1.1t" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853" +checksum = "3173cd3626c43e3854b1b727422a276e568d9ec5fe8cec197822cf52cfb743d6" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.75" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ - "autocfg", "cc", "libc", "openssl-src", @@ -2762,9 +2842,9 @@ checksum = "084fd65d5dd8b3772edccb5ffd1e4b7eba43897ecd0f9401e330e8c542959408" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -2838,15 +2918,6 @@ dependencies = [ "crypto-mac", ] -[[package]] -name = "pbkdf2" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" -dependencies = [ - "digest 0.10.3", -] - [[package]] name = "pbkdf2" version = "0.11.0" @@ -2919,9 +2990,9 @@ checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -2960,9 +3031,9 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -3030,8 +3101,8 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b83ec2d0af5c5c556257ff52c9f98934e243b9fd39604bfb2a9b75ec2e97f18" dependencies = [ - "proc-macro2 1.0.41", - "syn 1.0.98", + "proc-macro2 1.0.51", + "syn 1.0.109", ] [[package]] @@ -3060,9 +3131,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", "version_check", ] @@ -3072,7 +3143,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", "version_check", ] @@ -3088,9 +3159,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.41" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc2916cde080c1876ff40292a396541241fe0072ef928cd76582e9ea5d60d2" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -3163,9 +3234,9 @@ checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -3176,9 +3247,9 @@ checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -3288,7 +3359,7 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", ] [[package]] @@ -3422,6 +3493,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.0" @@ -3434,13 +3514,14 @@ dependencies = [ [[package]] name = "reed-solomon-erasure" -version = "5.0.3" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2fe31452b684b8b33f65f8730c8b8812c3f5a0bb8a096934717edb1ac488641" +checksum = "7263373d500d4d4f505d43a2a662d475a894aa94503a1ee28e9188b5f3960d4f" dependencies = [ "cc", "libc", "libm", + "lru", "parking_lot 0.11.2", "smallvec", "spin 0.9.3", @@ -3463,15 +3544,6 @@ version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" -[[package]] -name = "remove_dir_all" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "reqwest" version = "0.11.11" @@ -3533,9 +3605,9 @@ dependencies = [ [[package]] name = "rocksdb" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "620f4129485ff1a7128d184bc687470c21c7951b64779ebc9cfdad3dcd920290" +checksum = "7e9562ea1d70c0cc63a34a22d977753b50cca91cc6b6527750463bd5dd8697bc" dependencies = [ "libc", "librocksdb-sys", @@ -3590,13 +3662,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f5d1c6ed6d1c6915aa64749b809fc1bafff49d160f5d927463658093d7d62ab" dependencies = [ "bitflags", - "errno", - "io-lifetimes", + "errno 0.2.8", + "io-lifetimes 0.6.1", "libc", - "linux-raw-sys", + "linux-raw-sys 0.0.42", "winapi 0.3.9", ] +[[package]] +name = "rustix" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e78cc525325c06b4a7ff02db283472f3c042b7ff0c391f96c6d5ac6f4f91b75" +dependencies = [ + "bitflags", + "errno 0.3.0", + "io-lifetimes 1.0.9", + "libc", + "linux-raw-sys 0.3.1", + "windows-sys 0.45.0", +] + [[package]] name = "rustls" version = "0.19.1" @@ -3704,9 +3790,9 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -3782,9 +3868,9 @@ version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -4015,7 +4101,7 @@ dependencies = [ [[package]] name = "solana-account-decoder" -version = "1.11.6" +version = "1.14.24" dependencies = [ "Inflector", "base64 0.13.0", @@ -4026,8 +4112,9 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "solana-address-lookup-table-program", "solana-config-program", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-vote-program", "spl-token", "spl-token-2022", @@ -4037,7 +4124,7 @@ dependencies = [ [[package]] name = "solana-address-lookup-table-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "bytemuck", @@ -4046,23 +4133,23 @@ dependencies = [ "num-traits", "rustc_version", "serde", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-program 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-program 1.14.24", "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", ] [[package]] name = "solana-banks-client" -version = "1.11.6" +version = "1.14.24" dependencies = [ "borsh", "futures 0.3.21", "solana-banks-interface", - "solana-program 1.11.6", - "solana-sdk 1.11.6", + "solana-program 1.14.24", + "solana-sdk 1.14.24", "tarpc", "thiserror", "tokio", @@ -4071,16 +4158,16 @@ dependencies = [ [[package]] name = "solana-banks-interface" -version = "1.11.6" +version = "1.14.24" dependencies = [ "serde", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "tarpc", ] [[package]] name = "solana-banks-server" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "crossbeam-channel", @@ -4088,7 +4175,7 @@ dependencies = [ "solana-banks-interface", "solana-client", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-send-transaction-service", "tarpc", "tokio", @@ -4098,7 +4185,7 @@ dependencies = [ [[package]] name = "solana-bloom" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bv", "fnv", @@ -4108,31 +4195,31 @@ dependencies = [ "rustc_version", "serde", "serde_derive", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-sdk 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-sdk 1.14.24", ] [[package]] name = "solana-bpf-loader-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "byteorder 1.4.3", - "libsecp256k1", + "libsecp256k1 0.6.0", "log", "solana-measure", "solana-metrics", "solana-program-runtime", - "solana-sdk 1.11.6", - "solana-zk-token-sdk 1.11.6", + "solana-sdk 1.14.24", + "solana-zk-token-sdk 1.14.24", "solana_rbpf", "thiserror", ] [[package]] name = "solana-bpf-programs" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "byteorder 1.4.3", @@ -4147,11 +4234,12 @@ dependencies = [ "solana-bpf-rust-realloc", "solana-bpf-rust-realloc-invoke", "solana-cli-output", - "solana-logger 1.11.6", + "solana-ledger", + "solana-logger 1.14.24", "solana-measure", "solana-program-runtime", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-transaction-status", "solana_rbpf", "walkdir", @@ -4159,384 +4247,385 @@ dependencies = [ [[package]] name = "solana-bpf-rust-128bit" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-bpf-rust-128bit-dep", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-128bit-dep" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-alloc" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-call-depth" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-caller-access" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-curve25519" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", - "solana-zk-token-sdk 1.11.6", + "solana-program 1.14.24", + "solana-zk-token-sdk 1.14.24", ] [[package]] name = "solana-bpf-rust-custom-heap" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-dep-crate" -version = "1.11.6" +version = "1.14.24" dependencies = [ "byteorder 1.4.3", "solana-address-lookup-table-program", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-deprecated-loader" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-dup-accounts" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-error-handling" -version = "1.11.6" +version = "1.14.24" dependencies = [ "num-derive", "num-traits", - "solana-program 1.11.6", + "solana-program 1.14.24", "thiserror", ] [[package]] name = "solana-bpf-rust-external-spend" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-finalize" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-get-minimum-delegation" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-inner_instruction_alignment_check" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-instruction-introspection" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-invoke" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-bpf-rust-invoked", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-invoke-and-error" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-invoke-and-ok" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-invoke-and-return" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-invoked" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-iter" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-log-data" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-many-args" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-bpf-rust-many-args-dep", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-many-args-dep" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-mem" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", "solana-program-runtime", "solana-program-test", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-bpf-rust-membuiltins" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-bpf-rust-mem", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-noop" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-panic" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-param-passing" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-bpf-rust-param-passing-dep", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-param-passing-dep" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-rand" -version = "1.11.6" +version = "1.14.24" dependencies = [ "getrandom 0.1.14", "rand 0.7.3", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-realloc" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-realloc-invoke" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-bpf-rust-realloc", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-ro-account_modify" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-ro-modify" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-sanity" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", "solana-program-runtime", "solana-program-test", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-bpf-rust-secp256k1-recover" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "libsecp256k1 0.7.0", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-sha" -version = "1.11.6" +version = "1.14.24" dependencies = [ "blake3", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-sibling-instructions" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-sibling_inner-instructions" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-simulation" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-logger 1.11.6", - "solana-program 1.11.6", + "solana-logger 1.14.24", + "solana-program 1.14.24", "solana-program-test", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-validator", ] [[package]] name = "solana-bpf-rust-spoof1" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-spoof1-system" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-sysvar" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", "solana-program-runtime", "solana-program-test", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-bpf-rust-upgradeable" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bpf-rust-upgraded" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-bucket-map" -version = "1.11.6" +version = "1.14.24" dependencies = [ "log", "memmap2", "modular-bitfield", "rand 0.7.3", "solana-measure", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "tempfile", ] [[package]] name = "solana-clap-utils" -version = "1.11.6" +version = "1.14.24" dependencies = [ "chrono", "clap 2.33.3", "rpassword", "solana-perf", "solana-remote-wallet", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", "tiny-bip39", "uriparse", @@ -4545,7 +4634,7 @@ dependencies = [ [[package]] name = "solana-cli-config" -version = "1.11.6" +version = "1.14.24" dependencies = [ "dirs-next", "lazy_static", @@ -4553,13 +4642,13 @@ dependencies = [ "serde_derive", "serde_yaml", "solana-clap-utils", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "url 2.2.2", ] [[package]] name = "solana-cli-output" -version = "1.11.6" +version = "1.14.24" dependencies = [ "Inflector", "base64 0.13.0", @@ -4576,7 +4665,7 @@ dependencies = [ "solana-clap-utils", "solana-cli-config", "solana-client", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-transaction-status", "solana-vote-program", "spl-memo", @@ -4584,7 +4673,7 @@ dependencies = [ [[package]] name = "solana-client" -version = "1.11.6" +version = "1.14.24" dependencies = [ "async-mutex", "async-trait", @@ -4620,7 +4709,7 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-net-utils", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-transaction-status", "solana-version", @@ -4636,27 +4725,27 @@ dependencies = [ [[package]] name = "solana-compute-budget-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-config-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "chrono", "serde", "serde_derive", "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-core" -version = "1.11.6" +version = "1.14.24" dependencies = [ "ahash", "base64 0.13.0", @@ -4674,6 +4763,7 @@ dependencies = [ "log", "lru", "min-max-heap", + "num_enum", "rand 0.7.3", "rand_chacha 0.2.2", "rayon", @@ -4684,8 +4774,8 @@ dependencies = [ "solana-bloom", "solana-client", "solana-entry", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", "solana-geyser-plugin-manager", "solana-gossip", "solana-ledger", @@ -4698,7 +4788,7 @@ dependencies = [ "solana-rayon-threadlimit", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-send-transaction-service", "solana-streamer", "solana-transaction-status", @@ -4714,19 +4804,19 @@ dependencies = [ [[package]] name = "solana-download-utils" -version = "1.11.6" +version = "1.14.24" dependencies = [ "console", "indicatif", "log", "reqwest", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-entry" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "crossbeam-channel", @@ -4742,12 +4832,12 @@ dependencies = [ "solana-metrics", "solana-perf", "solana-rayon-threadlimit", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-faucet" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "byteorder 1.4.3", @@ -4758,9 +4848,9 @@ dependencies = [ "serde_derive", "solana-clap-utils", "solana-cli-config", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-metrics", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-version", "spl-memo", "thiserror", @@ -4769,29 +4859,41 @@ dependencies = [ [[package]] name = "solana-frozen-abi" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a5d3280421bb53fc12bdba1eaa505153fb4f99a06b5609dae22192652ead3b" +checksum = "23b4953578272ac0fadec245e85e83ae86454611f0c0a7fff7d906835124bdcf" dependencies = [ + "ahash", + "blake3", + "block-buffer 0.9.0", "bs58", "bv", + "byteorder 1.4.3", + "cc", + "either", "generic-array 0.14.5", + "getrandom 0.1.14", + "hashbrown 0.12.3", "im", "lazy_static", "log", "memmap2", + "once_cell", + "rand_core 0.6.3", "rustc_version", "serde", "serde_bytes", "serde_derive", + "serde_json", "sha2 0.10.2", - "solana-frozen-abi-macro 1.10.33", + "solana-frozen-abi-macro 1.14.16", + "subtle", "thiserror", ] [[package]] name = "solana-frozen-abi" -version = "1.11.6" +version = "1.14.24" dependencies = [ "ahash", "blake3", @@ -4816,55 +4918,55 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.10.2", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi-macro 1.14.24", "subtle", "thiserror", ] [[package]] name = "solana-frozen-abi-macro" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "635c60ac96b1347af272c625465068b908aff919d19f29b5795a44310310494d" +checksum = "57892538250428ad3dc3cbe05f6cd75ad14f4f16734fcb91bc7cd5fbb63d6315" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", "rustc_version", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] name = "solana-frozen-abi-macro" -version = "1.11.6" +version = "1.14.24" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", "rustc_version", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] name = "solana-genesis-utils" -version = "1.11.6" +version = "1.14.24" dependencies = [ "solana-download-utils", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-geyser-plugin-interface" -version = "1.11.6" +version = "1.14.24" dependencies = [ "log", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-transaction-status", "thiserror", ] [[package]] name = "solana-geyser-plugin-manager" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bs58", "crossbeam-channel", @@ -4877,14 +4979,14 @@ dependencies = [ "solana-metrics", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-transaction-status", "thiserror", ] [[package]] name = "solana-gossip" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "bv", @@ -4908,17 +5010,17 @@ dependencies = [ "solana-clap-utils", "solana-client", "solana-entry", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-measure", "solana-metrics", "solana-net-utils", "solana-perf", "solana-rayon-threadlimit", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "solana-version", "solana-vote-program", @@ -4927,8 +5029,9 @@ dependencies = [ [[package]] name = "solana-ledger" -version = "1.11.6" +version = "1.14.24" dependencies = [ + "assert_matches", "bincode", "bitflags", "byteorder 1.4.3", @@ -4955,22 +5058,25 @@ dependencies = [ "serde", "serde_bytes", "sha2 0.10.2", + "solana-account-decoder", "solana-bpf-loader-program", "solana-entry", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", "solana-measure", "solana-metrics", "solana-perf", "solana-program-runtime", "solana-rayon-threadlimit", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-stake-program", "solana-storage-bigtable", "solana-storage-proto", "solana-transaction-status", "solana-vote-program", + "spl-token", + "spl-token-2022", "static_assertions", "tempfile", "thiserror", @@ -4981,9 +5087,9 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12cb6e6f1f9c9876d356c928b8c2ac532f6715e7cd2a1b4343d747bee3eca73" +checksum = "06aa701c49493e93085dd1e800c05475baca15a9d4d527b59794f2ed0b66e055" dependencies = [ "env_logger", "lazy_static", @@ -4992,7 +5098,7 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.11.6" +version = "1.14.24" dependencies = [ "env_logger", "lazy_static", @@ -5001,36 +5107,36 @@ dependencies = [ [[package]] name = "solana-measure" -version = "1.11.6" +version = "1.14.24" dependencies = [ "log", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-merkle-tree" -version = "1.11.6" +version = "1.14.24" dependencies = [ "fast-math", "matches", - "solana-program 1.11.6", + "solana-program 1.14.24", ] [[package]] name = "solana-metrics" -version = "1.11.6" +version = "1.14.24" dependencies = [ "crossbeam-channel", "gethostname", "lazy_static", "log", "reqwest", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-net-utils" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "clap 3.1.6", @@ -5041,8 +5147,8 @@ dependencies = [ "serde", "serde_derive", "socket2", - "solana-logger 1.11.6", - "solana-sdk 1.11.6", + "solana-logger 1.14.24", + "solana-sdk 1.14.24", "solana-version", "tokio", "url 2.2.2", @@ -5050,7 +5156,7 @@ dependencies = [ [[package]] name = "solana-perf" -version = "1.11.6" +version = "1.14.24" dependencies = [ "ahash", "bincode", @@ -5069,13 +5175,13 @@ dependencies = [ "serde", "solana-metrics", "solana-rayon-threadlimit", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-vote-program", ] [[package]] name = "solana-poh" -version = "1.11.6" +version = "1.14.24" dependencies = [ "core_affinity", "crossbeam-channel", @@ -5085,16 +5191,16 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-sys-tuner", "thiserror", ] [[package]] name = "solana-program" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeecf504cee2821b006871f70e7a1f54db15f914cedf259eaf5976fe606470f0" +checksum = "3f99052873619df68913cb8e92e28ff251a5483828925e87fa97ba15a9cbad51" dependencies = [ "base64 0.13.0", "bincode", @@ -5105,36 +5211,43 @@ dependencies = [ "bs58", "bv", "bytemuck", + "cc", "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.1.14", + "getrandom 0.2.4", "itertools", "js-sys", "lazy_static", - "libsecp256k1", + "libc", + "libsecp256k1 0.6.0", "log", + "memoffset", "num-derive", "num-traits", "parking_lot 0.12.1", "rand 0.7.3", + "rand_chacha 0.2.2", "rustc_version", "rustversion", "serde", "serde_bytes", "serde_derive", + "serde_json", "sha2 0.10.2", "sha3 0.10.1", - "solana-frozen-abi 1.10.33", - "solana-frozen-abi-macro 1.10.33", - "solana-sdk-macro 1.10.33", + "solana-frozen-abi 1.14.16", + "solana-frozen-abi-macro 1.14.16", + "solana-sdk-macro 1.14.16", "thiserror", + "tiny-bip39", "wasm-bindgen", + "zeroize", ] [[package]] name = "solana-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "base64 0.13.0", "bincode", @@ -5154,7 +5267,7 @@ dependencies = [ "js-sys", "lazy_static", "libc", - "libsecp256k1", + "libsecp256k1 0.6.0", "log", "memoffset", "num-derive", @@ -5170,9 +5283,9 @@ dependencies = [ "serde_json", "sha2 0.10.2", "sha3 0.10.1", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-sdk-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-sdk-macro 1.14.24", "thiserror", "tiny-bip39", "wasm-bindgen", @@ -5181,7 +5294,7 @@ dependencies = [ [[package]] name = "solana-program-runtime" -version = "1.11.6" +version = "1.14.24" dependencies = [ "base64 0.13.0", "bincode", @@ -5193,19 +5306,20 @@ dependencies = [ "log", "num-derive", "num-traits", + "rand 0.7.3", "rustc_version", "serde", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", "solana-measure", "solana-metrics", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", ] [[package]] name = "solana-program-test" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_matches", "async-trait", @@ -5217,10 +5331,10 @@ dependencies = [ "solana-banks-client", "solana-banks-server", "solana-bpf-loader-program", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-program-runtime", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-vote-program", "thiserror", "tokio", @@ -5228,7 +5342,7 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" -version = "1.11.6" +version = "1.14.24" dependencies = [ "lazy_static", "num_cpus", @@ -5236,7 +5350,7 @@ dependencies = [ [[package]] name = "solana-remote-wallet" -version = "1.11.6" +version = "1.14.24" dependencies = [ "console", "dialoguer", @@ -5246,14 +5360,14 @@ dependencies = [ "parking_lot 0.12.1", "qstring", "semver", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", "uriparse", ] [[package]] name = "solana-rpc" -version = "1.11.6" +version = "1.14.24" dependencies = [ "base64 0.13.0", "bincode", @@ -5286,7 +5400,7 @@ dependencies = [ "solana-poh", "solana-rayon-threadlimit", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-send-transaction-service", "solana-stake-program", "solana-storage-bigtable", @@ -5304,7 +5418,7 @@ dependencies = [ [[package]] name = "solana-runtime" -version = "1.11.6" +version = "1.14.24" dependencies = [ "arrayref", "bincode", @@ -5323,6 +5437,7 @@ dependencies = [ "itertools", "lazy_static", "log", + "lru", "lz4", "memmap2", "num-derive", @@ -5340,17 +5455,17 @@ dependencies = [ "solana-bucket-map", "solana-compute-budget-program", "solana-config-program", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", "solana-measure", "solana-metrics", "solana-program-runtime", "solana-rayon-threadlimit", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-stake-program", "solana-vote-program", "solana-zk-token-proof-program", - "solana-zk-token-sdk 1.11.6", + "solana-zk-token-sdk 1.14.24", "strum", "strum_macros", "symlink", @@ -5362,9 +5477,9 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "636f6c615aca6f75e22b6baceaf0ffed9d74367f9320b07ed57cd9b5ce2e4ff9" +checksum = "edb47da3e18cb669f6ace0b40cee0610e278903783e0c9f7fce1e1beb881a1b7" dependencies = [ "assert_matches", "base64 0.13.0", @@ -5384,12 +5499,12 @@ dependencies = [ "itertools", "js-sys", "lazy_static", - "libsecp256k1", + "libsecp256k1 0.6.0", "log", "memmap2", "num-derive", "num-traits", - "pbkdf2 0.10.1", + "pbkdf2 0.11.0", "qstring", "rand 0.7.3", "rand_chacha 0.2.2", @@ -5401,11 +5516,11 @@ dependencies = [ "serde_json", "sha2 0.10.2", "sha3 0.10.1", - "solana-frozen-abi 1.10.33", - "solana-frozen-abi-macro 1.10.33", - "solana-logger 1.10.33", - "solana-program 1.10.33", - "solana-sdk-macro 1.10.33", + "solana-frozen-abi 1.14.16", + "solana-frozen-abi-macro 1.14.16", + "solana-logger 1.14.16", + "solana-program 1.14.16", + "solana-sdk-macro 1.14.16", "thiserror", "uriparse", "wasm-bindgen", @@ -5413,7 +5528,7 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.11.6" +version = "1.14.24" dependencies = [ "assert_matches", "base64 0.13.0", @@ -5433,7 +5548,7 @@ dependencies = [ "itertools", "js-sys", "lazy_static", - "libsecp256k1", + "libsecp256k1 0.6.0", "log", "memmap2", "num-derive", @@ -5450,11 +5565,11 @@ dependencies = [ "serde_json", "sha2 0.10.2", "sha3 0.10.1", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-logger 1.11.6", - "solana-program 1.11.6", - "solana-sdk-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-logger 1.14.24", + "solana-program 1.14.24", + "solana-sdk-macro 1.14.24", "thiserror", "uriparse", "wasm-bindgen", @@ -5462,31 +5577,31 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8bcac4394644f21dc013e932a7df9f536fcecef3e5df43fe362b4ec532ce30" +checksum = "7d41a09b9cecd0a4df63c78a192adee99ebf2d3757c19713a68246e1d9789c7c" dependencies = [ "bs58", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", "rustversion", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] name = "solana-sdk-macro" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bs58", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", "rustversion", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] name = "solana-send-transaction-service" -version = "1.11.6" +version = "1.14.24" dependencies = [ "crossbeam-channel", "log", @@ -5494,12 +5609,12 @@ dependencies = [ "solana-measure", "solana-metrics", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", ] [[package]] name = "solana-stake-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "log", @@ -5509,18 +5624,18 @@ dependencies = [ "serde", "serde_derive", "solana-config-program", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", "solana-metrics", "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-vote-program", "thiserror", ] [[package]] name = "solana-storage-bigtable" -version = "1.11.6" +version = "1.14.24" dependencies = [ "backoff", "bincode", @@ -5541,7 +5656,7 @@ dependencies = [ "serde_derive", "smpl_jwt", "solana-metrics", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-storage-proto", "solana-transaction-status", "thiserror", @@ -5552,7 +5667,7 @@ dependencies = [ [[package]] name = "solana-storage-proto" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "bs58", @@ -5560,14 +5675,14 @@ dependencies = [ "protobuf-src", "serde", "solana-account-decoder", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-transaction-status", "tonic-build 0.8.0", ] [[package]] name = "solana-streamer" -version = "1.11.6" +version = "1.14.24" dependencies = [ "crossbeam-channel", "futures-util", @@ -5586,7 +5701,7 @@ dependencies = [ "rustls 0.20.6", "solana-metrics", "solana-perf", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", "tokio", "x509-parser", @@ -5594,13 +5709,13 @@ dependencies = [ [[package]] name = "solana-sys-tuner" -version = "1.11.6" +version = "1.14.24" dependencies = [ "clap 2.33.3", "libc", "log", "nix", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-version", "sysctl", "unix_socket2", @@ -5609,7 +5724,7 @@ dependencies = [ [[package]] name = "solana-test-validator" -version = "1.11.6" +version = "1.14.24" dependencies = [ "base64 0.13.0", "log", @@ -5620,20 +5735,20 @@ dependencies = [ "solana-core", "solana-gossip", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-net-utils", "solana-program-runtime", "solana-program-test", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-streamer", "tokio", ] [[package]] name = "solana-transaction-status" -version = "1.11.6" +version = "1.14.24" dependencies = [ "Inflector", "base64 0.13.0", @@ -5646,10 +5761,10 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", + "solana-address-lookup-table-program", "solana-measure", "solana-metrics", - "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-vote-program", "spl-associated-token-account", "spl-memo", @@ -5660,7 +5775,7 @@ dependencies = [ [[package]] name = "solana-validator" -version = "1.11.6" +version = "1.14.24" dependencies = [ "chrono", "clap 2.33.3", @@ -5691,14 +5806,14 @@ dependencies = [ "solana-genesis-utils", "solana-gossip", "solana-ledger", - "solana-logger 1.11.6", + "solana-logger 1.14.24", "solana-metrics", "solana-net-utils", "solana-perf", "solana-poh", "solana-rpc", "solana-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "solana-send-transaction-service", "solana-storage-bigtable", "solana-streamer", @@ -5711,21 +5826,21 @@ dependencies = [ [[package]] name = "solana-version" -version = "1.11.6" +version = "1.14.24" dependencies = [ "log", "rustc_version", "semver", "serde", "serde_derive", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", - "solana-sdk 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", + "solana-sdk 1.14.24", ] [[package]] name = "solana-vote-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bincode", "log", @@ -5734,32 +5849,32 @@ dependencies = [ "rustc_version", "serde", "serde_derive", - "solana-frozen-abi 1.11.6", - "solana-frozen-abi-macro 1.11.6", + "solana-frozen-abi 1.14.24", + "solana-frozen-abi-macro 1.14.24", "solana-metrics", "solana-program-runtime", - "solana-sdk 1.11.6", + "solana-sdk 1.14.24", "thiserror", ] [[package]] name = "solana-zk-token-proof-program" -version = "1.11.6" +version = "1.14.24" dependencies = [ "bytemuck", "getrandom 0.1.14", "num-derive", "num-traits", "solana-program-runtime", - "solana-sdk 1.11.6", - "solana-zk-token-sdk 1.11.6", + "solana-sdk 1.14.24", + "solana-zk-token-sdk 1.14.24", ] [[package]] name = "solana-zk-token-sdk" -version = "1.10.33" +version = "1.14.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410ee53a26ac91098c289c983863535d4fbb6604b229ae1159503f48fa4fc90f" +checksum = "7ab38abd096769f79fd8e3fe8465070f04742395db724606a5263c8ebc215567" dependencies = [ "aes-gcm-siv", "arrayref", @@ -5770,6 +5885,7 @@ dependencies = [ "cipher 0.4.3", "curve25519-dalek", "getrandom 0.1.14", + "itertools", "lazy_static", "merlin", "num-derive", @@ -5778,8 +5894,8 @@ dependencies = [ "serde", "serde_json", "sha3 0.9.1", - "solana-program 1.10.33", - "solana-sdk 1.10.33", + "solana-program 1.14.16", + "solana-sdk 1.14.16", "subtle", "thiserror", "zeroize", @@ -5787,7 +5903,7 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "1.11.6" +version = "1.14.24" dependencies = [ "aes-gcm-siv", "arrayref", @@ -5798,6 +5914,7 @@ dependencies = [ "cipher 0.4.3", "curve25519-dalek", "getrandom 0.1.14", + "itertools", "lazy_static", "merlin", "num-derive", @@ -5806,8 +5923,8 @@ dependencies = [ "serde", "serde_json", "sha3 0.9.1", - "solana-program 1.11.6", - "solana-sdk 1.11.6", + "solana-program 1.14.24", + "solana-sdk 1.14.24", "subtle", "thiserror", "zeroize", @@ -5855,13 +5972,18 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "1.0.5" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b013067447a1396303ddfc294f36e3d260a32f8a16c501c295bcdc7de39b490" +checksum = "978dba3bcbe88d0c2c58366c254d9ea41c5f73357e72fc0bdee4d6b5fc99c8f4" dependencies = [ + "assert_matches", "borsh", - "solana-program 1.10.33", + "num-derive", + "num-traits", + "solana-program 1.14.16", "spl-token", + "spl-token-2022", + "thiserror", ] [[package]] @@ -5870,37 +5992,37 @@ version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" dependencies = [ - "solana-program 1.10.33", + "solana-program 1.14.16", ] [[package]] name = "spl-token" -version = "3.3.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d05653bed5932064a287340dbc8a3cb298ee717e5c7ec3353d7cdb9f8fb7e1" +checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d" dependencies = [ "arrayref", "bytemuck", "num-derive", "num-traits", "num_enum", - "solana-program 1.10.33", + "solana-program 1.14.16", "thiserror", ] [[package]] name = "spl-token-2022" -version = "0.4.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a97cbf60b91b610c846ccf8eecca96d92a24a19ffbf9fe06cd0c84e76ec45e" +checksum = "0043b590232c400bad5ee9eb983ced003d15163c4c5d56b090ac6d9a57457b47" dependencies = [ "arrayref", "bytemuck", "num-derive", "num-traits", "num_enum", - "solana-program 1.10.33", - "solana-zk-token-sdk 1.10.33", + "solana-program 1.14.16", + "solana-zk-token-sdk 1.14.16", "spl-memo", "spl-token", "thiserror", @@ -5957,10 +6079,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef" dependencies = [ "heck 0.4.0", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", "rustversion", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -5988,11 +6110,11 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", "unicode-ident", ] @@ -6009,9 +6131,9 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", "unicode-xid 0.2.3", ] @@ -6079,23 +6201,22 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "libc", - "redox_syscall 0.2.10", - "remove_dir_all", - "winapi 0.3.9", + "redox_syscall 0.3.5", + "rustix 0.37.5", + "windows-sys 0.45.0", ] [[package]] @@ -6147,9 +6268,9 @@ version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -6280,9 +6401,9 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -6471,10 +6592,10 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "prost-build 0.9.0", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -6484,10 +6605,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fbcd2800e34e743b9ae795867d5f77b535d3a3be69fd731e39145719752df8c" dependencies = [ "prettyplease", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "prost-build 0.11.0", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -6560,9 +6681,9 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] @@ -6876,9 +6997,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -6910,9 +7031,9 @@ version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7042,6 +7163,36 @@ dependencies = [ "windows_x86_64_msvc 0.32.0", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm", + "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", + "windows_x86_64_msvc 0.42.2", +] + +[[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_msvc" version = "0.30.0" @@ -7054,6 +7205,12 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_i686_gnu" version = "0.30.0" @@ -7066,6 +7223,12 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" +[[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_msvc" version = "0.30.0" @@ -7078,6 +7241,12 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_x86_64_gnu" version = "0.30.0" @@ -7090,6 +7259,18 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" +[[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_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_msvc" version = "0.30.0" @@ -7102,6 +7283,12 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "winreg" version = "0.10.1" @@ -7171,9 +7358,9 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7" dependencies = [ - "proc-macro2 1.0.41", + "proc-macro2 1.0.51", "quote 1.0.18", - "syn 1.0.98", + "syn 1.0.109", "synstructure", ] diff --git a/programs/bpf/Cargo.toml b/programs/bpf/Cargo.toml index f5e3cf24244a2c..7c797725aba07f 100644 --- a/programs/bpf/Cargo.toml +++ b/programs/bpf/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "solana-bpf-programs" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" documentation = "https://docs.rs/solana" homepage = "https://solana.com/" readme = "README.md" @@ -26,20 +26,23 @@ itertools = "0.10.1" log = "0.4.11" miow = "0.3.6" net2 = "0.2.37" -solana-account-decoder = { path = "../../account-decoder", version = "=1.11.6" } -solana-bpf-loader-program = { path = "../bpf_loader", version = "=1.11.6" } -solana-bpf-rust-invoke = { path = "rust/invoke", version = "=1.11.6" } -solana-bpf-rust-realloc = { path = "rust/realloc", version = "=1.11.6" } -solana-bpf-rust-realloc-invoke = { path = "rust/realloc_invoke", version = "=1.11.6" } -solana-cli-output = { path = "../../cli-output", version = "=1.11.6" } -solana-logger = { path = "../../logger", version = "=1.11.6" } -solana-measure = { path = "../../measure", version = "=1.11.6" } -solana-program-runtime = { path = "../../program-runtime", version = "=1.11.6" } -solana-runtime = { path = "../../runtime", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } -solana-transaction-status = { path = "../../transaction-status", version = "=1.11.6" } +solana-account-decoder = { path = "../../account-decoder", version = "=1.14.24" } +solana-bpf-loader-program = { path = "../bpf_loader", version = "=1.14.24" } +solana-bpf-rust-invoke = { path = "rust/invoke", version = "=1.14.24" } +solana-bpf-rust-realloc = { path = "rust/realloc", version = "=1.14.24" } +solana-bpf-rust-realloc-invoke = { path = "rust/realloc_invoke", version = "=1.14.24" } +solana-cli-output = { path = "../../cli-output", version = "=1.14.24" } +solana-logger = { path = "../../logger", version = "=1.14.24" } +solana-measure = { path = "../../measure", version = "=1.14.24" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.14.24" } +solana-runtime = { path = "../../runtime", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } +solana-transaction-status = { path = "../../transaction-status", version = "=1.14.24" } solana_rbpf = "=0.2.31" +[dev-dependencies] +solana-ledger = { path = "../../ledger", version = "=1.14.24" } + [[bench]] name = "bpf_loader" diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index 4334463b9cb667..360ee3c5961f69 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -22,7 +22,7 @@ use { bank::Bank, bank_client::BankClient, genesis_utils::{create_genesis_config, GenesisConfigInfo}, - loader_utils::load_program, + loader_utils::{create_deprecated_program, load_program_from_file}, }, solana_sdk::{ bpf_loader, @@ -30,54 +30,18 @@ use { entrypoint::SUCCESS, instruction::{AccountMeta, Instruction}, message::Message, - pubkey::Pubkey, - signature::{Keypair, Signer}, + signature::Signer, }, - std::{env, fs::File, io::Read, mem, path::PathBuf, sync::Arc}, + std::{mem, sync::Arc}, test::Bencher, }; -/// BPF program file extension -const PLATFORM_FILE_EXTENSION_BPF: &str = "so"; -/// Create a BPF program file name -fn create_bpf_path(name: &str) -> PathBuf { - let mut pathbuf = { - let current_exe = env::current_exe().unwrap(); - PathBuf::from(current_exe.parent().unwrap().parent().unwrap()) - }; - pathbuf.push("bpf/"); - pathbuf.push(name); - pathbuf.set_extension(PLATFORM_FILE_EXTENSION_BPF); - pathbuf -} - -fn load_elf(name: &str) -> Result, std::io::Error> { - let path = create_bpf_path(name); - let mut file = File::open(&path).expect(&format!("Unable to open {:?}", path)); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - Ok(elf) -} - -fn load_bpf_program( - bank_client: &BankClient, - loader_id: &Pubkey, - payer_keypair: &Keypair, - name: &str, -) -> Pubkey { - let path = create_bpf_path(name); - let mut file = File::open(path).unwrap(); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - load_program(bank_client, payer_keypair, loader_id, elf) -} - const ARMSTRONG_LIMIT: u64 = 500; const ARMSTRONG_EXPECTED: u64 = 5; #[bench] fn bench_program_create_executable(bencher: &mut Bencher) { - let elf = load_elf("bench_alu").unwrap(); + let elf = load_program_from_file("bench_alu"); bencher.iter(|| { let _ = Executable::::from_elf( @@ -98,7 +62,7 @@ fn bench_program_alu(bencher: &mut Bencher) { .write_u64::(ARMSTRONG_LIMIT) .unwrap(); inner_iter.write_u64::(0).unwrap(); - let elf = load_elf("bench_alu").unwrap(); + let elf = load_program_from_file("bench_alu"); let loader_id = bpf_loader::id(); with_mock_invoke_context(loader_id, 10000001, |invoke_context| { invoke_context @@ -191,7 +155,7 @@ fn bench_program_execute_noop(bencher: &mut Bencher) { let bank_client = BankClient::new_shared(&bank); let invoke_program_id = - load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, "noop"); + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, "noop"); let mint_pubkey = mint_keypair.pubkey(); let account_metas = vec![AccountMeta::new(mint_pubkey, true)]; @@ -214,7 +178,7 @@ fn bench_program_execute_noop(bencher: &mut Bencher) { #[bench] fn bench_create_vm(bencher: &mut Bencher) { - let elf = load_elf("noop").unwrap(); + let elf = load_program_from_file("noop"); let loader_id = bpf_loader::id(); with_mock_invoke_context(loader_id, 10000001, |invoke_context| { const BUDGET: u64 = 200_000; @@ -262,7 +226,7 @@ fn bench_create_vm(bencher: &mut Bencher) { #[bench] fn bench_instruction_count_tuner(_bencher: &mut Bencher) { - let elf = load_elf("tuner").unwrap(); + let elf = load_program_from_file("tuner"); let loader_id = bpf_loader::id(); with_mock_invoke_context(loader_id, 10000001, |invoke_context| { const BUDGET: u64 = 200_000; diff --git a/programs/bpf/build.rs b/programs/bpf/build.rs index 05220a1536b2bc..016eb55b9d180d 100644 --- a/programs/bpf/build.rs +++ b/programs/bpf/build.rs @@ -108,7 +108,7 @@ fn main() { program ); assert!(Command::new("../../cargo-build-bpf") - .args(&[ + .args([ "--manifest-path", &format!("rust/{}/Cargo.toml", program), "--bpf-out-dir", diff --git a/programs/bpf/rust/128bit/Cargo.toml b/programs/bpf/rust/128bit/Cargo.toml index 1b6a470d031e7d..236d66c6c2932a 100644 --- a/programs/bpf/rust/128bit/Cargo.toml +++ b/programs/bpf/rust/128bit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-128bit" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,8 +10,8 @@ documentation = "https://docs.rs/solana-bpf-rust-128bit" edition = "2021" [dependencies] -solana-bpf-rust-128bit-dep = { path = "../128bit_dep", version = "=1.11.6" } -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-bpf-rust-128bit-dep = { path = "../128bit_dep", version = "=1.14.24" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/128bit_dep/Cargo.toml b/programs/bpf/rust/128bit_dep/Cargo.toml index 0ef3294df774a2..dd4702af21dda9 100644 --- a/programs/bpf/rust/128bit_dep/Cargo.toml +++ b/programs/bpf/rust/128bit_dep/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-128bit-dep" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-128bit-dep" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/alloc/Cargo.toml b/programs/bpf/rust/alloc/Cargo.toml index b06df2b4aa8a6a..91831b86094d77 100644 --- a/programs/bpf/rust/alloc/Cargo.toml +++ b/programs/bpf/rust/alloc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-alloc" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-alloc" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/call_depth/Cargo.toml b/programs/bpf/rust/call_depth/Cargo.toml index 86f87423701a63..1b5ca4f733d8b5 100644 --- a/programs/bpf/rust/call_depth/Cargo.toml +++ b/programs/bpf/rust/call_depth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-call-depth" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-call-depth" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/caller_access/Cargo.toml b/programs/bpf/rust/caller_access/Cargo.toml index 90253a86829e6d..86a961716cd420 100644 --- a/programs/bpf/rust/caller_access/Cargo.toml +++ b/programs/bpf/rust/caller_access/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-caller-access" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-caller-access" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/curve25519/Cargo.toml b/programs/bpf/rust/curve25519/Cargo.toml index 36a316556aa2d9..090fad249dc06e 100644 --- a/programs/bpf/rust/curve25519/Cargo.toml +++ b/programs/bpf/rust/curve25519/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-curve25519" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,8 +10,8 @@ documentation = "https://docs.rs/solana-bpf-rust-zktoken_crypto" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } -solana-zk-token-sdk = { path = "../../../../zk-token-sdk", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } +solana-zk-token-sdk = { path = "../../../../zk-token-sdk", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/curve25519/src/lib.rs b/programs/bpf/rust/curve25519/src/lib.rs index 3f1e35ac4d5c20..a8096d65b34710 100644 --- a/programs/bpf/rust/curve25519/src/lib.rs +++ b/programs/bpf/rust/curve25519/src/lib.rs @@ -38,6 +38,13 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 { edwards::multiply_edwards(&scalar_one, &edwards_generator).expect("multiply_edwards") ); + msg!("multiscalar_multiply_edwards"); + assert_eq!( + edwards_generator, + edwards::multiscalar_multiply_edwards(&[scalar_one], &[edwards_generator]) + .expect("multiscalar_multiply_edwards"), + ); + let ristretto_identity = ristretto::PodRistrettoPoint([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -64,6 +71,13 @@ pub extern "C" fn entrypoint(_input: *mut u8) -> u64 { .expect("multiply_ristretto") ); + msg!("multiscalar_multiply_ristretto"); + assert_eq!( + ristretto_generator, + ristretto::multiscalar_multiply_ristretto(&[scalar_one], &[ristretto_generator]) + .expect("multiscalar_multiply_ristretto"), + ); + 0 } diff --git a/programs/bpf/rust/custom_heap/Cargo.toml b/programs/bpf/rust/custom_heap/Cargo.toml index 5ce1b8cbb3579b..8b5df366939216 100644 --- a/programs/bpf/rust/custom_heap/Cargo.toml +++ b/programs/bpf/rust/custom_heap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-custom-heap" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-custom-heap" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [features] default = ["custom-heap"] diff --git a/programs/bpf/rust/dep_crate/Cargo.toml b/programs/bpf/rust/dep_crate/Cargo.toml index d82280d99bf1a8..959496bf4eab6c 100644 --- a/programs/bpf/rust/dep_crate/Cargo.toml +++ b/programs/bpf/rust/dep_crate/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-dep-crate" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -12,8 +12,8 @@ edition = "2021" [dependencies] byteorder = { version = "1", default-features = false } # list of crates which must be buildable for bpf programs -solana-address-lookup-table-program = { path = "../../../../programs/address-lookup-table", version = "=1.11.6" } -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-address-lookup-table-program = { path = "../../../../programs/address-lookup-table", version = "=1.14.24" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/deprecated_loader/Cargo.toml b/programs/bpf/rust/deprecated_loader/Cargo.toml index e7b7b32524861f..b6f144d0e4c3a0 100644 --- a/programs/bpf/rust/deprecated_loader/Cargo.toml +++ b/programs/bpf/rust/deprecated_loader/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-deprecated-loader" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-deprecated-loader" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/dup_accounts/Cargo.toml b/programs/bpf/rust/dup_accounts/Cargo.toml index c337074a71ea67..5242a2a2e60e6e 100644 --- a/programs/bpf/rust/dup_accounts/Cargo.toml +++ b/programs/bpf/rust/dup_accounts/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-dup-accounts" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-dup-accounts" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/error_handling/Cargo.toml b/programs/bpf/rust/error_handling/Cargo.toml index 1245336ac29397..4d1a3222bc48d4 100644 --- a/programs/bpf/rust/error_handling/Cargo.toml +++ b/programs/bpf/rust/error_handling/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-error-handling" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -12,7 +12,7 @@ edition = "2021" [dependencies] num-derive = "0.3" num-traits = "0.2" -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } thiserror = "1.0" [lib] diff --git a/programs/bpf/rust/external_spend/Cargo.toml b/programs/bpf/rust/external_spend/Cargo.toml index ca8e300392234f..ce751e209afc1b 100644 --- a/programs/bpf/rust/external_spend/Cargo.toml +++ b/programs/bpf/rust/external_spend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-external-spend" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-external-spend" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/finalize/Cargo.toml b/programs/bpf/rust/finalize/Cargo.toml index 4d1b6a41c4d93d..8f7539a57765f2 100644 --- a/programs/bpf/rust/finalize/Cargo.toml +++ b/programs/bpf/rust/finalize/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-finalize" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-finalize" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/get_minimum_delegation/Cargo.toml b/programs/bpf/rust/get_minimum_delegation/Cargo.toml index 044a7bfaa2be47..dc373e9ff1061e 100644 --- a/programs/bpf/rust/get_minimum_delegation/Cargo.toml +++ b/programs/bpf/rust/get_minimum_delegation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-get-minimum-delegation" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-get-minimum-delegation" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/inner_instruction_alignment_check/Cargo.toml b/programs/bpf/rust/inner_instruction_alignment_check/Cargo.toml index b666bd0d612213..ea3368fb6f07ac 100644 --- a/programs/bpf/rust/inner_instruction_alignment_check/Cargo.toml +++ b/programs/bpf/rust/inner_instruction_alignment_check/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-inner_instruction_alignment_check" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-inner_instruction_alignment_che edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/instruction_introspection/Cargo.toml b/programs/bpf/rust/instruction_introspection/Cargo.toml index 3c30dd916a7443..2b61be741293bd 100644 --- a/programs/bpf/rust/instruction_introspection/Cargo.toml +++ b/programs/bpf/rust/instruction_introspection/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-instruction-introspection" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-instruction-introspection" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/invoke/Cargo.toml b/programs/bpf/rust/invoke/Cargo.toml index ce642afa1ccce6..b644ebca69cce6 100644 --- a/programs/bpf/rust/invoke/Cargo.toml +++ b/programs/bpf/rust/invoke/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-invoke" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -15,7 +15,7 @@ program = [] [dependencies] solana-bpf-rust-invoked = { path = "../invoked", default-features = false } -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["lib", "cdylib"] diff --git a/programs/bpf/rust/invoke_and_error/Cargo.toml b/programs/bpf/rust/invoke_and_error/Cargo.toml index baf27b244e882d..335fa24f313993 100644 --- a/programs/bpf/rust/invoke_and_error/Cargo.toml +++ b/programs/bpf/rust/invoke_and_error/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-invoke-and-error" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-invoke-and-error" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/invoke_and_ok/Cargo.toml b/programs/bpf/rust/invoke_and_ok/Cargo.toml index ce74e920490e33..0bd16c3a2885c7 100644 --- a/programs/bpf/rust/invoke_and_ok/Cargo.toml +++ b/programs/bpf/rust/invoke_and_ok/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-invoke-and-ok" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-invoke-and-ok" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/invoke_and_return/Cargo.toml b/programs/bpf/rust/invoke_and_return/Cargo.toml index 52ef6038242d2c..5942180ddba991 100644 --- a/programs/bpf/rust/invoke_and_return/Cargo.toml +++ b/programs/bpf/rust/invoke_and_return/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-invoke-and-return" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-invoke-and-return" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/invoked/Cargo.toml b/programs/bpf/rust/invoked/Cargo.toml index 779c16656625ad..5357023c2f95ca 100644 --- a/programs/bpf/rust/invoked/Cargo.toml +++ b/programs/bpf/rust/invoked/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-invoked" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,7 +14,7 @@ default = ["program"] program = [] [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["lib", "cdylib"] diff --git a/programs/bpf/rust/iter/Cargo.toml b/programs/bpf/rust/iter/Cargo.toml index bee7bac07abbcf..ac1210226f13c8 100644 --- a/programs/bpf/rust/iter/Cargo.toml +++ b/programs/bpf/rust/iter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-iter" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-iter" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/log_data/Cargo.toml b/programs/bpf/rust/log_data/Cargo.toml index b9ddc2c8c064c0..1ccf73a4e23a2c 100644 --- a/programs/bpf/rust/log_data/Cargo.toml +++ b/programs/bpf/rust/log_data/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-log-data" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-log-data" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [features] default = ["program"] diff --git a/programs/bpf/rust/many_args/Cargo.toml b/programs/bpf/rust/many_args/Cargo.toml index 6f3f518f509a29..72560d6e2a945b 100644 --- a/programs/bpf/rust/many_args/Cargo.toml +++ b/programs/bpf/rust/many_args/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-many-args" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,8 +10,8 @@ documentation = "https://docs.rs/solana-bpf-rust-many-args" edition = "2021" [dependencies] -solana-bpf-rust-many-args-dep = { path = "../many_args_dep", version = "=1.11.6" } -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-bpf-rust-many-args-dep = { path = "../many_args_dep", version = "=1.14.24" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/many_args_dep/Cargo.toml b/programs/bpf/rust/many_args_dep/Cargo.toml index 1ea21ac7af2064..11f6e77bc88a9d 100644 --- a/programs/bpf/rust/many_args_dep/Cargo.toml +++ b/programs/bpf/rust/many_args_dep/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-many-args-dep" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-many-args-dep" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/mem/Cargo.toml b/programs/bpf/rust/mem/Cargo.toml index 3dd12ab001e9c7..37729e62fb7c07 100644 --- a/programs/bpf/rust/mem/Cargo.toml +++ b/programs/bpf/rust/mem/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-mem" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -13,12 +13,12 @@ edition = "2021" no-entrypoint = [] [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [dev-dependencies] -solana-program-runtime = { path = "../../../../program-runtime", version = "=1.11.6" } -solana-program-test = { path = "../../../../program-test", version = "=1.11.6" } -solana-sdk = { path = "../../../../sdk", version = "=1.11.6" } +solana-program-runtime = { path = "../../../../program-runtime", version = "=1.14.24" } +solana-program-test = { path = "../../../../program-test", version = "=1.14.24" } +solana-sdk = { path = "../../../../sdk", version = "=1.14.24" } [lib] crate-type = ["cdylib", "lib"] diff --git a/programs/bpf/rust/membuiltins/Cargo.toml b/programs/bpf/rust/membuiltins/Cargo.toml index 71cec481abc887..0b2fd7720ee2a9 100644 --- a/programs/bpf/rust/membuiltins/Cargo.toml +++ b/programs/bpf/rust/membuiltins/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-membuiltins" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,8 +10,8 @@ documentation = "https://docs.rs/solana-bpf-rust-mem" edition = "2021" [dependencies] -solana-bpf-rust-mem = { path = "../mem", version = "=1.11.6", features = [ "no-entrypoint" ] } -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-bpf-rust-mem = { path = "../mem", version = "=1.14.24", features = [ "no-entrypoint" ] } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/noop/Cargo.toml b/programs/bpf/rust/noop/Cargo.toml index ddf2bd7dc33fb8..196b5c7033e55f 100644 --- a/programs/bpf/rust/noop/Cargo.toml +++ b/programs/bpf/rust/noop/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-noop" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-noop" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/panic/Cargo.toml b/programs/bpf/rust/panic/Cargo.toml index 80d1dc5f41e12b..471ba79cd87151 100644 --- a/programs/bpf/rust/panic/Cargo.toml +++ b/programs/bpf/rust/panic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-panic" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-panic" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [features] default = ["custom-panic"] diff --git a/programs/bpf/rust/param_passing/Cargo.toml b/programs/bpf/rust/param_passing/Cargo.toml index 7abaa6481a4818..1da23b42337895 100644 --- a/programs/bpf/rust/param_passing/Cargo.toml +++ b/programs/bpf/rust/param_passing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-param-passing" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,8 +10,8 @@ documentation = "https://docs.rs/solana-bpf-rust-param-passing" edition = "2021" [dependencies] -solana-bpf-rust-param-passing-dep = { path = "../param_passing_dep", version = "=1.11.6" } -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-bpf-rust-param-passing-dep = { path = "../param_passing_dep", version = "=1.14.24" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/param_passing_dep/Cargo.toml b/programs/bpf/rust/param_passing_dep/Cargo.toml index bcd852b53ba1a0..7950d6e558d6b9 100644 --- a/programs/bpf/rust/param_passing_dep/Cargo.toml +++ b/programs/bpf/rust/param_passing_dep/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-param-passing-dep" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-param-passing-dep" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/bpf/rust/rand/Cargo.toml b/programs/bpf/rust/rand/Cargo.toml index 48aeceb2151fa5..58970959afcccf 100644 --- a/programs/bpf/rust/rand/Cargo.toml +++ b/programs/bpf/rust/rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-rand" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -12,7 +12,7 @@ edition = "2021" [dependencies] getrandom = { version = "0.1.14", features = ["dummy"] } rand = "0.7" -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/realloc/Cargo.toml b/programs/bpf/rust/realloc/Cargo.toml index 3065c944f671d0..e4ff55aa38c656 100644 --- a/programs/bpf/rust/realloc/Cargo.toml +++ b/programs/bpf/rust/realloc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-realloc" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,7 +14,7 @@ default = ["program"] program = [] [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["lib", "cdylib"] diff --git a/programs/bpf/rust/realloc_invoke/Cargo.toml b/programs/bpf/rust/realloc_invoke/Cargo.toml index 758789c03615f4..bfe754334393dc 100644 --- a/programs/bpf/rust/realloc_invoke/Cargo.toml +++ b/programs/bpf/rust/realloc_invoke/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-realloc-invoke" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,8 +14,8 @@ default = ["program"] program = [] [dependencies] -solana-bpf-rust-realloc = { path = "../realloc", version = "=1.11.6", default-features = false } -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-bpf-rust-realloc = { path = "../realloc", version = "=1.14.24", default-features = false } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["lib", "cdylib"] diff --git a/programs/bpf/rust/ro_account_modify/Cargo.toml b/programs/bpf/rust/ro_account_modify/Cargo.toml index 355f1b7ff38a59..ed51106c855749 100644 --- a/programs/bpf/rust/ro_account_modify/Cargo.toml +++ b/programs/bpf/rust/ro_account_modify/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-ro-account_modify" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-ro-modify" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/ro_modify/Cargo.toml b/programs/bpf/rust/ro_modify/Cargo.toml index b7ac58f745d255..8dd11f59b18ce8 100644 --- a/programs/bpf/rust/ro_modify/Cargo.toml +++ b/programs/bpf/rust/ro_modify/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-ro-modify" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-ro-modify" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/sanity/Cargo.toml b/programs/bpf/rust/sanity/Cargo.toml index 621fe240295c46..17853805bdcaeb 100644 --- a/programs/bpf/rust/sanity/Cargo.toml +++ b/programs/bpf/rust/sanity/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-sanity" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -13,12 +13,12 @@ edition = "2021" test-bpf = [] [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [dev-dependencies] -solana-program-runtime = { path = "../../../../program-runtime", version = "=1.11.6" } -solana-program-test = { path = "../../../../program-test", version = "=1.11.6" } -solana-sdk = { path = "../../../../sdk", version = "=1.11.6" } +solana-program-runtime = { path = "../../../../program-runtime", version = "=1.14.24" } +solana-program-test = { path = "../../../../program-test", version = "=1.14.24" } +solana-sdk = { path = "../../../../sdk", version = "=1.14.24" } [lib] crate-type = ["cdylib", "lib"] diff --git a/programs/bpf/rust/secp256k1_recover/Cargo.toml b/programs/bpf/rust/secp256k1_recover/Cargo.toml index 18ad44a738cff4..be48c5d1644cdd 100644 --- a/programs/bpf/rust/secp256k1_recover/Cargo.toml +++ b/programs/bpf/rust/secp256k1_recover/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-secp256k1-recover" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,8 @@ documentation = "https://docs.rs/solana-bpf-rust-secp256k1-recover" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +libsecp256k1 = { version = "0.7.0", default-features = false } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/secp256k1_recover/src/lib.rs b/programs/bpf/rust/secp256k1_recover/src/lib.rs index 58fe2c7b96ba0f..5cdc07ad621885 100644 --- a/programs/bpf/rust/secp256k1_recover/src/lib.rs +++ b/programs/bpf/rust/secp256k1_recover/src/lib.rs @@ -1,11 +1,11 @@ //! Secp256k1Recover Syscall test extern crate solana_program; -use solana_program::{custom_heap_default, custom_panic_default, msg}; +use solana_program::{ + custom_heap_default, custom_panic_default, msg, secp256k1_recover::secp256k1_recover, +}; fn test_secp256k1_recover() { - use solana_program::secp256k1_recover::secp256k1_recover; - let expected: [u8; 64] = [ 0x42, 0xcd, 0x27, 0xe4, 0x0f, 0xdf, 0x7c, 0x97, 0x0a, 0xa2, 0xca, 0x0b, 0x88, 0x5b, 0x96, 0x0f, 0x8b, 0x62, 0x8a, 0x41, 0xa1, 0x81, 0xe7, 0xe6, 0x8e, 0x03, 0xea, 0x0b, 0x84, 0x20, @@ -32,11 +32,56 @@ fn test_secp256k1_recover() { assert_eq!(public_key.to_bytes(), expected); } +/// secp256k1_recover allows malleable signatures +fn test_secp256k1_recover_malleability() { + let message = b"hello world"; + let message_hash = { + let mut hasher = solana_program::keccak::Hasher::default(); + hasher.hash(message); + hasher.result() + }; + + let pubkey_bytes: [u8; 64] = [ + 0x9B, 0xEE, 0x7C, 0x18, 0x34, 0xE0, 0x18, 0x21, 0x7B, 0x40, 0x14, 0x9B, 0x84, 0x2E, 0xFA, + 0x80, 0x96, 0x00, 0x1A, 0x9B, 0x17, 0x88, 0x01, 0x80, 0xA8, 0x46, 0x99, 0x09, 0xE9, 0xC4, + 0x73, 0x6E, 0x39, 0x0B, 0x94, 0x00, 0x97, 0x68, 0xC2, 0x28, 0xB5, 0x55, 0xD3, 0x0C, 0x0C, + 0x42, 0x43, 0xC1, 0xEE, 0xA5, 0x0D, 0xC0, 0x48, 0x62, 0xD3, 0xAE, 0xB0, 0x3D, 0xA2, 0x20, + 0xAC, 0x11, 0x85, 0xEE, + ]; + let signature_bytes: [u8; 64] = [ + 0x93, 0x92, 0xC4, 0x6C, 0x42, 0xF6, 0x31, 0x73, 0x81, 0xD4, 0xB2, 0x44, 0xE9, 0x2F, 0xFC, + 0xE3, 0xF4, 0x57, 0xDD, 0x50, 0xB3, 0xA5, 0x20, 0x26, 0x3B, 0xE7, 0xEF, 0x8A, 0xB0, 0x69, + 0xBB, 0xDE, 0x2F, 0x90, 0x12, 0x93, 0xD7, 0x3F, 0xA0, 0x29, 0x0C, 0x46, 0x4B, 0x97, 0xC5, + 0x00, 0xAD, 0xEA, 0x6A, 0x64, 0x4D, 0xC3, 0x8D, 0x25, 0x24, 0xEF, 0x97, 0x6D, 0xC6, 0xD7, + 0x1D, 0x9F, 0x5A, 0x26, + ]; + let recovery_id: u8 = 0; + + let signature = libsecp256k1::Signature::parse_standard_slice(&signature_bytes).unwrap(); + + // Flip the S value in the signature to make a different but valid signature. + let mut alt_signature = signature.clone(); + alt_signature.s = -alt_signature.s; + let alt_recovery_id = libsecp256k1::RecoveryId::parse(recovery_id ^ 1).unwrap(); + + let alt_signature_bytes = alt_signature.serialize(); + let alt_recovery_id = alt_recovery_id.serialize(); + + let recovered_pubkey = + secp256k1_recover(&message_hash.0, recovery_id, &signature_bytes[..]).unwrap(); + assert_eq!(recovered_pubkey.to_bytes(), pubkey_bytes); + + let alt_recovered_pubkey = + secp256k1_recover(&message_hash.0, alt_recovery_id, &alt_signature_bytes[..]).unwrap(); + assert_eq!(alt_recovered_pubkey.to_bytes(), pubkey_bytes); +} + #[no_mangle] pub extern "C" fn entrypoint(_input: *mut u8) -> u64 { msg!("secp256k1_recover"); test_secp256k1_recover(); + test_secp256k1_recover_malleability(); 0 } diff --git a/programs/bpf/rust/sha/Cargo.toml b/programs/bpf/rust/sha/Cargo.toml index d125da8d979644..1556f24d677245 100644 --- a/programs/bpf/rust/sha/Cargo.toml +++ b/programs/bpf/rust/sha/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-sha" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -11,7 +11,7 @@ edition = "2021" [dependencies] blake3 = "1.0.0" -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/sibling_inner_instruction/Cargo.toml b/programs/bpf/rust/sibling_inner_instruction/Cargo.toml index a5de86d5c19433..2709c7c9896e7a 100644 --- a/programs/bpf/rust/sibling_inner_instruction/Cargo.toml +++ b/programs/bpf/rust/sibling_inner_instruction/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-sibling_inner-instructions" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-log-data" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [features] default = ["program"] diff --git a/programs/bpf/rust/sibling_instruction/Cargo.toml b/programs/bpf/rust/sibling_instruction/Cargo.toml index 6d7d6ec08fa53b..ccc54b3ed84d37 100644 --- a/programs/bpf/rust/sibling_instruction/Cargo.toml +++ b/programs/bpf/rust/sibling_instruction/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-sibling-instructions" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-log-data" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [features] default = ["program"] diff --git a/programs/bpf/rust/simulation/Cargo.toml b/programs/bpf/rust/simulation/Cargo.toml index 3e7862a3775c47..021398caa20538 100644 --- a/programs/bpf/rust/simulation/Cargo.toml +++ b/programs/bpf/rust/simulation/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-simulation" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF Program Simulation Differences" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -13,13 +13,13 @@ edition = "2021" test-bpf = [] [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [dev-dependencies] -solana-logger = { path = "../../../../logger", version = "=1.11.6" } -solana-program-test = { path = "../../../../program-test", version = "=1.11.6" } -solana-sdk = { path = "../../../../sdk", version = "=1.11.6" } -solana-validator = { path = "../../../../validator", version = "=1.11.6" } +solana-logger = { path = "../../../../logger", version = "=1.14.24" } +solana-program-test = { path = "../../../../program-test", version = "=1.14.24" } +solana-sdk = { path = "../../../../sdk", version = "=1.14.24" } +solana-validator = { path = "../../../../validator", version = "=1.14.24" } [lib] crate-type = ["cdylib", "lib"] diff --git a/programs/bpf/rust/spoof1/Cargo.toml b/programs/bpf/rust/spoof1/Cargo.toml index 3fac3f592d9dd9..596040d2eaf9c6 100644 --- a/programs/bpf/rust/spoof1/Cargo.toml +++ b/programs/bpf/rust/spoof1/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-spoof1" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-spoof1" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/spoof1_system/Cargo.toml b/programs/bpf/rust/spoof1_system/Cargo.toml index 56591da4adac91..d62ac2624ea280 100644 --- a/programs/bpf/rust/spoof1_system/Cargo.toml +++ b/programs/bpf/rust/spoof1_system/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-spoof1-system" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-spoof1-system" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/programs/bpf/rust/sysvar/Cargo.toml b/programs/bpf/rust/sysvar/Cargo.toml index 445af57e395485..93e6c1da19f151 100644 --- a/programs/bpf/rust/sysvar/Cargo.toml +++ b/programs/bpf/rust/sysvar/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-sysvar" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,12 +10,12 @@ documentation = "https://docs.rs/solana-bpf-rust-sysvar" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [dev-dependencies] -solana-program-runtime = { path = "../../../../program-runtime", version = "=1.11.6" } -solana-program-test = { path = "../../../../program-test", version = "=1.11.6" } -solana-sdk = { path = "../../../../sdk", version = "=1.11.6" } +solana-program-runtime = { path = "../../../../program-runtime", version = "=1.14.24" } +solana-program-test = { path = "../../../../program-test", version = "=1.14.24" } +solana-sdk = { path = "../../../../sdk", version = "=1.14.24" } [lib] crate-type = ["cdylib", "lib"] diff --git a/programs/bpf/rust/upgradeable/Cargo.toml b/programs/bpf/rust/upgradeable/Cargo.toml index aed7d7e1e4a054..417274c532a336 100644 --- a/programs/bpf/rust/upgradeable/Cargo.toml +++ b/programs/bpf/rust/upgradeable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-upgradeable" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-upgradeable" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] name = "solana_bpf_rust_upgradeable" diff --git a/programs/bpf/rust/upgraded/Cargo.toml b/programs/bpf/rust/upgraded/Cargo.toml index 36b06fdcecb863..3918524f6fb108 100644 --- a/programs/bpf/rust/upgraded/Cargo.toml +++ b/programs/bpf/rust/upgraded/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-rust-upgraded" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ documentation = "https://docs.rs/solana-bpf-rust-upgraded" edition = "2021" [dependencies] -solana-program = { path = "../../../../sdk/program", version = "=1.11.6" } +solana-program = { path = "../../../../sdk/program", version = "=1.14.24" } [lib] name = "solana_bpf_rust_upgraded" diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index de399b590033d7..bf3b78845e01b5 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -18,6 +18,7 @@ use { solana_bpf_rust_invoke::instructions::*, solana_bpf_rust_realloc::instructions::*, solana_bpf_rust_realloc_invoke::instructions::*, + solana_ledger::token_balances::collect_token_balances, solana_program_runtime::{ compute_budget::ComputeBudget, invoke_context::with_mock_invoke_context, timings::ExecuteTimings, @@ -36,8 +37,9 @@ use { bank_client::BankClient, genesis_utils::{create_genesis_config, GenesisConfigInfo}, loader_utils::{ - load_buffer_account, load_program, load_upgradeable_program, set_upgrade_authority, - upgrade_program, + create_deprecated_program, load_and_finalize_deprecated_program, + load_program_from_file, load_upgradeable_buffer, load_upgradeable_program, + set_upgrade_authority, upgrade_program, }, }, solana_sdk::{ @@ -64,147 +66,14 @@ use { transaction::{SanitizedTransaction, Transaction, TransactionError, VersionedTransaction}, }, solana_transaction_status::{ - token_balances::collect_token_balances, ConfirmedTransactionWithStatusMeta, - InnerInstructions, TransactionStatusMeta, TransactionWithStatusMeta, - VersionedTransactionWithStatusMeta, + ConfirmedTransactionWithStatusMeta, InnerInstructions, TransactionStatusMeta, + TransactionWithStatusMeta, VersionedTransactionWithStatusMeta, }, - std::{collections::HashMap, env, fs::File, io::Read, path::PathBuf, str::FromStr, sync::Arc}, + std::{collections::HashMap, str::FromStr, sync::Arc}, }; -/// BPF program file extension -const PLATFORM_FILE_EXTENSION_BPF: &str = "so"; - -/// Create a BPF program file name -fn create_bpf_path(name: &str) -> PathBuf { - let mut pathbuf = { - let current_exe = env::current_exe().unwrap(); - PathBuf::from(current_exe.parent().unwrap().parent().unwrap()) - }; - pathbuf.push("bpf/"); - pathbuf.push(name); - pathbuf.set_extension(PLATFORM_FILE_EXTENSION_BPF); - pathbuf -} - -fn load_bpf_program( - bank_client: &BankClient, - loader_id: &Pubkey, - payer_keypair: &Keypair, - name: &str, -) -> Pubkey { - let elf = read_bpf_program(name); - load_program(bank_client, payer_keypair, loader_id, elf) -} - -fn read_bpf_program(name: &str) -> Vec { - let path = create_bpf_path(name); - let mut file = File::open(&path).unwrap_or_else(|err| { - panic!("Failed to open {}: {}", path.display(), err); - }); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - - elf -} - -#[cfg(feature = "bpf_rust")] -fn write_bpf_program( - bank_client: &BankClient, - loader_id: &Pubkey, - payer_keypair: &Keypair, - program_keypair: &Keypair, - elf: &[u8], -) { - let chunk_size = 512; // Size of chunk just needs to fit into tx - let mut offset = 0; - for chunk in elf.chunks(chunk_size) { - let instruction = - loader_instruction::write(&program_keypair.pubkey(), loader_id, offset, chunk.to_vec()); - let message = Message::new(&[instruction], Some(&payer_keypair.pubkey())); - - bank_client - .send_and_confirm_message(&[payer_keypair, &program_keypair], message) - .unwrap(); - - offset += chunk_size as u32; - } -} - -fn load_upgradeable_bpf_program( - bank_client: &BankClient, - payer_keypair: &Keypair, - buffer_keypair: &Keypair, - executable_keypair: &Keypair, - authority_keypair: &Keypair, - name: &str, -) { - let path = create_bpf_path(name); - let mut file = File::open(&path).unwrap_or_else(|err| { - panic!("Failed to open {}: {}", path.display(), err); - }); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - load_upgradeable_program( - bank_client, - payer_keypair, - buffer_keypair, - executable_keypair, - authority_keypair, - elf, - ); -} - -fn load_upgradeable_buffer( - bank_client: &BankClient, - payer_keypair: &Keypair, - buffer_keypair: &Keypair, - buffer_authority_keypair: &Keypair, - name: &str, -) { - let path = create_bpf_path(name); - let mut file = File::open(&path).unwrap_or_else(|err| { - panic!("Failed to open {}: {}", path.display(), err); - }); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - load_buffer_account( - bank_client, - payer_keypair, - &buffer_keypair, - buffer_authority_keypair, - &elf, - ); -} - -fn upgrade_bpf_program( - bank_client: &BankClient, - payer_keypair: &Keypair, - buffer_keypair: &Keypair, - executable_pubkey: &Pubkey, - authority_keypair: &Keypair, - name: &str, -) { - load_upgradeable_buffer( - bank_client, - payer_keypair, - buffer_keypair, - authority_keypair, - name, - ); - upgrade_program( - bank_client, - payer_keypair, - executable_pubkey, - &buffer_keypair.pubkey(), - &authority_keypair, - &payer_keypair.pubkey(), - ); -} - fn run_program(name: &str) -> u64 { - let mut file = File::open(create_bpf_path(name)).unwrap(); - let mut data = vec![]; - file.read_to_end(&mut data).unwrap(); + let elf = load_program_from_file(name); let loader_id = bpf_loader::id(); with_mock_invoke_context(loader_id, 0, |invoke_context| { let (parameter_bytes, account_lengths) = serialize_parameters( @@ -225,7 +94,7 @@ fn run_program(name: &str) -> u64 { ..Config::default() }; let executable = Executable::::from_elf( - &data, + &elf, config, register_syscalls(invoke_context, true /* no sol_alloc_free */).unwrap(), ) @@ -440,6 +309,7 @@ fn execute_transactions( inner_instructions, durable_nonce_fee, return_data, + executed_units, .. } = details; @@ -482,6 +352,7 @@ fn execute_transactions( rewards: None, loaded_addresses: LoadedAddresses::default(), return_data, + compute_units_consumed: Some(executed_units), }; Ok(ConfirmedTransactionWithStatusMeta { @@ -567,7 +438,7 @@ fn test_program_bpf_sanity() { // Call user program let program_id = - load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.0); + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.0); let account_metas = vec![ AccountMeta::new(mint_keypair.pubkey(), true), AccountMeta::new(Keypair::new().pubkey(), false), @@ -618,7 +489,7 @@ fn test_program_bpf_loader_deprecated() { bank.add_builtin(&name, &id, entrypoint); let bank_client = BankClient::new(bank); - let program_id = load_bpf_program( + let program_id = create_deprecated_program( &bank_client, &bpf_loader_deprecated::id(), &mint_keypair, @@ -651,7 +522,7 @@ fn test_sol_alloc_free_no_longer_deployable() { bank.add_builtin(&name, &id, entrypoint); // Populate loader account with elf that depends on _sol_alloc_free syscall - let elf = read_bpf_program("solana_bpf_rust_deprecated_loader"); + let elf = load_program_from_file("solana_bpf_rust_deprecated_loader"); let mut program_account = AccountSharedData::new(1, elf.len(), &loader_address); program_account .data_as_mut_slice() @@ -743,7 +614,8 @@ fn test_program_bpf_duplicate_accounts() { bank.add_builtin(&name, &id, entrypoint); let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program); + let program_id = + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, program); let payee_account = AccountSharedData::new(10, 1, &program_id); let payee_pubkey = Pubkey::new_unique(); bank.store_account(&payee_pubkey, &payee_account); @@ -842,7 +714,8 @@ fn test_program_bpf_error_handling() { let (name, id, entrypoint) = solana_bpf_loader_program!(); bank.add_builtin(&name, &id, entrypoint); let bank_client = BankClient::new(bank); - let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program); + let program_id = + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, program); let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)]; let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas.clone()); @@ -946,7 +819,8 @@ fn test_return_data_and_log_data_syscall() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program); + let program_id = + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, program); bank.freeze(); @@ -1011,11 +885,11 @@ fn test_program_bpf_invoke_sanity() { let bank_client = BankClient::new_shared(&bank); let invoke_program_id = - load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.1); + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.1); let invoked_program_id = - load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.2); + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.2); let noop_program_id = - load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.3); + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, program.3); let argument_keypair = Keypair::new(); let account = AccountSharedData::new(42, 100, &invoke_program_id); @@ -1262,7 +1136,7 @@ fn test_program_bpf_invoke_sanity() { format!("Program log: invoke {program_lang} program"), "Program log: Test max account infos exceeded".into(), "skip".into(), // don't compare compute consumption logs - "Program failed to complete: Invoked an instruction with too many account info's (65 > 64)".into(), + "Program failed to complete: Invoked an instruction with too many account info's (129 > 128)".into(), format!("Program {invoke_program_id} failed: Program failed to complete"), ]), ); @@ -1409,13 +1283,13 @@ fn test_program_bpf_program_id_spoofing() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let malicious_swap_pubkey = load_bpf_program( + let malicious_swap_pubkey = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, "solana_bpf_rust_spoof1", ); - let malicious_system_pubkey = load_bpf_program( + let malicious_system_pubkey = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -1462,13 +1336,13 @@ fn test_program_bpf_caller_has_access_to_cpi_program() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let caller_pubkey = load_bpf_program( + let caller_pubkey = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, "solana_bpf_rust_caller_access", ); - let caller2_pubkey = load_bpf_program( + let caller2_pubkey = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -1502,7 +1376,7 @@ fn test_program_bpf_ro_modify() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let program_pubkey = load_bpf_program( + let program_pubkey = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -1557,7 +1431,7 @@ fn test_program_bpf_call_depth() { let (name, id, entrypoint) = solana_bpf_loader_program!(); bank.add_builtin(&name, &id, entrypoint); let bank_client = BankClient::new(bank); - let program_id = load_bpf_program( + let program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -1592,7 +1466,7 @@ fn test_program_bpf_compute_budget() { let (name, id, entrypoint) = solana_bpf_loader_program!(); bank.add_builtin(&name, &id, entrypoint); let bank_client = BankClient::new(bank); - let program_id = load_bpf_program( + let program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -1627,8 +1501,8 @@ fn assert_instruction_count() { ("noop++", 5), ("relative_call", 210), ("return_data", 980), - ("sanity", 2378), - ("sanity++", 2278), + ("sanity", 2379), + ("sanity++", 2279), ("secp256k1_recover", 25383), ("sha", 1355), ("struct_pass", 108), @@ -1638,21 +1512,21 @@ fn assert_instruction_count() { #[cfg(feature = "bpf_rust")] { programs.extend_from_slice(&[ - ("solana_bpf_rust_128bit", 584), - ("solana_bpf_rust_alloc", 4581), - ("solana_bpf_rust_custom_heap", 469), + ("solana_bpf_rust_128bit", 580), + ("solana_bpf_rust_alloc", 5060), + ("solana_bpf_rust_custom_heap", 509), ("solana_bpf_rust_dep_crate", 2), - ("solana_bpf_rust_external_spend", 338), + ("solana_bpf_rust_external_spend", 378), ("solana_bpf_rust_iter", 108), ("solana_bpf_rust_many_args", 1289), - ("solana_bpf_rust_mem", 2118), - ("solana_bpf_rust_membuiltins", 1539), - ("solana_bpf_rust_noop", 326), + ("solana_bpf_rust_mem", 2158), + ("solana_bpf_rust_membuiltins", 1541), + ("solana_bpf_rust_noop", 366), ("solana_bpf_rust_param_passing", 146), - ("solana_bpf_rust_rand", 429), - ("solana_bpf_rust_sanity", 52290), - ("solana_bpf_rust_secp256k1_recover", 25707), - ("solana_bpf_rust_sha", 25265), + ("solana_bpf_rust_rand", 469), + ("solana_bpf_rust_sanity", 52232), + ("solana_bpf_rust_secp256k1_recover", 91195), + ("solana_bpf_rust_sha", 24081), ]); } @@ -1693,7 +1567,7 @@ fn test_program_bpf_instruction_introspection() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let program_id = load_bpf_program( + let program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -1751,42 +1625,26 @@ fn test_program_bpf_test_use_latest_executor() { let (name, id, entrypoint) = solana_bpf_loader_program!(); bank.add_builtin(&name, &id, entrypoint); let bank_client = BankClient::new(bank); - let panic_id = load_bpf_program( + let panic_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, "solana_bpf_rust_panic", ); - let program_keypair = Keypair::new(); - // Write the panic program into the program account - let elf = read_bpf_program("solana_bpf_rust_panic"); - let message = Message::new( - &[system_instruction::create_account( - &mint_keypair.pubkey(), - &program_keypair.pubkey(), - 1, - elf.len() as u64 * 2, - &bpf_loader::id(), - )], - Some(&mint_keypair.pubkey()), - ); - assert!(bank_client - .send_and_confirm_message(&[&mint_keypair, &program_keypair], message) - .is_ok()); - write_bpf_program( + let (program_keypair, instruction) = load_and_finalize_deprecated_program( &bank_client, &bpf_loader::id(), + None, &mint_keypair, - &program_keypair, - &elf, + "solana_bpf_rust_panic", ); // Finalize the panic program, but fail the tx let message = Message::new( &[ - loader_instruction::finalize(&program_keypair.pubkey(), &bpf_loader::id()), + instruction, Instruction::new_with_bytes(panic_id, &[0], vec![]), ], Some(&mint_keypair.pubkey()), @@ -1796,159 +1654,19 @@ fn test_program_bpf_test_use_latest_executor() { .is_err()); // Write the noop program into the same program account - let elf = read_bpf_program("solana_bpf_rust_noop"); - write_bpf_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - &program_keypair, - &elf, - ); - - // Finalize the noop program - let message = Message::new( - &[loader_instruction::finalize( - &program_keypair.pubkey(), - &bpf_loader::id(), - )], - Some(&mint_keypair.pubkey()), - ); - assert!(bank_client - .send_and_confirm_message(&[&mint_keypair, &program_keypair], message) - .is_ok()); - - // Call the noop program, should get noop not panic - let message = Message::new( - &[Instruction::new_with_bytes( - program_keypair.pubkey(), - &[0], - vec![], - )], - Some(&mint_keypair.pubkey()), - ); - assert!(bank_client - .send_and_confirm_message(&[&mint_keypair], message) - .is_ok()); -} - -#[ignore] // Invoking BPF loaders from CPI not allowed -#[cfg(feature = "bpf_rust")] -#[test] -fn test_program_bpf_test_use_latest_executor2() { - solana_logger::setup(); - - let GenesisConfigInfo { - genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - let mut bank = Bank::new_for_tests(&genesis_config); - let (name, id, entrypoint) = solana_bpf_loader_program!(); - bank.add_builtin(&name, &id, entrypoint); - let bank_client = BankClient::new(bank); - let invoke_and_error = load_bpf_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - "solana_bpf_rust_invoke_and_error", - ); - let invoke_and_ok = load_bpf_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - "solana_bpf_rust_invoke_and_ok", - ); - - let program_keypair = Keypair::new(); - - // Write the panic program into the program account - let elf = read_bpf_program("solana_bpf_rust_panic"); - let message = Message::new( - &[system_instruction::create_account( - &mint_keypair.pubkey(), - &program_keypair.pubkey(), - 1, - elf.len() as u64 * 2, - &bpf_loader::id(), - )], - Some(&mint_keypair.pubkey()), - ); - assert!(bank_client - .send_and_confirm_message(&[&mint_keypair, &program_keypair], message) - .is_ok()); - write_bpf_program( + let (program_keypair, instruction) = load_and_finalize_deprecated_program( &bank_client, &bpf_loader::id(), + Some(program_keypair), &mint_keypair, - &program_keypair, - &elf, - ); - - // - invoke finalize and return error, swallow error - let mut instruction = - loader_instruction::finalize(&program_keypair.pubkey(), &bpf_loader::id()); - instruction.accounts.insert( - 0, - AccountMeta { - is_signer: false, - is_writable: false, - pubkey: instruction.program_id, - }, - ); - instruction.program_id = invoke_and_ok; - instruction.accounts.insert( - 0, - AccountMeta { - is_signer: false, - is_writable: false, - pubkey: invoke_and_error, - }, + "solana_bpf_rust_noop", ); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); - assert!(bank_client - .send_and_confirm_message(&[&mint_keypair, &program_keypair], message) - .is_ok()); - - // invoke program, verify not found - let message = Message::new( - &[Instruction::new_with_bytes( - program_keypair.pubkey(), - &[0], - vec![], - )], - Some(&mint_keypair.pubkey()), - ); - assert_eq!( - bank_client - .send_and_confirm_message(&[&mint_keypair], message) - .unwrap_err() - .unwrap(), - TransactionError::InvalidProgramForExecution - ); - - // Write the noop program into the same program account - let elf = read_bpf_program("solana_bpf_rust_noop"); - write_bpf_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - &program_keypair, - &elf, - ); - - // Finalize the noop program - let message = Message::new( - &[loader_instruction::finalize( - &program_keypair.pubkey(), - &bpf_loader::id(), - )], - Some(&mint_keypair.pubkey()), - ); - assert!(bank_client + bank_client .send_and_confirm_message(&[&mint_keypair, &program_keypair], message) - .is_ok()); + .unwrap(); - // Call the program, should get noop, not panic + // Call the noop program, should get noop not panic let message = Message::new( &[Instruction::new_with_bytes( program_keypair.pubkey(), @@ -1982,7 +1700,7 @@ fn test_program_bpf_upgrade() { let program_keypair = Keypair::new(); let program_id = program_keypair.pubkey(); let authority_keypair = Keypair::new(); - load_upgradeable_bpf_program( + load_upgradeable_program( &bank_client, &mint_keypair, &buffer_keypair, @@ -2003,7 +1721,7 @@ fn test_program_bpf_upgrade() { // Upgrade program let buffer_keypair = Keypair::new(); - upgrade_bpf_program( + upgrade_program( &bank_client, &mint_keypair, &buffer_keypair, @@ -2032,7 +1750,7 @@ fn test_program_bpf_upgrade() { // Upgrade back to the original program let buffer_keypair = Keypair::new(); - upgrade_bpf_program( + upgrade_program( &bank_client, &mint_keypair, &buffer_keypair, @@ -2052,7 +1770,7 @@ fn test_program_bpf_upgrade() { #[cfg(feature = "bpf_rust")] #[test] -fn test_program_bpf_upgrade_and_invoke_in_same_tx() { +fn test_program_bpf_invoke_in_same_tx_as_deployment() { solana_logger::setup(); let GenesisConfigInfo { @@ -2066,12 +1784,104 @@ fn test_program_bpf_upgrade_and_invoke_in_same_tx() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - // Deploy upgrade program + // Deploy upgradeable program let buffer_keypair = Keypair::new(); let program_keypair = Keypair::new(); let program_id = program_keypair.pubkey(); let authority_keypair = Keypair::new(); - load_upgradeable_bpf_program( + + // Deploy indirect invocation program + let indirect_program_keypair = Keypair::new(); + load_upgradeable_program( + &bank_client, + &mint_keypair, + &buffer_keypair, + &indirect_program_keypair, + &authority_keypair, + "solana_bpf_rust_invoke_and_return", + ); + + let invoke_instruction = + Instruction::new_with_bytes(program_id, &[0], vec![AccountMeta::new(clock::id(), false)]); + let indirect_invoke_instruction = Instruction::new_with_bytes( + indirect_program_keypair.pubkey(), + &[0], + vec![ + AccountMeta::new_readonly(program_id, false), + AccountMeta::new_readonly(clock::id(), false), + ], + ); + + // Prepare deployment + let program = load_upgradeable_buffer( + &bank_client, + &mint_keypair, + &buffer_keypair, + &authority_keypair, + "solana_bpf_rust_noop", + ); + let deployment_instructions = bpf_loader_upgradeable::deploy_with_max_program_len( + &mint_keypair.pubkey(), + &program_keypair.pubkey(), + &buffer_keypair.pubkey(), + &authority_keypair.pubkey(), + 1.max( + bank_client + .get_minimum_balance_for_rent_exemption( + bpf_loader_upgradeable::UpgradeableLoaderState::size_of_program(), + ) + .unwrap(), + ), + program.len() * 2, + ) + .unwrap(); + + // Deployment is invisible to top-level-instructions but visible to CPI instructions + for (invoke_instruction, expected) in [ + ( + invoke_instruction, + Err(TransactionError::ProgramAccountNotFound), + ), + (indirect_invoke_instruction, Ok(())), + ] { + let mut instructions = deployment_instructions.clone(); + instructions.push(invoke_instruction); + let tx = Transaction::new( + &[&mint_keypair, &program_keypair, &authority_keypair], + Message::new(&instructions, Some(&mint_keypair.pubkey())), + bank.last_blockhash(), + ); + let results = execute_transactions(&bank, vec![tx]); + if let Err(err) = expected { + assert_eq!(results[0].as_ref().unwrap_err(), &err); + } else { + assert!(results[0].is_ok()); + } + } +} + +#[test] +#[cfg(feature = "bpf_rust")] +fn test_program_bpf_invoke_in_same_tx_as_redeployment() { + solana_logger::setup(); + + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + let mut bank = Bank::new_for_tests(&genesis_config); + let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!(); + bank.add_builtin(&name, &id, entrypoint); + let bank = Arc::new(bank); + let bank_client = BankClient::new_shared(&bank); + + // Deploy upgradeable program + let buffer_keypair = Keypair::new(); + let program_keypair = Keypair::new(); + let program_id = program_keypair.pubkey(); + let authority_keypair = Keypair::new(); + load_upgradeable_program( &bank_client, &mint_keypair, &buffer_keypair, @@ -2080,48 +1890,172 @@ fn test_program_bpf_upgrade_and_invoke_in_same_tx() { "solana_bpf_rust_noop", ); + // Deploy indirect invocation program + let indirect_program_keypair = Keypair::new(); + load_upgradeable_program( + &bank_client, + &mint_keypair, + &buffer_keypair, + &indirect_program_keypair, + &authority_keypair, + "solana_bpf_rust_invoke_and_return", + ); + let invoke_instruction = Instruction::new_with_bytes(program_id, &[0], vec![AccountMeta::new(clock::id(), false)]); + let indirect_invoke_instruction = Instruction::new_with_bytes( + indirect_program_keypair.pubkey(), + &[0], + vec![ + AccountMeta::new_readonly(program_id, false), + AccountMeta::new_readonly(clock::id(), false), + ], + ); - // Call upgradeable program - let result = - bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone()); - assert!(result.is_ok()); + // Prepare redeployment + load_upgradeable_buffer( + &bank_client, + &mint_keypair, + &buffer_keypair, + &authority_keypair, + "solana_bpf_rust_panic", + ); + let redeployment_instruction = bpf_loader_upgradeable::upgrade( + &program_id, + &buffer_keypair.pubkey(), + &authority_keypair.pubkey(), + &mint_keypair.pubkey(), + ); + + // Redeployment is visible to both top-level-instructions and CPI instructions + for invoke_instruction in [invoke_instruction, indirect_invoke_instruction] { + // Call upgradeable program + let result = + bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone()); + assert!(result.is_ok()); + + // Upgrade the program and invoke in same tx + let message = Message::new( + &[redeployment_instruction.clone(), invoke_instruction], + Some(&mint_keypair.pubkey()), + ); + let tx = Transaction::new( + &[&mint_keypair, &authority_keypair], + message.clone(), + bank.last_blockhash(), + ); + let (result, _, _) = process_transaction_and_record_inner(&bank, tx); + assert_eq!( + result.unwrap_err(), + TransactionError::InstructionError(1, InstructionError::ProgramFailedToComplete), + ); + } +} - // Prepare for upgrade +#[test] +#[cfg(feature = "bpf_rust")] +fn test_program_bpf_invoke_in_same_tx_as_undeployment() { + solana_logger::setup(); + + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(50); + let mut bank = Bank::new_for_tests(&genesis_config); + let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!(); + bank.add_builtin(&name, &id, entrypoint); + let bank = Arc::new(bank); + let bank_client = BankClient::new_shared(&bank); + + // Deploy upgradeable program let buffer_keypair = Keypair::new(); - load_upgradeable_buffer( + let program_keypair = Keypair::new(); + let program_id = program_keypair.pubkey(); + let authority_keypair = Keypair::new(); + load_upgradeable_program( &bank_client, &mint_keypair, &buffer_keypair, + &program_keypair, + &authority_keypair, + "solana_bpf_rust_noop", + ); + + // Deploy indirect invocation program + let indirect_program_keypair = Keypair::new(); + load_upgradeable_program( + &bank_client, + &mint_keypair, + &buffer_keypair, + &indirect_program_keypair, + &authority_keypair, + "solana_bpf_rust_invoke_and_return", + ); + + // Deploy panic program + let panic_program_keypair = Keypair::new(); + load_upgradeable_program( + &bank_client, + &mint_keypair, + &buffer_keypair, + &panic_program_keypair, &authority_keypair, "solana_bpf_rust_panic", ); - // Invoke, then upgrade the program, and then invoke again in same tx - let message = Message::new( - &[ - invoke_instruction.clone(), - bpf_loader_upgradeable::upgrade( - &program_id, - &buffer_keypair.pubkey(), - &authority_keypair.pubkey(), - &mint_keypair.pubkey(), - ), - invoke_instruction, + let invoke_instruction = + Instruction::new_with_bytes(program_id, &[0], vec![AccountMeta::new(clock::id(), false)]); + let indirect_invoke_instruction = Instruction::new_with_bytes( + indirect_program_keypair.pubkey(), + &[0], + vec![ + AccountMeta::new_readonly(program_id, false), + AccountMeta::new_readonly(clock::id(), false), ], - Some(&mint_keypair.pubkey()), ); - let tx = Transaction::new( - &[&mint_keypair, &authority_keypair], - message.clone(), - bank.last_blockhash(), + let panic_instruction = + Instruction::new_with_bytes(panic_program_keypair.pubkey(), &[], vec![]); + + // Prepare undeployment + let (programdata_address, _) = Pubkey::find_program_address( + &[program_keypair.pubkey().as_ref()], + &bpf_loader_upgradeable::id(), ); - let (result, _, _) = process_transaction_and_record_inner(&bank, tx); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete) + let undeployment_instruction = bpf_loader_upgradeable::close_any( + &programdata_address, + &mint_keypair.pubkey(), + Some(&authority_keypair.pubkey()), + Some(&program_id), ); + + // Undeployment is invisible to both top-level-instructions and CPI instructions + for invoke_instruction in [invoke_instruction, indirect_invoke_instruction] { + // Call upgradeable program + let result = + bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone()); + assert!(result.is_ok()); + + // Upgrade the program and invoke in same tx + let message = Message::new( + &[ + undeployment_instruction.clone(), + invoke_instruction, + panic_instruction.clone(), // Make sure the TX fails, so we don't have to deploy another program + ], + Some(&mint_keypair.pubkey()), + ); + let tx = Transaction::new( + &[&mint_keypair, &authority_keypair], + message.clone(), + bank.last_blockhash(), + ); + let (result, _, _) = process_transaction_and_record_inner(&bank, tx); + assert_eq!( + result.unwrap_err(), + TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete), + ); + } } #[cfg(feature = "bpf_rust")] @@ -2140,7 +2074,7 @@ fn test_program_bpf_invoke_upgradeable_via_cpi() { let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!(); bank.add_builtin(&name, &id, entrypoint); let bank_client = BankClient::new(bank); - let invoke_and_return = load_bpf_program( + let invoke_and_return = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -2152,7 +2086,7 @@ fn test_program_bpf_invoke_upgradeable_via_cpi() { let program_keypair = Keypair::new(); let program_id = program_keypair.pubkey(); let authority_keypair = Keypair::new(); - load_upgradeable_bpf_program( + load_upgradeable_program( &bank_client, &mint_keypair, &buffer_keypair, @@ -2180,7 +2114,7 @@ fn test_program_bpf_invoke_upgradeable_via_cpi() { // Upgrade program let buffer_keypair = Keypair::new(); - upgrade_bpf_program( + upgrade_program( &bank_client, &mint_keypair, &buffer_keypair, @@ -2209,7 +2143,7 @@ fn test_program_bpf_invoke_upgradeable_via_cpi() { // Upgrade back to the original program let buffer_keypair = Keypair::new(); - upgrade_bpf_program( + upgrade_program( &bank_client, &mint_keypair, &buffer_keypair, @@ -2253,7 +2187,8 @@ fn test_program_bpf_disguised_as_bpf_loader() { bank.add_builtin(&name, &id, entrypoint); let bank_client = BankClient::new(bank); - let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, program); + let program_id = + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, program); let account_metas = vec![AccountMeta::new_readonly(program_id, false)]; let instruction = Instruction::new_with_bytes(bpf_loader::id(), &[1], account_metas); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); @@ -2284,7 +2219,8 @@ fn test_program_bpf_c_dup() { let bank_client = BankClient::new(bank); - let program_id = load_bpf_program(&bank_client, &bpf_loader::id(), &mint_keypair, "ser"); + let program_id = + create_deprecated_program(&bank_client, &bpf_loader::id(), &mint_keypair, "ser"); let account_metas = vec![ AccountMeta::new_readonly(account_address, false), AccountMeta::new_readonly(account_address, false), @@ -2311,7 +2247,7 @@ fn test_program_bpf_upgrade_via_cpi() { let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!(); bank.add_builtin(&name, &id, entrypoint); let bank_client = BankClient::new(bank); - let invoke_and_return = load_bpf_program( + let invoke_and_return = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -2323,7 +2259,7 @@ fn test_program_bpf_upgrade_via_cpi() { let program_keypair = Keypair::new(); let program_id = program_keypair.pubkey(); let authority_keypair = Keypair::new(); - load_upgradeable_bpf_program( + load_upgradeable_program( &bank_client, &mint_keypair, &buffer_keypair, @@ -2361,19 +2297,13 @@ fn test_program_bpf_upgrade_via_cpi() { ); // Load the buffer account - let path = create_bpf_path("solana_bpf_rust_upgraded"); - let mut file = File::open(&path).unwrap_or_else(|err| { - panic!("Failed to open {}: {}", path.display(), err); - }); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); let buffer_keypair = Keypair::new(); - load_buffer_account( + load_upgradeable_buffer( &bank_client, &mint_keypair, &buffer_keypair, &authority_keypair, - &elf, + "solana_bpf_rust_upgraded", ); // Upgrade program via CPI @@ -2408,95 +2338,6 @@ fn test_program_bpf_upgrade_via_cpi() { assert_ne!(programdata, original_programdata); } -#[cfg(feature = "bpf_rust")] -#[test] -fn test_program_bpf_upgrade_self_via_cpi() { - solana_logger::setup(); - - let GenesisConfigInfo { - genesis_config, - mint_keypair, - .. - } = create_genesis_config(50); - let mut bank = Bank::new_for_tests(&genesis_config); - let (name, id, entrypoint) = solana_bpf_loader_program!(); - bank.add_builtin(&name, &id, entrypoint); - let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!(); - bank.add_builtin(&name, &id, entrypoint); - let bank = Arc::new(bank); - let bank_client = BankClient::new_shared(&bank); - let noop_program_id = load_bpf_program( - &bank_client, - &bpf_loader::id(), - &mint_keypair, - "solana_bpf_rust_noop", - ); - - // Deploy upgradeable program - let buffer_keypair = Keypair::new(); - let program_keypair = Keypair::new(); - let program_id = program_keypair.pubkey(); - let authority_keypair = Keypair::new(); - load_upgradeable_bpf_program( - &bank_client, - &mint_keypair, - &buffer_keypair, - &program_keypair, - &authority_keypair, - "solana_bpf_rust_invoke_and_return", - ); - - let mut invoke_instruction = Instruction::new_with_bytes( - program_id, - &[0], - vec![ - AccountMeta::new_readonly(noop_program_id, false), - AccountMeta::new_readonly(clock::id(), false), - ], - ); - - // Call the upgraded program - invoke_instruction.data[0] += 1; - let result = - bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone()); - assert!(result.is_ok()); - - // Prepare for upgrade - let buffer_keypair = Keypair::new(); - load_upgradeable_buffer( - &bank_client, - &mint_keypair, - &buffer_keypair, - &authority_keypair, - "solana_bpf_rust_panic", - ); - - // Invoke, then upgrade the program, and then invoke again in same tx - let message = Message::new( - &[ - invoke_instruction.clone(), - bpf_loader_upgradeable::upgrade( - &program_id, - &buffer_keypair.pubkey(), - &authority_keypair.pubkey(), - &mint_keypair.pubkey(), - ), - invoke_instruction, - ], - Some(&mint_keypair.pubkey()), - ); - let tx = Transaction::new( - &[&mint_keypair, &authority_keypair], - message.clone(), - bank.last_blockhash(), - ); - let (result, _, _) = process_transaction_and_record_inner(&bank, tx); - assert_eq!( - result.unwrap_err(), - TransactionError::InstructionError(2, InstructionError::ProgramFailedToComplete) - ); -} - #[cfg(feature = "bpf_rust")] #[test] fn test_program_bpf_set_upgrade_authority_via_cpi() { @@ -2515,7 +2356,7 @@ fn test_program_bpf_set_upgrade_authority_via_cpi() { let bank_client = BankClient::new(bank); // Deploy CPI invoker program - let invoke_and_return = load_bpf_program( + let invoke_and_return = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -2527,7 +2368,7 @@ fn test_program_bpf_set_upgrade_authority_via_cpi() { let program_keypair = Keypair::new(); let program_id = program_keypair.pubkey(); let authority_keypair = Keypair::new(); - load_upgradeable_bpf_program( + load_upgradeable_program( &bank_client, &mint_keypair, &buffer_keypair, @@ -2606,7 +2447,7 @@ fn test_program_upgradeable_locks() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - load_upgradeable_bpf_program( + load_upgradeable_program( &bank_client, &mint_keypair, buffer_keypair, @@ -2616,18 +2457,12 @@ fn test_program_upgradeable_locks() { ); // Load the buffer account - let path = create_bpf_path("solana_bpf_rust_noop"); - let mut file = File::open(&path).unwrap_or_else(|err| { - panic!("Failed to open {}: {}", path.display(), err); - }); - let mut elf = Vec::new(); - file.read_to_end(&mut elf).unwrap(); - load_buffer_account( + load_upgradeable_buffer( &bank_client, &mint_keypair, buffer_keypair, &payer_keypair, - &elf, + "solana_bpf_rust_noop", ); bank_client @@ -2733,46 +2568,30 @@ fn test_program_bpf_finalize() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let program_pubkey = load_bpf_program( + let program_pubkey = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, "solana_bpf_rust_finalize", ); - let noop_keypair = Keypair::new(); - // Write the noop program into the same program account - let elf = read_bpf_program("solana_bpf_rust_noop"); - let message = Message::new( - &[system_instruction::create_account( - &mint_keypair.pubkey(), - &noop_keypair.pubkey(), - 1, - elf.len() as u64 * 2, - &bpf_loader::id(), - )], - Some(&mint_keypair.pubkey()), - ); - assert!(bank_client - .send_and_confirm_message(&[&mint_keypair, &noop_keypair], message) - .is_ok()); - write_bpf_program( + let (program_keypair, _instruction) = load_and_finalize_deprecated_program( &bank_client, &bpf_loader::id(), + None, &mint_keypair, - &noop_keypair, - &elf, + "solana_bpf_rust_noop", ); let account_metas = vec![ - AccountMeta::new(noop_keypair.pubkey(), true), + AccountMeta::new(program_keypair.pubkey(), true), AccountMeta::new_readonly(bpf_loader::id(), false), AccountMeta::new(rent::id(), false), ]; let instruction = Instruction::new_with_bytes(program_pubkey, &[], account_metas.clone()); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); - let result = bank_client.send_and_confirm_message(&[&mint_keypair, &noop_keypair], message); + let result = bank_client.send_and_confirm_message(&[&mint_keypair, &program_keypair], message); assert_eq!( result.unwrap_err().unwrap(), TransactionError::InstructionError(0, InstructionError::ProgramFailedToComplete) @@ -2795,7 +2614,7 @@ fn test_program_bpf_ro_account_modify() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let program_id = load_bpf_program( + let program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -2862,7 +2681,7 @@ fn test_program_bpf_realloc() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let program_id = load_bpf_program( + let program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -3159,14 +2978,14 @@ fn test_program_bpf_realloc_invoke() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let realloc_program_id = load_bpf_program( + let realloc_program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, "solana_bpf_rust_realloc", ); - let realloc_invoke_program_id = load_bpf_program( + let realloc_invoke_program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -3677,25 +3496,25 @@ fn test_program_bpf_processed_inner_instruction() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let sibling_program_id = load_bpf_program( + let sibling_program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, "solana_bpf_rust_sibling_instructions", ); - let sibling_inner_program_id = load_bpf_program( + let sibling_inner_program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, "solana_bpf_rust_sibling_inner_instructions", ); - let noop_program_id = load_bpf_program( + let noop_program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, "solana_bpf_rust_noop", ); - let invoke_and_return_program_id = load_bpf_program( + let invoke_and_return_program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -3760,7 +3579,7 @@ fn test_program_fees() { bank.add_builtin(&name, &id, entrypoint); let bank_client = BankClient::new(bank); - let program_id = load_bpf_program( + let program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -3780,6 +3599,7 @@ fn test_program_fees() { &fee_structure, true, true, + true, ); bank_client .send_and_confirm_message(&[&mint_keypair], message) @@ -3802,6 +3622,7 @@ fn test_program_fees() { &fee_structure, true, true, + true, ); assert!(expected_normal_fee < expected_prioritized_fee); @@ -3828,7 +3649,7 @@ fn test_get_minimum_delegation() { let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); - let program_id = load_bpf_program( + let program_id = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -3863,7 +3684,7 @@ fn test_program_bpf_inner_instruction_alignment_checks() { let bank_client = BankClient::new(bank); // load aligned program - let noop = load_bpf_program( + let noop = create_deprecated_program( &bank_client, &bpf_loader::id(), &mint_keypair, @@ -3871,7 +3692,7 @@ fn test_program_bpf_inner_instruction_alignment_checks() { ); // Load unaligned program - let inner_instruction_alignment_check = load_bpf_program( + let inner_instruction_alignment_check = create_deprecated_program( &bank_client, &bpf_loader_deprecated::id(), &mint_keypair, diff --git a/programs/bpf_loader/Cargo.toml b/programs/bpf_loader/Cargo.toml index 652719b3406e71..45b79811fd3872 100644 --- a/programs/bpf_loader/Cargo.toml +++ b/programs/bpf_loader/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-bpf-loader-program" -version = "1.11.6" +version = "1.14.24" description = "Solana BPF loader" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,17 +14,17 @@ bincode = "1.3.3" byteorder = "1.4.3" libsecp256k1 = "0.6.0" log = "0.4.17" -solana-measure = { path = "../../measure", version = "=1.11.6" } -solana-metrics = { path = "../../metrics", version = "=1.11.6" } -solana-program-runtime = { path = "../../program-runtime", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } -solana-zk-token-sdk = { path = "../../zk-token-sdk", version = "=1.11.6" } +solana-measure = { path = "../../measure", version = "=1.14.24" } +solana-metrics = { path = "../../metrics", version = "=1.14.24" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } +solana-zk-token-sdk = { path = "../../zk-token-sdk", version = "=1.14.24" } solana_rbpf = "=0.2.31" thiserror = "1.0" [dev-dependencies] rand = "0.7.3" -solana-runtime = { path = "../../runtime", version = "=1.11.6" } +solana-runtime = { path = "../../runtime", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/programs/bpf_loader/gen-syscall-list/Cargo.toml b/programs/bpf_loader/gen-syscall-list/Cargo.toml index 08162a6e8e920c..c73d04eaf55cf2 100644 --- a/programs/bpf_loader/gen-syscall-list/Cargo.toml +++ b/programs/bpf_loader/gen-syscall-list/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gen-syscall-list" -version = "1.11.6" +version = "1.14.24" edition = "2021" license = "Apache-2.0" publish = false diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 741f5ea5533d37..bcf7776cc2f6ac 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -20,8 +20,9 @@ use { log::{log_enabled, trace, Level::Trace}, solana_measure::measure::Measure, solana_program_runtime::{ + executor_cache::Executor, ic_logger_msg, ic_msg, - invoke_context::{ComputeMeter, Executor, InvokeContext}, + invoke_context::{ComputeMeter, InvokeContext}, log_collector::LogCollector, stable_log, sysvar_cache::get_sysvar_with_account_check, @@ -44,8 +45,8 @@ use { cap_accounts_data_len, cap_bpf_program_instruction_accounts, disable_bpf_deprecated_load_instructions, disable_bpf_unresolved_symbols_at_runtime, disable_deploy_of_alloc_free_syscall, disable_deprecated_loader, - enable_bpf_loader_extend_program_data_ix, - error_on_syscall_bpf_function_hash_collisions, reject_callx_r10, + enable_bpf_loader_extend_program_ix, error_on_syscall_bpf_function_hash_collisions, + reject_callx_r10, }, instruction::{AccountMeta, InstructionError}, loader_instruction::LoaderInstruction, @@ -588,7 +589,6 @@ fn process_loader_upgradeable_instruction( return Err(InstructionError::InvalidArgument); } let buffer_key = *buffer.get_key(); - let buffer_lamports = buffer.get_lamports(); let buffer_data_offset = UpgradeableLoaderState::size_of_buffer_metadata(); let buffer_data_len = buffer.get_data().len().saturating_sub(buffer_data_offset); let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata(); @@ -623,12 +623,11 @@ fn process_loader_upgradeable_instruction( // Drain the Buffer account to payer before paying for programdata account { - let mut payer = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - payer.checked_add_lamports(buffer_lamports)?; - drop(payer); let mut buffer = instruction_context.try_borrow_instruction_account(transaction_context, 3)?; + let mut payer = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + payer.checked_add_lamports(buffer.get_lamports())?; buffer.set_lamports(0)?; } @@ -870,23 +869,18 @@ fn process_loader_upgradeable_instruction( .fill(0); // Fund ProgramData to rent-exemption, spill the rest - - let programdata_lamports = programdata.get_lamports(); - programdata.set_lamports(programdata_balance_required)?; - drop(programdata); - let mut buffer = instruction_context.try_borrow_instruction_account(transaction_context, 2)?; - buffer.set_lamports(0)?; - drop(buffer); - let mut spill = instruction_context.try_borrow_instruction_account(transaction_context, 3)?; spill.checked_add_lamports( - programdata_lamports + programdata + .get_lamports() .saturating_add(buffer_lamports) .saturating_sub(programdata_balance_required), )?; + buffer.set_lamports(0)?; + programdata.set_lamports(programdata_balance_required)?; ic_logger_msg!(log_collector, "Upgraded program {:?}", new_program_id); } @@ -971,12 +965,10 @@ fn process_loader_upgradeable_instruction( let close_key = *close_account.get_key(); match close_account.get_state()? { UpgradeableLoaderState::Uninitialized => { - let close_lamports = close_account.get_lamports(); - close_account.set_lamports(0)?; - drop(close_account); let mut recipient_account = instruction_context .try_borrow_instruction_account(transaction_context, 1)?; - recipient_account.checked_add_lamports(close_lamports)?; + recipient_account.checked_add_lamports(close_account.get_lamports())?; + close_account.set_lamports(0)?; ic_logger_msg!(log_collector, "Closed Uninitialized {}", close_key); } @@ -1045,10 +1037,10 @@ fn process_loader_upgradeable_instruction( } } } - UpgradeableLoaderInstruction::ExtendProgramData { additional_bytes } => { + UpgradeableLoaderInstruction::ExtendProgram { additional_bytes } => { if !invoke_context .feature_set - .is_active(&enable_bpf_loader_extend_program_data_ix::ID) + .is_active(&enable_bpf_loader_extend_program_ix::ID) { return Err(InstructionError::InvalidInstructionData); } @@ -1059,10 +1051,11 @@ fn process_loader_upgradeable_instruction( } const PROGRAM_DATA_ACCOUNT_INDEX: usize = 0; + const PROGRAM_ACCOUNT_INDEX: usize = 1; #[allow(dead_code)] // System program is only required when a CPI is performed - const OPTIONAL_SYSTEM_PROGRAM_ACCOUNT_INDEX: usize = 1; - const OPTIONAL_PAYER_ACCOUNT_INDEX: usize = 2; + const OPTIONAL_SYSTEM_PROGRAM_ACCOUNT_INDEX: usize = 2; + const OPTIONAL_PAYER_ACCOUNT_INDEX: usize = 3; let programdata_account = instruction_context .try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?; @@ -1077,6 +1070,35 @@ fn process_loader_upgradeable_instruction( return Err(InstructionError::InvalidArgument); } + let program_account = instruction_context + .try_borrow_instruction_account(transaction_context, PROGRAM_ACCOUNT_INDEX)?; + if !program_account.is_writable() { + ic_logger_msg!(log_collector, "Program account is not writable"); + return Err(InstructionError::InvalidArgument); + } + if program_account.get_owner() != program_id { + ic_logger_msg!(log_collector, "Program account not owned by loader"); + return Err(InstructionError::InvalidAccountOwner); + } + match program_account.get_state()? { + UpgradeableLoaderState::Program { + programdata_address, + } => { + if programdata_address != programdata_key { + ic_logger_msg!( + log_collector, + "Program account does not match ProgramData account" + ); + return Err(InstructionError::InvalidArgument); + } + } + _ => { + ic_logger_msg!(log_collector, "Invalid Program account"); + return Err(InstructionError::InvalidAccountData); + } + } + drop(program_account); + let old_len = programdata_account.get_data().len(); let new_len = old_len.saturating_add(additional_bytes as usize); if new_len > MAX_PERMITTED_DATA_LENGTH as usize { diff --git a/programs/bpf_loader/src/syscalls/cpi.rs b/programs/bpf_loader/src/syscalls/cpi.rs index f569812565d3e3..bf65d47d0402d3 100644 --- a/programs/bpf_loader/src/syscalls/cpi.rs +++ b/programs/bpf_loader/src/syscalls/cpi.rs @@ -13,6 +13,7 @@ struct CallerAccount<'a> { data: &'a mut [u8], vm_data_addr: u64, ref_to_len_in_vm: &'a mut u64, + serialized_len_ptr: *mut u64, executable: bool, rent_epoch: u64, } @@ -173,7 +174,7 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { invoke_context.get_check_aligned(), )?; - let (data, vm_data_addr, ref_to_len_in_vm) = { + let (data, vm_data_addr, ref_to_len_in_vm, serialized_len_ptr) = { // Double translate data out of RefCell let data = *translate_type::<&[u8]>( memory_mapping, @@ -194,6 +195,20 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { 8, )? as *mut u64; let ref_to_len_in_vm = unsafe { &mut *translated }; + let serialized_len_ptr = if invoke_context + .feature_set + .is_active(&feature_set::move_serialized_len_ptr_in_cpi::id()) + { + std::ptr::null_mut() + } else { + let ref_of_len_in_input_buffer = + (data.as_ptr() as *const _ as u64).saturating_sub(8); + translate_type_mut::( + memory_mapping, + ref_of_len_in_input_buffer, + invoke_context.get_check_aligned(), + )? + }; let vm_data_addr = data.as_ptr() as u64; ( translate_slice_mut::( @@ -205,6 +220,7 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { )?, vm_data_addr, ref_to_len_in_vm, + serialized_len_ptr, ) }; @@ -215,6 +231,7 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedRust<'a, 'b> { data, vm_data_addr, ref_to_len_in_vm, + serialized_len_ptr, executable: account_info.executable, rent_epoch: account_info.rent_epoch, }) @@ -524,6 +541,21 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedC<'a, 'b> { )?; let ref_to_len_in_vm = unsafe { &mut *(addr as *mut u64) }; + let ref_of_len_in_input_buffer = + (account_info.data_addr as *mut u8 as u64).saturating_sub(8); + let serialized_len_ptr = if invoke_context + .feature_set + .is_active(&feature_set::move_serialized_len_ptr_in_cpi::id()) + { + std::ptr::null_mut() + } else { + translate_type_mut::( + memory_mapping, + ref_of_len_in_input_buffer, + invoke_context.get_check_aligned(), + )? + }; + Ok(CallerAccount { lamports, owner, @@ -531,6 +563,7 @@ impl<'a, 'b> SyscallInvokeSigned<'a, 'b> for SyscallInvokeSignedC<'a, 'b> { data, vm_data_addr, ref_to_len_in_vm, + serialized_len_ptr, executable: account_info.executable, rent_epoch: account_info.rent_epoch, }) @@ -792,8 +825,16 @@ fn check_account_infos( .feature_set .is_active(&feature_set::loosen_cpi_size_restriction::id()) { + let max_cpi_account_infos = if invoke_context + .feature_set + .is_active(&feature_set::increase_tx_account_lock_limit::id()) + { + MAX_CPI_ACCOUNT_INFOS + } else { + 64 + }; let num_account_infos = num_account_infos as u64; - let max_account_infos = MAX_CPI_ACCOUNT_INFOS as u64; + let max_account_infos = max_cpi_account_infos as u64; if num_account_infos > max_account_infos { return Err(SyscallError::MaxInstructionAccountInfosExceeded { num_account_infos, @@ -959,14 +1000,23 @@ fn call<'a, 'b: 'a>( invoke_context.get_check_size(), )?; *caller_account.ref_to_len_in_vm = new_len as u64; - let serialized_len_ptr = translate_type_mut::( - memory_mapping, - caller_account - .vm_data_addr - .saturating_sub(std::mem::size_of::() as u64), - invoke_context.get_check_aligned(), - )?; - *serialized_len_ptr = new_len as u64; + if invoke_context + .feature_set + .is_active(&feature_set::move_serialized_len_ptr_in_cpi::id()) + { + let serialized_len_ptr = translate_type_mut::( + memory_mapping, + caller_account + .vm_data_addr + .saturating_sub(std::mem::size_of::() as u64), + invoke_context.get_check_aligned(), + )?; + *serialized_len_ptr = new_len as u64; + } else { + unsafe { + *caller_account.serialized_len_ptr = new_len as u64; + } + } } let to_slice = &mut caller_account.data; let from_slice = callee_account diff --git a/programs/bpf_loader/src/syscalls/mem_ops.rs b/programs/bpf_loader/src/syscalls/mem_ops.rs index 2fb5cd064aad2d..1b18df3157c229 100644 --- a/programs/bpf_loader/src/syscalls/mem_ops.rs +++ b/programs/bpf_loader/src/syscalls/mem_ops.rs @@ -36,7 +36,7 @@ declare_syscall!( .feature_set .is_active(&check_physical_overlapping::id()); - if !is_nonoverlapping(src_addr, dst_addr, n) { + if !is_nonoverlapping(src_addr, n, dst_addr, n) { *result = Err(SyscallError::CopyOverlapping.into()); return; } @@ -64,7 +64,7 @@ declare_syscall!( ) .as_ptr(); if do_check_physical_overlapping - && !is_nonoverlapping(src_ptr as usize, dst_ptr as usize, n as usize) + && !is_nonoverlapping(src_ptr as usize, n as usize, dst_ptr as usize, n as usize) { unsafe { std::ptr::copy(src_ptr, dst_ptr, n as usize); diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index 2c33dd96d1725c..a012813bbbba5c 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -1,16 +1,16 @@ +pub use self::{ + cpi::{SyscallInvokeSignedC, SyscallInvokeSignedRust}, + logging::{ + SyscallLog, SyscallLogBpfComputeUnits, SyscallLogData, SyscallLogPubkey, SyscallLogU64, + }, + mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset}, + sysvar::{ + SyscallGetClockSysvar, SyscallGetEpochScheduleSysvar, SyscallGetFeesSysvar, + SyscallGetRentSysvar, + }, +}; #[allow(deprecated)] use { - self::{ - cpi::{SyscallInvokeSignedC, SyscallInvokeSignedRust}, - logging::{ - SyscallLog, SyscallLogBpfComputeUnits, SyscallLogData, SyscallLogPubkey, SyscallLogU64, - }, - mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset}, - sysvar::{ - SyscallGetClockSysvar, SyscallGetEpochScheduleSysvar, SyscallGetFeesSysvar, - SyscallGetRentSysvar, - }, - }, crate::{allocator_bump::BpfAllocator, BpfError}, solana_program_runtime::{ ic_logger_msg, ic_msg, @@ -34,7 +34,7 @@ use { entrypoint::{BPF_ALIGN_OF_U128, MAX_PERMITTED_DATA_INCREASE, SUCCESS}, feature_set::{ self, blake3_syscall_enabled, check_physical_overlapping, check_slice_translation_size, - curve25519_syscall_enabled, disable_fees_sysvar, + check_syscall_outputs_do_not_overlap, curve25519_syscall_enabled, disable_fees_sysvar, enable_early_verification_of_account_modifications, libsecp256k1_0_5_upgrade_enabled, limit_secp256k1_recovery_id, prevent_calling_precompiles_as_programs, syscall_saturated_math, @@ -238,9 +238,7 @@ pub fn register_syscalls( SyscallBlake3::call, )?; - // Elliptic Curve Point Validation - // - // TODO: add group operations and multiscalar multiplications + // Elliptic Curve Operations register_feature_gated_syscall!( syscall_registry, curve25519_syscall_enabled, @@ -255,6 +253,13 @@ pub fn register_syscalls( SyscallCurveGroupOps::init, SyscallCurveGroupOps::call, )?; + register_feature_gated_syscall!( + syscall_registry, + curve25519_syscall_enabled, + b"sol_curve_multiscalar_mul", + SyscallCurveMultiscalarMultiplication::init, + SyscallCurveMultiscalarMultiplication::call, + )?; // Sysvars syscall_registry.register_syscall_by_name( @@ -445,7 +450,7 @@ fn translate_slice_inner<'a, T>( } let total_size = len.saturating_mul(size_of::() as u64); - if check_size & isize::try_from(total_size).is_err() { + if check_size && isize::try_from(total_size).is_err() { return Err(SyscallError::InvalidLength.into()); } @@ -812,6 +817,18 @@ declare_syscall!( ), result ); + if !is_nonoverlapping( + bump_seed_ref as *const _ as usize, + std::mem::size_of_val(bump_seed_ref), + address.as_ptr() as usize, + std::mem::size_of::(), + ) && invoke_context + .feature_set + .is_active(&check_syscall_outputs_do_not_overlap::id()) + { + *result = Err(SyscallError::CopyOverlapping.into()); + return; + } *bump_seed_ref = bump_seed[0]; address.copy_from_slice(new_address.as_ref()); *result = Ok(0); @@ -1239,7 +1256,8 @@ declare_syscall!( ), result ) = result_point; - *result = Ok(0); + } else { + *result = Ok(1); } } SUB => { @@ -1274,7 +1292,8 @@ declare_syscall!( ), result ) = result_point; - *result = Ok(0); + } else { + *result = Ok(1); } } MUL => { @@ -1309,7 +1328,8 @@ declare_syscall!( ), result ) = result_point; - *result = Ok(0); + } else { + *result = Ok(1); } } _ => { @@ -1350,7 +1370,8 @@ declare_syscall!( ), result ) = result_point; - *result = Ok(0); + } else { + *result = Ok(1); } } SUB => { @@ -1387,7 +1408,8 @@ declare_syscall!( ), result ) = result_point; - *result = Ok(0); + } else { + *result = Ok(1); } } MUL => { @@ -1422,7 +1444,8 @@ declare_syscall!( ), result ) = result_point; - *result = Ok(0); + } else { + *result = Ok(1); } } _ => { @@ -1437,6 +1460,138 @@ declare_syscall!( } ); +declare_syscall!( + // Elliptic Curve Multiscalar Multiplication + // + // Currently, only curve25519 Edwards and Ristretto representations are supported + SyscallCurveMultiscalarMultiplication, + fn call( + &mut self, + curve_id: u64, + scalars_addr: u64, + points_addr: u64, + points_len: u64, + result_point_addr: u64, + memory_mapping: &mut MemoryMapping, + result: &mut Result>, + ) { + use solana_zk_token_sdk::curve25519::{ + curve_syscall_traits::*, edwards, ristretto, scalar, + }; + + let invoke_context = question_mark!( + self.invoke_context + .try_borrow() + .map_err(|_| SyscallError::InvokeContextBorrowFailed), + result + ); + + match curve_id { + CURVE25519_EDWARDS => { + let cost = invoke_context + .get_compute_budget() + .curve25519_edwards_msm_base_cost + .saturating_add( + invoke_context + .get_compute_budget() + .curve25519_edwards_msm_incremental_cost + .saturating_mul(points_len.saturating_sub(1)), + ); + question_mark!(invoke_context.get_compute_meter().consume(cost), result); + + let scalars = question_mark!( + translate_slice::( + memory_mapping, + scalars_addr, + points_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ), + result + ); + + let points = question_mark!( + translate_slice::( + memory_mapping, + points_addr, + points_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ), + result + ); + + if let Some(result_point) = edwards::multiscalar_multiply_edwards(scalars, points) { + *question_mark!( + translate_type_mut::( + memory_mapping, + result_point_addr, + invoke_context.get_check_aligned(), + ), + result + ) = result_point; + } else { + *result = Ok(1); + } + } + + CURVE25519_RISTRETTO => { + let cost = invoke_context + .get_compute_budget() + .curve25519_ristretto_msm_base_cost + .saturating_add( + invoke_context + .get_compute_budget() + .curve25519_ristretto_msm_incremental_cost + .saturating_mul(points_len.saturating_sub(1)), + ); + question_mark!(invoke_context.get_compute_meter().consume(cost), result); + + let scalars = question_mark!( + translate_slice::( + memory_mapping, + scalars_addr, + points_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ), + result + ); + + let points = question_mark!( + translate_slice::( + memory_mapping, + points_addr, + points_len, + invoke_context.get_check_aligned(), + invoke_context.get_check_size(), + ), + result + ); + + if let Some(result_point) = + ristretto::multiscalar_multiply_ristretto(scalars, points) + { + *question_mark!( + translate_type_mut::( + memory_mapping, + result_point_addr, + invoke_context.get_check_aligned(), + ), + result + ) = result_point; + } else { + *result = Ok(1); + } + } + + _ => { + *result = Ok(1); + } + } + } +); + declare_syscall!( // Blake3 SyscallBlake3, @@ -1675,6 +1830,19 @@ declare_syscall!( result ); + if !is_nonoverlapping( + to_slice.as_ptr() as usize, + length as usize, + program_id_result as *const _ as usize, + std::mem::size_of::(), + ) && invoke_context + .feature_set + .is_active(&check_syscall_outputs_do_not_overlap::id()) + { + *result = Err(SyscallError::CopyOverlapping.into()); + return; + } + *program_id_result = *program_id; } @@ -1738,10 +1906,7 @@ declare_syscall!( }; if let Some(instruction_context) = instruction_context { - let ProcessedSiblingInstruction { - data_len, - accounts_len, - } = question_mark!( + let result_header = question_mark!( translate_type_mut::( memory_mapping, meta_addr, @@ -1750,8 +1915,9 @@ declare_syscall!( result ); - if *data_len == instruction_context.get_instruction_data().len() - && *accounts_len == instruction_context.get_number_of_instruction_accounts() + if result_header.data_len == (instruction_context.get_instruction_data().len() as u64) + && result_header.accounts_len + == (instruction_context.get_number_of_instruction_accounts() as u64) { let program_id = question_mark!( translate_type_mut::( @@ -1765,7 +1931,7 @@ declare_syscall!( translate_slice_mut::( memory_mapping, data_addr, - *data_len as u64, + result_header.data_len as u64, invoke_context.get_check_aligned(), invoke_context.get_check_size(), ), @@ -1775,13 +1941,54 @@ declare_syscall!( translate_slice_mut::( memory_mapping, accounts_addr, - *accounts_len as u64, + result_header.accounts_len as u64, invoke_context.get_check_aligned(), invoke_context.get_check_size(), ), result ); + if (!is_nonoverlapping( + result_header as *const _ as usize, + std::mem::size_of::(), + program_id as *const _ as usize, + std::mem::size_of::(), + ) || !is_nonoverlapping( + result_header as *const _ as usize, + std::mem::size_of::(), + accounts.as_ptr() as usize, + std::mem::size_of::() + .saturating_mul(result_header.accounts_len as usize), + ) || !is_nonoverlapping( + result_header as *const _ as usize, + std::mem::size_of::(), + data.as_ptr() as usize, + result_header.data_len as usize, + ) || !is_nonoverlapping( + program_id as *const _ as usize, + std::mem::size_of::(), + data.as_ptr() as usize, + result_header.data_len as usize, + ) || !is_nonoverlapping( + program_id as *const _ as usize, + std::mem::size_of::(), + accounts.as_ptr() as usize, + std::mem::size_of::() + .saturating_mul(result_header.accounts_len as usize), + ) || !is_nonoverlapping( + data.as_ptr() as usize, + result_header.data_len as usize, + accounts.as_ptr() as usize, + std::mem::size_of::() + .saturating_mul(result_header.accounts_len as usize), + )) && invoke_context + .feature_set + .is_active(&check_syscall_outputs_do_not_overlap::id()) + { + *result = Err(SyscallError::CopyOverlapping.into()); + return; + } + *program_id = *question_mark!( instruction_context .get_last_program_key(invoke_context.transaction_context) @@ -1809,8 +2016,9 @@ declare_syscall!( ); accounts.clone_from_slice(account_metas.as_slice()); } - *data_len = instruction_context.get_instruction_data().len(); - *accounts_len = instruction_context.get_number_of_instruction_accounts(); + result_header.data_len = instruction_context.get_instruction_data().len() as u64; + result_header.accounts_len = + instruction_context.get_number_of_instruction_accounts() as u64; *result = Ok(true as u64); return; } @@ -2788,6 +2996,770 @@ mod tests { ); } + #[test] + fn test_syscall_edwards_curve_point_validation() { + use solana_zk_token_sdk::curve25519::curve_syscall_traits::CURVE25519_EDWARDS; + + let config = Config::default(); + prepare_mockup!( + invoke_context, + transaction_context, + program_id, + bpf_loader::id(), + ); + + let valid_bytes: [u8; 32] = [ + 201, 179, 241, 122, 180, 185, 239, 50, 183, 52, 221, 0, 153, 195, 43, 18, 22, 38, 187, + 206, 179, 192, 210, 58, 53, 45, 150, 98, 89, 17, 158, 11, + ]; + let valid_bytes_va = 0x100000000; + + let invalid_bytes: [u8; 32] = [ + 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84, + 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79, + ]; + let invalid_bytes_va = 0x200000000; + + let mut memory_mapping = MemoryMapping::new::( + vec![ + MemoryRegion::default(), + MemoryRegion { + host_addr: valid_bytes.as_ptr() as *const _ as u64, + vm_addr: valid_bytes_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: invalid_bytes.as_ptr() as *const _ as u64, + vm_addr: invalid_bytes_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + ], + &config, + ) + .unwrap(); + + invoke_context + .get_compute_meter() + .borrow_mut() + .mock_set_remaining( + (invoke_context + .get_compute_budget() + .curve25519_edwards_validate_point_cost) + * 2, + ); + let mut syscall = SyscallCurvePointValidation { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + }; + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + valid_bytes_va, + 0, + 0, + 0, + &mut memory_mapping, + &mut result, + ); + assert_eq!(0, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + invalid_bytes_va, + 0, + 0, + 0, + &mut memory_mapping, + &mut result, + ); + assert_eq!(1, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + valid_bytes_va, + 0, + 0, + 0, + &mut memory_mapping, + &mut result, + ); + assert_eq!( + Err(EbpfError::UserError(BpfError::SyscallError( + SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) + ))), + result + ); + } + + #[test] + fn test_syscall_ristretto_curve_point_validation() { + use solana_zk_token_sdk::curve25519::curve_syscall_traits::CURVE25519_RISTRETTO; + + let config = Config::default(); + prepare_mockup!( + invoke_context, + transaction_context, + program_id, + bpf_loader::id(), + ); + + let valid_bytes: [u8; 32] = [ + 226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11, + 106, 165, 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118, + ]; + let valid_bytes_va = 0x100000000; + + let invalid_bytes: [u8; 32] = [ + 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84, + 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79, + ]; + let invalid_bytes_va = 0x200000000; + + let mut memory_mapping = MemoryMapping::new::( + vec![ + MemoryRegion::default(), + MemoryRegion { + host_addr: valid_bytes.as_ptr() as *const _ as u64, + vm_addr: valid_bytes_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: invalid_bytes.as_ptr() as *const _ as u64, + vm_addr: invalid_bytes_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + ], + &config, + ) + .unwrap(); + + invoke_context + .get_compute_meter() + .borrow_mut() + .mock_set_remaining( + (invoke_context + .get_compute_budget() + .curve25519_ristretto_validate_point_cost) + * 2, + ); + let mut syscall = SyscallCurvePointValidation { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + }; + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + valid_bytes_va, + 0, + 0, + 0, + &mut memory_mapping, + &mut result, + ); + assert_eq!(0, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + invalid_bytes_va, + 0, + 0, + 0, + &mut memory_mapping, + &mut result, + ); + assert_eq!(1, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + valid_bytes_va, + 0, + 0, + 0, + &mut memory_mapping, + &mut result, + ); + assert_eq!( + Err(EbpfError::UserError(BpfError::SyscallError( + SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) + ))), + result + ); + } + + #[test] + fn test_syscall_edwards_curve_group_ops() { + use solana_zk_token_sdk::curve25519::curve_syscall_traits::{ + ADD, CURVE25519_EDWARDS, MUL, SUB, + }; + + let config = Config::default(); + prepare_mockup!( + invoke_context, + transaction_context, + program_id, + bpf_loader::id(), + ); + + let left_point: [u8; 32] = [ + 33, 124, 71, 170, 117, 69, 151, 247, 59, 12, 95, 125, 133, 166, 64, 5, 2, 27, 90, 27, + 200, 167, 59, 164, 52, 54, 52, 200, 29, 13, 34, 213, + ]; + let left_point_va = 0x100000000; + let right_point: [u8; 32] = [ + 70, 222, 137, 221, 253, 204, 71, 51, 78, 8, 124, 1, 67, 200, 102, 225, 122, 228, 111, + 183, 129, 14, 131, 210, 212, 95, 109, 246, 55, 10, 159, 91, + ]; + let right_point_va = 0x200000000; + let scalar: [u8; 32] = [ + 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250, + 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6, + ]; + let scalar_va = 0x300000000; + let invalid_point: [u8; 32] = [ + 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84, + 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79, + ]; + let invalid_point_va = 0x400000000; + let result_point: [u8; 32] = [0; 32]; + let result_point_va = 0x500000000; + + let mut memory_mapping = MemoryMapping::new::( + vec![ + MemoryRegion::default(), + MemoryRegion { + host_addr: left_point.as_ptr() as *const _ as u64, + vm_addr: left_point_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: right_point.as_ptr() as *const _ as u64, + vm_addr: right_point_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: scalar.as_ptr() as *const _ as u64, + vm_addr: scalar_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: invalid_point.as_ptr() as *const _ as u64, + vm_addr: invalid_point_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_point.as_ptr() as *const _ as u64, + vm_addr: result_point_va, + len: 32, + vm_gap_shift: 63, + is_writable: true, + }, + ], + &config, + ) + .unwrap(); + + invoke_context + .get_compute_meter() + .borrow_mut() + .mock_set_remaining( + (invoke_context + .get_compute_budget() + .curve25519_edwards_add_cost + + invoke_context + .get_compute_budget() + .curve25519_edwards_subtract_cost + + invoke_context + .get_compute_budget() + .curve25519_edwards_multiply_cost) + * 2, + ); + let mut syscall = SyscallCurveGroupOps { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + }; + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + ADD, + left_point_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + assert_eq!(0, result.unwrap()); + let expected_sum = [ + 7, 251, 187, 86, 186, 232, 57, 242, 193, 236, 49, 200, 90, 29, 254, 82, 46, 80, 83, 70, + 244, 153, 23, 156, 2, 138, 207, 51, 165, 38, 200, 85, + ]; + assert_eq!(expected_sum, result_point); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + ADD, + invalid_point_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + assert_eq!(1, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + SUB, + left_point_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + assert_eq!(0, result.unwrap()); + let expected_difference = [ + 60, 87, 90, 68, 232, 25, 7, 172, 247, 120, 158, 104, 52, 127, 94, 244, 5, 79, 253, 15, + 48, 69, 82, 134, 155, 70, 188, 81, 108, 95, 212, 9, + ]; + assert_eq!(expected_difference, result_point); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + SUB, + invalid_point_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + assert_eq!(1, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + MUL, + scalar_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + result.unwrap(); + let expected_product = [ + 64, 150, 40, 55, 80, 49, 217, 209, 105, 229, 181, 65, 241, 68, 2, 106, 220, 234, 211, + 71, 159, 76, 156, 114, 242, 68, 147, 31, 243, 211, 191, 124, + ]; + assert_eq!(expected_product, result_point); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + MUL, + scalar_va, + invalid_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + assert_eq!(1, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + MUL, + scalar_va, + invalid_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + assert_eq!( + Err(EbpfError::UserError(BpfError::SyscallError( + SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) + ))), + result + ); + } + + #[test] + fn test_syscall_ristretto_curve_group_ops() { + use solana_zk_token_sdk::curve25519::curve_syscall_traits::{ + ADD, CURVE25519_RISTRETTO, MUL, SUB, + }; + + let config = Config::default(); + prepare_mockup!( + invoke_context, + transaction_context, + program_id, + bpf_loader::id(), + ); + + let left_point: [u8; 32] = [ + 208, 165, 125, 204, 2, 100, 218, 17, 170, 194, 23, 9, 102, 156, 134, 136, 217, 190, 98, + 34, 183, 194, 228, 153, 92, 11, 108, 103, 28, 57, 88, 15, + ]; + let left_point_va = 0x100000000; + let right_point: [u8; 32] = [ + 208, 241, 72, 163, 73, 53, 32, 174, 54, 194, 71, 8, 70, 181, 244, 199, 93, 147, 99, + 231, 162, 127, 25, 40, 39, 19, 140, 132, 112, 212, 145, 108, + ]; + let right_point_va = 0x200000000; + let scalar: [u8; 32] = [ + 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250, + 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6, + ]; + let scalar_va = 0x300000000; + let invalid_point: [u8; 32] = [ + 120, 140, 152, 233, 41, 227, 203, 27, 87, 115, 25, 251, 219, 5, 84, 148, 117, 38, 84, + 60, 87, 144, 161, 146, 42, 34, 91, 155, 158, 189, 121, 79, + ]; + let invalid_point_va = 0x400000000; + let result_point: [u8; 32] = [0; 32]; + let result_point_va = 0x500000000; + + let mut memory_mapping = MemoryMapping::new::( + vec![ + MemoryRegion::default(), + MemoryRegion { + host_addr: left_point.as_ptr() as *const _ as u64, + vm_addr: left_point_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: right_point.as_ptr() as *const _ as u64, + vm_addr: right_point_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: scalar.as_ptr() as *const _ as u64, + vm_addr: scalar_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: invalid_point.as_ptr() as *const _ as u64, + vm_addr: invalid_point_va, + len: 32, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_point.as_ptr() as *const _ as u64, + vm_addr: result_point_va, + len: 32, + vm_gap_shift: 63, + is_writable: true, + }, + ], + &config, + ) + .unwrap(); + + invoke_context + .get_compute_meter() + .borrow_mut() + .mock_set_remaining( + (invoke_context + .get_compute_budget() + .curve25519_ristretto_add_cost + + invoke_context + .get_compute_budget() + .curve25519_ristretto_subtract_cost + + invoke_context + .get_compute_budget() + .curve25519_ristretto_multiply_cost) + * 2, + ); + let mut syscall = SyscallCurveGroupOps { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + }; + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + ADD, + left_point_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + assert_eq!(0, result.unwrap()); + let expected_sum = [ + 78, 173, 9, 241, 180, 224, 31, 107, 176, 210, 144, 240, 118, 73, 70, 191, 128, 119, + 141, 113, 125, 215, 161, 71, 49, 176, 87, 38, 180, 177, 39, 78, + ]; + assert_eq!(expected_sum, result_point); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + ADD, + invalid_point_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + assert_eq!(1, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + SUB, + left_point_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + assert_eq!(0, result.unwrap()); + let expected_difference = [ + 150, 72, 222, 61, 148, 79, 96, 130, 151, 176, 29, 217, 231, 211, 0, 215, 76, 86, 212, + 146, 110, 128, 24, 151, 187, 144, 108, 233, 221, 208, 157, 52, + ]; + assert_eq!(expected_difference, result_point); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + SUB, + invalid_point_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + assert_eq!(1, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + MUL, + scalar_va, + right_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + result.unwrap(); + let expected_product = [ + 4, 16, 46, 2, 53, 151, 201, 133, 117, 149, 232, 164, 119, 109, 136, 20, 153, 24, 124, + 21, 101, 124, 80, 19, 119, 100, 77, 108, 65, 187, 228, 5, + ]; + assert_eq!(expected_product, result_point); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + MUL, + scalar_va, + invalid_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + assert_eq!(1, result.unwrap()); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + MUL, + scalar_va, + invalid_point_va, + result_point_va, + &mut memory_mapping, + &mut result, + ); + assert_eq!( + Err(EbpfError::UserError(BpfError::SyscallError( + SyscallError::InstructionError(InstructionError::ComputationalBudgetExceeded) + ))), + result + ); + } + + #[test] + fn test_syscall_multiscalar_multiplication() { + use solana_zk_token_sdk::curve25519::curve_syscall_traits::{ + CURVE25519_EDWARDS, CURVE25519_RISTRETTO, + }; + + let config = Config::default(); + prepare_mockup!( + invoke_context, + transaction_context, + program_id, + bpf_loader::id(), + ); + + let scalar_a: [u8; 32] = [ + 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250, + 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6, + ]; + let scalar_b: [u8; 32] = [ + 254, 198, 23, 138, 67, 243, 184, 110, 236, 115, 236, 205, 205, 215, 79, 114, 45, 250, + 78, 137, 3, 107, 136, 237, 49, 126, 117, 223, 37, 191, 88, 6, + ]; + + let scalars = [scalar_a, scalar_b]; + let scalars_va = 0x100000000; + + let edwards_point_x: [u8; 32] = [ + 252, 31, 230, 46, 173, 95, 144, 148, 158, 157, 63, 10, 8, 68, 58, 176, 142, 192, 168, + 53, 61, 105, 194, 166, 43, 56, 246, 236, 28, 146, 114, 133, + ]; + let edwards_point_y: [u8; 32] = [ + 10, 111, 8, 236, 97, 189, 124, 69, 89, 176, 222, 39, 199, 253, 111, 11, 248, 186, 128, + 90, 120, 128, 248, 210, 232, 183, 93, 104, 111, 150, 7, 241, + ]; + let edwards_points = [edwards_point_x, edwards_point_y]; + let edwards_points_va = 0x200000000; + + let ristretto_point_x: [u8; 32] = [ + 130, 35, 97, 25, 18, 199, 33, 239, 85, 143, 119, 111, 49, 51, 224, 40, 167, 185, 240, + 179, 25, 194, 213, 41, 14, 155, 104, 18, 181, 197, 15, 112, + ]; + let ristretto_point_y: [u8; 32] = [ + 152, 156, 155, 197, 152, 232, 92, 206, 219, 159, 193, 134, 121, 128, 139, 36, 56, 191, + 51, 143, 72, 204, 87, 76, 110, 124, 101, 96, 238, 158, 42, 108, + ]; + let ristretto_points = [ristretto_point_x, ristretto_point_y]; + let ristretto_points_va = 0x300000000; + + let result_point: [u8; 32] = [0; 32]; + let result_point_va = 0x400000000; + + let mut memory_mapping = MemoryMapping::new::( + vec![ + MemoryRegion::default(), + MemoryRegion { + host_addr: scalars.as_ptr() as *const _ as u64, + vm_addr: scalars_va, + len: 64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: edwards_points.as_ptr() as *const _ as u64, + vm_addr: edwards_points_va, + len: 64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: ristretto_points.as_ptr() as *const _ as u64, + vm_addr: ristretto_points_va, + len: 64, + vm_gap_shift: 63, + is_writable: false, + }, + MemoryRegion { + host_addr: result_point.as_ptr() as *const _ as u64, + vm_addr: result_point_va, + len: 32, + vm_gap_shift: 63, + is_writable: true, + }, + ], + &config, + ) + .unwrap(); + + invoke_context + .get_compute_meter() + .borrow_mut() + .mock_set_remaining( + invoke_context + .get_compute_budget() + .curve25519_edwards_msm_base_cost + + invoke_context + .get_compute_budget() + .curve25519_edwards_msm_incremental_cost + + invoke_context + .get_compute_budget() + .curve25519_ristretto_msm_base_cost + + invoke_context + .get_compute_budget() + .curve25519_ristretto_msm_incremental_cost, + ); + + let mut syscall = SyscallCurveMultiscalarMultiplication { + invoke_context: Rc::new(RefCell::new(&mut invoke_context)), + }; + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_EDWARDS, + scalars_va, + edwards_points_va, + 2, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + assert_eq!(0, result.unwrap()); + let expected_product = [ + 30, 174, 168, 34, 160, 70, 63, 166, 236, 18, 74, 144, 185, 222, 208, 243, 5, 54, 223, + 172, 185, 75, 244, 26, 70, 18, 248, 46, 207, 184, 235, 60, + ]; + assert_eq!(expected_product, result_point); + + let mut result: Result> = Ok(0); + syscall.call( + CURVE25519_RISTRETTO, + scalars_va, + ristretto_points_va, + 2, + result_point_va, + &mut memory_mapping, + &mut result, + ); + + assert_eq!(0, result.unwrap()); + let expected_product = [ + 78, 120, 86, 111, 152, 64, 146, 84, 14, 236, 77, 147, 237, 190, 251, 241, 136, 167, 21, + 94, 84, 118, 92, 140, 120, 81, 30, 246, 173, 140, 195, 86, + ]; + assert_eq!(expected_product, result_point); + } + fn create_filled_type(zero_init: bool) -> T { let mut val = T::default(); let p = &mut val as *mut _ as *mut u8; diff --git a/programs/compute-budget/Cargo.toml b/programs/compute-budget/Cargo.toml index 3e6af5658b1047..176049253acc27 100644 --- a/programs/compute-budget/Cargo.toml +++ b/programs/compute-budget/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "solana-compute-budget-program" description = "Solana Compute Budget program" -version = "1.11.6" +version = "1.14.24" homepage = "https://solana.com/" documentation = "https://docs.rs/solana-compute-budget-program" repository = "https://github.com/solana-labs/solana" @@ -10,8 +10,8 @@ license = "Apache-2.0" edition = "2021" [dependencies] -solana-program-runtime = { path = "../../program-runtime", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/programs/config/Cargo.toml b/programs/config/Cargo.toml index e4119422d0f4f2..de9904bfc55c3c 100644 --- a/programs/config/Cargo.toml +++ b/programs/config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-config-program" -version = "1.11.6" +version = "1.14.24" description = "Solana Config program" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,11 +14,11 @@ bincode = "1.3.3" chrono = { version = "0.4.11", features = ["serde"] } serde = "1.0.138" serde_derive = "1.0.103" -solana-program-runtime = { path = "../../program-runtime", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } [dev-dependencies] -solana-logger = { path = "../../logger", version = "=1.11.6" } +solana-logger = { path = "../../logger", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/programs/ed25519-tests/Cargo.toml b/programs/ed25519-tests/Cargo.toml index c62418a5dec0bb..aa41b4d4cb78af 100644 --- a/programs/ed25519-tests/Cargo.toml +++ b/programs/ed25519-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-ed25519-program-tests" -version = "1.11.6" +version = "1.14.24" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" @@ -12,8 +12,8 @@ publish = false assert_matches = "1.5.0" ed25519-dalek = "=1.0.1" rand = "0.7.0" -solana-program-test = { path = "../../program-test", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } +solana-program-test = { path = "../../program-test", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/programs/stake/Cargo.toml b/programs/stake/Cargo.toml index 8492822326e675..a586c85e603dfe 100644 --- a/programs/stake/Cargo.toml +++ b/programs/stake/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-stake-program" -version = "1.11.6" +version = "1.14.24" description = "Solana Stake program" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -16,19 +16,19 @@ num-derive = "0.3" num-traits = "0.2" serde = "1.0.138" serde_derive = "1.0.103" -solana-config-program = { path = "../config", version = "=1.11.6" } -solana-frozen-abi = { path = "../../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "=1.11.6" } -solana-metrics = { path = "../../metrics", version = "=1.11.6" } -solana-program-runtime = { path = "../../program-runtime", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } -solana-vote-program = { path = "../vote", version = "=1.11.6" } +solana-config-program = { path = "../config", version = "=1.14.24" } +solana-frozen-abi = { path = "../../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "=1.14.24" } +solana-metrics = { path = "../../metrics", version = "=1.14.24" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } +solana-vote-program = { path = "../vote", version = "=1.14.24" } thiserror = "1.0" [dev-dependencies] assert_matches = "1.5.0" proptest = "1.0" -solana-logger = { path = "../../logger", version = "=1.11.6" } +solana-logger = { path = "../../logger", version = "=1.14.24" } test-case = "2.1.0" [build-dependencies] diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 84cd9a4f965cd1..6ebb77ed8b5873 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -4311,7 +4311,7 @@ mod tests { /// When a destination account already has funds, ensure the minimum split amount reduces /// accordingly. #[test_case(feature_set_old_behavior(), &[Ok(()), Ok(())]; "old_behavior")] - #[test_case(feature_set_new_behavior(), &[ Err(InstructionError::InsufficientFunds), Err(InstructionError::InsufficientFunds) ] ; "new_behavior")] + #[test_case(feature_set_new_behavior(), &[ Err(StakeError::InsufficientDelegation.into()), Err(StakeError::InsufficientDelegation.into()) ] ; "new_behavior")] fn test_staked_split_destination_minimum_balance( feature_set: FeatureSet, expected_results: &[Result<(), InstructionError>], @@ -4961,7 +4961,7 @@ mod tests { &serialize(&StakeInstruction::Split(stake_lamports / 2)).unwrap(), transaction_accounts, instruction_accounts, - Err(StakeError::InsufficientStake.into()), + Err(StakeError::InsufficientDelegation.into()), ); } diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index a3d55e3d10ca5a..4b9a8a9f23b6c4 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -15,8 +15,8 @@ use { account_utils::StateMut, clock::{Clock, Epoch}, feature_set::{ - self, stake_allow_zero_undelegated_amount, stake_merge_with_unmatched_credits_observed, - stake_split_uses_rent_sysvar, FeatureSet, + self, clean_up_delegation_errors, stake_allow_zero_undelegated_amount, + stake_merge_with_unmatched_credits_observed, stake_split_uses_rent_sysvar, FeatureSet, }, instruction::{checked_add, InstructionError}, pubkey::Pubkey, @@ -731,6 +731,15 @@ pub fn split( } else { // Otherwise, the new split stake should reflect the entire split // requested, less any lamports needed to cover the split_rent_exempt_reserve. + + if invoke_context + .feature_set + .is_active(&clean_up_delegation_errors::id()) + && stake.delegation.stake.saturating_sub(lamports) < minimum_delegation + { + return Err(StakeError::InsufficientDelegation.into()); + } + ( lamports, lamports.saturating_sub( @@ -740,6 +749,15 @@ pub fn split( ), ) }; + + if invoke_context + .feature_set + .is_active(&clean_up_delegation_errors::id()) + && split_stake_amount < minimum_delegation + { + return Err(StakeError::InsufficientDelegation.into()); + } + let split_stake = stake.split(remaining_stake_delta, split_stake_amount)?; let mut split_meta = meta; split_meta.rent_exempt_reserve = validated_split_info.destination_rent_exempt_reserve; @@ -1287,7 +1305,12 @@ fn validate_split_amount( // account, the split amount must be at least the minimum stake delegation. So if the minimum // stake delegation was 10 lamports, then a split amount of 1 lamport would not meet the // *delegation* requirements. - if source_stake.is_some() && lamports < additional_required_lamports { + if !invoke_context + .feature_set + .is_active(&clean_up_delegation_errors::id()) + && source_stake.is_some() + && lamports < additional_required_lamports + { return Err(InstructionError::InsufficientFunds); } diff --git a/programs/vote/Cargo.toml b/programs/vote/Cargo.toml index 5f2c0bf7cd408e..5c58a9a913596f 100644 --- a/programs/vote/Cargo.toml +++ b/programs/vote/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-vote-program" -version = "1.11.6" +version = "1.14.24" description = "Solana Vote program" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -16,15 +16,18 @@ num-derive = "0.3" num-traits = "0.2" serde = "1.0.138" serde_derive = "1.0.103" -solana-frozen-abi = { path = "../../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "=1.11.6" } -solana-metrics = { path = "../../metrics", version = "=1.11.6" } -solana-program-runtime = { path = "../../program-runtime", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } +solana-frozen-abi = { path = "../../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "=1.14.24" } +solana-metrics = { path = "../../metrics", version = "=1.14.24" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } thiserror = "1.0" [dev-dependencies] -solana-logger = { path = "../../logger", version = "=1.11.6" } +itertools = "0.10.3" +rand = "0.7.0" +solana-logger = { path = "../../logger", version = "=1.14.24" } +test-case = "2.1.0" [build-dependencies] rustc_version = "0.4" diff --git a/programs/vote/src/vote_error.rs b/programs/vote/src/vote_error.rs index b057b91db58721..2c60331273c51f 100644 --- a/programs/vote/src/vote_error.rs +++ b/programs/vote/src/vote_error.rs @@ -67,6 +67,9 @@ pub enum VoteError { #[error("Cannot close vote account unless it stopped voting at least one full epoch ago")] ActiveVoteAccountClose, + + #[error("Cannot update commission at this point in the epoch")] + CommissionUpdateTooLate, } impl DecodeError for VoteError { diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index 18b568376f766b..8056714de51026 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -4,7 +4,7 @@ use { crate::{ id, vote_state::{ - CompactVoteStateUpdate, Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs, + serde_compact_vote_state_update, Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs, VoteAuthorizeWithSeedArgs, VoteInit, VoteState, VoteStateUpdate, }, }, @@ -103,20 +103,6 @@ pub enum VoteInstruction { /// 1. `[SIGNER]` Vote authority UpdateVoteStateSwitch(VoteStateUpdate, Hash), - /// Update the onchain vote state for the signer. - /// - /// # Account references - /// 0. `[Write]` Vote account to vote with - /// 1. `[SIGNER]` Vote authority - CompactUpdateVoteState(CompactVoteStateUpdate), - - /// Update the onchain vote state for the signer along with a switching proof. - /// - /// # Account references - /// 0. `[Write]` Vote account to vote with - /// 1. `[SIGNER]` Vote authority - CompactUpdateVoteStateSwitch(CompactVoteStateUpdate, Hash), - /// Given that the current Voter or Withdrawer authority is a derived key, /// this instruction allows someone who can sign for that derived key's /// base key to authorize a new Voter or Withdrawer for a vote account. @@ -140,6 +126,24 @@ pub enum VoteInstruction { /// 2. `[SIGNER]` Base key of current Voter or Withdrawer authority's derived key /// 3. `[SIGNER]` New vote or withdraw authority AuthorizeCheckedWithSeed(VoteAuthorizeCheckedWithSeedArgs), + + /// Update the onchain vote state for the signer. + /// + /// # Account references + /// 0. `[Write]` Vote account to vote with + /// 1. `[SIGNER]` Vote authority + #[serde(with = "serde_compact_vote_state_update")] + CompactUpdateVoteState(VoteStateUpdate), + + /// Update the onchain vote state for the signer along with a switching proof. + /// + /// # Account references + /// 0. `[Write]` Vote account to vote with + /// 1. `[SIGNER]` Vote authority + CompactUpdateVoteStateSwitch( + #[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate, + Hash, + ), } fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction { @@ -387,7 +391,7 @@ pub fn update_vote_state_switch( pub fn compact_update_vote_state( vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, - compact_vote_state_update: CompactVoteStateUpdate, + vote_state_update: VoteStateUpdate, ) -> Instruction { let account_metas = vec![ AccountMeta::new(*vote_pubkey, false), @@ -396,7 +400,7 @@ pub fn compact_update_vote_state( Instruction::new_with_bincode( id(), - &VoteInstruction::CompactUpdateVoteState(compact_vote_state_update), + &VoteInstruction::CompactUpdateVoteState(vote_state_update), account_metas, ) } @@ -404,7 +408,7 @@ pub fn compact_update_vote_state( pub fn compact_update_vote_state_switch( vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, - vote_state_update: CompactVoteStateUpdate, + vote_state_update: VoteStateUpdate, proof_hash: Hash, ) -> Instruction { let account_metas = vec![ diff --git a/programs/vote/src/vote_processor.rs b/programs/vote/src/vote_processor.rs index dae06c4d047393..2eb92d53c5b8bf 100644 --- a/programs/vote/src/vote_processor.rs +++ b/programs/vote/src/vote_processor.rs @@ -3,8 +3,9 @@ use { crate::{ id, + vote_error::VoteError, vote_instruction::VoteInstruction, - vote_state::{self, VoteAuthorize, VoteStateUpdate}, + vote_state::{self, VoteAuthorize}, }, log::*, solana_program_runtime::{ @@ -136,6 +137,16 @@ pub fn process_instruction( vote_state::update_validator_identity(&mut me, node_pubkey, &signers) } VoteInstruction::UpdateCommission(commission) => { + if invoke_context.feature_set.is_active( + &feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id(), + ) { + let sysvar_cache = invoke_context.get_sysvar_cache(); + let epoch_schedule = sysvar_cache.get_epoch_schedule()?; + let clock = sysvar_cache.get_clock()?; + if !vote_state::is_commission_update_allowed(clock.slot, &epoch_schedule) { + return Err(VoteError::CommissionUpdateTooLate.into()); + } + } vote_state::update_commission(&mut me, commission, &signers) } VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => { @@ -173,8 +184,8 @@ pub fn process_instruction( Err(InstructionError::InvalidInstructionData) } } - VoteInstruction::CompactUpdateVoteState(compact_vote_state_update) - | VoteInstruction::CompactUpdateVoteStateSwitch(compact_vote_state_update, _) => { + VoteInstruction::CompactUpdateVoteState(vote_state_update) + | VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, _) => { if invoke_context .feature_set .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id()) @@ -189,7 +200,7 @@ pub fn process_instruction( &mut me, slot_hashes.slot_hashes(), &clock, - VoteStateUpdate::from(compact_vote_state_update), + vote_state_update, &signers, &invoke_context.feature_set, ) @@ -277,7 +288,10 @@ mod tests { hash::Hash, instruction::{AccountMeta, Instruction}, pubkey::Pubkey, - sysvar::{self, clock::Clock, rent::Rent, slot_hashes::SlotHashes}, + sysvar::{ + self, clock::Clock, epoch_schedule::EpochSchedule, rent::Rent, + slot_hashes::SlotHashes, + }, }, std::{collections::HashSet, str::FromStr}, }; @@ -345,6 +359,7 @@ mod tests { .map(|meta| meta.pubkey) .collect(); pubkeys.insert(sysvar::clock::id()); + pubkeys.insert(sysvar::epoch_schedule::id()); pubkeys.insert(sysvar::rent::id()); pubkeys.insert(sysvar::slot_hashes::id()); let transaction_accounts: Vec<_> = pubkeys @@ -354,6 +369,10 @@ mod tests { *pubkey, if sysvar::clock::check_id(pubkey) { account::create_account_shared_data_for_test(&Clock::default()) + } else if sysvar::epoch_schedule::check_id(pubkey) { + account::create_account_shared_data_for_test( + &EpochSchedule::without_warmup(), + ) } else if sysvar::slot_hashes::check_id(pubkey) { account::create_account_shared_data_for_test(&SlotHashes::default()) } else if sysvar::rent::check_id(pubkey) { @@ -668,6 +687,15 @@ mod tests { let transaction_accounts = vec![ (vote_pubkey, vote_account), (authorized_withdrawer, AccountSharedData::default()), + // Add the sysvar accounts so they're in the cache for mock processing + ( + sysvar::clock::id(), + account::create_account_shared_data_for_test(&Clock::default()), + ), + ( + sysvar::epoch_schedule::id(), + account::create_account_shared_data_for_test(&EpochSchedule::without_warmup()), + ), ]; let mut instruction_accounts = vec![ AccountMeta { diff --git a/programs/vote/src/vote_state/mod.rs b/programs/vote/src/vote_state/mod.rs index 48fcedbe84e40d..f374ca434032f0 100644 --- a/programs/vote/src/vote_state/mod.rs +++ b/programs/vote/src/vote_state/mod.rs @@ -11,12 +11,12 @@ use { solana_sdk::{ account::{AccountSharedData, ReadableAccount, WritableAccount}, clock::{Epoch, Slot, UnixTimestamp}, + epoch_schedule::EpochSchedule, feature_set::{self, filter_votes_outside_slot_hashes, FeatureSet}, hash::Hash, instruction::InstructionError, pubkey::Pubkey, rent::Rent, - short_vec, slot_hashes::SlotHash, sysvar::clock::Clock, transaction_context::{BorrowedAccount, InstructionContext, TransactionContext}, @@ -42,12 +42,13 @@ pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64; // Offset of VoteState::prior_voters, for determining initialization status without deserialization const DEFAULT_PRIOR_VOTERS_OFFSET: usize = 82; -#[frozen_abi(digest = "EYPXjH9Zn2vLzxyjHejkRkoTh4Tg4sirvb4FX9ye25qF")] +#[frozen_abi(digest = "4RSrLCthxW7e6KgpzDCf1kQUxa2v2aCg9mxn3975V7bm")] #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, AbiEnumVisitor, AbiExample)] pub enum VoteTransaction { Vote(Vote), VoteStateUpdate(VoteStateUpdate), - CompactVoteStateUpdate(CompactVoteStateUpdate), + #[serde(with = "serde_compact_vote_state_update")] + CompactVoteStateUpdate(VoteStateUpdate), } impl VoteTransaction { @@ -55,32 +56,26 @@ impl VoteTransaction { match self { VoteTransaction::Vote(vote) => vote.slots.clone(), VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.slots(), - VoteTransaction::CompactVoteStateUpdate(compact_state_update) => { - compact_state_update.slots() - } + VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.slots(), } } pub fn slot(&self, i: usize) -> Slot { match self { VoteTransaction::Vote(vote) => vote.slots[i], - VoteTransaction::VoteStateUpdate(vote_state_update) => { + VoteTransaction::VoteStateUpdate(vote_state_update) + | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => { vote_state_update.lockouts[i].slot } - VoteTransaction::CompactVoteStateUpdate(compact_state_update) => { - compact_state_update.slots()[i] - } } } pub fn len(&self) -> usize { match self { VoteTransaction::Vote(vote) => vote.slots.len(), - VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.lockouts.len(), - VoteTransaction::CompactVoteStateUpdate(compact_state_update) => { - 1 + compact_state_update.lockouts_32.len() - + compact_state_update.lockouts_16.len() - + compact_state_update.lockouts_8.len() + VoteTransaction::VoteStateUpdate(vote_state_update) + | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => { + vote_state_update.lockouts.len() } } } @@ -88,10 +83,10 @@ impl VoteTransaction { pub fn is_empty(&self) -> bool { match self { VoteTransaction::Vote(vote) => vote.slots.is_empty(), - VoteTransaction::VoteStateUpdate(vote_state_update) => { + VoteTransaction::VoteStateUpdate(vote_state_update) + | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => { vote_state_update.lockouts.is_empty() } - VoteTransaction::CompactVoteStateUpdate(_) => false, } } @@ -99,18 +94,16 @@ impl VoteTransaction { match self { VoteTransaction::Vote(vote) => vote.hash, VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.hash, - VoteTransaction::CompactVoteStateUpdate(compact_state_update) => { - compact_state_update.hash - } + VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.hash, } } pub fn timestamp(&self) -> Option { match self { VoteTransaction::Vote(vote) => vote.timestamp, - VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.timestamp, - VoteTransaction::CompactVoteStateUpdate(compact_state_update) => { - compact_state_update.timestamp + VoteTransaction::VoteStateUpdate(vote_state_update) + | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => { + vote_state_update.timestamp } } } @@ -118,9 +111,9 @@ impl VoteTransaction { pub fn set_timestamp(&mut self, ts: Option) { match self { VoteTransaction::Vote(vote) => vote.timestamp = ts, - VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.timestamp = ts, - VoteTransaction::CompactVoteStateUpdate(compact_state_update) => { - compact_state_update.timestamp = ts + VoteTransaction::VoteStateUpdate(vote_state_update) + | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => { + vote_state_update.timestamp = ts } } } @@ -128,12 +121,10 @@ impl VoteTransaction { pub fn last_voted_slot(&self) -> Option { match self { VoteTransaction::Vote(vote) => vote.slots.last().copied(), - VoteTransaction::VoteStateUpdate(vote_state_update) => { + VoteTransaction::VoteStateUpdate(vote_state_update) + | VoteTransaction::CompactVoteStateUpdate(vote_state_update) => { Some(vote_state_update.lockouts.back()?.slot) } - VoteTransaction::CompactVoteStateUpdate(compact_state_update) => { - compact_state_update.slots().last().copied() - } } } @@ -154,12 +145,6 @@ impl From for VoteTransaction { } } -impl From for VoteTransaction { - fn from(compact_state_update: CompactVoteStateUpdate) -> Self { - VoteTransaction::CompactVoteStateUpdate(compact_state_update) - } -} - #[frozen_abi(digest = "Ch2vVEwos2EjAVqSHCyJjnN2MNX1yrpapZTGhMSCjWUH")] #[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)] pub struct Vote { @@ -212,28 +197,6 @@ impl Lockout { } } -#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)] -pub struct CompactLockout { - // Offset to the next vote, 0 if this is the last vote in the tower - pub offset: T, - // Confirmation count, guarenteed to be < 32 - pub confirmation_count: u8, -} - -impl CompactLockout { - pub fn new(offset: T) -> Self { - Self { - offset, - confirmation_count: 1, - } - } - - // The number of slots for which this vote is locked - pub fn lockout(&self) -> u64 { - (INITIAL_LOCKOUT as u64).pow(self.confirmation_count.into()) - } -} - #[frozen_abi(digest = "BctadFJjUKbvPJzr6TszbX6rBfQUNSRKpKKngkzgXgeY")] #[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)] pub struct VoteStateUpdate { @@ -278,192 +241,9 @@ impl VoteStateUpdate { pub fn slots(&self) -> Vec { self.lockouts.iter().map(|lockout| lockout.slot).collect() } -} - -/// Ignoring overhead, in a full `VoteStateUpdate` the lockouts take up -/// 31 * (64 + 32) = 2976 bits. -/// -/// In this schema we separate the votes into 3 separate lockout structures -/// and store offsets rather than slot number, allowing us to use smaller fields. -/// -/// In a full `CompactVoteStateUpdate` the lockouts take up -/// 64 + (32 + 8) * 16 + (16 + 8) * 8 + (8 + 8) * 6 = 992 bits -/// allowing us to greatly reduce block size. -#[frozen_abi(digest = "C8ZrdXqqF3VxgsoCxnqNaYJggV6rr9PC3rtmVudJFmqG")] -#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)] -pub struct CompactVoteStateUpdate { - /// The proposed root, u64::MAX if there is no root - pub root: Slot, - /// The offset from the root (or 0 if no root) to the first vote - pub root_to_first_vote_offset: u64, - /// Part of the proposed tower, votes with confirmation_count > 15 - #[serde(with = "short_vec")] - pub lockouts_32: Vec>, - /// Part of the proposed tower, votes with 15 >= confirmation_count > 7 - #[serde(with = "short_vec")] - pub lockouts_16: Vec>, - /// Part of the proposed tower, votes with 7 >= confirmation_count - #[serde(with = "short_vec")] - pub lockouts_8: Vec>, - - /// Signature of the bank's state at the last slot - pub hash: Hash, - /// Processing timestamp of last slot - pub timestamp: Option, -} - -impl From> for CompactVoteStateUpdate { - fn from(recent_slots: Vec<(Slot, u32)>) -> Self { - let lockouts: VecDeque = recent_slots - .into_iter() - .map(|(slot, confirmation_count)| Lockout { - slot, - confirmation_count, - }) - .collect(); - Self::new(lockouts, None, Hash::default()) - } -} - -impl CompactVoteStateUpdate { - pub fn new(mut lockouts: VecDeque, root: Option, hash: Hash) -> Self { - if lockouts.is_empty() { - return Self::default(); - } - let mut cur_slot = root.unwrap_or(0u64); - let mut cur_confirmation_count = 0; - let offset = lockouts - .pop_front() - .map( - |Lockout { - slot, - confirmation_count, - }| { - assert!(confirmation_count < 32); - - let offset = slot - cur_slot; - cur_slot = slot; - cur_confirmation_count = confirmation_count; - offset - }, - ) - .expect("Tower should not be empty"); - let mut lockouts_32 = Vec::new(); - let mut lockouts_16 = Vec::new(); - let mut lockouts_8 = Vec::new(); - - for Lockout { - slot, - confirmation_count, - } in lockouts - { - assert!(confirmation_count < 32); - let offset = slot - cur_slot; - if cur_confirmation_count > 15 { - lockouts_32.push(CompactLockout { - offset: offset.try_into().unwrap(), - confirmation_count: cur_confirmation_count.try_into().unwrap(), - }); - } else if cur_confirmation_count > 7 { - lockouts_16.push(CompactLockout { - offset: offset.try_into().unwrap(), - confirmation_count: cur_confirmation_count.try_into().unwrap(), - }); - } else { - lockouts_8.push(CompactLockout { - offset: offset.try_into().unwrap(), - confirmation_count: cur_confirmation_count.try_into().unwrap(), - }) - } - - cur_slot = slot; - cur_confirmation_count = confirmation_count; - } - // Last vote should be at the top of tower, so we don't have to explicitly store it - assert!(cur_confirmation_count == 1); - Self { - root: root.unwrap_or(u64::MAX), - root_to_first_vote_offset: offset, - lockouts_32, - lockouts_16, - lockouts_8, - hash, - timestamp: None, - } - } - - pub fn root(&self) -> Option { - if self.root == u64::MAX { - None - } else { - Some(self.root) - } - } - - pub fn slots(&self) -> Vec { - std::iter::once(self.root_to_first_vote_offset) - .chain(self.lockouts_32.iter().map(|lockout| lockout.offset.into())) - .chain(self.lockouts_16.iter().map(|lockout| lockout.offset.into())) - .chain(self.lockouts_8.iter().map(|lockout| lockout.offset.into())) - .scan(self.root().unwrap_or(0), |prev_slot, offset| { - let slot = *prev_slot + offset; - *prev_slot = slot; - Some(slot) - }) - .collect() - } -} - -impl From for VoteStateUpdate { - fn from(vote_state_update: CompactVoteStateUpdate) -> Self { - let lockouts = vote_state_update - .lockouts_32 - .iter() - .map(|lockout| (lockout.offset.into(), lockout.confirmation_count)) - .chain( - vote_state_update - .lockouts_16 - .iter() - .map(|lockout| (lockout.offset.into(), lockout.confirmation_count)), - ) - .chain( - vote_state_update - .lockouts_8 - .iter() - .map(|lockout| (lockout.offset.into(), lockout.confirmation_count)), - ) - .chain( - // To pick up the last element - std::iter::once((0, 1)), - ) - .scan( - vote_state_update.root().unwrap_or(0) + vote_state_update.root_to_first_vote_offset, - |slot, (offset, confirmation_count): (u64, u8)| { - let cur_slot = *slot; - *slot += offset; - Some(Lockout { - slot: cur_slot, - confirmation_count: confirmation_count.into(), - }) - }, - ) - .collect(); - Self { - lockouts, - root: vote_state_update.root(), - hash: vote_state_update.hash, - timestamp: vote_state_update.timestamp, - } - } -} -impl From for CompactVoteStateUpdate { - fn from(vote_state_update: VoteStateUpdate) -> Self { - CompactVoteStateUpdate::new( - vote_state_update.lockouts, - vote_state_update.root, - vote_state_update.hash, - ) + pub fn last_voted_slot(&self) -> Option { + self.lockouts.back().map(|l| l.slot) } } @@ -693,6 +473,7 @@ impl VoteState { &self, vote_state_update: &mut VoteStateUpdate, slot_hashes: &[(Slot, Hash)], + feature_set: Option<&FeatureSet>, ) -> Result<(), VoteError> { if vote_state_update.lockouts.is_empty() { return Err(VoteError::EmptySlots); @@ -724,18 +505,43 @@ impl VoteState { } // Check if the proposed root is too old - if let Some(new_proposed_root) = vote_state_update.root { - // If the root is less than the earliest slot hash in the history such that we - // cannot verify whether the slot was actually was on this fork, set the root - // to the current vote state root for safety. + let is_root_fix_enabled = feature_set + .map(|feature_set| { + feature_set.is_active(&feature_set::vote_state_update_root_fix::id()) + }) + .unwrap_or(false); + + let original_proposed_root = vote_state_update.root; + if let Some(new_proposed_root) = original_proposed_root { + // If the new proposed root `R` is less than the earliest slot hash in the history + // such that we cannot verify whether the slot was actually was on this fork, set + // the root to the latest vote in the current vote that's less than R. if earliest_slot_hash_in_history > new_proposed_root { vote_state_update.root = self.root_slot; + if is_root_fix_enabled { + let mut prev_slot = Slot::MAX; + let current_root = vote_state_update.root; + for lockout in self.votes.iter().rev() { + let is_slot_bigger_than_root = current_root + .map(|current_root| lockout.slot > current_root) + .unwrap_or(true); + // Ensure we're iterating from biggest to smallest vote in the + // current vote state + assert!(lockout.slot < prev_slot && is_slot_bigger_than_root); + if lockout.slot <= new_proposed_root { + vote_state_update.root = Some(lockout.slot); + break; + } + prev_slot = lockout.slot; + } + } } } - // index into the new proposed vote state's slots, starting with the root if it exists then - // we use this mutable root to fold the root slot case into this loop for performance - let mut check_root = vote_state_update.root; + // Index into the new proposed vote state's slots, starting with the root if it exists then + // we use this mutable root to fold checking the root slot into the below loop + // for performance + let mut root_to_check = vote_state_update.root; let mut vote_state_update_index = 0; // index into the slot_hashes, starting at the oldest known @@ -757,12 +563,12 @@ impl VoteState { // because have to ensure that every slot is actually part of the history, not just the most // recent ones while vote_state_update_index < vote_state_update.lockouts.len() && slot_hashes_index > 0 { - let proposed_vote_slot = if let Some(root) = check_root { + let proposed_vote_slot = if let Some(root) = root_to_check { root } else { vote_state_update.lockouts[vote_state_update_index].slot }; - if check_root.is_none() + if root_to_check.is_none() && vote_state_update_index > 0 && proposed_vote_slot <= vote_state_update.lockouts[vote_state_update_index - 1].slot @@ -779,7 +585,7 @@ impl VoteState { // The vote slot does not exist in the SlotHashes history because it's too old, // i.e. older than the oldest slot in the history. assert!(proposed_vote_slot < earliest_slot_hash_in_history); - if !self.contains_slot(proposed_vote_slot) && check_root.is_none() { + if !self.contains_slot(proposed_vote_slot) && root_to_check.is_none() { // If the vote slot is both: // 1) Too old // 2) Doesn't already exist in vote state @@ -787,12 +593,23 @@ impl VoteState { // Then filter it out vote_state_update_indexes_to_filter.push(vote_state_update_index); } - if check_root.is_some() { - // If the vote state update has a root < earliest_slot_hash_in_history - // then we use the current root. The only case where this can happen - // is if the current root itself is not in slot hashes. - assert!(self.root_slot.unwrap() < earliest_slot_hash_in_history); - check_root = None; + if let Some(new_proposed_root) = root_to_check { + if is_root_fix_enabled { + // 1. Because `root_to_check.is_some()`, then we know that + // we haven't checked the root yet in this loop, so + // `proposed_vote_slot` == `new_proposed_root` == `vote_state_update.root`. + assert_eq!(new_proposed_root, proposed_vote_slot); + // 2. We know from the assert earlier in the function that + // `proposed_vote_slot < earliest_slot_hash_in_history`, + // so from 1. we know that `new_proposed_root < earliest_slot_hash_in_history`. + assert!(new_proposed_root < earliest_slot_hash_in_history); + } else { + // If the vote state update has a root < earliest_slot_hash_in_history + // then we use the current root. The only case where this can happen + // is if the current root itself is not in slot hashes. + assert!(self.root_slot.unwrap() < earliest_slot_hash_in_history); + } + root_to_check = None; } else { vote_state_update_index += 1; } @@ -801,7 +618,7 @@ impl VoteState { // If the vote slot is new enough to be in the slot history, // but is not part of the slot history, then it must belong to another fork, // which means this vote state update is invalid. - if check_root.is_some() { + if root_to_check.is_some() { return Err(VoteError::RootOnDifferentFork); } else { return Err(VoteError::SlotsMismatch); @@ -817,8 +634,8 @@ impl VoteState { // Once the slot in `vote_state_update.lockouts` is found, bump to the next slot // in `vote_state_update.lockouts` and continue. If we were checking the root, // start checking the vote state instead. - if check_root.is_some() { - check_root = None; + if root_to_check.is_some() { + root_to_check = None; } else { vote_state_update_index += 1; slot_hashes_index -= 1; @@ -976,7 +793,7 @@ impl VoteState { Ok(()) } - //`Ensure check_update_vote_state_slots_are_valid()` runs on the slots in `new_state` + // `Ensure check_update_vote_state_slots_are_valid()` runs on the slots in `new_state` // before `process_new_vote_state()` is called // This function should guarantee the following about `new_state`: @@ -1051,12 +868,12 @@ impl VoteState { return Err(VoteError::ConfirmationTooLarge); } else if let Some(new_root) = new_root { if vote.slot <= new_root - && - // This check is necessary because - // https://github.com/ryoqun/solana/blob/df55bfb46af039cbc597cd60042d49b9d90b5961/core/src/consensus.rs#L120 - // always sets a root for even empty towers, which is then hard unwrapped here - // https://github.com/ryoqun/solana/blob/df55bfb46af039cbc597cd60042d49b9d90b5961/core/src/consensus.rs#L776 - new_root != Slot::default() + && + // This check is necessary because + // https://github.com/ryoqun/solana/blob/df55bfb46af039cbc597cd60042d49b9d90b5961/core/src/consensus.rs#L120 + // always sets a root for even empty towers, which is then hard unwrapped here + // https://github.com/ryoqun/solana/blob/df55bfb46af039cbc597cd60042d49b9d90b5961/core/src/consensus.rs#L776 + new_root != Slot::default() { return Err(VoteError::SlotSmallerThanRoot); } @@ -1440,6 +1257,102 @@ impl VoteState { } } +pub mod serde_compact_vote_state_update { + use { + super::*, + serde::{Deserialize, Deserializer, Serialize, Serializer}, + solana_sdk::{serde_varint, short_vec}, + }; + + #[derive(Deserialize, Serialize, AbiExample)] + struct LockoutOffset { + #[serde(with = "serde_varint")] + offset: Slot, + confirmation_count: u8, + } + + #[derive(Deserialize, Serialize)] + struct CompactVoteStateUpdate { + root: Slot, + #[serde(with = "short_vec")] + lockout_offsets: Vec, + hash: Hash, + timestamp: Option, + } + + pub fn serialize( + vote_state_update: &VoteStateUpdate, + serializer: S, + ) -> Result + where + S: Serializer, + { + let lockout_offsets = vote_state_update.lockouts.iter().scan( + vote_state_update.root.unwrap_or_default(), + |slot, lockout| { + let offset = match lockout.slot.checked_sub(*slot) { + None => return Some(Err(serde::ser::Error::custom("Invalid vote lockout"))), + Some(offset) => offset, + }; + let confirmation_count = match u8::try_from(lockout.confirmation_count) { + Ok(confirmation_count) => confirmation_count, + Err(_) => { + return Some(Err(serde::ser::Error::custom("Invalid confirmation count"))) + } + }; + let lockout_offset = LockoutOffset { + offset, + confirmation_count, + }; + *slot = lockout.slot; + Some(Ok(lockout_offset)) + }, + ); + let compact_vote_state_update = CompactVoteStateUpdate { + root: vote_state_update.root.unwrap_or(Slot::MAX), + lockout_offsets: lockout_offsets.collect::>()?, + hash: vote_state_update.hash, + timestamp: vote_state_update.timestamp, + }; + compact_vote_state_update.serialize(serializer) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let CompactVoteStateUpdate { + root, + lockout_offsets, + hash, + timestamp, + } = CompactVoteStateUpdate::deserialize(deserializer)?; + let root = (root != Slot::MAX).then(|| root); + let lockouts = + lockout_offsets + .iter() + .scan(root.unwrap_or_default(), |slot, lockout_offset| { + *slot = match slot.checked_add(lockout_offset.offset) { + None => { + return Some(Err(serde::de::Error::custom("Invalid lockout offset"))) + } + Some(slot) => slot, + }; + let lockout = Lockout { + slot: *slot, + confirmation_count: u32::from(lockout_offset.confirmation_count), + }; + Some(Ok(lockout)) + }); + Ok(VoteStateUpdate { + root, + lockouts: lockouts.collect::>()?, + hash, + timestamp, + }) + } +} + /// Authorize the given pubkey to withdraw or sign votes. This may be called multiple times, /// but will implicitly withdraw authorization from the previously authorized /// key @@ -1528,6 +1441,22 @@ pub fn update_commission( vote_account.set_state(&VoteStateVersions::new_current(vote_state)) } +/// Given the current slot and epoch schedule, determine if a commission change +/// is allowed +pub fn is_commission_update_allowed(slot: Slot, epoch_schedule: &EpochSchedule) -> bool { + // always allowed during warmup epochs + if let Some(relative_slot) = slot + .saturating_sub(epoch_schedule.first_normal_slot) + .checked_rem(epoch_schedule.slots_per_epoch) + { + // allowed up to the midpoint of the epoch + relative_slot.saturating_mul(2) <= epoch_schedule.slots_per_epoch + } else { + // no slots per epoch, just allow it, even though this should never happen + true + } +} + fn verify_authorized_signer( authorized: &Pubkey, signers: &HashSet, @@ -1667,22 +1596,42 @@ pub fn process_vote_state_update( vote_account: &mut BorrowedAccount, slot_hashes: &[SlotHash], clock: &Clock, - mut vote_state_update: VoteStateUpdate, + vote_state_update: VoteStateUpdate, signers: &HashSet, feature_set: &FeatureSet, ) -> Result<(), InstructionError> { let mut vote_state = verify_and_get_vote_state(vote_account, clock, signers)?; - vote_state.check_update_vote_state_slots_are_valid(&mut vote_state_update, slot_hashes)?; - vote_state.process_new_vote_state( - vote_state_update.lockouts, - vote_state_update.root, - vote_state_update.timestamp, + do_process_vote_state_update( + &mut vote_state, + slot_hashes, clock.epoch, + vote_state_update, Some(feature_set), )?; vote_account.set_state(&VoteStateVersions::new_current(vote_state)) } +pub fn do_process_vote_state_update( + vote_state: &mut VoteState, + slot_hashes: &[SlotHash], + epoch: u64, + mut vote_state_update: VoteStateUpdate, + feature_set: Option<&FeatureSet>, +) -> Result<(), VoteError> { + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + slot_hashes, + feature_set, + )?; + vote_state.process_new_vote_state( + vote_state_update.lockouts, + vote_state_update.root, + vote_state_update.timestamp, + epoch, + feature_set, + ) +} + pub fn create_account_with_authorized( node_pubkey: &Pubkey, authorized_voter: &Pubkey, @@ -1723,8 +1672,14 @@ mod tests { use { super::*, crate::vote_state, - solana_sdk::{account::AccountSharedData, account_utils::StateMut, hash::hash}, + itertools::Itertools, + rand::Rng, + solana_sdk::{ + account::AccountSharedData, account_utils::StateMut, clock::DEFAULT_SLOTS_PER_EPOCH, + hash::hash, + }, std::cell::RefCell, + test_case::test_case, }; const MAX_RECENT_VOTES: usize = 16; @@ -3328,7 +3283,8 @@ mod tests { assert_eq!( empty_vote_state.check_update_vote_state_slots_are_valid( &mut vote_state_update, - &empty_slot_hashes + &empty_slot_hashes, + Some(&FeatureSet::all_enabled()) ), Err(VoteError::EmptySlots), ); @@ -3338,7 +3294,8 @@ mod tests { assert_eq!( empty_vote_state.check_update_vote_state_slots_are_valid( &mut vote_state_update, - &empty_slot_hashes + &empty_slot_hashes, + Some(&FeatureSet::all_enabled()) ), Err(VoteError::SlotsMismatch), ); @@ -3354,8 +3311,11 @@ mod tests { // should return error `VoteTooOld` let mut vote_state_update = VoteStateUpdate::from(vec![(latest_vote, 1)]); assert_eq!( - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes), + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()) + ), Err(VoteError::VoteTooOld), ); @@ -3366,48 +3326,239 @@ mod tests { let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history]); let mut vote_state_update = VoteStateUpdate::from(vec![(earliest_slot_in_history - 1, 1)]); assert_eq!( - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes), + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()), + ), Err(VoteError::VoteTooOld), ); } - #[test] - fn test_check_update_vote_state_older_than_history_root() { - let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]); - let mut vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes); + fn run_test_check_update_vote_state_older_than_history_root( + earliest_slot_in_history: Slot, + current_vote_state_slots: Vec, + current_vote_state_root: Option, + vote_state_update_slots_and_lockouts: Vec<(Slot, u32)>, + vote_state_update_root: Slot, + expected_root: Option, + expected_vote_state: Vec, + ) { + assert!(vote_state_update_root < earliest_slot_in_history); + assert_eq!( + expected_root, + current_vote_state_slots + .iter() + .rev() + .find(|slot| **slot <= vote_state_update_root) + .cloned() + ); + let latest_slot_in_history = vote_state_update_slots_and_lockouts + .last() + .unwrap() + .0 + .max(earliest_slot_in_history); + let mut slot_hashes = build_slot_hashes( + (current_vote_state_slots.first().copied().unwrap_or(0)..=latest_slot_in_history) + .collect::>(), + ); - // Test with a `vote_state_update` where the root is less than `earliest_slot_in_history`. - // Root slot in the `vote_state_update` should be updated to match the root slot in the - // current vote state - let earliest_slot_in_history = 5; - let slot_hashes = build_slot_hashes(vec![earliest_slot_in_history, 6, 7, 8]); - let earliest_slot_in_history_hash = slot_hashes + let mut vote_state = build_vote_state(current_vote_state_slots, &slot_hashes); + vote_state.root_slot = current_vote_state_root; + + slot_hashes.retain(|slot| slot.0 >= earliest_slot_in_history); + assert!(!vote_state_update_slots_and_lockouts.is_empty()); + let vote_state_update_hash = slot_hashes .iter() - .find(|(slot, _hash)| *slot == earliest_slot_in_history) + .find(|(slot, _hash)| *slot == vote_state_update_slots_and_lockouts.last().unwrap().0) .unwrap() .1; - let mut vote_state_update = VoteStateUpdate::from(vec![(earliest_slot_in_history, 1)]); - vote_state_update.hash = earliest_slot_in_history_hash; - vote_state_update.root = Some(earliest_slot_in_history - 1); - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes) - .unwrap(); - assert!(vote_state.root_slot.is_none()); - assert_eq!(vote_state_update.root, vote_state.root_slot); // Test with a `vote_state_update` where the root is less than `earliest_slot_in_history`. // Root slot in the `vote_state_update` should be updated to match the root slot in the // current vote state - vote_state.root_slot = Some(0); - let mut vote_state_update = VoteStateUpdate::from(vec![(earliest_slot_in_history, 1)]); - vote_state_update.hash = earliest_slot_in_history_hash; - vote_state_update.root = Some(earliest_slot_in_history - 1); + let mut vote_state_update = VoteStateUpdate::from(vote_state_update_slots_and_lockouts); + vote_state_update.hash = vote_state_update_hash; + vote_state_update.root = Some(vote_state_update_root); vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes) + .check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()), + ) .unwrap(); - assert_eq!(vote_state.root_slot, Some(0)); - assert_eq!(vote_state_update.root, vote_state.root_slot); + assert_eq!(vote_state_update.root, expected_root); + + // The proposed root slot should become the biggest slot in the current vote state less than + // `earliest_slot_in_history`. + assert!(do_process_vote_state_update( + &mut vote_state, + &slot_hashes, + 0, + vote_state_update.clone(), + Some(&FeatureSet::all_enabled()), + ) + .is_ok()); + assert_eq!(vote_state.root_slot, expected_root); + assert_eq!( + vote_state.votes.into_iter().collect::>(), + expected_vote_state, + ); + } + + #[test] + fn test_check_update_vote_state_older_than_history_root() { + // Test when `vote_state_update_root` is in `current_vote_state_slots` but it's not the latest + // slot + let earliest_slot_in_history = 5; + let current_vote_state_slots: Vec = vec![1, 2, 3, 4]; + let current_vote_state_root = None; + let vote_state_update_slots_and_lockouts = vec![(5, 1)]; + let vote_state_update_root = 4; + let expected_root = Some(4); + let expected_vote_state = vec![Lockout { + slot: 5, + confirmation_count: 1, + }]; + run_test_check_update_vote_state_older_than_history_root( + earliest_slot_in_history, + current_vote_state_slots, + current_vote_state_root, + vote_state_update_slots_and_lockouts, + vote_state_update_root, + expected_root, + expected_vote_state, + ); + + // Test when `vote_state_update_root` is in `current_vote_state_slots` but it's not the latest + // slot and the `current_vote_state_root.is_some()`. + let earliest_slot_in_history = 5; + let current_vote_state_slots: Vec = vec![1, 2, 3, 4]; + let current_vote_state_root = Some(0); + let vote_state_update_slots_and_lockouts = vec![(5, 1)]; + let vote_state_update_root = 4; + let expected_root = Some(4); + let expected_vote_state = vec![Lockout { + slot: 5, + confirmation_count: 1, + }]; + run_test_check_update_vote_state_older_than_history_root( + earliest_slot_in_history, + current_vote_state_slots, + current_vote_state_root, + vote_state_update_slots_and_lockouts, + vote_state_update_root, + expected_root, + expected_vote_state, + ); + + // Test when `vote_state_update_root` is in `current_vote_state_slots` but it's not the latest + // slot + let earliest_slot_in_history = 5; + let current_vote_state_slots: Vec = vec![1, 2, 3, 4]; + let current_vote_state_root = Some(0); + let vote_state_update_slots_and_lockouts = vec![(4, 2), (5, 1)]; + let vote_state_update_root = 3; + let expected_root = Some(3); + let expected_vote_state = vec![ + Lockout { + slot: 4, + confirmation_count: 2, + }, + Lockout { + slot: 5, + confirmation_count: 1, + }, + ]; + run_test_check_update_vote_state_older_than_history_root( + earliest_slot_in_history, + current_vote_state_slots, + current_vote_state_root, + vote_state_update_slots_and_lockouts, + vote_state_update_root, + expected_root, + expected_vote_state, + ); + + // Test when `vote_state_update_root` is not in `current_vote_state_slots` + let earliest_slot_in_history = 5; + let current_vote_state_slots: Vec = vec![1, 2, 4]; + let current_vote_state_root = Some(0); + let vote_state_update_slots_and_lockouts = vec![(4, 2), (5, 1)]; + let vote_state_update_root = 3; + let expected_root = Some(2); + let expected_vote_state = vec![ + Lockout { + slot: 4, + confirmation_count: 2, + }, + Lockout { + slot: 5, + confirmation_count: 1, + }, + ]; + run_test_check_update_vote_state_older_than_history_root( + earliest_slot_in_history, + current_vote_state_slots, + current_vote_state_root, + vote_state_update_slots_and_lockouts, + vote_state_update_root, + expected_root, + expected_vote_state, + ); + + // Test when the `vote_state_update_root` is smaller than all the slots in + // `current_vote_state_slots`, no roots should be set. + let earliest_slot_in_history = 4; + let current_vote_state_slots: Vec = vec![3, 4]; + let current_vote_state_root = None; + let vote_state_update_slots_and_lockouts = vec![(3, 3), (4, 2), (5, 1)]; + let vote_state_update_root = 2; + let expected_root = None; + let expected_vote_state = vec![ + Lockout { + slot: 3, + confirmation_count: 3, + }, + Lockout { + slot: 4, + confirmation_count: 2, + }, + Lockout { + slot: 5, + confirmation_count: 1, + }, + ]; + run_test_check_update_vote_state_older_than_history_root( + earliest_slot_in_history, + current_vote_state_slots, + current_vote_state_root, + vote_state_update_slots_and_lockouts, + vote_state_update_root, + expected_root, + expected_vote_state, + ); + + // Test when `current_vote_state_slots` is empty, no roots should be set + let earliest_slot_in_history = 4; + let current_vote_state_slots: Vec = vec![]; + let current_vote_state_root = None; + let vote_state_update_slots_and_lockouts = vec![(5, 1)]; + let vote_state_update_root = 2; + let expected_root = None; + let expected_vote_state = vec![Lockout { + slot: 5, + confirmation_count: 1, + }]; + run_test_check_update_vote_state_older_than_history_root( + earliest_slot_in_history, + current_vote_state_slots, + current_vote_state_root, + vote_state_update_slots_and_lockouts, + vote_state_update_root, + expected_root, + expected_vote_state, + ); } #[test] @@ -3425,8 +3576,11 @@ mod tests { let mut vote_state_update = VoteStateUpdate::from(vec![(2, 2), (1, 3), (vote_slot, 1)]); vote_state_update.hash = vote_slot_hash; assert_eq!( - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes), + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()) + ), Err(VoteError::SlotsNotOrdered), ); @@ -3434,8 +3588,11 @@ mod tests { let mut vote_state_update = VoteStateUpdate::from(vec![(2, 2), (2, 2), (vote_slot, 1)]); vote_state_update.hash = vote_slot_hash; assert_eq!( - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes), + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()), + ), Err(VoteError::SlotsNotOrdered), ); } @@ -3443,7 +3600,7 @@ mod tests { #[test] fn test_check_update_vote_state_older_than_history_slots_filtered() { let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]); - let vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes); + let mut vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes); // Test with a `vote_state_update` where there: // 1) Exists a slot less than `earliest_slot_in_history` @@ -3458,24 +3615,46 @@ mod tests { .unwrap() .1; let missing_older_than_history_slot = earliest_slot_in_history - 1; - let mut vote_state_update = - VoteStateUpdate::from(vec![(missing_older_than_history_slot, 2), (vote_slot, 3)]); + let mut vote_state_update = VoteStateUpdate::from(vec![ + (1, 4), + (missing_older_than_history_slot, 2), + (vote_slot, 3), + ]); vote_state_update.hash = vote_slot_hash; vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes) + .check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()), + ) .unwrap(); // Check the earlier slot was filtered out assert_eq!( vote_state_update + .clone() .lockouts .into_iter() .collect::>(), - vec![Lockout { - slot: vote_slot, - confirmation_count: 3, - }] + vec![ + Lockout { + slot: 1, + confirmation_count: 4, + }, + Lockout { + slot: vote_slot, + confirmation_count: 3, + } + ] ); + assert!(do_process_vote_state_update( + &mut vote_state, + &slot_hashes, + 0, + vote_state_update, + Some(&FeatureSet::all_enabled()), + ) + .is_ok()); } #[test] @@ -3488,8 +3667,8 @@ mod tests { #[test] fn test_check_update_vote_state_older_than_history_slots_not_filtered() { - let slot_hashes = build_slot_hashes(vec![1, 2, 3, 4]); - let vote_state = build_vote_state(vec![1, 2, 3, 4], &slot_hashes); + let slot_hashes = build_slot_hashes(vec![4]); + let mut vote_state = build_vote_state(vec![4], &slot_hashes); // Test with a `vote_state_update` where there: // 1) Exists a slot less than `earliest_slot_in_history` @@ -3505,35 +3684,48 @@ mod tests { .1; let existing_older_than_history_slot = 4; let mut vote_state_update = - VoteStateUpdate::from(vec![(existing_older_than_history_slot, 2), (vote_slot, 3)]); + VoteStateUpdate::from(vec![(existing_older_than_history_slot, 3), (vote_slot, 2)]); vote_state_update.hash = vote_slot_hash; vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes) + .check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()), + ) .unwrap(); // Check the earlier slot was *NOT* filtered out assert_eq!(vote_state_update.lockouts.len(), 2); assert_eq!( vote_state_update + .clone() .lockouts .into_iter() .collect::>(), vec![ Lockout { slot: existing_older_than_history_slot, - confirmation_count: 2, + confirmation_count: 3, }, Lockout { slot: vote_slot, - confirmation_count: 3, + confirmation_count: 2, } ] ); + assert!(do_process_vote_state_update( + &mut vote_state, + &slot_hashes, + 0, + vote_state_update, + Some(&FeatureSet::all_enabled()), + ) + .is_ok()); } #[test] fn test_check_update_vote_state_older_than_history_slots_filtered_and_not_filtered() { - let slot_hashes = build_slot_hashes(vec![1, 2, 3, 6]); - let vote_state = build_vote_state(vec![1, 2, 3, 6], &slot_hashes); + let slot_hashes = build_slot_hashes(vec![6]); + let mut vote_state = build_vote_state(vec![6], &slot_hashes); // Test with a `vote_state_update` where there exists both a slot: // 1) Less than `earliest_slot_in_history` @@ -3565,11 +3757,16 @@ mod tests { ]); vote_state_update.hash = vote_slot_hash; vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes) + .check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()), + ) .unwrap(); assert_eq!(vote_state_update.lockouts.len(), 3); assert_eq!( vote_state_update + .clone() .lockouts .into_iter() .collect::>(), @@ -3588,6 +3785,14 @@ mod tests { } ] ); + assert!(do_process_vote_state_update( + &mut vote_state, + &slot_hashes, + 0, + vote_state_update, + Some(&FeatureSet::all_enabled()), + ) + .is_ok()); } #[test] @@ -3614,8 +3819,11 @@ mod tests { VoteStateUpdate::from(vec![(missing_vote_slot, 2), (vote_slot, 3)]); vote_state_update.hash = vote_slot_hash; assert_eq!( - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes), + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()) + ), Err(VoteError::SlotsMismatch), ); @@ -3630,8 +3838,11 @@ mod tests { ]); vote_state_update.hash = vote_slot_hash; assert_eq!( - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes), + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()) + ), Err(VoteError::SlotsMismatch), ); } @@ -3639,7 +3850,7 @@ mod tests { #[test] fn test_check_update_vote_state_root_on_different_fork() { let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]); - let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes); + let vote_state = build_vote_state(vec![6], &slot_hashes); // Test with a `vote_state_update` where: // 1) The root is not present in slot hashes history @@ -3649,8 +3860,9 @@ mod tests { let new_root = 3; // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld - // errors - let vote_slot = vote_state.votes.back().unwrap().slot + 2; + // errors, but also this slot must be present in SlotHashes + let vote_slot = 8; + assert_eq!(vote_slot, slot_hashes.first().unwrap().0); let vote_slot_hash = slot_hashes .iter() .find(|(slot, _hash)| *slot == vote_slot) @@ -3660,8 +3872,11 @@ mod tests { vote_state_update.hash = vote_slot_hash; vote_state_update.root = Some(new_root); assert_eq!( - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes), + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()) + ), Err(VoteError::RootOnDifferentFork), ); } @@ -3681,8 +3896,11 @@ mod tests { let mut vote_state_update = VoteStateUpdate::from(vec![(8, 2), (missing_vote_slot, 3)]); vote_state_update.hash = vote_slot_hash; assert_eq!( - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes), + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()) + ), Err(VoteError::SlotsMismatch), ); } @@ -3690,7 +3908,7 @@ mod tests { #[test] fn test_check_update_vote_state_slot_all_slot_hashes_in_update_ok() { let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8]); - let vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes); + let mut vote_state = build_vote_state(vec![2, 4, 6], &slot_hashes); // Test with a `vote_state_update` where every slot in the history is // in the update @@ -3707,12 +3925,17 @@ mod tests { VoteStateUpdate::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]); vote_state_update.hash = vote_slot_hash; vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes) + .check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()), + ) .unwrap(); // Nothing in the update should have been filtered out assert_eq!( vote_state_update + .clone() .lockouts .into_iter() .collect::>(), @@ -3735,14 +3958,23 @@ mod tests { } ] ); + + assert!(do_process_vote_state_update( + &mut vote_state, + &slot_hashes, + 0, + vote_state_update, + Some(&FeatureSet::all_enabled()), + ) + .is_ok()); } #[test] fn test_check_update_vote_state_slot_some_slot_hashes_in_update_ok() { let slot_hashes = build_slot_hashes(vec![2, 4, 6, 8, 10]); - let vote_state = build_vote_state(vec![6], &slot_hashes); + let mut vote_state = build_vote_state(vec![6], &slot_hashes); - // Test with a `vote_state_update` where every only some slots in the history are + // Test with a `vote_state_update` where only some slots in the history are // in the update, and others slots in the history are missing. // Have to vote for a slot greater than the last vote in the vote state to avoid VoteTooOld @@ -3756,12 +3988,17 @@ mod tests { let mut vote_state_update = VoteStateUpdate::from(vec![(4, 2), (vote_slot, 1)]); vote_state_update.hash = vote_slot_hash; vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes) + .check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()), + ) .unwrap(); // Nothing in the update should have been filtered out assert_eq!( vote_state_update + .clone() .lockouts .into_iter() .collect::>(), @@ -3776,6 +4013,20 @@ mod tests { } ] ); + + // Because 6 from the original VoteState + // should not have been popped off in the proposed state, + // we should get a lockout conflict + assert_eq!( + do_process_vote_state_update( + &mut vote_state, + &slot_hashes, + 0, + vote_state_update, + Some(&FeatureSet::all_enabled()) + ), + Err(VoteError::LockoutConflict) + ); } #[test] @@ -3793,77 +4044,99 @@ mod tests { VoteStateUpdate::from(vec![(2, 4), (4, 3), (6, 2), (vote_slot, 1)]); vote_state_update.hash = vote_slot_hash; assert_eq!( - vote_state - .check_update_vote_state_slots_are_valid(&mut vote_state_update, &slot_hashes), + vote_state.check_update_vote_state_slots_are_valid( + &mut vote_state_update, + &slot_hashes, + Some(&FeatureSet::all_enabled()) + ), Err(VoteError::SlotHashMismatch), ); } #[test] - fn test_compact_vote_state_update_parity() { - let mut vote_state_update = VoteStateUpdate::from(vec![(2, 4), (4, 3), (6, 2), (7, 1)]); - vote_state_update.hash = Hash::new_unique(); - vote_state_update.root = Some(1); - - let compact_vote_state_update = CompactVoteStateUpdate::from(vote_state_update.clone()); - - assert_eq!(vote_state_update.slots(), compact_vote_state_update.slots()); - assert_eq!(vote_state_update.hash, compact_vote_state_update.hash); - assert_eq!(vote_state_update.root, compact_vote_state_update.root()); - - let vote_state_update_new = VoteStateUpdate::from(compact_vote_state_update); - assert_eq!(vote_state_update, vote_state_update_new); + fn test_serde_compact_vote_state_update() { + let mut rng = rand::thread_rng(); + for _ in 0..5000 { + run_serde_compact_vote_state_update(&mut rng); + } } - #[test] - fn test_compact_vote_state_update_large_offsets() { - let vote_state_update = VoteStateUpdate::from(vec![ - (0, 31), - (1, 30), - (2, 29), - (3, 28), - (u64::pow(2, 28), 17), - (u64::pow(2, 28) + u64::pow(2, 16), 1), - ]); - let compact_vote_state_update = CompactVoteStateUpdate::from(vote_state_update.clone()); - - assert_eq!(vote_state_update.slots(), compact_vote_state_update.slots()); - - let vote_state_update_new = VoteStateUpdate::from(compact_vote_state_update); - assert_eq!(vote_state_update, vote_state_update_new); + #[allow(clippy::integer_arithmetic)] + fn run_serde_compact_vote_state_update(rng: &mut R) { + let lockouts: VecDeque<_> = std::iter::repeat_with(|| Lockout { + slot: 149_303_885 + rng.gen_range(0, 10_000), + confirmation_count: rng.gen_range(0, 33), + }) + .take(32) + .sorted_by_key(|lockout| lockout.slot) + .collect(); + let root = rng + .gen_ratio(1, 2) + .then(|| lockouts[0].slot - rng.gen_range(0, 1_000)); + let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen()); + let hash = Hash::from(rng.gen::<[u8; 32]>()); + let vote_state_update = VoteStateUpdate { + lockouts, + root, + hash, + timestamp, + }; + #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] + enum VoteInstruction { + #[serde(with = "serde_compact_vote_state_update")] + UpdateVoteState(VoteStateUpdate), + UpdateVoteStateSwitch( + #[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate, + Hash, + ), + } + let vote = VoteInstruction::UpdateVoteState(vote_state_update.clone()); + let bytes = bincode::serialize(&vote).unwrap(); + assert_eq!(vote, bincode::deserialize(&bytes).unwrap()); + let hash = Hash::from(rng.gen::<[u8; 32]>()); + let vote = VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash); + let bytes = bincode::serialize(&vote).unwrap(); + assert_eq!(vote, bincode::deserialize(&bytes).unwrap()); + } + + #[test_case(0, true; "first slot")] + #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")] + #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2 + 1, false; "halfway through epoch plus one")] + #[test_case(DEFAULT_SLOTS_PER_EPOCH - 1, false; "last slot in epoch")] + #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")] + fn test_epoch_half_check(slot: Slot, expected_allowed: bool) { + let epoch_schedule = EpochSchedule::without_warmup(); + assert_eq!( + is_commission_update_allowed(slot, &epoch_schedule), + expected_allowed + ); } #[test] - fn test_compact_vote_state_update_border_conditions() { - let two_31 = u64::pow(2, 31); - let two_15 = u64::pow(2, 15); - let vote_state_update = VoteStateUpdate::from(vec![ - (0, 31), - (two_31, 16), - (two_31 + 1, 15), - (two_31 + two_15, 7), - (two_31 + two_15 + 1, 6), - (two_31 + two_15 + 1 + 64, 1), - ]); - let compact_vote_state_update = CompactVoteStateUpdate::from(vote_state_update.clone()); - - assert_eq!(vote_state_update.slots(), compact_vote_state_update.slots()); - - let vote_state_update_new = VoteStateUpdate::from(compact_vote_state_update); - assert_eq!(vote_state_update, vote_state_update_new); + fn test_warmup_epoch_half_check_with_warmup() { + let epoch_schedule = EpochSchedule::default(); + let first_normal_slot = epoch_schedule.first_normal_slot; + // first slot works + assert!(is_commission_update_allowed(0, &epoch_schedule)); + // right before first normal slot works, since all warmup slots allow + // commission updates + assert!(is_commission_update_allowed( + first_normal_slot - 1, + &epoch_schedule + )); } - #[test] - fn test_compact_vote_state_update_large_root() { - let two_58 = u64::pow(2, 58); - let two_31 = u64::pow(2, 31); - let mut vote_state_update = VoteStateUpdate::from(vec![(two_58, 31), (two_58 + two_31, 1)]); - vote_state_update.root = Some(two_31); - let compact_vote_state_update = CompactVoteStateUpdate::from(vote_state_update.clone()); - - assert_eq!(vote_state_update.slots(), compact_vote_state_update.slots()); - - let vote_state_update_new = VoteStateUpdate::from(compact_vote_state_update); - assert_eq!(vote_state_update, vote_state_update_new); + #[test_case(0, true; "first slot")] + #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2, true; "halfway through epoch")] + #[test_case(DEFAULT_SLOTS_PER_EPOCH / 2 + 1, false; "halfway through epoch plus one")] + #[test_case(DEFAULT_SLOTS_PER_EPOCH - 1, false; "last slot in epoch")] + #[test_case(DEFAULT_SLOTS_PER_EPOCH, true; "first slot in second epoch")] + fn test_epoch_half_check_with_warmup(slot: Slot, expected_allowed: bool) { + let epoch_schedule = EpochSchedule::default(); + let first_normal_slot = epoch_schedule.first_normal_slot; + assert_eq!( + is_commission_update_allowed(first_normal_slot + slot, &epoch_schedule), + expected_allowed + ); } } diff --git a/programs/zk-token-proof-tests/Cargo.toml b/programs/zk-token-proof-tests/Cargo.toml new file mode 100644 index 00000000000000..04324e7329c5f9 --- /dev/null +++ b/programs/zk-token-proof-tests/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "solana-zk-token-proof-program-tests" +authors = ["Solana Labs Maintainers "] +repository = "https://github.com/solana-labs/solana" +version = "1.14.24" +license = "Apache-2.0" +edition = "2021" +publish = false + +[dev-dependencies] +bytemuck = { version = "1.11.0", features = ["derive"] } +solana-program-runtime = { path = "../../program-runtime", version = "=1.14.24" } +solana-program-test = { path = "../../program-test", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } +solana-zk-token-sdk = { path = "../../zk-token-sdk", version = "=1.14.24" } diff --git a/programs/zk-token-proof-tests/tests/process_transaction.rs b/programs/zk-token-proof-tests/tests/process_transaction.rs new file mode 100644 index 00000000000000..ca94774b788386 --- /dev/null +++ b/programs/zk-token-proof-tests/tests/process_transaction.rs @@ -0,0 +1,800 @@ +use { + bytemuck::Pod, + solana_program_test::*, + solana_sdk::{ + instruction::InstructionError, + signature::Signer, + signer::keypair::Keypair, + system_instruction, + transaction::{Transaction, TransactionError}, + }, + solana_zk_token_sdk::{ + encryption::{elgamal::ElGamalKeypair, pedersen::PedersenOpening}, + instruction::*, + zk_token_proof_instruction::*, + zk_token_proof_program, + zk_token_proof_state::ProofContextState, + }, + std::mem::size_of, +}; + +const VERIFY_INSTRUCTION_TYPES: [ProofInstruction; 6] = [ + ProofInstruction::VerifyZeroBalance, + ProofInstruction::VerifyWithdraw, + ProofInstruction::VerifyCiphertextCiphertextEquality, + ProofInstruction::VerifyTransfer, + ProofInstruction::VerifyTransferWithFee, + ProofInstruction::VerifyPubkeyValidity, +]; + +#[tokio::test] +async fn test_zero_balance() { + let elgamal_keypair = ElGamalKeypair::new_rand(); + + let zero_ciphertext = elgamal_keypair.public.encrypt(0_u64); + let success_proof_data = ZeroBalanceProofData::new(&elgamal_keypair, &zero_ciphertext).unwrap(); + + let incorrect_keypair = ElGamalKeypair { + public: ElGamalKeypair::new_rand().public, + secret: ElGamalKeypair::new_rand().secret, + }; + let fail_proof_data = ZeroBalanceProofData::new(&incorrect_keypair, &zero_ciphertext).unwrap(); + + test_verify_proof_without_context( + ProofInstruction::VerifyZeroBalance, + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_verify_proof_with_context( + ProofInstruction::VerifyZeroBalance, + size_of::>(), + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_close_context_state( + ProofInstruction::VerifyZeroBalance, + size_of::>(), + &success_proof_data, + ) + .await; +} + +#[tokio::test] +async fn test_ciphertext_ciphertext_equality() { + let source_keypair = ElGamalKeypair::new_rand(); + let destination_keypair = ElGamalKeypair::new_rand(); + + let amount: u64 = 0; + let source_ciphertext = source_keypair.public.encrypt(amount); + + let destination_opening = PedersenOpening::new_rand(); + let destination_ciphertext = destination_keypair + .public + .encrypt_with(amount, &destination_opening); + + let success_proof_data = CiphertextCiphertextEqualityProofData::new( + &source_keypair, + &destination_keypair.public, + &source_ciphertext, + &destination_ciphertext, + &destination_opening, + amount, + ) + .unwrap(); + + let incorrect_keypair = ElGamalKeypair { + public: ElGamalKeypair::new_rand().public, + secret: ElGamalKeypair::new_rand().secret, + }; + let fail_proof_data = CiphertextCiphertextEqualityProofData::new( + &incorrect_keypair, + &destination_keypair.public, + &source_ciphertext, + &destination_ciphertext, + &destination_opening, + amount, + ) + .unwrap(); + + test_verify_proof_without_context( + ProofInstruction::VerifyCiphertextCiphertextEquality, + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_verify_proof_with_context( + ProofInstruction::VerifyCiphertextCiphertextEquality, + size_of::>(), + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_close_context_state( + ProofInstruction::VerifyCiphertextCiphertextEquality, + size_of::>(), + &success_proof_data, + ) + .await; +} + +#[tokio::test] +async fn test_transfer() { + let source_keypair = ElGamalKeypair::new_rand(); + let dest_pubkey = ElGamalKeypair::new_rand().public; + let auditor_pubkey = ElGamalKeypair::new_rand().public; + + let spendable_balance: u64 = 0; + let spendable_ciphertext = source_keypair.public.encrypt(spendable_balance); + + let transfer_amount: u64 = 0; + + let success_proof_data = TransferData::new( + transfer_amount, + (spendable_balance, &spendable_ciphertext), + &source_keypair, + (&dest_pubkey, &auditor_pubkey), + ) + .unwrap(); + + let incorrect_keypair = ElGamalKeypair { + public: ElGamalKeypair::new_rand().public, + secret: ElGamalKeypair::new_rand().secret, + }; + + let fail_proof_data = TransferData::new( + transfer_amount, + (spendable_balance, &spendable_ciphertext), + &incorrect_keypair, + (&dest_pubkey, &auditor_pubkey), + ) + .unwrap(); + + test_verify_proof_without_context( + ProofInstruction::VerifyTransfer, + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_verify_proof_with_context( + ProofInstruction::VerifyTransfer, + size_of::>(), + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_close_context_state( + ProofInstruction::VerifyTransfer, + size_of::>(), + &success_proof_data, + ) + .await; +} + +#[tokio::test] +async fn test_transfer_with_fee() { + let source_keypair = ElGamalKeypair::new_rand(); + let destination_pubkey = ElGamalKeypair::new_rand().public; + let auditor_pubkey = ElGamalKeypair::new_rand().public; + let withdraw_withheld_authority_pubkey = ElGamalKeypair::new_rand().public; + + let spendable_balance: u64 = 120; + let spendable_ciphertext = source_keypair.public.encrypt(spendable_balance); + + let transfer_amount: u64 = 0; + + let fee_parameters = FeeParameters { + fee_rate_basis_points: 400, + maximum_fee: 3, + }; + + let success_proof_data = TransferWithFeeData::new( + transfer_amount, + (spendable_balance, &spendable_ciphertext), + &source_keypair, + (&destination_pubkey, &auditor_pubkey), + fee_parameters, + &withdraw_withheld_authority_pubkey, + ) + .unwrap(); + + let incorrect_keypair = ElGamalKeypair { + public: ElGamalKeypair::new_rand().public, + secret: ElGamalKeypair::new_rand().secret, + }; + + let fail_proof_data = TransferWithFeeData::new( + transfer_amount, + (spendable_balance, &spendable_ciphertext), + &incorrect_keypair, + (&destination_pubkey, &auditor_pubkey), + fee_parameters, + &withdraw_withheld_authority_pubkey, + ) + .unwrap(); + + test_verify_proof_without_context( + ProofInstruction::VerifyTransferWithFee, + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_verify_proof_with_context( + ProofInstruction::VerifyTransferWithFee, + size_of::>(), + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_close_context_state( + ProofInstruction::VerifyTransferWithFee, + size_of::>(), + &success_proof_data, + ) + .await; +} + +#[tokio::test] +async fn test_withdraw() { + let elgamal_keypair = ElGamalKeypair::new_rand(); + + let current_balance: u64 = 77; + let current_ciphertext = elgamal_keypair.public.encrypt(current_balance); + let withdraw_amount: u64 = 55; + + let success_proof_data = WithdrawData::new( + withdraw_amount, + &elgamal_keypair, + current_balance, + ¤t_ciphertext, + ) + .unwrap(); + + let incorrect_keypair = ElGamalKeypair { + public: ElGamalKeypair::new_rand().public, + secret: ElGamalKeypair::new_rand().secret, + }; + let fail_proof_data = WithdrawData::new( + withdraw_amount, + &incorrect_keypair, + current_balance, + ¤t_ciphertext, + ) + .unwrap(); + + test_verify_proof_without_context( + ProofInstruction::VerifyWithdraw, + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_verify_proof_with_context( + ProofInstruction::VerifyWithdraw, + size_of::>(), + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_close_context_state( + ProofInstruction::VerifyWithdraw, + size_of::>(), + &success_proof_data, + ) + .await; +} + +#[tokio::test] +async fn test_pubkey_validity() { + let elgamal_keypair = ElGamalKeypair::new_rand(); + + let success_proof_data = PubkeyValidityData::new(&elgamal_keypair).unwrap(); + + let incorrect_keypair = ElGamalKeypair { + public: ElGamalKeypair::new_rand().public, + secret: ElGamalKeypair::new_rand().secret, + }; + + let fail_proof_data = PubkeyValidityData::new(&incorrect_keypair).unwrap(); + + test_verify_proof_without_context( + ProofInstruction::VerifyPubkeyValidity, + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_verify_proof_with_context( + ProofInstruction::VerifyPubkeyValidity, + size_of::>(), + &success_proof_data, + &fail_proof_data, + ) + .await; + + test_close_context_state( + ProofInstruction::VerifyPubkeyValidity, + size_of::>(), + &success_proof_data, + ) + .await; +} + +async fn test_verify_proof_without_context( + proof_instruction: ProofInstruction, + success_proof_data: &T, + fail_proof_data: &T, +) where + T: Pod + ZkProofData, + U: Pod, +{ + let mut context = ProgramTest::default().start_with_context().await; + + let client = &mut context.banks_client; + let payer = &context.payer; + let recent_blockhash = context.last_blockhash; + + // verify a valid proof (wihtout creating a context account) + let instructions = vec![proof_instruction.encode_verify_proof(None, success_proof_data)]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer], + recent_blockhash, + ); + client.process_transaction(transaction).await.unwrap(); + + // try to verify an invalid proof (without creating a context account) + let instructions = vec![proof_instruction.encode_verify_proof(None, fail_proof_data)]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer], + recent_blockhash, + ); + let err = client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + err, + TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) + ); + + // try to verify a valid proof, but with a wrong proof type + for wrong_instruction_type in VERIFY_INSTRUCTION_TYPES { + if proof_instruction == wrong_instruction_type { + continue; + } + + let instruction = + vec![wrong_instruction_type.encode_verify_proof(None, success_proof_data)]; + let transaction = Transaction::new_signed_with_payer( + &instruction, + Some(&payer.pubkey()), + &[payer], + recent_blockhash, + ); + let err = client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + err, + TransactionError::InstructionError(0, InstructionError::InvalidInstructionData) + ); + } +} + +async fn test_verify_proof_with_context( + instruction_type: ProofInstruction, + space: usize, + success_proof_data: &T, + fail_proof_data: &T, +) where + T: Pod + ZkProofData, + U: Pod, +{ + let mut context = ProgramTest::default().start_with_context().await; + let rent = context.banks_client.get_rent().await.unwrap(); + + let client = &mut context.banks_client; + let payer = &context.payer; + let recent_blockhash = context.last_blockhash; + + let context_state_account = Keypair::new(); + let context_state_authority = Keypair::new(); + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + // try to create proof context state with an invalid proof + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), fail_proof_data), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account], + recent_blockhash, + ); + let err = client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + err, + TransactionError::InstructionError(1, InstructionError::InvalidInstructionData) + ); + + // try to create proof context state with incorrect account data length + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + (space.checked_sub(1).unwrap()) as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account], + recent_blockhash, + ); + let err = client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + err, + TransactionError::InstructionError(1, InstructionError::InvalidAccountData) + ); + + // try to create proof context state with insufficient rent + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space).checked_sub(1).unwrap(), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account], + recent_blockhash, + ); + let err = client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + err, + TransactionError::InsufficientFundsForRent { account_index: 1 }, + ); + + // try to create proof context state with an invalid `ProofType` + for wrong_instruction_type in VERIFY_INSTRUCTION_TYPES { + if instruction_type == wrong_instruction_type { + continue; + } + + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + wrong_instruction_type + .encode_verify_proof(Some(context_state_info), success_proof_data), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account], + recent_blockhash, + ); + let err = client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + err, + TransactionError::InstructionError(1, InstructionError::InvalidInstructionData) + ); + } + + // successfully create a proof context state + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account], + recent_blockhash, + ); + client.process_transaction(transaction).await.unwrap(); + + // try overwriting the context state + let instructions = + vec![instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data)]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer], + recent_blockhash, + ); + let err = client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + err, + TransactionError::InstructionError(0, InstructionError::AccountAlreadyInitialized) + ); + + // self-owned context state account + let context_state_account_and_authority = Keypair::new(); + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account_and_authority.pubkey(), + context_state_authority: &context_state_account_and_authority.pubkey(), + }; + + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account_and_authority.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account_and_authority], + recent_blockhash, + ); + client.process_transaction(transaction).await.unwrap(); +} + +async fn test_close_context_state( + instruction_type: ProofInstruction, + space: usize, + success_proof_data: &T, +) where + T: Pod + ZkProofData, + U: Pod, +{ + let mut context = ProgramTest::default().start_with_context().await; + let rent = context.banks_client.get_rent().await.unwrap(); + + let client = &mut context.banks_client; + let payer = &context.payer; + let recent_blockhash = context.last_blockhash; + + let context_state_account = Keypair::new(); + let context_state_authority = Keypair::new(); + + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }; + + let destination_account = Keypair::new(); + + // create a proof context state + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account], + recent_blockhash, + ); + client.process_transaction(transaction).await.unwrap(); + + // try to close context state with incorrect authority + let incorrect_authority = Keypair::new(); + let instruction = close_context_state( + ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &incorrect_authority.pubkey(), + }, + &destination_account.pubkey(), + ); + let transaction = Transaction::new_signed_with_payer( + &[instruction], + Some(&payer.pubkey()), + &[payer, &incorrect_authority], + recent_blockhash, + ); + let err = client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + err, + TransactionError::InstructionError(0, InstructionError::InvalidAccountOwner) + ); + + // successfully close proof context state + let instruction = close_context_state( + ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }, + &destination_account.pubkey(), + ); + let transaction = Transaction::new_signed_with_payer( + &[instruction.clone()], + Some(&payer.pubkey()), + &[payer, &context_state_authority], + recent_blockhash, + ); + client.process_transaction(transaction).await.unwrap(); + + // create and close proof context in a single transaction + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account.pubkey(), + 0_u64, // do not deposit rent + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data), + close_context_state( + ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }, + &destination_account.pubkey(), + ), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account, &context_state_authority], + recent_blockhash, + ); + client.process_transaction(transaction).await.unwrap(); + + // close proof context state with owner as destination + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account.pubkey(), + 0_u64, + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data), + close_context_state( + ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }, + &context_state_authority.pubkey(), + ), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account, &context_state_authority], + recent_blockhash, + ); + client.process_transaction(transaction).await.unwrap(); + + // try close account with itself as destination + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account.pubkey(), + 0_u64, + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data), + close_context_state( + ContextStateInfo { + context_state_account: &context_state_account.pubkey(), + context_state_authority: &context_state_authority.pubkey(), + }, + &context_state_account.pubkey(), + ), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account, &context_state_authority], + recent_blockhash, + ); + let err = client + .process_transaction(transaction) + .await + .unwrap_err() + .unwrap(); + assert_eq!( + err, + TransactionError::InstructionError(2, InstructionError::InvalidInstructionData) + ); + + // close self-owned proof context accounts + let context_state_account_and_authority = Keypair::new(); + let context_state_info = ContextStateInfo { + context_state_account: &context_state_account_and_authority.pubkey(), + context_state_authority: &context_state_account_and_authority.pubkey(), + }; + + let instructions = vec![ + system_instruction::create_account( + &payer.pubkey(), + &context_state_account_and_authority.pubkey(), + 0_u64, + space as u64, + &zk_token_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), success_proof_data), + close_context_state(context_state_info, &context_state_account.pubkey()), + ]; + let transaction = Transaction::new_signed_with_payer( + &instructions, + Some(&payer.pubkey()), + &[payer, &context_state_account_and_authority], + recent_blockhash, + ); + client.process_transaction(transaction).await.unwrap(); +} diff --git a/programs/zk-token-proof/Cargo.toml b/programs/zk-token-proof/Cargo.toml index e4b9c68582f21e..a459ce946fa4a2 100644 --- a/programs/zk-token-proof/Cargo.toml +++ b/programs/zk-token-proof/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-zk-token-proof-program" description = "Solana Zk Token Proof Program" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" -version = "1.11.6" +version = "1.14.24" license = "Apache-2.0" edition = "2021" @@ -12,6 +12,6 @@ bytemuck = { version = "1.11.0", features = ["derive"] } getrandom = { version = "0.1", features = ["dummy"] } num-derive = "0.3" num-traits = "0.2" -solana-program-runtime = { path = "../../program-runtime", version = "=1.11.6" } -solana-sdk = { path = "../../sdk", version = "=1.11.6" } -solana-zk-token-sdk = { path = "../../zk-token-sdk", version = "=1.11.6" } +solana-program-runtime = { path = "../../program-runtime", version = "=1.14.24" } +solana-sdk = { path = "../../sdk", version = "=1.14.24" } +solana-zk-token-sdk = { path = "../../zk-token-sdk", version = "=1.14.24" } diff --git a/programs/zk-token-proof/src/lib.rs b/programs/zk-token-proof/src/lib.rs index 6c51ade1d836f1..bd1e4f46c1339a 100644 --- a/programs/zk-token-proof/src/lib.rs +++ b/programs/zk-token-proof/src/lib.rs @@ -3,26 +3,114 @@ use { bytemuck::Pod, solana_program_runtime::{ic_msg, invoke_context::InvokeContext}, - solana_sdk::instruction::{InstructionError, TRANSACTION_LEVEL_STACK_HEIGHT}, - solana_zk_token_sdk::zk_token_proof_instruction::*, + solana_sdk::{ + instruction::{InstructionError, TRANSACTION_LEVEL_STACK_HEIGHT}, + system_program, + }, + solana_zk_token_sdk::{ + zk_token_proof_instruction::*, + zk_token_proof_program::id, + zk_token_proof_state::{ProofContextState, ProofContextStateMeta}, + }, std::result::Result, }; -fn verify(invoke_context: &mut InvokeContext) -> Result<(), InstructionError> { +fn process_verify_proof(invoke_context: &mut InvokeContext) -> Result<(), InstructionError> +where + T: Pod + ZkProofData, + U: Pod, +{ let transaction_context = &invoke_context.transaction_context; let instruction_context = transaction_context.get_current_instruction_context()?; let instruction_data = instruction_context.get_instruction_data(); - let instruction = ProofInstruction::decode_data::(instruction_data); - - let proof = instruction.ok_or_else(|| { + let proof_data = ProofInstruction::proof_data::(instruction_data).ok_or_else(|| { ic_msg!(invoke_context, "invalid proof data"); InstructionError::InvalidInstructionData })?; - proof.verify().map_err(|err| { - ic_msg!(invoke_context, "proof verification failed: {:?}", err); + proof_data.verify_proof().map_err(|err| { + ic_msg!(invoke_context, "proof_verification failed: {:?}", err); InstructionError::InvalidInstructionData - }) + })?; + + // create context state if accounts are provided with the instruction + if instruction_context.get_number_of_instruction_accounts() > 0 { + let context_state_authority = *instruction_context + .try_borrow_instruction_account(transaction_context, 1)? + .get_key(); + + let mut proof_context_account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + + if *proof_context_account.get_owner() != id() { + return Err(InstructionError::InvalidAccountOwner); + } + + let proof_context_state_meta = + ProofContextStateMeta::try_from_bytes(proof_context_account.get_data())?; + + if proof_context_state_meta.proof_type != ProofType::Uninitialized.into() { + return Err(InstructionError::AccountAlreadyInitialized); + } + + let context_state_data = ProofContextState::encode( + &context_state_authority, + T::PROOF_TYPE, + proof_data.context_data(), + ); + + if proof_context_account.get_data().len() != context_state_data.len() { + return Err(InstructionError::InvalidAccountData); + } + + proof_context_account.set_data(&context_state_data)?; + } + + Ok(()) +} + +fn process_close_proof_context(invoke_context: &mut InvokeContext) -> Result<(), InstructionError> { + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + + let owner_pubkey = { + let owner_account = + instruction_context.try_borrow_instruction_account(transaction_context, 2)?; + + if !owner_account.is_signer() { + return Err(InstructionError::MissingRequiredSignature); + } + *owner_account.get_key() + }; // done with `owner_account`, so drop it to prevent a potential double borrow + + let proof_context_account_pubkey = *instruction_context + .try_borrow_instruction_account(transaction_context, 0)? + .get_key(); + let destination_account_pubkey = *instruction_context + .try_borrow_instruction_account(transaction_context, 1)? + .get_key(); + if proof_context_account_pubkey == destination_account_pubkey { + return Err(InstructionError::InvalidInstructionData); + } + + let mut proof_context_account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + let proof_context_state_meta = + ProofContextStateMeta::try_from_bytes(proof_context_account.get_data())?; + let expected_owner_pubkey = proof_context_state_meta.context_state_authority; + + if owner_pubkey != expected_owner_pubkey { + return Err(InstructionError::InvalidAccountOwner); + } + + let mut destination_account = + instruction_context.try_borrow_instruction_account(transaction_context, 1)?; + destination_account.checked_add_lamports(proof_context_account.get_lamports())?; + proof_context_account.set_lamports(0)?; + proof_context_account.set_data_length(0)?; + proof_context_account.set_owner(system_program::id().as_ref())?; + + Ok(()) } pub fn process_instruction( @@ -44,28 +132,40 @@ pub fn process_instruction( let transaction_context = &invoke_context.transaction_context; let instruction_context = transaction_context.get_current_instruction_context()?; let instruction_data = instruction_context.get_instruction_data(); - let instruction = ProofInstruction::decode_type(instruction_data); + let instruction = ProofInstruction::instruction_type(instruction_data) + .ok_or(InstructionError::InvalidInstructionData)?; - match instruction.ok_or(InstructionError::InvalidInstructionData)? { - ProofInstruction::VerifyCloseAccount => { + match instruction { + ProofInstruction::CloseContextState => { + ic_msg!(invoke_context, "CloseContextState"); + process_close_proof_context(invoke_context) + } + ProofInstruction::VerifyZeroBalance => { ic_msg!(invoke_context, "VerifyCloseAccount"); - verify::(invoke_context) + process_verify_proof::(invoke_context) } ProofInstruction::VerifyWithdraw => { ic_msg!(invoke_context, "VerifyWithdraw"); - verify::(invoke_context) + process_verify_proof::(invoke_context) } - ProofInstruction::VerifyWithdrawWithheldTokens => { + ProofInstruction::VerifyCiphertextCiphertextEquality => { ic_msg!(invoke_context, "VerifyWithdrawWithheldTokens"); - verify::(invoke_context) + process_verify_proof::< + CiphertextCiphertextEqualityProofData, + CiphertextCiphertextEqualityProofContext, + >(invoke_context) } ProofInstruction::VerifyTransfer => { ic_msg!(invoke_context, "VerifyTransfer"); - verify::(invoke_context) + process_verify_proof::(invoke_context) } ProofInstruction::VerifyTransferWithFee => { ic_msg!(invoke_context, "VerifyTransferWithFee"); - verify::(invoke_context) + process_verify_proof::(invoke_context) + } + ProofInstruction::VerifyPubkeyValidity => { + ic_msg!(invoke_context, "VerifyPubkeyValidity"); + process_verify_proof::(invoke_context) } } } diff --git a/rayon-threadlimit/Cargo.toml b/rayon-threadlimit/Cargo.toml index 7d14d47379c5fa..986623d7556d5a 100644 --- a/rayon-threadlimit/Cargo.toml +++ b/rayon-threadlimit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-rayon-threadlimit" -version = "1.11.6" +version = "1.14.24" description = "solana-rayon-threadlimit" homepage = "https://solana.com/" documentation = "https://docs.rs/solana-rayon-threadlimit" diff --git a/rbpf-cli/Cargo.toml b/rbpf-cli/Cargo.toml index 51227be62e1609..2e3b2d8399c7b7 100644 --- a/rbpf-cli/Cargo.toml +++ b/rbpf-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rbpf-cli" -version = "1.11.6" +version = "1.14.24" description = "CLI to test and analyze eBPF programs" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/rbpf" @@ -13,8 +13,8 @@ publish = false clap = { version = "3.1.5", features = ["cargo"] } serde = "1.0.138" serde_json = "1.0.81" -solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-program-runtime = { path = "../program-runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-program-runtime = { path = "../program-runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } solana_rbpf = "=0.2.31" diff --git a/rbpf-cli/src/main.rs b/rbpf-cli/src/main.rs index 3a136606b12422..e5aba7c053dfa6 100644 --- a/rbpf-cli/src/main.rs +++ b/rbpf-cli/src/main.rs @@ -128,7 +128,7 @@ native machine code before execting it in the virtual machine.", .long("use") .takes_value(true) .value_name("VALUE") - .possible_values(&["cfg", "disassembler", "interpreter", "jit"]) + .possible_values(["cfg", "disassembler", "interpreter", "jit"]) .default_value("jit"), ) .arg( @@ -159,7 +159,7 @@ native machine code before execting it in the virtual machine.", .value_name("FORMAT") .global(true) .takes_value(true) - .possible_values(&["json", "json-compact"]), + .possible_values(["json", "json-compact"]), ) .get_matches(); @@ -243,7 +243,7 @@ native machine code before execting it in the virtual machine.", let mut instruction_meter = ThisInstructionMeter { compute_meter }; let program = matches.value_of("PROGRAM").unwrap(); - let mut file = File::open(&Path::new(program)).unwrap(); + let mut file = File::open(Path::new(program)).unwrap(); let mut magic = [0u8; 4]; file.read_exact(&mut magic).unwrap(); file.seek(SeekFrom::Start(0)).unwrap(); diff --git a/receipt-tree/Cargo.toml b/receipt-tree/Cargo.toml new file mode 100644 index 00000000000000..95c3355634f94e --- /dev/null +++ b/receipt-tree/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "solana-receipt-tree" +version = "1.14.24" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +firedancer-sys = {path ="../firedancer/ffi/rust/firedancer-sys"} +solana-sdk = { path = "../sdk", version = "=1.14.24" } +log = "0.4.17" + +[dev-dependencies] +rand = "0.8.5" diff --git a/receipt-tree/src/lib.rs b/receipt-tree/src/lib.rs new file mode 100644 index 00000000000000..33af1bb2f4da5f --- /dev/null +++ b/receipt-tree/src/lib.rs @@ -0,0 +1,4 @@ + +mod receipt_tree; +pub use receipt_tree::*; + diff --git a/receipt-tree/src/receipt_tree.rs b/receipt-tree/src/receipt_tree.rs new file mode 100644 index 00000000000000..eb9bad0c4225d6 --- /dev/null +++ b/receipt-tree/src/receipt_tree.rs @@ -0,0 +1,164 @@ +use std::fmt::Debug; + +use { + firedancer_sys::ballet::{ + fd_bmtree32_commit_append, fd_bmtree32_commit_fini, fd_bmtree32_commit_t, + fd_bmtree32_hash_leaf, fd_bmtree32_node, + }, + log::warn, + solana_sdk::hash::Hash, + std::{mem, os::raw::c_void, slice::from_raw_parts}, +}; + +pub fn hash_leaf(node: &mut fd_bmtree32_node, data: &mut &[&[u8]]) { + // &mut &[&[u8]; 2]) + unsafe { + fd_bmtree32_hash_leaf( + node, + data as *mut _ as *mut c_void, + mem::size_of::<&[&[u8]; 2]>().try_into().unwrap(), + ) + }; +} + +pub struct ReceiptTree { + pub tree: fd_bmtree32_commit_t, + pub root: [u8; 32], +} +impl Default for ReceiptTree{ + fn default() -> Self { + ReceiptTree::new() + } +} +impl Debug for ReceiptTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ReceiptTree") + .field("leaves", &self.tree.leaf_cnt) + .field("root", &self.root) + .finish() + } +} +impl ReceiptTree { + /// Creates an empty tree + pub fn new() -> Self { + let mut nodes = vec![]; + for _ in 0..63 { + let n = fd_bmtree32_node { hash: [0u8; 32] }; + nodes.push(n); + } + let state = fd_bmtree32_commit_t { + leaf_cnt: 0, + __bindgen_padding_0: [0u64; 3], + node_buf: convert_to_array(nodes), + }; + + ReceiptTree { + tree: state, + root: [0u8; 32], + } + } + // Append just one leaf to the tree + pub fn append_leaf(&mut self, mut leaf: &[&[u8]]) { + let mut leaf_node = fd_bmtree32_node { hash: [0u8; 32] }; + // let mut k = &[data[i].0.as_slice(), &data[i].1.to_be_bytes()]; + hash_leaf(&mut leaf_node, &mut leaf); + unsafe { + fd_bmtree32_commit_append(&mut self.tree, &leaf_node, 1); + } + } + /// Finalise the commitment and get the 32 byte root + pub fn get_root_mutable(&mut self) -> Result { + let byte = unsafe { fd_bmtree32_commit_fini(&mut self.tree) }; + let root = unsafe { from_raw_parts(byte, 32) }; + match slice_to_array_32(root) { + Ok(hash) => { + Ok(Hash::new_from_array(*hash))}, + Err(e) => Err(e), + } + } + // /// Finalise the commitment and get the 32 byte root + // pub fn get_root() -> Result { + // let mut tree = ReceiptTree::new(); + // let byte = unsafe { fd_bmtree32_commit_fini(&mut tree.tree) }; + // let root = unsafe { from_raw_parts(byte, 32) }; + // match slice_to_array_32(root) { + // Ok(hash) => { + // Ok(Hash::new_from_array(*hash))}, + // Err(e) => Err(e), + // } + // } +} +fn convert_to_array(v: Vec) -> [T; 63] { + v.try_into() + .unwrap_or_else(|v: Vec| panic!("Expected a Vec of length 63 but it was {}", v.len())) +} + +#[derive(Debug)] +pub struct TryFromSliceError(String); + +fn slice_to_array_32(slice: &[T]) -> Result<&[T; 32], TryFromSliceError> { + if slice.len() <= 32 { + let ptr = slice.as_ptr() as *const [T; 32]; + unsafe { Ok(&*ptr) } + } else { + Err(TryFromSliceError(format!( + "The array was expected to be of length 32 but is actually {}", + slice.len() + ))) + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + rand::{self, Rng}, + solana_sdk::signature::Signature, + }; + + #[test] + fn create_receipt_tree_for_one() { + let mut sigs = vec![]; + let mut statuses = vec![]; + for _ in 0..1 { + sigs.push(Signature::new_unique().to_string().as_bytes().to_owned()); + statuses.push(rand::thread_rng().gen_range(0..2) as u8); + } + let data: Vec<(Vec, u8)> = sigs.into_iter().zip(statuses.clone().into_iter()).collect(); + let mut receipt_tree = ReceiptTree::new(); + receipt_tree.append_leaf(&[data[0].0.as_slice(), &data[0].1.to_be_bytes()]); + let root = receipt_tree.get_root_mutable(); + match root { + Ok(hash) => { + println!("Hash: {:?}", hash.to_string()); + } + Err(e) => { + println!("Error: {:?}", e); + } + } + } + #[test] + fn create_receipt_tree_for_many() { + let mut sigs = vec![]; + let mut statuses = vec![]; + for _ in 0..100000 { + sigs.push(Signature::new_unique().to_string().as_bytes().to_owned()); + statuses.push(rand::thread_rng().gen_range(0..2) as u8); + } + let data: Vec<(Vec, u8)> = sigs.into_iter().zip(statuses.clone().into_iter()).collect(); + let mut receipt_tree = ReceiptTree::new(); + for i in 0..data.len() { + receipt_tree.append_leaf(&[data[i].0.as_slice(), &data[i].1.to_be_bytes()]); + } + + let root = receipt_tree.get_root_mutable(); + match root { + Ok(hash) => { + println!("Hash: {:?}", hash.to_string()); + } + Err(e) => { + println!("Error: {:?}", e); + } + } + } +} diff --git a/remote-wallet/Cargo.toml b/remote-wallet/Cargo.toml index 5552ffcde9e580..8cba36c4b9a8f4 100644 --- a/remote-wallet/Cargo.toml +++ b/remote-wallet/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-remote-wallet" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -19,7 +19,7 @@ num-traits = { version = "0.2" } parking_lot = "0.12" qstring = "0.7.2" semver = "1.0" -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } thiserror = "1.0" uriparse = "0.6.4" diff --git a/remote-wallet/src/bin/ledger-udev.rs b/remote-wallet/src/bin/ledger-udev.rs index d5f4b8573730ea..fef91aeb518158 100644 --- a/remote-wallet/src/bin/ledger-udev.rs +++ b/remote-wallet/src/bin/ledger-udev.rs @@ -36,7 +36,7 @@ fn main() -> Result<(), Box> { Command::new("udevadm").arg("trigger").output().unwrap(); Command::new("udevadm") - .args(&["control", "--reload-rules"]) + .args(["control", "--reload-rules"]) .output() .unwrap(); diff --git a/remote-wallet/src/ledger.rs b/remote-wallet/src/ledger.rs index ec929257acc0bf..ba10554dce7a79 100644 --- a/remote-wallet/src/ledger.rs +++ b/remote-wallet/src/ledger.rs @@ -419,10 +419,7 @@ impl RemoteWallet for LedgerWallet { 0, &derivation_path, )?; - if key.len() != 32 { - return Err(RemoteWalletError::Protocol("Key packet size mismatch")); - } - Ok(Pubkey::new(&key)) + Pubkey::try_from(key).map_err(|_| RemoteWalletError::Protocol("Key packet size mismatch")) } fn sign_message( diff --git a/rpc-test/Cargo.toml b/rpc-test/Cargo.toml index 9110fb25521447..82a281f13e3364 100644 --- a/rpc-test/Cargo.toml +++ b/rpc-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-rpc-test" -version = "1.11.6" +version = "1.14.24" description = "Solana RPC Test" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -19,17 +19,17 @@ log = "0.4.17" reqwest = { version = "0.11.11", default-features = false, features = ["blocking", "brotli", "deflate", "gzip", "rustls-tls", "json"] } serde = "1.0.138" serde_json = "1.0.81" -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-rpc = { path = "../rpc", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-test-validator = { path = "../test-validator", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-rpc = { path = "../rpc", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-test-validator = { path = "../test-validator", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } tokio = { version = "~1.14.1", features = ["full"] } [dev-dependencies] -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/rpc-test/tests/rpc.rs b/rpc-test/tests/rpc.rs index 89df239f6791e6..d1aa922289fd8c 100644 --- a/rpc-test/tests/rpc.rs +++ b/rpc-test/tests/rpc.rs @@ -31,7 +31,10 @@ use { std::{ collections::HashSet, net::UdpSocket, - sync::Arc, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, thread::sleep, time::{Duration, Instant}, }, @@ -239,7 +242,7 @@ fn test_rpc_subscriptions() { let alice = Keypair::new(); let test_validator = - TestValidator::with_no_fees(alice.pubkey(), None, SocketAddrSpace::Unspecified); + TestValidator::with_no_fees_udp(alice.pubkey(), None, SocketAddrSpace::Unspecified); let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap(); transactions_socket.connect(test_validator.tpu()).unwrap(); @@ -261,15 +264,13 @@ fn test_rpc_subscriptions() { .collect(); let mut signature_set: HashSet = transactions.iter().map(|tx| tx.signatures[0]).collect(); - let account_set: HashSet = transactions + let mut account_set: HashSet = transactions .iter() .map(|tx| tx.message.account_keys[1]) .collect(); - // Track when subscriptions are ready - let (ready_sender, ready_receiver) = unbounded::<()>(); // Track account notifications are received - let (account_sender, account_receiver) = unbounded::>(); + let (account_sender, account_receiver) = unbounded::<(Pubkey, RpcResponse)>(); // Track when status notifications are received let (status_sender, status_receiver) = unbounded::<(Signature, RpcResponse)>(); @@ -278,12 +279,19 @@ fn test_rpc_subscriptions() { let rt = Runtime::new().unwrap(); let rpc_pubsub_url = test_validator.rpc_pubsub_url(); let signature_set_clone = signature_set.clone(); + let account_set_clone = account_set.clone(); + let signature_subscription_ready = Arc::new(AtomicUsize::new(0)); + let account_subscription_ready = Arc::new(AtomicUsize::new(0)); + let signature_subscription_ready_clone = signature_subscription_ready.clone(); + let account_subscription_ready_clone = account_subscription_ready.clone(); + rt.spawn(async move { let pubsub_client = Arc::new(PubsubClient::new(&rpc_pubsub_url).await.unwrap()); // Subscribe to signature notifications for signature in signature_set_clone { let status_sender = status_sender.clone(); + let signature_subscription_ready_clone = signature_subscription_ready_clone.clone(); tokio::spawn({ let _pubsub_client = Arc::clone(&pubsub_client); async move { @@ -298,6 +306,8 @@ fn test_rpc_subscriptions() { .await .unwrap(); + signature_subscription_ready_clone.fetch_add(1, Ordering::SeqCst); + let response = sig_notifications.next().await.unwrap(); status_sender.send((signature, response)).unwrap(); sig_unsubscribe().await; @@ -306,8 +316,9 @@ fn test_rpc_subscriptions() { } // Subscribe to account notifications - for pubkey in account_set { + for pubkey in account_set_clone { let account_sender = account_sender.clone(); + let account_subscription_ready_clone = account_subscription_ready_clone.clone(); tokio::spawn({ let _pubsub_client = Arc::clone(&pubsub_client); async move { @@ -322,28 +333,43 @@ fn test_rpc_subscriptions() { .await .unwrap(); + account_subscription_ready_clone.fetch_add(1, Ordering::SeqCst); + let response = account_notifications.next().await.unwrap(); - account_sender.send(response).unwrap(); + account_sender.send((pubkey, response)).unwrap(); account_unsubscribe().await; } }); } - - // Signal ready after the next slot notification - tokio::spawn({ - let _pubsub_client = Arc::clone(&pubsub_client); - async move { - let (mut slot_notifications, slot_unsubscribe) = - _pubsub_client.slot_subscribe().await.unwrap(); - let _response = slot_notifications.next().await.unwrap(); - ready_sender.send(()).unwrap(); - slot_unsubscribe().await; - } - }); }); - // Wait for signature subscriptions - ready_receiver.recv_timeout(Duration::from_secs(2)).unwrap(); + let now = Instant::now(); + while (signature_subscription_ready.load(Ordering::SeqCst) != transactions.len() + || account_subscription_ready.load(Ordering::SeqCst) != transactions.len()) + && now.elapsed() < Duration::from_secs(15) + { + sleep(Duration::from_millis(100)) + } + + // check signature subscription + let num = signature_subscription_ready.load(Ordering::SeqCst); + if num != transactions.len() { + error!( + "signature subscription didn't setup properly, want: {}, got: {}", + transactions.len(), + num + ); + } + + // check account subscription + let num = account_subscription_ready.load(Ordering::SeqCst); + if num != transactions.len() { + error!( + "account subscriptions didn't setup properly, want: {}, got: {}", + transactions.len(), + num + ); + } let rpc_client = RpcClient::new(test_validator.rpc_url()); let mut mint_balance = rpc_client @@ -374,7 +400,13 @@ fn test_rpc_subscriptions() { } // Wait for all signature subscriptions - let deadline = Instant::now() + Duration::from_secs(15); + /* Set a large 30-sec timeout here because the timing of the above tokio process is + * highly non-deterministic. The test was too flaky at 15-second timeout. Debugging + * show occasional multi-second delay which could come from multiple sources -- other + * tokio tasks, tokio scheduler, OS scheduler. The async nature makes it hard to + * track down the origin of the delay. + */ + let deadline = Instant::now() + Duration::from_secs(30); while !signature_set.is_empty() { let timeout = deadline.saturating_duration_since(Instant::now()); match status_receiver.recv_timeout(timeout) { @@ -396,19 +428,18 @@ fn test_rpc_subscriptions() { } } - let deadline = Instant::now() + Duration::from_secs(5); - let mut account_notifications = transactions.len(); - while account_notifications > 0 { + let deadline = Instant::now() + Duration::from_secs(10); + while !account_set.is_empty() { let timeout = deadline.saturating_duration_since(Instant::now()); match account_receiver.recv_timeout(timeout) { - Ok(result) => { + Ok((pubkey, result)) => { assert_eq!(result.value.lamports, Rent::default().minimum_balance(0)); - account_notifications -= 1; + assert!(account_set.remove(&pubkey)); } Err(_err) => { panic!( "recv_timeout, {}/{} accounts remaining", - account_notifications, + account_set.len(), transactions.len() ); } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 13afd4bfc177ee..12b9ace6f42fc3 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-rpc" -version = "1.11.6" +version = "1.14.24" description = "Solana RPC" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -29,28 +29,28 @@ serde = "1.0.138" serde_derive = "1.0.103" serde_json = "1.0.81" soketto = "0.7" -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-faucet = { path = "../faucet", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-poh = { path = "../poh", version = "=1.11.6" } -solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.11.6" } -solana-stake-program = { path = "../programs/stake", version = "=1.11.6" } -solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } -spl-token = { version = "=3.3.1", features = ["no-entrypoint"] } -spl-token-2022 = { version = "=0.4.2", features = ["no-entrypoint"] } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-faucet = { path = "../faucet", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-poh = { path = "../poh", version = "=1.14.24" } +solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.14.24" } +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } +solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } +spl-token = { version = "=3.5.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "=0.6.1", features = ["no-entrypoint"] } stream-cancel = "0.8.1" thiserror = "1.0" tokio = { version = "~1.14.1", features = ["full"] } @@ -58,9 +58,9 @@ tokio-util = { version = "0.6", features = ["codec", "compat"] } [dev-dependencies] serial_test = "0.8.0" -solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-stake-program = { path = "../programs/stake", version = "=1.11.6" } +solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } symlink = "0.1.0" [lib] diff --git a/rpc/src/cluster_tpu_info.rs b/rpc/src/cluster_tpu_info.rs index 7e1982cf50b945..5889399b63d2b8 100644 --- a/rpc/src/cluster_tpu_info.rs +++ b/rpc/src/cluster_tpu_info.rs @@ -59,7 +59,7 @@ impl TpuInfo for ClusterTpuInfo { mod test { use { super::*, - solana_gossip::contact_info::ContactInfo, + solana_gossip::legacy_contact_info::LegacyContactInfo as ContactInfo, solana_ledger::{ blockstore::Blockstore, get_tmp_ledger_path, leader_schedule_cache::LeaderScheduleCache, }, diff --git a/rpc/src/optimistically_confirmed_bank_tracker.rs b/rpc/src/optimistically_confirmed_bank_tracker.rs index 3dc5645e605dff..cd840b6e4e36c3 100644 --- a/rpc/src/optimistically_confirmed_bank_tracker.rs +++ b/rpc/src/optimistically_confirmed_bank_tracker.rs @@ -1,12 +1,20 @@ //! The `optimistically_confirmed_bank_tracker` module implements a threaded service to track the //! most recent optimistically confirmed bank for use in rpc services, and triggers gossip -//! subscription notifications +//! subscription notifications. +//! This module also supports notifying of slot status for subscribers using the SlotNotificationSender. +//! It receives the BankNotification events, transforms them into SlotNotification and sends them via +//! SlotNotificationSender in the following way: +//! BankNotification::OptimisticallyConfirmed --> SlotNotification::OptimisticallyConfirmed +//! BankNotification::Frozen --> SlotNotification::Frozen +//! BankNotification::NewRootedChain --> SlotNotification::Root for the roots in the chain. use { crate::rpc_subscriptions::RpcSubscriptions, crossbeam_channel::{Receiver, RecvTimeoutError, Sender}, solana_client::rpc_response::{SlotTransactionStats, SlotUpdate}, - solana_runtime::{bank::Bank, bank_forks::BankForks}, + solana_runtime::{ + bank::Bank, bank_forks::BankForks, prioritization_fee_cache::PrioritizationFeeCache, + }, solana_sdk::{clock::Slot, timing::timestamp}, std::{ collections::HashSet, @@ -35,7 +43,18 @@ impl OptimisticallyConfirmedBank { pub enum BankNotification { OptimisticallyConfirmed(Slot), Frozen(Arc), - Root(Arc), + NewRootBank(Arc), + /// The newly rooted slot chain including the parent slot of the oldest bank in the rooted chain. + NewRootedChain(Vec), +} + +#[derive(Clone, Debug)] +pub enum SlotNotification { + OptimisticallyConfirmed(Slot), + /// The (Slot, Parent Slot) pair for the slot frozen + Frozen((Slot, Slot)), + /// The (Slot, Parent Slot) pair for the root slot + Root((Slot, Slot)), } impl std::fmt::Debug for BankNotification { @@ -45,7 +64,8 @@ impl std::fmt::Debug for BankNotification { write!(f, "OptimisticallyConfirmed({:?})", slot) } BankNotification::Frozen(bank) => write!(f, "Frozen({})", bank.slot()), - BankNotification::Root(bank) => write!(f, "Root({})", bank.slot()), + BankNotification::NewRootBank(bank) => write!(f, "Root({})", bank.slot()), + BankNotification::NewRootedChain(chain) => write!(f, "RootedChain({chain:?})"), } } } @@ -53,6 +73,15 @@ impl std::fmt::Debug for BankNotification { pub type BankNotificationReceiver = Receiver; pub type BankNotificationSender = Sender; +#[derive(Clone)] +pub struct BankNotificationSenderConfig { + pub sender: BankNotificationSender, + pub should_send_parents: bool, +} + +pub type SlotNotificationReceiver = Receiver; +pub type SlotNotificationSender = Sender; + pub struct OptimisticallyConfirmedBankTracker { thread_hdl: JoinHandle<()>, } @@ -64,14 +93,16 @@ impl OptimisticallyConfirmedBankTracker { bank_forks: Arc>, optimistically_confirmed_bank: Arc>, subscriptions: Arc, - bank_notification_subscribers: Option>>>, + slot_notification_subscribers: Option>>>, + prioritization_fee_cache: Arc, ) -> Self { let exit_ = exit.clone(); let mut pending_optimistically_confirmed_banks = HashSet::new(); let mut last_notified_confirmed_slot: Slot = 0; let mut highest_confirmed_slot: Slot = 0; + let mut newest_root_slot: Slot = 0; let thread_hdl = Builder::new() - .name("solana-optimistic-bank-tracker".to_string()) + .name("solOpConfBnkTrk".to_string()) .spawn(move || loop { if exit_.load(Ordering::Relaxed) { break; @@ -85,7 +116,9 @@ impl OptimisticallyConfirmedBankTracker { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, - &bank_notification_subscribers, + &mut newest_root_slot, + &slot_notification_subscribers, + &prioritization_fee_cache, ) { break; } @@ -94,6 +127,7 @@ impl OptimisticallyConfirmedBankTracker { Self { thread_hdl } } + #[allow(clippy::too_many_arguments)] fn recv_notification( receiver: &Receiver, bank_forks: &Arc>, @@ -102,7 +136,9 @@ impl OptimisticallyConfirmedBankTracker { pending_optimistically_confirmed_banks: &mut HashSet, last_notified_confirmed_slot: &mut Slot, highest_confirmed_slot: &mut Slot, - bank_notification_subscribers: &Option>>>, + newest_root_slot: &mut Slot, + slot_notification_subscribers: &Option>>>, + prioritization_fee_cache: &PrioritizationFeeCache, ) -> Result<(), RecvTimeoutError> { let notification = receiver.recv_timeout(Duration::from_secs(1))?; Self::process_notification( @@ -113,17 +149,19 @@ impl OptimisticallyConfirmedBankTracker { pending_optimistically_confirmed_banks, last_notified_confirmed_slot, highest_confirmed_slot, - bank_notification_subscribers, + newest_root_slot, + slot_notification_subscribers, + prioritization_fee_cache, ); Ok(()) } fn notify_slot_status( - bank_notification_subscribers: &Option>>>, - notification: BankNotification, + slot_notification_subscribers: &Option>>>, + notification: SlotNotification, ) { - if let Some(bank_notification_subscribers) = bank_notification_subscribers { - for sender in bank_notification_subscribers.read().unwrap().iter() { + if let Some(slot_notification_subscribers) = slot_notification_subscribers { + for sender in slot_notification_subscribers.read().unwrap().iter() { match sender.send(notification.clone()) { Ok(_) => {} Err(err) => { @@ -143,7 +181,7 @@ impl OptimisticallyConfirmedBankTracker { bank: &Arc, last_notified_confirmed_slot: &mut Slot, pending_optimistically_confirmed_banks: &mut HashSet, - bank_notification_subscribers: &Option>>>, + slot_notification_subscribers: &Option>>>, ) { if bank.is_frozen() { if bank.slot() > *last_notified_confirmed_slot { @@ -154,8 +192,8 @@ impl OptimisticallyConfirmedBankTracker { subscriptions.notify_gossip_subscribers(bank.slot()); *last_notified_confirmed_slot = bank.slot(); Self::notify_slot_status( - bank_notification_subscribers, - BankNotification::OptimisticallyConfirmed(bank.slot()), + slot_notification_subscribers, + SlotNotification::OptimisticallyConfirmed(bank.slot()), ); } } else if bank.slot() > bank_forks.read().unwrap().root_bank().slot() { @@ -171,7 +209,7 @@ impl OptimisticallyConfirmedBankTracker { slot_threshold: Slot, last_notified_confirmed_slot: &mut Slot, pending_optimistically_confirmed_banks: &mut HashSet, - bank_notification_subscribers: &Option>>>, + slot_notification_subscribers: &Option>>>, ) { for confirmed_bank in bank.clone().parents_inclusive().iter().rev() { if confirmed_bank.slot() > slot_threshold { @@ -185,12 +223,41 @@ impl OptimisticallyConfirmedBankTracker { confirmed_bank, last_notified_confirmed_slot, pending_optimistically_confirmed_banks, - bank_notification_subscribers, + slot_notification_subscribers, ); } } } + fn notify_new_root_slots( + roots: &mut Vec, + newest_root_slot: &mut Slot, + slot_notification_subscribers: &Option>>>, + ) { + if slot_notification_subscribers.is_none() { + return; + } + roots.sort_unstable(); + // The chain are sorted already and must contain at least the parent of a newly rooted slot as the first element + assert!(roots.len() >= 2); + for i in 1..roots.len() { + let root = roots[i]; + if root > *newest_root_slot { + let parent = roots[i - 1]; + debug!( + "Doing SlotNotification::Root for root {}, parent: {}", + root, parent + ); + Self::notify_slot_status( + slot_notification_subscribers, + SlotNotification::Root((root, parent)), + ); + *newest_root_slot = root; + } + } + } + + #[allow(clippy::too_many_arguments)] pub fn process_notification( notification: BankNotification, bank_forks: &Arc>, @@ -199,7 +266,9 @@ impl OptimisticallyConfirmedBankTracker { pending_optimistically_confirmed_banks: &mut HashSet, last_notified_confirmed_slot: &mut Slot, highest_confirmed_slot: &mut Slot, - bank_notification_subscribers: &Option>>>, + newest_root_slot: &mut Slot, + slot_notification_subscribers: &Option>>>, + prioritization_fee_cache: &PrioritizationFeeCache, ) { debug!("received bank notification: {:?}", notification); match notification { @@ -222,7 +291,7 @@ impl OptimisticallyConfirmedBankTracker { *highest_confirmed_slot, last_notified_confirmed_slot, pending_optimistically_confirmed_banks, - bank_notification_subscribers, + slot_notification_subscribers, ); *highest_confirmed_slot = slot; @@ -239,6 +308,9 @@ impl OptimisticallyConfirmedBankTracker { slot, timestamp: timestamp(), }); + + // finalize block's minimum prioritization fee cache for this bank + prioritization_fee_cache.finalize_priority_fee(slot); } BankNotification::Frozen(bank) => { let frozen_slot = bank.slot(); @@ -258,8 +330,8 @@ impl OptimisticallyConfirmedBankTracker { }); Self::notify_slot_status( - bank_notification_subscribers, - BankNotification::Frozen(bank.clone()), + slot_notification_subscribers, + SlotNotification::Frozen((bank.slot(), bank.parent_slot())), ); } @@ -276,7 +348,7 @@ impl OptimisticallyConfirmedBankTracker { *last_notified_confirmed_slot, last_notified_confirmed_slot, pending_optimistically_confirmed_banks, - bank_notification_subscribers, + slot_notification_subscribers, ); let mut w_optimistically_confirmed_bank = @@ -287,11 +359,7 @@ impl OptimisticallyConfirmedBankTracker { drop(w_optimistically_confirmed_bank); } } - BankNotification::Root(bank) => { - Self::notify_slot_status( - bank_notification_subscribers, - BankNotification::Root(bank.clone()), - ); + BankNotification::NewRootBank(bank) => { let root_slot = bank.slot(); let mut w_optimistically_confirmed_bank = optimistically_confirmed_bank.write().unwrap(); @@ -299,8 +367,16 @@ impl OptimisticallyConfirmedBankTracker { w_optimistically_confirmed_bank.bank = bank; } drop(w_optimistically_confirmed_bank); + pending_optimistically_confirmed_banks.retain(|&s| s > root_slot); } + BankNotification::NewRootedChain(mut roots) => { + Self::notify_new_root_slots( + &mut roots, + newest_root_slot, + slot_notification_subscribers, + ); + } } } @@ -317,6 +393,7 @@ impl OptimisticallyConfirmedBankTracker { mod tests { use { super::*, + crossbeam_channel::unbounded, solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo}, solana_runtime::{ accounts_background_service::AbsRequestSender, commitment::BlockCommitmentCache, @@ -325,6 +402,26 @@ mod tests { std::sync::atomic::AtomicU64, }; + /// Given a bank get the parent slot chains, this include the parent slot of the oldest parent bank. + fn get_parent_chains(bank: &Arc) -> Vec { + let mut parents = bank.parents(); + let oldest_parent = parents.last().map(|last| last.parent_slot()); + parents.push(bank.clone()); + let mut rooted_slots: Vec<_> = parents.iter().map(|bank| bank.slot()).collect(); + rooted_slots.push(oldest_parent.unwrap_or_else(|| bank.parent_slot())); + rooted_slots + } + + /// Receive the Root notifications from the channel, if no item received within 100 ms, break and return all + /// of those received. + fn get_root_notifications(receiver: &Receiver) -> Vec { + let mut notifications = Vec::new(); + while let Ok(notification) = receiver.recv_timeout(Duration::from_millis(100)) { + notifications.push(notification); + } + notifications + } + #[test] fn test_process_notification() { let exit = Arc::new(AtomicBool::new(false)); @@ -346,9 +443,11 @@ mod tests { let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default())); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), block_commitment_cache, optimistically_confirmed_bank.clone(), @@ -358,6 +457,8 @@ mod tests { assert_eq!(optimistically_confirmed_bank.read().unwrap().bank.slot(), 0); let mut highest_confirmed_slot: Slot = 0; + let mut newest_root_slot: Slot = 0; + let mut last_notified_confirmed_slot: Slot = 0; OptimisticallyConfirmedBankTracker::process_notification( BankNotification::OptimisticallyConfirmed(2), @@ -367,7 +468,9 @@ mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut newest_root_slot, &None, + &PrioritizationFeeCache::default(), ); assert_eq!(optimistically_confirmed_bank.read().unwrap().bank.slot(), 2); assert_eq!(highest_confirmed_slot, 2); @@ -381,7 +484,9 @@ mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut newest_root_slot, &None, + &PrioritizationFeeCache::default(), ); assert_eq!(optimistically_confirmed_bank.read().unwrap().bank.slot(), 2); assert_eq!(highest_confirmed_slot, 2); @@ -395,7 +500,9 @@ mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut newest_root_slot, &None, + &PrioritizationFeeCache::default(), ); assert_eq!(optimistically_confirmed_bank.read().unwrap().bank.slot(), 2); assert_eq!(pending_optimistically_confirmed_banks.len(), 1); @@ -414,7 +521,9 @@ mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut newest_root_slot, &None, + &PrioritizationFeeCache::default(), ); assert_eq!(optimistically_confirmed_bank.read().unwrap().bank.slot(), 3); assert_eq!(highest_confirmed_slot, 3); @@ -432,7 +541,9 @@ mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut newest_root_slot, &None, + &PrioritizationFeeCache::default(), ); assert_eq!(optimistically_confirmed_bank.read().unwrap().bank.slot(), 3); assert_eq!(pending_optimistically_confirmed_banks.len(), 1); @@ -443,20 +554,50 @@ mod tests { let bank5 = Bank::new_from_parent(&bank4, &Pubkey::default(), 5); bank_forks.write().unwrap().insert(bank5); let bank5 = bank_forks.read().unwrap().get(5).unwrap(); + + let mut bank_notification_senders = Vec::new(); + let (sender, receiver) = unbounded(); + bank_notification_senders.push(sender); + + let subscribers = Some(Arc::new(RwLock::new(bank_notification_senders))); + let parent_roots = get_parent_chains(&bank5); OptimisticallyConfirmedBankTracker::process_notification( - BankNotification::Root(bank5), + BankNotification::NewRootBank(bank5), &bank_forks, &optimistically_confirmed_bank, &subscriptions, &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, - &None, + &mut newest_root_slot, + &subscribers, + &PrioritizationFeeCache::default(), ); assert_eq!(optimistically_confirmed_bank.read().unwrap().bank.slot(), 5); assert_eq!(pending_optimistically_confirmed_banks.len(), 0); assert!(!pending_optimistically_confirmed_banks.contains(&4)); assert_eq!(highest_confirmed_slot, 4); + // The newest_root_slot is updated via NewRootedChain only + assert_eq!(newest_root_slot, 0); + + OptimisticallyConfirmedBankTracker::process_notification( + BankNotification::NewRootedChain(parent_roots), + &bank_forks, + &optimistically_confirmed_bank, + &subscriptions, + &mut pending_optimistically_confirmed_banks, + &mut last_notified_confirmed_slot, + &mut highest_confirmed_slot, + &mut newest_root_slot, + &subscribers, + &PrioritizationFeeCache::default(), + ); + + assert_eq!(newest_root_slot, 5); + + // Obtain the root notifications, we expect 5, including that for bank5. + let notifications = get_root_notifications(&receiver); + assert_eq!(notifications.len(), 5); // Banks <= root do not get added to pending list, even if not frozen let bank5 = bank_forks.read().unwrap().get(5).unwrap(); @@ -477,11 +618,55 @@ mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut newest_root_slot, &None, + &PrioritizationFeeCache::default(), ); assert_eq!(optimistically_confirmed_bank.read().unwrap().bank.slot(), 5); assert_eq!(pending_optimistically_confirmed_banks.len(), 0); assert!(!pending_optimistically_confirmed_banks.contains(&6)); assert_eq!(highest_confirmed_slot, 4); + assert_eq!(newest_root_slot, 5); + + let bank7 = bank_forks.read().unwrap().get(7).unwrap(); + + let parent_roots = get_parent_chains(&bank7); + + OptimisticallyConfirmedBankTracker::process_notification( + BankNotification::NewRootBank(bank7), + &bank_forks, + &optimistically_confirmed_bank, + &subscriptions, + &mut pending_optimistically_confirmed_banks, + &mut last_notified_confirmed_slot, + &mut highest_confirmed_slot, + &mut newest_root_slot, + &subscribers, + &PrioritizationFeeCache::default(), + ); + assert_eq!(optimistically_confirmed_bank.read().unwrap().bank.slot(), 7); + assert_eq!(pending_optimistically_confirmed_banks.len(), 0); + assert!(!pending_optimistically_confirmed_banks.contains(&6)); + assert_eq!(highest_confirmed_slot, 4); + assert_eq!(newest_root_slot, 5); + + OptimisticallyConfirmedBankTracker::process_notification( + BankNotification::NewRootedChain(parent_roots), + &bank_forks, + &optimistically_confirmed_bank, + &subscriptions, + &mut pending_optimistically_confirmed_banks, + &mut last_notified_confirmed_slot, + &mut highest_confirmed_slot, + &mut newest_root_slot, + &subscribers, + &PrioritizationFeeCache::default(), + ); + + assert_eq!(newest_root_slot, 7); + + // Obtain the root notifications, we expect 1, which is for bank7 only as its parent bank5 is already notified. + let notifications = get_root_notifications(&receiver); + assert_eq!(notifications.len(), 1); } } diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 7c4a34ded4d0e7..6f282c039e1091 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -9,7 +9,6 @@ use { crossbeam_channel::{unbounded, Receiver, Sender}, jsonrpc_core::{futures::future, types::error, BoxFuture, Error, Metadata, Result}, jsonrpc_derive::rpc, - serde::{Deserialize, Serialize}, solana_account_decoder::{ parse_token::{is_known_spl_token_id, token_amount_to_ui_amount, UiTokenAmount}, UiAccount, UiAccountEncoding, UiDataSliceConfig, MAX_BASE58_BYTES, @@ -32,7 +31,9 @@ use { }, solana_entry::entry::Entry, solana_faucet::faucet::request_airdrop_transaction, - solana_gossip::{cluster_info::ClusterInfo, contact_info::ContactInfo}, + solana_gossip::{ + cluster_info::ClusterInfo, legacy_contact_info::LegacyContactInfo as ContactInfo, + }, solana_ledger::{ blockstore::{Blockstore, SignatureInfosForAddress}, blockstore_db::BlockstoreError, @@ -50,6 +51,7 @@ use { inline_spl_token::{SPL_TOKEN_ACCOUNT_MINT_OFFSET, SPL_TOKEN_ACCOUNT_OWNER_OFFSET}, inline_spl_token_2022::{self, ACCOUNTTYPE_ACCOUNT}, non_circulating_supply::calculate_non_circulating_supply, + prioritization_fee_cache::PrioritizationFeeCache, snapshot_config::SnapshotConfig, snapshot_utils, }, @@ -64,7 +66,7 @@ use { feature_set, fee_calculator::FeeCalculator, hash::Hash, - message::{Message, SanitizedMessage}, + message::SanitizedMessage, pubkey::{Pubkey, PUBKEY_BYTES}, signature::{Keypair, Signature, Signer}, stake::state::{StakeActivationStatus, StakeState}, @@ -73,7 +75,7 @@ use { sysvar::stake_history, transaction::{ self, AddressLoader, MessageHash, SanitizedTransaction, TransactionError, - VersionedTransaction, + VersionedTransaction, MAX_TX_ACCOUNT_LOCKS, }, }, solana_send_transaction_service::{ @@ -126,16 +128,6 @@ fn new_response(bank: &Bank, value: T) -> RpcResponse { } } -/// Wrapper for rpc return types of methods that provide responses both with and without context. -/// Main purpose of this is to fix methods that lack context information in their return type, -/// without breaking backwards compatibility. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionalContext { - Context(RpcResponse), - NoContext(T), -} - fn is_finalized( block_commitment_cache: &BlockCommitmentCache, bank: &Bank, @@ -211,6 +203,8 @@ pub struct JsonRpcRequestProcessor { max_slots: Arc, leader_schedule_cache: Arc, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, + prioritization_fee_cache: Arc, } impl Metadata for JsonRpcRequestProcessor {} @@ -316,6 +310,8 @@ impl JsonRpcRequestProcessor { max_slots: Arc, leader_schedule_cache: Arc, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, + prioritization_fee_cache: Arc, ) -> (Self, Receiver) { let (sender, receiver) = unbounded(); ( @@ -336,6 +332,8 @@ impl JsonRpcRequestProcessor { max_slots, leader_schedule_cache, max_complete_transaction_status_slot, + max_complete_rewards_slot, + prioritization_fee_cache, }, receiver, ) @@ -369,6 +367,7 @@ impl JsonRpcRequestProcessor { &connection_cache, 1000, 1, + exit.clone(), ); Self { @@ -400,6 +399,8 @@ impl JsonRpcRequestProcessor { max_slots: Arc::new(MaxSlots::default()), leader_schedule_cache: Arc::new(LeaderScheduleCache::new_from_bank(bank)), max_complete_transaction_status_slot: Arc::new(AtomicU64::default()), + max_complete_rewards_slot: Arc::new(AtomicU64::default()), + prioritization_fee_cache: Arc::new(PrioritizationFeeCache::default()), } } @@ -1054,11 +1055,12 @@ impl JsonRpcRequestProcessor { Ok(()) } - fn check_status_is_complete(&self, slot: Slot) -> Result<()> { + fn check_blockstore_writes_complete(&self, slot: Slot) -> Result<()> { if slot > self .max_complete_transaction_status_slot .load(Ordering::SeqCst) + || slot > self.max_complete_rewards_slot.load(Ordering::SeqCst) { Err(RpcCustomError::BlockStatusNotAvailableYet { slot }.into()) } else { @@ -1092,7 +1094,7 @@ impl JsonRpcRequestProcessor { .unwrap() .highest_confirmed_root() { - self.check_status_is_complete(slot)?; + self.check_blockstore_writes_complete(slot)?; let result = self.blockstore.get_rooted_block(slot, true); self.check_blockstore_root(&result, slot)?; let encode_block = |confirmed_block: ConfirmedBlock| -> Result { @@ -1123,7 +1125,7 @@ impl JsonRpcRequestProcessor { // Check if block is confirmed let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed())); if confirmed_bank.status_cache_ancestors().contains(&slot) { - self.check_status_is_complete(slot)?; + self.check_blockstore_writes_complete(slot)?; let result = self.blockstore.get_complete_block(slot, true); return result .ok() @@ -2161,22 +2163,27 @@ impl JsonRpcRequestProcessor { Ok(new_response(&bank, is_valid)) } - fn get_fee_for_message( - &self, - message: &SanitizedMessage, - config: RpcContextConfig, - ) -> Result>> { - let bank = self.get_bank_with_config(config)?; - let fee = bank.get_fee_for_message(message); - Ok(new_response(&bank, fee)) - } - fn get_stake_minimum_delegation(&self, config: RpcContextConfig) -> Result> { let bank = self.get_bank_with_config(config)?; let stake_minimum_delegation = solana_stake_program::get_minimum_delegation(&bank.feature_set); Ok(new_response(&bank, stake_minimum_delegation)) } + + fn get_recent_prioritization_fees( + &self, + pubkeys: Vec, + ) -> Result> { + Ok(self + .prioritization_fee_cache + .get_prioritization_fees(&pubkeys) + .into_iter() + .map(|(slot, prioritization_fee)| RpcPrioritizationFee { + slot, + prioritization_fee, + }) + .collect()) + } } fn optimize_filters(filters: &mut [RpcFilterType]) { @@ -2186,10 +2193,14 @@ fn optimize_filters(filters: &mut [RpcFilterType]) { match &compare.bytes { #[allow(deprecated)] Binary(bytes) | Base58(bytes) => { - compare.bytes = Bytes(bs58::decode(bytes).into_vec().unwrap()); + if let Ok(bytes) = bs58::decode(bytes).into_vec() { + compare.bytes = Bytes(bytes); + } } Base64(bytes) => { - compare.bytes = Bytes(base64::decode(bytes).unwrap()); + if let Ok(bytes) = base64::decode(bytes) { + compare.bytes = Bytes(bytes); + } } _ => {} } @@ -2379,7 +2390,7 @@ fn get_spl_token_owner_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> .. }) if *offset == SPL_TOKEN_ACCOUNT_OWNER_OFFSET => { if bytes.len() == PUBKEY_BYTES { - owner_key = Some(Pubkey::new(bytes)); + owner_key = Pubkey::try_from(&bytes[..]).ok(); } else { incorrect_owner_len = Some(bytes.len()); } @@ -2435,7 +2446,7 @@ fn get_spl_token_mint_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> .. }) if *offset == SPL_TOKEN_ACCOUNT_MINT_OFFSET => { if bytes.len() == PUBKEY_BYTES { - mint = Some(Pubkey::new(bytes)); + mint = Pubkey::try_from(&bytes[..]).ok(); } else { incorrect_mint_len = Some(bytes.len()); } @@ -3270,7 +3281,10 @@ pub mod rpc_accounts { // Full RPC interface that an API node is expected to provide // (rpc_minimal should also be provided by an API node) pub mod rpc_full { - use super::*; + use { + super::*, + solana_sdk::message::{SanitizedVersionedMessage, VersionedMessage}, + }; #[rpc] pub trait Full { type Metadata; @@ -3416,6 +3430,13 @@ pub mod rpc_full { meta: Self::Metadata, config: Option, ) -> Result>; + + #[rpc(meta, name = "getRecentPrioritizationFees")] + fn get_recent_prioritization_fees( + &self, + meta: Self::Metadata, + pubkey_strs: Option>, + ) -> Result>; } pub struct FullImpl; @@ -3978,12 +3999,21 @@ pub mod rpc_full { config: Option, ) -> Result>> { debug!("get_fee_for_message rpc request received"); - let (_, message) = - decode_and_deserialize::(data, TransactionBinaryEncoding::Base64)?; - let sanitized_message = SanitizedMessage::try_from(message).map_err(|err| { - Error::invalid_params(format!("invalid transaction message: {}", err)) - })?; - meta.get_fee_for_message(&sanitized_message, config.unwrap_or_default()) + let (_, message) = decode_and_deserialize::( + data, + TransactionBinaryEncoding::Base64, + )?; + let bank = &*meta.get_bank_with_config(config.unwrap_or_default())?; + let sanitized_versioned_message = SanitizedVersionedMessage::try_from(message) + .map_err(|err| { + Error::invalid_params(format!("invalid transaction message: {}", err)) + })?; + let sanitized_message = SanitizedMessage::try_new(sanitized_versioned_message, bank) + .map_err(|err| { + Error::invalid_params(format!("invalid transaction message: {}", err)) + })?; + let fee = bank.get_fee_for_message(&sanitized_message); + Ok(new_response(bank, fee)) } fn get_stake_minimum_delegation( @@ -3994,6 +4024,29 @@ pub mod rpc_full { debug!("get_stake_minimum_delegation rpc request received"); meta.get_stake_minimum_delegation(config.unwrap_or_default()) } + + fn get_recent_prioritization_fees( + &self, + meta: Self::Metadata, + pubkey_strs: Option>, + ) -> Result> { + let pubkey_strs = pubkey_strs.unwrap_or_default(); + debug!( + "get_recent_prioritization_fees rpc request received: {:?} pubkeys", + pubkey_strs.len() + ); + if pubkey_strs.len() > MAX_TX_ACCOUNT_LOCKS { + return Err(Error::invalid_params(format!( + "Too many inputs provided; max {}", + MAX_TX_ACCOUNT_LOCKS + ))); + } + let pubkeys = pubkey_strs + .into_iter() + .map(|pubkey_str| verify_pubkey(&pubkey_str)) + .collect::>>()?; + meta.get_recent_prioritization_fees(pubkeys) + } } } @@ -4519,8 +4572,14 @@ pub fn populate_blockstore_for_tests( ) { let slot = bank.slot(); let parent_slot = bank.parent_slot(); - let shreds = - solana_ledger::blockstore::entries_to_test_shreds(&entries, slot, parent_slot, true, 0); + let shreds = solana_ledger::blockstore::entries_to_test_shreds( + &entries, + slot, + parent_slot, + true, + 0, + true, // merkle_variant + ); blockstore.insert_shreds(shreds, None, false).unwrap(); blockstore.set_roots(std::iter::once(&slot)).unwrap(); @@ -4583,7 +4642,7 @@ pub mod tests { rpc_filter::{Memcmp, MemcmpEncodedBytes}, }, solana_entry::entry::next_versioned_entry, - solana_gossip::{contact_info::ContactInfo, socketaddr}, + solana_gossip::socketaddr, solana_ledger::{ blockstore_meta::PerfSample, blockstore_processor::fill_blockstore_slot_with_ticks, @@ -4596,10 +4655,14 @@ pub mod tests { solana_sdk::{ account::{Account, WritableAccount}, clock::MAX_RECENT_BLOCKHASHES, - fee_calculator::DEFAULT_BURN_PERCENT, + compute_budget::ComputeBudgetInstruction, + fee_calculator::{FeeRateGovernor, DEFAULT_BURN_PERCENT}, hash::{hash, Hash}, instruction::InstructionError, - message::{v0, v0::MessageAddressTableLookup, MessageHeader, VersionedMessage}, + message::{ + v0::{self, MessageAddressTableLookup}, + Message, MessageHeader, VersionedMessage, + }, nonce::{self, state::DurableNonce}, rpc_port, signature::{Keypair, Signer}, @@ -4630,7 +4693,8 @@ pub mod tests { std::{borrow::Cow, collections::HashMap}, }; - const TEST_MINT_LAMPORTS: u64 = 1_000_000; + const TEST_MINT_LAMPORTS: u64 = 1_000_000_000; + const TEST_SIGNATURE_FEE: u64 = 5_000; const TEST_SLOTS_PER_EPOCH: u64 = DELINQUENT_VALIDATOR_SLOT_DISTANCE + 1; fn create_test_request(method: &str, params: Option) -> serde_json::Value { @@ -4708,6 +4772,7 @@ pub mod tests { let max_slots = Arc::new(MaxSlots::default()); // note that this means that slot 0 will always be considered complete let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(0)); + let max_complete_rewards_slot = Arc::new(AtomicU64::new(0)); let meta = JsonRpcRequestProcessor::new( JsonRpcConfig { @@ -4728,6 +4793,8 @@ pub mod tests { max_slots.clone(), Arc::new(LeaderScheduleCache::new_from_bank(&bank)), max_complete_transaction_status_slot.clone(), + max_complete_rewards_slot, + Arc::new(PrioritizationFeeCache::default()), ) .0; @@ -4775,8 +4842,12 @@ pub mod tests { let keypair3 = Keypair::new(); let bank = self.working_bank(); let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0); - bank.transfer(rent_exempt_amount, mint_keypair, &keypair2.pubkey()) - .unwrap(); + bank.transfer( + rent_exempt_amount + TEST_SIGNATURE_FEE, + mint_keypair, + &keypair2.pubkey(), + ) + .unwrap(); let (entries, signatures) = create_test_transaction_entries( vec![&self.mint_keypair, &keypair1, &keypair2, &keypair3], @@ -4940,6 +5011,20 @@ pub mod tests { bank.store_account(vote_pubkey, &vote_account); } + fn update_prioritization_fee_cache(&self, transactions: Vec) { + let bank = self.working_bank(); + let prioritization_fee_cache = &self.meta.prioritization_fee_cache; + let transactions: Vec<_> = transactions + .into_iter() + .map(|tx| SanitizedTransaction::try_from_legacy_transaction(tx).unwrap()) + .collect(); + prioritization_fee_cache.update(bank, transactions.iter()); + } + + fn get_prioritization_fee_cache(&self) -> &PrioritizationFeeCache { + &self.meta.prioritization_fee_cache + } + fn working_bank(&self) -> Arc { self.bank_forks.read().unwrap().working_bank() } @@ -5403,7 +5488,7 @@ pub mod tests { "context": {"slot": 0, "apiVersion": RpcApiVersion::default()}, "value":{ "owner": "11111111111111111111111111111111", - "lamports": 1_000_000, + "lamports": TEST_MINT_LAMPORTS, "data": "", "executable": false, "rentEpoch": 0 @@ -5480,7 +5565,7 @@ pub mod tests { let expected = json!([ { "owner": "11111111111111111111111111111111", - "lamports": 1_000_000, + "lamports": TEST_MINT_LAMPORTS, "data": ["", "base64"], "executable": false, "rentEpoch": 0 @@ -5512,7 +5597,7 @@ pub mod tests { let expected = json!([ { "owner": "11111111111111111111111111111111", - "lamports": 1_000_000, + "lamports": TEST_MINT_LAMPORTS, "data": ["", "base58"], "executable": false, "rentEpoch": 0 @@ -6099,7 +6184,7 @@ pub mod tests { "value":{ "blockhash": recent_blockhash.to_string(), "feeCalculator": { - "lamportsPerSignature": 0, + "lamportsPerSignature": TEST_SIGNATURE_FEE, } }, }, @@ -6128,7 +6213,7 @@ pub mod tests { "value": { "blockhash": recent_blockhash.to_string(), "feeCalculator": { - "lamportsPerSignature": 0, + "lamportsPerSignature": TEST_SIGNATURE_FEE, }, "lastValidSlot": MAX_RECENT_BLOCKHASHES, "lastValidBlockHeight": MAX_RECENT_BLOCKHASHES, @@ -6208,9 +6293,9 @@ pub mod tests { "value":{ "feeRateGovernor": { "burnPercent": DEFAULT_BURN_PERCENT, - "maxLamportsPerSignature": 0, - "minLamportsPerSignature": 0, - "targetLamportsPerSignature": 0, + "maxLamportsPerSignature": TEST_SIGNATURE_FEE, + "minLamportsPerSignature": TEST_SIGNATURE_FEE, + "targetLamportsPerSignature": TEST_SIGNATURE_FEE, "targetSignaturesPerSlot": 0 } }, @@ -6302,6 +6387,8 @@ pub mod tests { Arc::new(MaxSlots::default()), Arc::new(LeaderScheduleCache::default()), Arc::new(AtomicU64::default()), + Arc::new(AtomicU64::default()), + Arc::new(PrioritizationFeeCache::default()), ); let connection_cache = Arc::new(ConnectionCache::default()); SendTransactionService::new::( @@ -6312,6 +6399,7 @@ pub mod tests { &connection_cache, 1000, 1, + exit, ); let mut bad_transaction = system_transaction::transfer( @@ -6478,6 +6566,7 @@ pub mod tests { genesis_config.rent.exemption_threshold = 2.0; genesis_config.epoch_schedule = EpochSchedule::custom(TEST_SLOTS_PER_EPOCH, TEST_SLOTS_PER_EPOCH, false); + genesis_config.fee_rate_governor = FeeRateGovernor::new(TEST_SIGNATURE_FEE, 0); let bank = Bank::new_for_tests(&genesis_config); ( @@ -6571,6 +6660,8 @@ pub mod tests { Arc::new(MaxSlots::default()), Arc::new(LeaderScheduleCache::default()), Arc::new(AtomicU64::default()), + Arc::new(AtomicU64::default()), + Arc::new(PrioritizationFeeCache::default()), ); let connection_cache = Arc::new(ConnectionCache::default()); SendTransactionService::new::( @@ -6581,6 +6672,7 @@ pub mod tests { &connection_cache, 1000, 1, + exit, ); assert_eq!( request_processor.get_block_commitment(0), @@ -6672,7 +6764,11 @@ pub mod tests { let response = parse_failure_response(rpc.handle_request_sync(request)); let expected = ( JSON_RPC_SERVER_ERROR_UNSUPPORTED_TRANSACTION_VERSION, - String::from("Transaction version (0) is not supported"), + String::from( + "Transaction version (0) is not supported by the requesting client. \ + Please try the request again with the following configuration parameter: \ + \"maxSupportedTransactionVersion\": 0", + ), ); assert_eq!(response, expected); } @@ -7273,12 +7369,12 @@ pub mod tests { let rpc = RpcHandler::start(); let bank = rpc.working_bank(); let RpcHandler { io, meta, .. } = rpc; - let mint = SplTokenPubkey::new(&[2; 32]); - let owner = SplTokenPubkey::new(&[3; 32]); - let delegate = SplTokenPubkey::new(&[4; 32]); + let mint = SplTokenPubkey::new_from_array([2; 32]); + let owner = SplTokenPubkey::new_from_array([3; 32]); + let delegate = SplTokenPubkey::new_from_array([4; 32]); let token_account_pubkey = solana_sdk::pubkey::new_rand(); let token_with_different_mint_pubkey = solana_sdk::pubkey::new_rand(); - let new_mint = SplTokenPubkey::new(&[5; 32]); + let new_mint = SplTokenPubkey::new_from_array([5; 32]); if program_id == inline_spl_token_2022::id() { // Add the token account let account_base = TokenAccount { @@ -7775,9 +7871,9 @@ pub mod tests { let bank = rpc.working_bank(); let RpcHandler { io, meta, .. } = rpc; - let mint = SplTokenPubkey::new(&[2; 32]); - let owner = SplTokenPubkey::new(&[3; 32]); - let delegate = SplTokenPubkey::new(&[4; 32]); + let mint = SplTokenPubkey::new_from_array([2; 32]); + let owner = SplTokenPubkey::new_from_array([3; 32]); + let delegate = SplTokenPubkey::new_from_array([4; 32]); let token_account_pubkey = solana_sdk::pubkey::new_rand(); let (program_name, account_size, mint_size) = if program_id == inline_spl_token_2022::id() @@ -8174,9 +8270,11 @@ pub mod tests { OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let mut pending_optimistically_confirmed_banks = HashSet::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, - max_complete_transaction_status_slot, + max_complete_transaction_status_slot.clone(), + max_complete_rewards_slot.clone(), bank_forks.clone(), block_commitment_cache.clone(), optimistically_confirmed_bank.clone(), @@ -8197,7 +8295,9 @@ pub mod tests { Arc::new(RwLock::new(LargestAccountsCache::new(30))), Arc::new(MaxSlots::default()), Arc::new(LeaderScheduleCache::default()), - Arc::new(AtomicU64::default()), + max_complete_transaction_status_slot, + max_complete_rewards_slot, + Arc::new(PrioritizationFeeCache::default()), ); let mut io = MetaIoHandler::default(); @@ -8211,6 +8311,7 @@ pub mod tests { let slot: Slot = serde_json::from_value(json["result"].clone()).unwrap(); assert_eq!(slot, 0); let mut highest_confirmed_slot: Slot = 0; + let mut highest_root_slot: Slot = 0; let mut last_notified_confirmed_slot: Slot = 0; OptimisticallyConfirmedBankTracker::process_notification( @@ -8221,7 +8322,9 @@ pub mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); let req = r#"{"jsonrpc":"2.0","id":1,"method":"getSlot","params":[{"commitment": "confirmed"}]}"#; @@ -8239,7 +8342,9 @@ pub mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); let req = r#"{"jsonrpc":"2.0","id":1,"method":"getSlot","params":[{"commitment": "confirmed"}]}"#; @@ -8257,7 +8362,9 @@ pub mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); let req = r#"{"jsonrpc":"2.0","id":1,"method":"getSlot","params":[{"commitment": "confirmed"}]}"#; @@ -8276,7 +8383,9 @@ pub mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); let req = r#"{"jsonrpc":"2.0","id":1,"method":"getSlot","params":[{"commitment": "confirmed"}]}"#; @@ -8384,7 +8493,7 @@ pub mod tests { assert_eq!( sanitize_transaction(versioned_tx, SimpleAddressLoader::Disabled).unwrap_err(), Error::invalid_params( - "invalid transaction: Transaction loads an address table account that doesn't exist".to_string(), + "invalid transaction: Transaction version is unsupported".to_string(), ) ); } @@ -8405,4 +8514,167 @@ pub mod tests { expected_stake_minimum_delegation ); } + + #[test] + fn test_rpc_get_recent_prioritization_fees() { + fn wait_for_cache_blocks(cache: &PrioritizationFeeCache, num_blocks: usize) { + while cache.available_block_count() < num_blocks { + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + + fn assert_fee_vec_eq( + expected: &mut Vec, + actual: &mut Vec, + ) { + expected.sort_by(|a, b| a.slot.partial_cmp(&b.slot).unwrap()); + actual.sort_by(|a, b| a.slot.partial_cmp(&b.slot).unwrap()); + assert_eq!(expected, actual); + } + + let rpc = RpcHandler::start(); + assert_eq!( + rpc.get_prioritization_fee_cache().available_block_count(), + 0 + ); + let slot0 = rpc.working_bank().slot(); + let account0 = Pubkey::new_unique(); + let account1 = Pubkey::new_unique(); + let account2 = Pubkey::new_unique(); + let price0 = 42; + let transactions = vec![ + Transaction::new_unsigned(Message::new( + &[ + system_instruction::transfer(&account0, &account1, 1), + ComputeBudgetInstruction::set_compute_unit_price(price0), + ], + Some(&account0), + )), + Transaction::new_unsigned(Message::new( + &[system_instruction::transfer(&account0, &account2, 1)], + Some(&account0), + )), + ]; + rpc.update_prioritization_fee_cache(transactions); + let cache = rpc.get_prioritization_fee_cache(); + cache.finalize_priority_fee(slot0); + wait_for_cache_blocks(cache, 1); + + let request = create_test_request("getRecentPrioritizationFees", None); + let mut response: Vec = + parse_success_result(rpc.handle_request_sync(request)); + assert_fee_vec_eq( + &mut response, + &mut vec![RpcPrioritizationFee { + slot: slot0, + prioritization_fee: 0, + }], + ); + + let request = create_test_request( + "getRecentPrioritizationFees", + Some(json!([[account1.to_string()]])), + ); + let mut response: Vec = + parse_success_result(rpc.handle_request_sync(request)); + assert_fee_vec_eq( + &mut response, + &mut vec![RpcPrioritizationFee { + slot: slot0, + prioritization_fee: price0, + }], + ); + + let request = create_test_request( + "getRecentPrioritizationFees", + Some(json!([[account2.to_string()]])), + ); + let mut response: Vec = + parse_success_result(rpc.handle_request_sync(request)); + assert_fee_vec_eq( + &mut response, + &mut vec![RpcPrioritizationFee { + slot: slot0, + prioritization_fee: 0, + }], + ); + + rpc.advance_bank_to_confirmed_slot(1); + let slot1 = rpc.working_bank().slot(); + let price1 = 11; + let transactions = vec![ + Transaction::new_unsigned(Message::new( + &[ + system_instruction::transfer(&account0, &account2, 1), + ComputeBudgetInstruction::set_compute_unit_price(price1), + ], + Some(&account0), + )), + Transaction::new_unsigned(Message::new( + &[system_instruction::transfer(&account0, &account1, 1)], + Some(&account0), + )), + ]; + rpc.update_prioritization_fee_cache(transactions); + let cache = rpc.get_prioritization_fee_cache(); + cache.finalize_priority_fee(slot1); + wait_for_cache_blocks(cache, 2); + + let request = create_test_request("getRecentPrioritizationFees", None); + let mut response: Vec = + parse_success_result(rpc.handle_request_sync(request)); + assert_fee_vec_eq( + &mut response, + &mut vec![ + RpcPrioritizationFee { + slot: slot0, + prioritization_fee: 0, + }, + RpcPrioritizationFee { + slot: slot1, + prioritization_fee: 0, + }, + ], + ); + + let request = create_test_request( + "getRecentPrioritizationFees", + Some(json!([[account1.to_string()]])), + ); + let mut response: Vec = + parse_success_result(rpc.handle_request_sync(request)); + assert_fee_vec_eq( + &mut response, + &mut vec![ + RpcPrioritizationFee { + slot: slot0, + prioritization_fee: price0, + }, + RpcPrioritizationFee { + slot: slot1, + prioritization_fee: 0, + }, + ], + ); + + let request = create_test_request( + "getRecentPrioritizationFees", + Some(json!([[account2.to_string()]])), + ); + let mut response: Vec = + parse_success_result(rpc.handle_request_sync(request)); + assert_fee_vec_eq( + &mut response, + &mut vec![ + RpcPrioritizationFee { + slot: slot0, + prioritization_fee: 0, + }, + RpcPrioritizationFee { + slot: slot1, + prioritization_fee: price1, + }, + ], + ); + } } diff --git a/rpc/src/rpc_completed_slots_service.rs b/rpc/src/rpc_completed_slots_service.rs index 919f66a98d9fa4..fb1c20f3199b03 100644 --- a/rpc/src/rpc_completed_slots_service.rs +++ b/rpc/src/rpc_completed_slots_service.rs @@ -24,7 +24,7 @@ impl RpcCompletedSlotsService { exit: Arc, ) -> JoinHandle<()> { Builder::new() - .name("solana-rpc-completed-slots-service".to_string()) + .name("solRpcComplSlot".to_string()) .spawn(move || loop { // received exit signal, shutdown the service if exit.load(Ordering::Relaxed) { diff --git a/rpc/src/rpc_health.rs b/rpc/src/rpc_health.rs index 89acc2e06ee697..5653ca4d7d4967 100644 --- a/rpc/src/rpc_health.rs +++ b/rpc/src/rpc_health.rs @@ -129,8 +129,8 @@ impl RpcHealth { #[cfg(test)] pub(crate) fn stub() -> Arc { use { - solana_gossip::contact_info::ContactInfo, solana_sdk::signer::keypair::Keypair, - solana_streamer::socket::SocketAddrSpace, + solana_gossip::legacy_contact_info::LegacyContactInfo as ContactInfo, + solana_sdk::signer::keypair::Keypair, solana_streamer::socket::SocketAddrSpace, }; Arc::new(Self::new( Arc::new(ClusterInfo::new( diff --git a/rpc/src/rpc_pubsub.rs b/rpc/src/rpc_pubsub.rs index 162a8a06ff6cf5..7d7cbeb2520a99 100644 --- a/rpc/src/rpc_pubsub.rs +++ b/rpc/src/rpc_pubsub.rs @@ -676,9 +676,11 @@ mod tests { let blockhash = bank.last_blockhash(); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &Arc::new(AtomicBool::new(false)), max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -805,8 +807,10 @@ mod tests { let mut io = IoHandler::<()>::default(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::default_with_bank_forks( max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, )); let (rpc, _receiver) = rpc_pubsub_service::test_connection(&subscriptions); @@ -862,9 +866,11 @@ mod tests { let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1); bank_forks.write().unwrap().insert(bank1); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &Arc::new(AtomicBool::new(false)), max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots( 1, 1, @@ -992,9 +998,11 @@ mod tests { let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1); bank_forks.write().unwrap().insert(bank1); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &Arc::new(AtomicBool::new(false)), max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots( 1, 1, @@ -1084,8 +1092,10 @@ mod tests { let mut io = IoHandler::<()>::default(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::default_with_bank_forks( max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, )); let (rpc, _receiver) = rpc_pubsub_service::test_connection(&subscriptions); @@ -1132,9 +1142,11 @@ mod tests { let exit = Arc::new(AtomicBool::new(false)); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -1186,9 +1198,11 @@ mod tests { let exit = Arc::new(AtomicBool::new(false)); let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), block_commitment_cache, OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), @@ -1258,8 +1272,10 @@ mod tests { let bank = Bank::new_for_tests(&genesis_config); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::default_with_bank_forks( max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, )); let (rpc, mut receiver) = rpc_pubsub_service::test_connection(&rpc_subscriptions); @@ -1290,8 +1306,10 @@ mod tests { let bank = Bank::new_for_tests(&genesis_config); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::default_with_bank_forks( max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, )); let (rpc, mut receiver) = rpc_pubsub_service::test_connection(&rpc_subscriptions); @@ -1336,9 +1354,11 @@ mod tests { let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, block_commitment_cache, optimistically_confirmed_bank, @@ -1372,8 +1392,10 @@ mod tests { let bank = Bank::new_for_tests(&genesis_config); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::default_with_bank_forks( max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, )); let (rpc, _receiver) = rpc_pubsub_service::test_connection(&rpc_subscriptions); @@ -1389,8 +1411,10 @@ mod tests { let bank = Bank::new_for_tests(&genesis_config); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let rpc_subscriptions = Arc::new(RpcSubscriptions::default_with_bank_forks( max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, )); let (rpc, _receiver) = rpc_pubsub_service::test_connection(&rpc_subscriptions); diff --git a/rpc/src/rpc_pubsub_service.rs b/rpc/src/rpc_pubsub_service.rs index 4efc2b0aa12683..95f7b5d9c8256b 100644 --- a/rpc/src/rpc_pubsub_service.rs +++ b/rpc/src/rpc_pubsub_service.rs @@ -85,7 +85,7 @@ impl PubSubService { let (trigger, tripwire) = Tripwire::new(); let thread_hdl = Builder::new() - .name("solana-pubsub".to_string()) + .name("solRpcPubSub".to_string()) .spawn(move || { let runtime = tokio::runtime::Builder::new_multi_thread() .worker_threads(pubsub_config.worker_threads) @@ -401,6 +401,7 @@ mod tests { let pubsub_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); let exit = Arc::new(AtomicBool::new(false)); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000); let bank = Bank::new_for_tests(&genesis_config); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); @@ -409,6 +410,7 @@ mod tests { let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), optimistically_confirmed_bank, @@ -416,6 +418,6 @@ mod tests { let (_trigger, pubsub_service) = PubSubService::new(PubSubConfig::default(), &subscriptions, pubsub_addr); let thread = pubsub_service.thread_hdl.thread(); - assert_eq!(thread.name().unwrap(), "solana-pubsub"); + assert_eq!(thread.name().unwrap(), "solRpcPubSub"); } } diff --git a/rpc/src/rpc_service.rs b/rpc/src/rpc_service.rs index 02c2198b9763c2..abf30c83feeae2 100644 --- a/rpc/src/rpc_service.rs +++ b/rpc/src/rpc_service.rs @@ -30,6 +30,7 @@ use { solana_poh::poh_recorder::PohRecorder, solana_runtime::{ bank_forks::BankForks, commitment::BlockCommitmentCache, + prioritization_fee_cache::PrioritizationFeeCache, snapshot_archive_info::SnapshotArchiveInfoGetter, snapshot_config::SnapshotConfig, snapshot_utils, }, @@ -120,6 +121,10 @@ impl RpcRequestMiddleware { .unwrap() } + fn strip_leading_slash(path: &str) -> Option<&str> { + path.strip_prefix('/') + } + fn is_file_get_path(&self, path: &str) -> bool { if path == DEFAULT_GENESIS_DOWNLOAD_PATH { return true; @@ -129,15 +134,13 @@ impl RpcRequestMiddleware { return false; } - let starting_character = '/'; - if !path.starts_with(starting_character) { - return false; + match Self::strip_leading_slash(path) { + None => false, + Some(path) => { + self.full_snapshot_archive_path_regex.is_match(path) + || self.incremental_snapshot_archive_path_regex.is_match(path) + } } - - let path = path.trim_start_matches(starting_character); - - self.full_snapshot_archive_path_regex.is_match(path) - || self.incremental_snapshot_archive_path_regex.is_match(path) } #[cfg(unix)] @@ -187,8 +190,8 @@ impl RpcRequestMiddleware { } fn process_file_get(&self, path: &str) -> RequestMiddlewareAction { - let stem = path.split_at(1).1; // Drop leading '/' from path let filename = { + let stem = Self::strip_leading_slash(path).expect("path already verified"); match path { DEFAULT_GENESIS_DOWNLOAD_PATH => { inc_new_counter_info!("rpc-get_genesis", 1); @@ -346,6 +349,7 @@ impl JsonRpcService { genesis_hash: Hash, ledger_path: &Path, validator_exit: Arc>, + exit: Arc, known_validators: Option>, override_health_check: Arc, startup_verification_complete: Arc, @@ -354,7 +358,9 @@ impl JsonRpcService { max_slots: Arc, leader_schedule_cache: Arc, connection_cache: Arc, - current_transaction_status_slot: Arc, + max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, + prioritization_fee_cache: Arc, ) -> Self { info!("rpc bound to {:?}", rpc_addr); info!("rpc configuration: {:?}", config); @@ -385,7 +391,7 @@ impl JsonRpcService { tokio::runtime::Builder::new_multi_thread() .worker_threads(rpc_threads) .on_thread_start(move || renice_this_thread(rpc_niceness_adj).unwrap()) - .thread_name("sol-rpc-el") + .thread_name("solRpcEl") .enable_all() .build() .expect("Runtime"), @@ -421,7 +427,8 @@ impl JsonRpcService { bigtable_ledger_storage.clone(), blockstore.clone(), block_commitment_cache.clone(), - current_transaction_status_slot.clone(), + max_complete_transaction_status_slot.clone(), + max_complete_rewards_slot.clone(), ConfirmedBlockUploadConfig::default(), exit_bigtable_ledger_upload_service.clone(), ))) @@ -459,7 +466,9 @@ impl JsonRpcService { largest_accounts_cache, max_slots, leader_schedule_cache, - current_transaction_status_slot, + max_complete_transaction_status_slot, + max_complete_rewards_slot, + prioritization_fee_cache, ); let leader_info = @@ -471,6 +480,7 @@ impl JsonRpcService { receiver, &connection_cache, send_transaction_service_config, + exit, )); #[cfg(test)] @@ -480,7 +490,7 @@ impl JsonRpcService { let (close_handle_sender, close_handle_receiver) = unbounded(); let thread_hdl = Builder::new() - .name("solana-jsonrpc".to_string()) + .name("solJsonRpcSvc".to_string()) .spawn(move || { renice_this_thread(rpc_niceness_adj).unwrap(); @@ -540,7 +550,9 @@ impl JsonRpcService { validator_exit .write() .unwrap() - .register_exit(Box::new(move || close_handle_.close())); + .register_exit(Box::new(move || { + close_handle_.close(); + })); Self { thread_hdl, #[cfg(test)] @@ -555,7 +567,8 @@ impl JsonRpcService { } } - pub fn join(self) -> thread::Result<()> { + pub fn join(mut self) -> thread::Result<()> { + self.exit(); self.thread_hdl.join() } } @@ -567,9 +580,9 @@ mod tests { crate::rpc::create_validator_exit, solana_client::rpc_config::RpcContextConfig, solana_gossip::{ - contact_info::ContactInfo, crds::GossipRoute, crds_value::{CrdsData, CrdsValue, SnapshotHashes}, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_ledger::{ genesis_utils::{create_genesis_config, GenesisConfigInfo}, @@ -628,6 +641,7 @@ mod tests { Hash::default(), &PathBuf::from("farf"), validator_exit, + exit, None, Arc::new(AtomicBool::new(false)), Arc::new(AtomicBool::new(true)), @@ -641,9 +655,11 @@ mod tests { Arc::new(LeaderScheduleCache::default()), connection_cache, Arc::new(AtomicU64::default()), + Arc::new(AtomicU64::default()), + Arc::new(PrioritizationFeeCache::default()), ); let thread = rpc_service.thread_hdl.thread(); - assert_eq!(thread.name().unwrap(), "solana-jsonrpc"); + assert_eq!(thread.name().unwrap(), "solJsonRpcSvc"); assert_eq!( 10_000, @@ -677,6 +693,35 @@ mod tests { ); } + #[test] + fn test_strip_prefix() { + assert_eq!(RpcRequestMiddleware::strip_leading_slash("/"), Some("")); + assert_eq!(RpcRequestMiddleware::strip_leading_slash("//"), Some("/")); + assert_eq!( + RpcRequestMiddleware::strip_leading_slash("/abc"), + Some("abc") + ); + assert_eq!( + RpcRequestMiddleware::strip_leading_slash("//abc"), + Some("/abc") + ); + assert_eq!( + RpcRequestMiddleware::strip_leading_slash("/./abc"), + Some("./abc") + ); + assert_eq!( + RpcRequestMiddleware::strip_leading_slash("/../abc"), + Some("../abc") + ); + + assert_eq!(RpcRequestMiddleware::strip_leading_slash(""), None); + assert_eq!(RpcRequestMiddleware::strip_leading_slash("./"), None); + assert_eq!(RpcRequestMiddleware::strip_leading_slash("../"), None); + assert_eq!(RpcRequestMiddleware::strip_leading_slash("."), None); + assert_eq!(RpcRequestMiddleware::strip_leading_slash(".."), None); + assert_eq!(RpcRequestMiddleware::strip_leading_slash("abc"), None); + } + #[test] fn test_is_file_get_path() { let bank_forks = create_bank_forks(); @@ -695,6 +740,8 @@ mod tests { assert!(rrm.is_file_get_path(DEFAULT_GENESIS_DOWNLOAD_PATH)); assert!(!rrm.is_file_get_path(DEFAULT_GENESIS_ARCHIVE)); + assert!(!rrm.is_file_get_path("//genesis.tar.bz2")); + assert!(!rrm.is_file_get_path("/../genesis.tar.bz2")); assert!(!rrm.is_file_get_path("/snapshot.tar.bz2")); // This is a redirect @@ -744,8 +791,34 @@ mod tests { .is_file_get_path("../../../test/incremental-snapshot-123-456-xxx.tar")); assert!(!rrm.is_file_get_path("/")); + assert!(!rrm.is_file_get_path("//")); + assert!(!rrm.is_file_get_path("/.")); + assert!(!rrm.is_file_get_path("/./")); + assert!(!rrm.is_file_get_path("/..")); + assert!(!rrm.is_file_get_path("/../")); + assert!(!rrm.is_file_get_path(".")); + assert!(!rrm.is_file_get_path("./")); + assert!(!rrm.is_file_get_path(".//")); assert!(!rrm.is_file_get_path("..")); + assert!(!rrm.is_file_get_path("../")); + assert!(!rrm.is_file_get_path("..//")); assert!(!rrm.is_file_get_path("🎣")); + + assert!(!rrm_with_snapshot_config + .is_file_get_path("//snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar")); + assert!(!rrm_with_snapshot_config + .is_file_get_path("/./snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar")); + assert!(!rrm_with_snapshot_config + .is_file_get_path("/../snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar")); + assert!(!rrm_with_snapshot_config.is_file_get_path( + "//incremental-snapshot-100-200-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar" + )); + assert!(!rrm_with_snapshot_config.is_file_get_path( + "/./incremental-snapshot-100-200-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar" + )); + assert!(!rrm_with_snapshot_config.is_file_get_path( + "/../incremental-snapshot-100-200-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar" + )); } #[test] diff --git a/rpc/src/rpc_subscriptions.rs b/rpc/src/rpc_subscriptions.rs index bd9fe337460279..2bb22341931986 100644 --- a/rpc/src/rpc_subscriptions.rs +++ b/rpc/src/rpc_subscriptions.rs @@ -546,6 +546,7 @@ impl RpcSubscriptions { pub fn new( exit: &Arc, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, blockstore: Arc, bank_forks: Arc>, block_commitment_cache: Arc>, @@ -554,17 +555,20 @@ impl RpcSubscriptions { Self::new_with_config( exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, blockstore, bank_forks, block_commitment_cache, optimistically_confirmed_bank, &PubSubConfig::default(), + None, ) } pub fn new_for_tests( exit: &Arc, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, bank_forks: Arc>, block_commitment_cache: Arc>, optimistically_confirmed_bank: Arc>, @@ -573,44 +577,63 @@ impl RpcSubscriptions { let blockstore = Blockstore::open(&ledger_path).unwrap(); let blockstore = Arc::new(blockstore); - Self::new_with_config( + Self::new_for_tests_with_blockstore( exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, blockstore, bank_forks, block_commitment_cache, optimistically_confirmed_bank, - &PubSubConfig::default_for_tests(), ) } pub fn new_for_tests_with_blockstore( exit: &Arc, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, blockstore: Arc, bank_forks: Arc>, block_commitment_cache: Arc>, optimistically_confirmed_bank: Arc>, ) -> Self { - Self::new_with_config( + let rpc_notifier_ready = Arc::new(AtomicBool::new(false)); + + let rpc_subscriptions = Self::new_with_config( exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, blockstore, bank_forks, block_commitment_cache, optimistically_confirmed_bank, &PubSubConfig::default_for_tests(), - ) + Some(rpc_notifier_ready.clone()), + ); + + // Ensure RPC notifier is ready to receive notifications before proceeding + let start_time = Instant::now(); + loop { + if rpc_notifier_ready.load(Ordering::Relaxed) { + break; + } else if (Instant::now() - start_time).as_millis() > 5000 { + panic!("RPC notifier thread setup took too long"); + } + } + + rpc_subscriptions } pub fn new_with_config( exit: &Arc, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, blockstore: Arc, bank_forks: Arc>, block_commitment_cache: Arc>, optimistically_confirmed_bank: Arc>, config: &PubSubConfig, + rpc_notifier_ready: Option>, ) -> Self { let (notification_sender, notification_receiver) = crossbeam_channel::unbounded(); @@ -632,17 +655,21 @@ impl RpcSubscriptions { } else { Some( Builder::new() - .name("solana-rpc-notifications".to_string()) + .name("solRpcNotifier".to_string()) .spawn(move || { let pool = rayon::ThreadPoolBuilder::new() .num_threads(notification_threads) - .thread_name(|i| format!("sol-sub-notif-{}", i)) + .thread_name(|i| format!("solRpcNotify{:02}", i)) .build() .unwrap(); pool.install(|| { + if let Some(rpc_notifier_ready) = rpc_notifier_ready { + rpc_notifier_ready.fetch_or(true, Ordering::Relaxed); + } Self::process_notifications( exit_clone, max_complete_transaction_status_slot, + max_complete_rewards_slot, blockstore, notifier, notification_receiver, @@ -678,6 +705,7 @@ impl RpcSubscriptions { // For tests only... pub fn default_with_bank_forks( max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, bank_forks: Arc>, ) -> Self { let ledger_path = get_tmp_ledger_path!(); @@ -688,6 +716,7 @@ impl RpcSubscriptions { Self::new( &Arc::new(AtomicBool::new(false)), max_complete_transaction_status_slot, + max_complete_rewards_slot, blockstore, bank_forks, Arc::new(RwLock::new(BlockCommitmentCache::default())), @@ -757,9 +786,11 @@ impl RpcSubscriptions { } } + #[allow(clippy::too_many_arguments)] fn process_notifications( exit: Arc, max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, blockstore: Arc, notifier: RpcNotifier, notification_receiver: Receiver, @@ -799,7 +830,7 @@ impl RpcSubscriptions { { debug!("slot notify: {:?}", slot_info); inc_new_counter_info!("rpc-subscription-notify-slot", 1); - notifier.notify(&slot_info, sub, false); + notifier.notify(slot_info, sub, false); } } NotificationEntry::SlotUpdate(slot_update) => { @@ -838,13 +869,14 @@ impl RpcSubscriptions { { debug!("root notify: {:?}", root); inc_new_counter_info!("rpc-subscription-notify-root", 1); - notifier.notify(&root, sub, false); + notifier.notify(root, sub, false); } } NotificationEntry::Bank(commitment_slots) => { const SOURCE: &str = "bank"; RpcSubscriptions::notify_watchers( max_complete_transaction_status_slot.clone(), + max_complete_rewards_slot.clone(), subscriptions.commitment_watchers(), &bank_forks, &blockstore, @@ -861,6 +893,7 @@ impl RpcSubscriptions { const SOURCE: &str = "gossip"; RpcSubscriptions::notify_watchers( max_complete_transaction_status_slot.clone(), + max_complete_rewards_slot.clone(), subscriptions.gossip_watchers(), &bank_forks, &blockstore, @@ -915,6 +948,7 @@ impl RpcSubscriptions { fn notify_watchers( max_complete_transaction_status_slot: Arc, + max_complete_rewards_slot: Arc, subscriptions: &HashMap>, bank_forks: &Arc>, blockstore: &Blockstore, @@ -1011,7 +1045,9 @@ impl RpcSubscriptions { // caused by non-deterministic concurrency accesses, we // break out of the loop. Besides if the current `s` is // greater, then any `s + K` is also greater. - if s > max_complete_transaction_status_slot.load(Ordering::SeqCst) { + if s > max_complete_transaction_status_slot.load(Ordering::SeqCst) + || s > max_complete_rewards_slot.load(Ordering::SeqCst) + { break; } @@ -1251,6 +1287,7 @@ pub(crate) mod tests { solana_runtime::{ commitment::BlockCommitment, genesis_utils::{create_genesis_config, GenesisConfigInfo}, + prioritization_fee_cache::PrioritizationFeeCache, }, solana_sdk::{ commitment_config::CommitmentConfig, @@ -1304,9 +1341,11 @@ pub(crate) mod tests { let exit = Arc::new(AtomicBool::new(false)); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots( 1, 1, @@ -1421,9 +1460,11 @@ pub(crate) mod tests { let blockstore = Blockstore::open(&ledger_path).unwrap(); let blockstore = Arc::new(blockstore); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests_with_blockstore( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, blockstore.clone(), bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), @@ -1539,9 +1580,11 @@ pub(crate) mod tests { let blockstore = Blockstore::open(&ledger_path).unwrap(); let blockstore = Arc::new(blockstore); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests_with_blockstore( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, blockstore.clone(), bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), @@ -1655,9 +1698,11 @@ pub(crate) mod tests { let blockstore = Blockstore::open(&ledger_path).unwrap(); let blockstore = Arc::new(blockstore); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests_with_blockstore( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, blockstore.clone(), bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), @@ -1786,9 +1831,11 @@ pub(crate) mod tests { let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), optimistically_confirmed_bank, @@ -1933,9 +1980,11 @@ pub(crate) mod tests { OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let mut pending_optimistically_confirmed_banks = HashSet::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots( 1, 1, @@ -1970,6 +2019,7 @@ pub(crate) mod tests { })); let mut highest_confirmed_slot: Slot = 0; + let mut highest_root_slot: Slot = 0; let mut last_notified_confirmed_slot: Slot = 0; // Optimistically notifying slot 3 without notifying slot 1 and 2, bank3 is unfrozen, we expect // to see transaction for alice and bob to be notified in order. @@ -1981,7 +2031,9 @@ pub(crate) mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); // a closure to reduce code duplications in building expected responses: @@ -2031,7 +2083,9 @@ pub(crate) mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); let response = receiver.recv(); @@ -2103,9 +2157,11 @@ pub(crate) mod tests { OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let mut pending_optimistically_confirmed_banks = HashSet::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots( 1, 1, @@ -2137,6 +2193,7 @@ pub(crate) mod tests { })); let mut highest_confirmed_slot: Slot = 0; + let mut highest_root_slot: Slot = 0; let mut last_notified_confirmed_slot: Slot = 0; // Optimistically notifying slot 3 without notifying slot 1 and 2, bank3 is not in the bankforks, we do not // expect to see any RPC notifications. @@ -2148,7 +2205,9 @@ pub(crate) mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); // The following should panic @@ -2214,9 +2273,11 @@ pub(crate) mod tests { OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let mut pending_optimistically_confirmed_banks = HashSet::new(); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots( 1, 1, @@ -2249,6 +2310,7 @@ pub(crate) mod tests { })); let mut highest_confirmed_slot: Slot = 0; + let mut highest_root_slot: Slot = 0; let mut last_notified_confirmed_slot: Slot = 0; // Optimistically notifying slot 3 without notifying slot 1 and 2, bank3 is not in the bankforks, we expect // to see transaction for alice and bob to be notified only when bank3 is added to the fork and @@ -2261,7 +2323,9 @@ pub(crate) mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); // a closure to reduce code duplications in building expected responses: @@ -2313,7 +2377,9 @@ pub(crate) mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); let response = receiver.recv(); @@ -2401,9 +2467,11 @@ pub(crate) mod tests { let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, Arc::new(RwLock::new(block_commitment_cache)), optimistically_confirmed_bank, @@ -2575,9 +2643,11 @@ pub(crate) mod tests { let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), optimistically_confirmed_bank, @@ -2621,9 +2691,11 @@ pub(crate) mod tests { let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), optimistically_confirmed_bank, @@ -2679,9 +2751,11 @@ pub(crate) mod tests { let exit = Arc::new(AtomicBool::new(false)); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots( 1, 1, @@ -2729,6 +2803,7 @@ pub(crate) mod tests { // First, notify the unfrozen bank first to queue pending notification let mut highest_confirmed_slot: Slot = 0; + let mut highest_root_slot: Slot = 0; let mut last_notified_confirmed_slot: Slot = 0; OptimisticallyConfirmedBankTracker::process_notification( BankNotification::OptimisticallyConfirmed(2), @@ -2738,7 +2813,9 @@ pub(crate) mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); // Now, notify the frozen bank and ensure its notifications are processed @@ -2751,7 +2828,9 @@ pub(crate) mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); let response = receiver0.recv(); @@ -2801,7 +2880,9 @@ pub(crate) mod tests { &mut pending_optimistically_confirmed_banks, &mut last_notified_confirmed_slot, &mut highest_confirmed_slot, + &mut highest_root_slot, &None, + &PrioritizationFeeCache::default(), ); let response = receiver1.recv(); let expected = json!({ @@ -2869,11 +2950,13 @@ pub(crate) mod tests { let exit = Arc::new(AtomicBool::new(false)); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let optimistically_confirmed_bank = OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks); let subscriptions = Arc::new(RpcSubscriptions::new_for_tests( &exit, max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks.clone(), Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), optimistically_confirmed_bank, @@ -2943,9 +3026,11 @@ pub(crate) mod tests { let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(100); let bank = Bank::new_for_tests(&genesis_config); let max_complete_transaction_status_slot = Arc::new(AtomicU64::default()); + let max_complete_rewards_slot = Arc::new(AtomicU64::default()); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); let subscriptions = Arc::new(RpcSubscriptions::default_with_bank_forks( max_complete_transaction_status_slot, + max_complete_rewards_slot, bank_forks, )); diff --git a/rpc/src/transaction_status_service.rs b/rpc/src/transaction_status_service.rs index 53493780345a3f..abdb3e6fc14783 100644 --- a/rpc/src/transaction_status_service.rs +++ b/rpc/src/transaction_status_service.rs @@ -37,7 +37,7 @@ impl TransactionStatusService { ) -> Self { let exit = exit.clone(); let thread_hdl = Builder::new() - .name("solana-transaction-status-writer".to_string()) + .name("solTxStatusWrtr".to_string()) .spawn(move || loop { if exit.load(Ordering::Relaxed) { break; @@ -103,6 +103,7 @@ impl TransactionStatusService { inner_instructions, durable_nonce_fee, return_data, + executed_units, .. } = details; let lamports_per_signature = match durable_nonce_fee { @@ -160,6 +161,7 @@ impl TransactionStatusService { rewards, loaded_addresses, return_data, + compute_units_consumed: Some(executed_units), }; if let Some(transaction_notifier) = transaction_notifier.as_ref() { @@ -226,7 +228,7 @@ pub(crate) mod tests { clock::Slot, hash::Hash, instruction::CompiledInstruction, - message::{Message, MessageHeader, SanitizedMessage}, + message::{LegacyMessage, Message, MessageHeader, SanitizedMessage}, nonce::{self, state::DurableNonce}, nonce_account, pubkey::Pubkey, @@ -349,7 +351,7 @@ pub(crate) mod tests { let mut nonce_account = nonce_account::create_account(1).into_inner(); let durable_nonce = DurableNonce::from_blockhash(&Hash::new(&[42u8; 32])); - let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42); + let data = nonce::state::Data::new(Pubkey::from([1u8; 32]), durable_nonce, 42); nonce_account .set_state(&nonce::state::Versions::new(nonce::State::Initialized( data, @@ -370,7 +372,7 @@ pub(crate) mod tests { durable_nonce_fee: Some(DurableNonceFee::from( &NonceFull::from_partial( rollback_partial, - &SanitizedMessage::Legacy(message), + &SanitizedMessage::Legacy(LegacyMessage::new(message)), &[(pubkey, nonce_account)], &rent_debits, ) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 5085718e12fc00..7fcfff4634986f 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-runtime" -version = "1.11.6" +version = "1.14.24" description = "Solana runtime" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -12,11 +12,11 @@ edition = "2021" [dependencies] arrayref = "0.3.6" bincode = "1.3.3" -blake3 = "1.3.1" +blake3 = "=1.3.1" bv = { version = "0.11.1", features = ["serde"] } bytemuck = "1.11.0" byteorder = "1.4.3" -bzip2 = "0.4.3" +bzip2 = "0.4.4" crossbeam-channel = "0.5" dashmap = { version = "4.0.2", features = ["rayon", "raw-api"] } dir-diff = "0.3.2" @@ -27,7 +27,8 @@ index_list = "0.2.7" itertools = "0.10.3" lazy_static = "1.4.0" log = "0.4.17" -lz4 = "1.23.3" +lru = "0.7.7" +lz4 = "1.24.0" memmap2 = "0.5.3" num-derive = { version = "0.3" } num-traits = { version = "0.2" } @@ -39,26 +40,28 @@ rayon = "1.5.3" regex = "1.5.6" serde = { version = "1.0.138", features = ["rc"] } serde_derive = "1.0.103" -solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.11.6" } -solana-bucket-map = { path = "../bucket_map", version = "=1.11.6" } -solana-compute-budget-program = { path = "../programs/compute-budget", version = "=1.11.6" } -solana-config-program = { path = "../programs/config", version = "=1.11.6" } -solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-program-runtime = { path = "../program-runtime", version = "=1.11.6" } -solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-stake-program = { path = "../programs/stake", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } -solana-zk-token-proof-program = { path = "../programs/zk-token-proof", version = "=1.11.6" } -solana-zk-token-sdk = { path = "../zk-token-sdk", version = "=1.11.6" } +solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.14.24" } +solana-bucket-map = { path = "../bucket_map", version = "=1.14.24" } +solana-compute-budget-program = { path = "../programs/compute-budget", version = "=1.14.24" } +solana-config-program = { path = "../programs/config", version = "=1.14.24" } +solana-frozen-abi = { path = "../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-program-runtime = { path = "../program-runtime", version = "=1.14.24" } +solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } +solana-zk-token-proof-program = { path = "../programs/zk-token-proof", version = "=1.14.24" } +solana-zk-token-sdk = { path = "../zk-token-sdk", version = "=1.14.24" } +solana-merkle-tree = {path = "../merkle-tree",version = "=1.14.24"} +# solana-receipt-tree = { path = "../receipt-tree", version = "=1.14.24" } strum = { version = "0.24", features = ["derive"] } strum_macros = "0.24" symlink = "0.1.0" tar = "0.4.38" -tempfile = "3.3.0" +tempfile = "3.4.0" thiserror = "1.0" zstd = "0.11.2" @@ -71,10 +74,13 @@ assert_matches = "1.5.0" ed25519-dalek = "=1.0.1" libsecp256k1 = "0.6.0" rand_chacha = "0.2.2" -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [build-dependencies] rustc_version = "0.4" + +[[bench]] +name = "prioritization_fee_cache" diff --git a/runtime/benches/accounts.rs b/runtime/benches/accounts.rs index 8fd7d00f959e9d..27f22de6634ae0 100644 --- a/runtime/benches/accounts.rs +++ b/runtime/benches/accounts.rs @@ -127,6 +127,7 @@ fn test_accounts_hash_bank_hash(bencher: &mut Bencher) { false, false, false, + false, )) }); } diff --git a/runtime/benches/bank.rs b/runtime/benches/bank.rs index 139cd15692414a..26441f566587cf 100644 --- a/runtime/benches/bank.rs +++ b/runtime/benches/bank.rs @@ -47,7 +47,7 @@ pub fn create_builtin_transactions( bank_client: &BankClient, mint_keypair: &Keypair, ) -> Vec { - let program_id = Pubkey::new(&BUILTIN_PROGRAM_ID); + let program_id = Pubkey::from(BUILTIN_PROGRAM_ID); (0..4096) .map(|_| { @@ -69,7 +69,7 @@ pub fn create_native_loader_transactions( bank_client: &BankClient, mint_keypair: &Keypair, ) -> Vec { - let program_id = Pubkey::new(&NOOP_PROGRAM_ID); + let program_id = Pubkey::from(NOOP_PROGRAM_ID); (0..4096) .map(|_| { @@ -137,10 +137,10 @@ fn do_bench_transactions( let mut bank = Bank::new_from_parent(&Arc::new(bank), &Pubkey::default(), 1); bank.add_builtin( "builtin_program", - &Pubkey::new(&BUILTIN_PROGRAM_ID), + &Pubkey::from(BUILTIN_PROGRAM_ID), process_instruction, ); - bank.add_builtin_account("solana_noop_program", &Pubkey::new(&NOOP_PROGRAM_ID), false); + bank.add_builtin_account("solana_noop_program", &Pubkey::from(NOOP_PROGRAM_ID), false); let bank = Arc::new(bank); let bank_client = BankClient::new_shared(&bank); let transactions = create_transactions(&bank_client, &mint_keypair); diff --git a/runtime/benches/prioritization_fee_cache.rs b/runtime/benches/prioritization_fee_cache.rs new file mode 100644 index 00000000000000..e04e783b99d43e --- /dev/null +++ b/runtime/benches/prioritization_fee_cache.rs @@ -0,0 +1,113 @@ +#![feature(test)] +extern crate test; + +use { + rand::{thread_rng, Rng}, + solana_runtime::{ + bank::Bank, + bank_forks::BankForks, + genesis_utils::{create_genesis_config, GenesisConfigInfo}, + prioritization_fee_cache::*, + }, + solana_sdk::{ + compute_budget::ComputeBudgetInstruction, + message::Message, + pubkey::Pubkey, + system_instruction, + transaction::{SanitizedTransaction, Transaction}, + }, + std::sync::Arc, + test::Bencher, +}; +const TRANSFER_TRANSACTION_COMPUTE_UNIT: u32 = 200; + +fn build_sanitized_transaction( + compute_unit_price: u64, + signer_account: &Pubkey, + write_account: &Pubkey, +) -> SanitizedTransaction { + let transfer_lamports = 1; + let transaction = Transaction::new_unsigned(Message::new( + &[ + system_instruction::transfer(signer_account, write_account, transfer_lamports), + ComputeBudgetInstruction::set_compute_unit_limit(TRANSFER_TRANSACTION_COMPUTE_UNIT), + ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price), + ], + Some(signer_account), + )); + + SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap() +} + +#[bench] +#[ignore] +fn bench_process_transactions_single_slot(bencher: &mut Bencher) { + let prioritization_fee_cache = PrioritizationFeeCache::default(); + + let bank = Arc::new(Bank::default_for_tests()); + + // build test transactions + let transactions: Vec<_> = (0..5000) + .map(|n| { + let compute_unit_price = n % 7; + build_sanitized_transaction( + compute_unit_price, + &Pubkey::new_unique(), + &Pubkey::new_unique(), + ) + }) + .collect(); + + bencher.iter(|| { + prioritization_fee_cache.update(bank.clone(), transactions.iter()); + }); +} + +fn process_transactions_multiple_slots(banks: &[Arc], num_slots: usize, num_threads: usize) { + let prioritization_fee_cache = Arc::new(PrioritizationFeeCache::default()); + + let pool = rayon::ThreadPoolBuilder::new() + .num_threads(num_threads) + .build() + .unwrap(); + + // each threads updates a slot a batch of 50 transactions, for 100 times + for _ in 0..100 { + pool.install(|| { + let transactions: Vec<_> = (0..50) + .map(|n| { + let compute_unit_price = n % 7; + build_sanitized_transaction( + compute_unit_price, + &Pubkey::new_unique(), + &Pubkey::new_unique(), + ) + }) + .collect(); + + let index = thread_rng().gen_range(0, num_slots); + + prioritization_fee_cache.update(banks[index].clone(), transactions.iter()); + }) + } +} + +#[bench] +#[ignore] +fn bench_process_transactions_multiple_slots(bencher: &mut Bencher) { + const NUM_SLOTS: usize = 5; + const NUM_THREADS: usize = 3; + + let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000); + let bank0 = Bank::new_for_benches(&genesis_config); + let bank_forks = BankForks::new(bank0); + let bank = bank_forks.working_bank(); + let collector = solana_sdk::pubkey::new_rand(); + let banks = (1..=NUM_SLOTS) + .map(|n| Arc::new(Bank::new_from_parent(&bank, &collector, n as u64))) + .collect::>(); + + bencher.iter(|| { + process_transactions_multiple_slots(&banks, NUM_SLOTS, NUM_THREADS); + }); +} diff --git a/runtime/benches/status_cache.rs b/runtime/benches/status_cache.rs index c207a71246cc10..dfe6ccfc98ec18 100644 --- a/runtime/benches/status_cache.rs +++ b/runtime/benches/status_cache.rs @@ -25,7 +25,7 @@ fn bench_status_cache_serialize(bencher: &mut Bencher) { id = hash(id.as_ref()); sigbytes.extend(id.as_ref()); let sig = Signature::new(&sigbytes); - status_cache.insert(&blockhash, &sig, 0, Ok(())); + status_cache.insert(&blockhash, sig, 0, Ok(())); } } bencher.iter(|| { diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 1da0fb7d8406e7..0ec9549b01f685 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -4,7 +4,7 @@ use { account_rent_state::{check_rent_state_with_account, RentState}, accounts_db::{ AccountShrinkThreshold, AccountsAddRootTiming, AccountsDb, AccountsDbConfig, - BankHashInfo, LoadHint, LoadedAccount, ScanStorageResult, + BankHashInfo, LoadHint, LoadZeroLamports, LoadedAccount, ScanStorageResult, ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING, }, accounts_index::{ @@ -35,7 +35,9 @@ use { bpf_loader_upgradeable::{self, UpgradeableLoaderState}, clock::{BankId, Slot, INITIAL_RENT_EPOCH}, feature_set::{ - self, add_set_compute_unit_price_ix, use_default_units_in_fee_calculation, FeatureSet, + self, add_set_compute_unit_price_ix, enable_request_heap_frame_ix, + return_none_for_zero_lamport_accounts, use_default_units_in_fee_calculation, + FeatureSet, }, fee::FeeStructure, genesis_config::ClusterType, @@ -253,6 +255,13 @@ impl Accounts { feature_set: &FeatureSet, account_overrides: Option<&AccountOverrides>, ) -> Result { + let load_zero_lamports = + if feature_set.is_active(&return_none_for_zero_lamport_accounts::id()) { + LoadZeroLamports::None + } else { + LoadZeroLamports::SomeWithZeroLamportAccount + }; + // Copy all the accounts let message = tx.message(); // NOTE: this check will never fail because `tx` is sanitized @@ -288,7 +297,7 @@ impl Accounts { (account_override.clone(), 0) } else { self.accounts_db - .load_with_fixed_root(ancestors, key) + .load_with_fixed_root(ancestors, key, load_zero_lamports) .map(|(mut account, _)| { if message.is_writable(i) { let rent_due = rent_collector @@ -337,9 +346,12 @@ impl Accounts { programdata_address, }) = account.state() { - if let Some((programdata_account, _)) = self - .accounts_db - .load_with_fixed_root(ancestors, &programdata_address) + if let Some((programdata_account, _)) = + self.accounts_db.load_with_fixed_root( + ancestors, + &programdata_address, + load_zero_lamports, + ) { account_deps .push((programdata_address, programdata_account)); @@ -379,10 +391,12 @@ impl Accounts { .iter() .map(|instruction| { self.load_executable_accounts( + feature_set, ancestors, &mut accounts, instruction.program_id_index as usize, error_counters, + load_zero_lamports, ) }) .collect::>>>()?; @@ -450,10 +464,12 @@ impl Accounts { fn load_executable_accounts( &self, + feature_set: &FeatureSet, ancestors: &Ancestors, accounts: &mut Vec, mut program_account_index: usize, error_counters: &mut TransactionErrorMetrics, + load_zero_lamports: LoadZeroLamports, ) -> Result> { let mut account_indices = Vec::new(); let mut program_id = match accounts.get(program_account_index) { @@ -471,20 +487,16 @@ impl Accounts { } depth += 1; - program_account_index = match self - .accounts_db - .load_with_fixed_root(ancestors, &program_id) + program_account_index = accounts.len(); + if let Some((program_account, _)) = + self.accounts_db + .load_with_fixed_root(ancestors, &program_id, load_zero_lamports) { - Some((program_account, _)) => { - let account_index = accounts.len(); - accounts.push((program_id, program_account)); - account_index - } - None => { - error_counters.account_not_found += 1; - return Err(TransactionError::ProgramAccountNotFound); - } - }; + accounts.push((program_id, program_account)); + } else { + error_counters.account_not_found += 1; + return Err(TransactionError::ProgramAccountNotFound); + } let program = &accounts[program_account_index].1; if !program.executable() { error_counters.invalid_program_for_execution += 1; @@ -492,28 +504,25 @@ impl Accounts { } // Add loader to chain - let program_owner = *program.owner(); + let owner_id = *program.owner(); account_indices.insert(0, program_account_index); - if bpf_loader_upgradeable::check_id(&program_owner) { + if bpf_loader_upgradeable::check_id(&owner_id) { // The upgradeable loader requires the derived ProgramData account if let Ok(UpgradeableLoaderState::Program { programdata_address, }) = program.state() { - let programdata_account_index = match self - .accounts_db - .load_with_fixed_root(ancestors, &programdata_address) - { - Some((programdata_account, _)) => { - let account_index = accounts.len(); - accounts.push((programdata_address, programdata_account)); - account_index - } - None => { - error_counters.account_not_found += 1; - return Err(TransactionError::ProgramAccountNotFound); - } - }; + let programdata_account_index = accounts.len(); + if let Some((programdata_account, _)) = self.accounts_db.load_with_fixed_root( + ancestors, + &programdata_address, + load_zero_lamports, + ) { + accounts.push((programdata_address, programdata_account)); + } else { + error_counters.account_not_found += 1; + return Err(TransactionError::ProgramAccountNotFound); + } account_indices.insert(0, programdata_account_index); } else { error_counters.invalid_program_for_execution += 1; @@ -521,7 +530,30 @@ impl Accounts { } } - program_id = program_owner; + if feature_set.is_active(&feature_set::disable_builtin_loader_ownership_chains::id()) { + if native_loader::check_id(&owner_id) { + return Ok(account_indices); + } + let owner_account_index = accounts.len(); + if let Some((owner_account, _)) = + self.accounts_db + .load_with_fixed_root(ancestors, &owner_id, load_zero_lamports) + { + if !native_loader::check_id(owner_account.owner()) + || !owner_account.executable() + { + error_counters.invalid_program_for_execution += 1; + return Err(TransactionError::InvalidProgramForExecution); + } + accounts.push((owner_id, owner_account)); + } else { + error_counters.account_not_found += 1; + return Err(TransactionError::ProgramAccountNotFound); + } + account_indices.insert(0, owner_account_index); + return Ok(account_indices); + } + program_id = owner_id; } Ok(account_indices) } @@ -556,6 +588,9 @@ impl Accounts { fee_structure, feature_set.is_active(&add_set_compute_unit_price_ix::id()), feature_set.is_active(&use_default_units_in_fee_calculation::id()), + feature_set.is_active(&enable_request_heap_frame_ix::id()) + || self.accounts_db.expected_cluster_type() + != ClusterType::MainnetBeta, ) } else { return (Err(TransactionError::BlockhashNotFound), None); @@ -601,10 +636,15 @@ impl Accounts { ancestors: &Ancestors, address_table_lookup: &MessageAddressTableLookup, slot_hashes: &SlotHashes, + load_zero_lamports: LoadZeroLamports, ) -> std::result::Result { let table_account = self .accounts_db - .load_with_fixed_root(ancestors, &address_table_lookup.account_key) + .load_with_fixed_root( + ancestors, + &address_table_lookup.account_key, + load_zero_lamports, + ) .map(|(account, _rent)| account) .ok_or(AddressLookupError::LookupTableAccountNotFound)?; @@ -826,6 +866,7 @@ impl Accounts { can_cached_slot_be_unflushed: bool, ignore_mismatch: bool, store_detailed_debug_info: bool, + use_bg_thread_pool: bool, ) -> bool { if let Err(err) = self.accounts_db.verify_bank_hash_and_lamports_new( slot, @@ -837,6 +878,7 @@ impl Accounts { can_cached_slot_be_unflushed, ignore_mismatch, store_detailed_debug_info, + use_bg_thread_pool, ) { warn!("verify_bank_hash failed: {:?}, slot: {}", err, slot); false @@ -1119,9 +1161,11 @@ impl Accounts { pub fn lock_accounts<'a>( &self, txs: impl Iterator, + tx_account_lock_limit: usize, ) -> Vec> { - let tx_account_locks_results: Vec> = - txs.map(|tx| tx.get_account_locks()).collect(); + let tx_account_locks_results: Vec> = txs + .map(|tx| tx.get_account_locks(tx_account_lock_limit)) + .collect(); self.lock_accounts_inner(tx_account_locks_results) } @@ -1131,11 +1175,12 @@ impl Accounts { &self, txs: impl Iterator, results: impl Iterator>, + tx_account_lock_limit: usize, ) -> Vec> { let tx_account_locks_results: Vec> = txs .zip(results) .map(|(tx, result)| match result { - Ok(()) => tx.get_account_locks(), + Ok(()) => tx.get_account_locks(tx_account_lock_limit), Err(err) => Err(err.clone()), }) .collect(); @@ -1414,7 +1459,7 @@ mod tests { }, assert_matches::assert_matches, solana_address_lookup_table_program::state::LookupTableMeta, - solana_program_runtime::invoke_context::Executors, + solana_program_runtime::executor_cache::Executors, solana_sdk::{ account::{AccountSharedData, WritableAccount}, epoch_schedule::EpochSchedule, @@ -1533,7 +1578,7 @@ mod tests { #[test] fn test_hold_range_in_memory() { let accts = Accounts::default_for_tests(); - let range = Pubkey::new(&[0; 32])..=Pubkey::new(&[0xff; 32]); + let range = Pubkey::from([0; 32])..=Pubkey::from([0xff; 32]); accts.hold_range_in_memory(&range, true, &test_thread_pool()); accts.hold_range_in_memory(&range, false, &test_thread_pool()); accts.hold_range_in_memory(&range, true, &test_thread_pool()); @@ -1545,7 +1590,7 @@ mod tests { #[test] fn test_hold_range_in_memory2() { let accts = Accounts::default_for_tests(); - let range = Pubkey::new(&[0; 32])..=Pubkey::new(&[0xff; 32]); + let range = Pubkey::from([0; 32])..=Pubkey::from([0xff; 32]); let idx = &accts.accounts_db.accounts_index; let bins = idx.account_maps.len(); // use bins * 2 to get the first half of the range within bin 0 @@ -1618,7 +1663,7 @@ mod tests { let keypair = Keypair::new(); let key0 = keypair.pubkey(); - let key1 = Pubkey::new(&[5u8; 32]); + let key1 = Pubkey::from([5u8; 32]); let account = AccountSharedData::new(1, 0, &Pubkey::default()); accounts.push((key0, account)); @@ -1672,6 +1717,7 @@ mod tests { &FeeStructure::default(), true, true, + true, ); assert_eq!(fee, lamports_per_signature); @@ -1804,7 +1850,7 @@ mod tests { let keypair = Keypair::new(); let key0 = keypair.pubkey(); - let key1 = Pubkey::new(&[5u8; 32]); + let key1 = Pubkey::from([5u8; 32]); let mut account = AccountSharedData::new(1, 0, &Pubkey::default()); account.set_rent_epoch(1); @@ -1838,72 +1884,6 @@ mod tests { } } - #[test] - fn test_load_accounts_max_call_depth() { - let mut accounts: Vec = Vec::new(); - let mut error_counters = TransactionErrorMetrics::default(); - - let keypair = Keypair::new(); - let key0 = keypair.pubkey(); - let key1 = Pubkey::new(&[5u8; 32]); - let key2 = Pubkey::new(&[6u8; 32]); - let key3 = Pubkey::new(&[7u8; 32]); - let key4 = Pubkey::new(&[8u8; 32]); - let key5 = Pubkey::new(&[9u8; 32]); - let key6 = Pubkey::new(&[10u8; 32]); - - let account = AccountSharedData::new(1, 0, &Pubkey::default()); - accounts.push((key0, account)); - - let mut account = AccountSharedData::new(40, 1, &Pubkey::default()); - account.set_executable(true); - account.set_owner(native_loader::id()); - accounts.push((key1, account)); - - let mut account = AccountSharedData::new(41, 1, &Pubkey::default()); - account.set_executable(true); - account.set_owner(key1); - accounts.push((key2, account)); - - let mut account = AccountSharedData::new(42, 1, &Pubkey::default()); - account.set_executable(true); - account.set_owner(key2); - accounts.push((key3, account)); - - let mut account = AccountSharedData::new(43, 1, &Pubkey::default()); - account.set_executable(true); - account.set_owner(key3); - accounts.push((key4, account)); - - let mut account = AccountSharedData::new(44, 1, &Pubkey::default()); - account.set_executable(true); - account.set_owner(key4); - accounts.push((key5, account)); - - let mut account = AccountSharedData::new(45, 1, &Pubkey::default()); - account.set_executable(true); - account.set_owner(key5); - accounts.push((key6, account)); - - let instructions = vec![CompiledInstruction::new(1, &(), vec![0])]; - let tx = Transaction::new_with_compiled_instructions( - &[&keypair], - &[], - Hash::default(), - vec![key6], - instructions, - ); - - let loaded_accounts = load_accounts(tx, &accounts, &mut error_counters); - - assert_eq!(error_counters.call_chain_too_deep, 1); - assert_eq!(loaded_accounts.len(), 1); - assert_eq!( - loaded_accounts[0], - (Err(TransactionError::CallChainTooDeep), None,) - ); - } - #[test] fn test_load_accounts_bad_owner() { let mut accounts: Vec = Vec::new(); @@ -1911,7 +1891,7 @@ mod tests { let keypair = Keypair::new(); let key0 = keypair.pubkey(); - let key1 = Pubkey::new(&[5u8; 32]); + let key1 = Pubkey::from([5u8; 32]); let account = AccountSharedData::new(1, 0, &Pubkey::default()); accounts.push((key0, account)); @@ -1946,7 +1926,7 @@ mod tests { let keypair = Keypair::new(); let key0 = keypair.pubkey(); - let key1 = Pubkey::new(&[5u8; 32]); + let key1 = Pubkey::from([5u8; 32]); let account = AccountSharedData::new(1, 0, &Pubkey::default()); accounts.push((key0, account)); @@ -1980,8 +1960,8 @@ mod tests { let keypair = Keypair::new(); let key0 = keypair.pubkey(); - let key1 = Pubkey::new(&[5u8; 32]); - let key2 = Pubkey::new(&[6u8; 32]); + let key1 = Pubkey::from([5u8; 32]); + let key2 = Pubkey::from([6u8; 32]); let mut account = AccountSharedData::new(1, 0, &Pubkey::default()); account.set_rent_epoch(1); @@ -2063,6 +2043,7 @@ mod tests { &ancestors, &address_table_lookup, &SlotHashes::default(), + LoadZeroLamports::SomeWithZeroLamportAccount, ), Err(AddressLookupError::LookupTableAccountNotFound), ); @@ -2080,7 +2061,8 @@ mod tests { ); let invalid_table_key = Pubkey::new_unique(); - let invalid_table_account = AccountSharedData::default(); + let mut invalid_table_account = AccountSharedData::default(); + invalid_table_account.set_lamports(1); accounts.store_slow_uncached(0, &invalid_table_key, &invalid_table_account); let address_table_lookup = MessageAddressTableLookup { @@ -2094,6 +2076,7 @@ mod tests { &ancestors, &address_table_lookup, &SlotHashes::default(), + LoadZeroLamports::SomeWithZeroLamportAccount, ), Err(AddressLookupError::InvalidAccountOwner), ); @@ -2126,6 +2109,7 @@ mod tests { &ancestors, &address_table_lookup, &SlotHashes::default(), + LoadZeroLamports::SomeWithZeroLamportAccount, ), Err(AddressLookupError::InvalidAccountData), ); @@ -2170,6 +2154,7 @@ mod tests { &ancestors, &address_table_lookup, &SlotHashes::default(), + LoadZeroLamports::SomeWithZeroLamportAccount, ), Ok(LoadedAddresses { writable: vec![table_addresses[0]], @@ -2190,20 +2175,20 @@ mod tests { // Load accounts owned by various programs into AccountsDb let pubkey0 = solana_sdk::pubkey::new_rand(); - let account0 = AccountSharedData::new(1, 0, &Pubkey::new(&[2; 32])); + let account0 = AccountSharedData::new(1, 0, &Pubkey::from([2; 32])); accounts.store_slow_uncached(0, &pubkey0, &account0); let pubkey1 = solana_sdk::pubkey::new_rand(); - let account1 = AccountSharedData::new(1, 0, &Pubkey::new(&[2; 32])); + let account1 = AccountSharedData::new(1, 0, &Pubkey::from([2; 32])); accounts.store_slow_uncached(0, &pubkey1, &account1); let pubkey2 = solana_sdk::pubkey::new_rand(); - let account2 = AccountSharedData::new(1, 0, &Pubkey::new(&[3; 32])); + let account2 = AccountSharedData::new(1, 0, &Pubkey::from([3; 32])); accounts.store_slow_uncached(0, &pubkey2, &account2); - let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::new(&[2; 32]))); + let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::from([2; 32]))); assert_eq!(loaded.len(), 2); - let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::new(&[3; 32]))); + let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::from([3; 32]))); assert_eq!(loaded, vec![(pubkey2, account2)]); - let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::new(&[4; 32]))); + let loaded = accounts.load_by_program_slot(0, Some(&Pubkey::from([4; 32]))); assert_eq!(loaded, vec![]); } @@ -2214,8 +2199,8 @@ mod tests { let keypair = Keypair::new(); let key0 = keypair.pubkey(); - let key1 = Pubkey::new(&[5u8; 32]); - let key2 = Pubkey::new(&[6u8; 32]); + let key1 = Pubkey::from([5u8; 32]); + let key2 = Pubkey::from([6u8; 32]); let mut account = AccountSharedData::new(1, 0, &Pubkey::default()); account.set_rent_epoch(1); @@ -2270,10 +2255,10 @@ mod tests { let keypair = Keypair::new(); let key0 = keypair.pubkey(); - let key1 = Pubkey::new(&[5u8; 32]); - let key2 = Pubkey::new(&[6u8; 32]); - let programdata_key1 = Pubkey::new(&[7u8; 32]); - let programdata_key2 = Pubkey::new(&[8u8; 32]); + let key1 = Pubkey::from([5u8; 32]); + let key2 = Pubkey::from([6u8; 32]); + let programdata_key1 = Pubkey::from([7u8; 32]); + let programdata_key2 = Pubkey::from([8u8; 32]); let mut account = AccountSharedData::new(1, 0, &Pubkey::default()); account.set_rent_epoch(1); @@ -2367,8 +2352,8 @@ mod tests { let keypair = Keypair::new(); let key0 = keypair.pubkey(); - let key1 = Pubkey::new(&[5u8; 32]); - let key2 = Pubkey::new(&[6u8; 32]); + let key1 = Pubkey::from([5u8; 32]); + let key2 = Pubkey::from([6u8; 32]); let mut account = AccountSharedData::new(1, 0, &Pubkey::default()); account.set_rent_epoch(1); @@ -2462,10 +2447,12 @@ mod tests { assert_eq!( accounts.load_executable_accounts( + &FeatureSet::default(), &ancestors, &mut vec![(keypair.pubkey(), account)], 0, &mut error_counters, + LoadZeroLamports::SomeWithZeroLamportAccount, ), Err(TransactionError::ProgramAccountNotFound) ); @@ -2506,7 +2493,7 @@ mod tests { }; let tx = new_sanitized_tx(&[&keypair], message, Hash::default()); - let results = accounts.lock_accounts([tx].iter()); + let results = accounts.lock_accounts([tx].iter(), MAX_TX_ACCOUNT_LOCKS); assert_eq!(results[0], Err(TransactionError::AccountLoadedTwice)); } @@ -2539,7 +2526,7 @@ mod tests { }; let txs = vec![new_sanitized_tx(&[&keypair], message, Hash::default())]; - let results = accounts.lock_accounts(txs.iter()); + let results = accounts.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); assert_eq!(results[0], Ok(())); accounts.unlock_accounts(txs.iter(), &results); } @@ -2561,7 +2548,7 @@ mod tests { }; let txs = vec![new_sanitized_tx(&[&keypair], message, Hash::default())]; - let results = accounts.lock_accounts(txs.iter()); + let results = accounts.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); assert_eq!(results[0], Err(TransactionError::TooManyAccountLocks)); } } @@ -2600,7 +2587,7 @@ mod tests { instructions, ); let tx = new_sanitized_tx(&[&keypair0], message, Hash::default()); - let results0 = accounts.lock_accounts([tx.clone()].iter()); + let results0 = accounts.lock_accounts([tx.clone()].iter(), MAX_TX_ACCOUNT_LOCKS); assert!(results0[0].is_ok()); assert_eq!( @@ -2635,7 +2622,7 @@ mod tests { ); let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default()); let txs = vec![tx0, tx1]; - let results1 = accounts.lock_accounts(txs.iter()); + let results1 = accounts.lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); assert!(results1[0].is_ok()); // Read-only account (keypair1) can be referenced multiple times assert!(results1[1].is_err()); // Read-only account (keypair1) cannot also be locked as writable @@ -2662,7 +2649,7 @@ mod tests { instructions, ); let tx = new_sanitized_tx(&[&keypair1], message, Hash::default()); - let results2 = accounts.lock_accounts([tx].iter()); + let results2 = accounts.lock_accounts([tx].iter(), MAX_TX_ACCOUNT_LOCKS); assert!(results2[0].is_ok()); // Now keypair1 account can be locked as writable // Check that read-only lock with zero references is deleted @@ -2731,7 +2718,9 @@ mod tests { let exit_clone = exit_clone.clone(); loop { let txs = vec![writable_tx.clone()]; - let results = accounts_clone.clone().lock_accounts(txs.iter()); + let results = accounts_clone + .clone() + .lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); for result in results.iter() { if result.is_ok() { counter_clone.clone().fetch_add(1, Ordering::SeqCst); @@ -2746,7 +2735,9 @@ mod tests { let counter_clone = counter; for _ in 0..5 { let txs = vec![readonly_tx.clone()]; - let results = accounts_arc.clone().lock_accounts(txs.iter()); + let results = accounts_arc + .clone() + .lock_accounts(txs.iter(), MAX_TX_ACCOUNT_LOCKS); if results[0].is_ok() { let counter_value = counter_clone.clone().load(Ordering::SeqCst); thread::sleep(time::Duration::from_millis(50)); @@ -2792,7 +2783,7 @@ mod tests { instructions, ); let tx = new_sanitized_tx(&[&keypair0], message, Hash::default()); - let results0 = accounts.lock_accounts([tx].iter()); + let results0 = accounts.lock_accounts([tx].iter(), MAX_TX_ACCOUNT_LOCKS); assert!(results0[0].is_ok()); // Instruction program-id account demoted to readonly @@ -2883,7 +2874,11 @@ mod tests { Ok(()), ]; - let results = accounts.lock_accounts_with_results(txs.iter(), qos_results.iter()); + let results = accounts.lock_accounts_with_results( + txs.iter(), + qos_results.iter(), + MAX_TX_ACCOUNT_LOCKS, + ); assert!(results[0].is_ok()); // Read-only account (keypair0) can be referenced multiple times assert!(results[1].is_err()); // is not locked due to !qos_results[1].is_ok() @@ -3296,7 +3291,7 @@ mod tests { let expect_account = post_account.clone(); // Wrong key assert!(run_prepare_if_nonce_account_test( - &Pubkey::new(&[1u8; 32]), + &Pubkey::from([1u8; 32]), &mut post_account, &Ok(()), false, diff --git a/runtime/src/accounts_background_service.rs b/runtime/src/accounts_background_service.rs index a0695e3373774e..9ba12f742dafe0 100644 --- a/runtime/src/accounts_background_service.rs +++ b/runtime/src/accounts_background_service.rs @@ -370,6 +370,7 @@ impl SnapshotRequestHandler { SnapshotError::MismatchedBaseSlot(..) => true, SnapshotError::NoSnapshotArchives => true, SnapshotError::MismatchedSlotHash(..) => true, + SnapshotError::VerifySlotDeltas(..) => true, } } } @@ -464,7 +465,7 @@ impl AccountsBackgroundService { let mut total_remove_slots_time = 0; let mut last_expiration_check_time = Instant::now(); let t_background = Builder::new() - .name("solana-bg-accounts".to_string()) + .name("solBgAccounts".to_string()) .spawn(move || { let mut stats = StatsManager::new(); let mut last_snapshot_end_time = None; diff --git a/runtime/src/accounts_db.rs b/runtime/src/accounts_db.rs index fd57db023e59e6..137f74b6657702 100644 --- a/runtime/src/accounts_db.rs +++ b/runtime/src/accounts_db.rs @@ -45,7 +45,6 @@ use { bank::Rewrites, cache_hash_data::CacheHashData, contains::Contains, - expected_rent_collection::{ExpectedRentCollection, SlotInfoInEpoch}, pubkey_bins::PubkeyBinCalculator24, read_only_accounts_cache::ReadOnlyAccountsCache, rent_collector::RentCollector, @@ -142,6 +141,17 @@ pub enum StoreReclaims { Ignore, } +/// specifies how to return zero lamport accounts +/// This will only be useful until a feature activation occurs. +#[derive(Clone, Copy)] +pub enum LoadZeroLamports { + /// return None if loaded account has zero lamports + None, + /// return Some(account with zero lamports) if loaded account has zero lamports + /// Today this is the default. With feature activation, this will no longer be possible. + SomeWithZeroLamportAccount, +} + // the current best way to add filler accounts is gradually. // In other scenarios, such as monitoring catchup with large # of accounts, it may be useful to be able to // add filler accounts at the beginning, so that code path remains but won't execute at the moment. @@ -418,6 +428,7 @@ pub(crate) type SlotStores = Arc>; type AppendVecOffsets = HashMap>; type ReclaimResult = (AccountSlots, AppendVecOffsets); +type PubkeysRemovedFromAccountsIndex = HashSet; type ShrinkCandidates = HashMap>>; trait Versioned { @@ -1707,7 +1718,7 @@ pub fn make_min_priority_thread_pool() -> ThreadPool { // Use lower thread count to reduce priority. let num_threads = quarter_thread_count(); rayon::ThreadPoolBuilder::new() - .thread_name(|i| format!("solana-cleanup-accounts-{}", i)) + .thread_name(|i| format!("solAccountsLo{:02}", i)) .num_threads(num_threads) .build() .unwrap() @@ -1781,26 +1792,22 @@ trait AppendVecScan: Send + Sync + Clone { /// These would have been captured in a fn from within the scan function. /// Some of these are constant across all pubkeys, some are constant across a slot. /// Some could be unique per pubkey. -struct ScanState<'a, T: Fn(Slot) -> Option + Sync + Send + Clone> { +struct ScanState<'a> { /// slot we're currently scanning current_slot: Slot, /// accumulated results accum: BinnedHashData, - /// max slot (inclusive) that we're calculating accounts hash on - max_slot_info: SlotInfoInEpoch, bin_calculator: &'a PubkeyBinCalculator24, bin_range: &'a Range, config: &'a CalcAccountsHashConfig<'a>, mismatch_found: Arc, - stats: &'a crate::accounts_hash::HashStats, - find_unskipped_slot: &'a T, filler_account_suffix: Option<&'a Pubkey>, range: usize, sort_time: Arc, pubkey_to_bin_index: usize, } -impl<'a, T: Fn(Slot) -> Option + Sync + Send + Clone> AppendVecScan for ScanState<'a, T> { +impl<'a> AppendVecScan for ScanState<'a> { fn set_slot(&mut self, slot: Slot) { self.current_slot = slot; } @@ -1829,20 +1836,6 @@ impl<'a, T: Fn(Slot) -> Option + Sync + Send + Clone> AppendVecScan for Sc }; let loaded_hash = loaded_account.loaded_hash(); - let new_hash = ExpectedRentCollection::maybe_rehash_skipped_rewrite( - loaded_account, - &loaded_hash, - pubkey, - self.current_slot, - self.config.epoch_schedule, - self.config.rent_collector, - self.stats, - &self.max_slot_info, - self.find_unskipped_slot, - self.filler_account_suffix, - ); - let loaded_hash = new_hash.unwrap_or(loaded_hash); - let source_item = CalculateHashIntermediate::new(loaded_hash, balance, *pubkey); if self.config.check_hash @@ -1902,7 +1895,7 @@ impl AccountsDb { num_hash_scan_passes: Option, ) -> Self { let num_threads = get_thread_count(); - const MAX_READ_ONLY_CACHE_DATA_SIZE: usize = 200_000_000; + const MAX_READ_ONLY_CACHE_DATA_SIZE: usize = 400_000_000; // 400M bytes let mut temp_accounts_hash_cache_path = None; let accounts_hash_cache_path = accounts_hash_cache_path.unwrap_or_else(|| { @@ -1953,7 +1946,7 @@ impl AccountsDb { file_size: DEFAULT_FILE_SIZE, thread_pool: rayon::ThreadPoolBuilder::new() .num_threads(num_threads) - .thread_name(|i| format!("solana-db-accounts-{}", i)) + .thread_name(|i| format!("solAccounts{:02}", i)) .stack_size(ACCOUNTS_STACK_SIZE) .build() .unwrap(), @@ -2151,15 +2144,20 @@ impl AccountsDb { purges: Vec, max_clean_root: Option, ancient_account_cleans: &AtomicU64, - ) -> ReclaimResult { + ) -> (ReclaimResult, PubkeysRemovedFromAccountsIndex) { + let pubkeys_removed_from_accounts_index = HashSet::default(); if purges.is_empty() { - return ReclaimResult::default(); + return ( + ReclaimResult::default(), + pubkeys_removed_from_accounts_index, + ); } // This number isn't carefully chosen; just guessed randomly such that // the hot loop will be the order of ~Xms. const INDEX_CLEAN_BULK_COUNT: usize = 4096; let one_epoch_old = self.get_accounts_hash_complete_one_epoch_old(); + let pubkeys_removed_from_accounts_index = Mutex::new(pubkeys_removed_from_accounts_index); let mut clean_rooted = Measure::start("clean_old_root-ms"); let reclaim_vecs = purges @@ -2167,9 +2165,20 @@ impl AccountsDb { .filter_map(|pubkeys: &[Pubkey]| { let mut reclaims = Vec::new(); for pubkey in pubkeys { - self.accounts_index - .clean_rooted_entries(pubkey, &mut reclaims, max_clean_root); + let removed_from_index = self.accounts_index.clean_rooted_entries( + pubkey, + &mut reclaims, + max_clean_root, + ); + + if removed_from_index { + pubkeys_removed_from_accounts_index + .lock() + .unwrap() + .insert(*pubkey); + } } + (!reclaims.is_empty()).then(|| { // figure out how many ancient accounts have been reclaimed let old_reclaims = reclaims @@ -2183,6 +2192,8 @@ impl AccountsDb { .collect::>(); clean_rooted.stop(); inc_new_counter_info!("clean-old-root-par-clean-ms", clean_rooted.as_ms() as usize); + let pubkeys_removed_from_accounts_index = + pubkeys_removed_from_accounts_index.into_inner().unwrap(); self.clean_accounts_stats .clean_old_root_us .fetch_add(clean_rooted.as_us(), Ordering::Relaxed); @@ -2199,6 +2210,7 @@ impl AccountsDb { None, Some((&self.clean_accounts_stats.purge_stats, &mut reclaim_result)), reset_accounts, + &pubkeys_removed_from_accounts_index, ); measure.stop(); debug!("{} {}", clean_rooted, measure); @@ -2206,7 +2218,7 @@ impl AccountsDb { self.clean_accounts_stats .clean_old_root_reclaim_us .fetch_add(measure.as_us(), Ordering::Relaxed); - reclaim_result + (reclaim_result, pubkeys_removed_from_accounts_index) } fn do_reset_uncleaned_roots(&self, max_clean_root: Option) { @@ -2306,7 +2318,7 @@ impl AccountsDb { fn start_background_hasher(&mut self) { let (sender, receiver) = unbounded(); Builder::new() - .name("solana-db-store-hasher-accounts".to_string()) + .name("solDbStoreHashr".to_string()) .spawn(move || { Self::background_hasher(receiver); }) @@ -2314,10 +2326,11 @@ impl AccountsDb { self.sender_bg_hasher = Some(sender); } + #[must_use] pub(crate) fn purge_keys_exact<'a, C: 'a>( &'a self, pubkey_to_slot_set: impl Iterator, - ) -> Vec<(Slot, AccountInfo)> + ) -> (Vec<(Slot, AccountInfo)>, PubkeysRemovedFromAccountsIndex) where C: Contains<'a, Slot>, { @@ -2333,9 +2346,10 @@ impl AccountsDb { } } - self.accounts_index + let pubkeys_removed_from_accounts_index = self + .accounts_index .handle_dead_keys(&dead_keys, &self.account_indexes); - reclaims + (reclaims, pubkeys_removed_from_accounts_index) } fn max_clean_root(&self, proposed_clean_root: Option) -> Option { @@ -2655,11 +2669,12 @@ impl AccountsDb { accounts_scan.stop(); let mut clean_old_rooted = Measure::start("clean_old_roots"); - let (purged_account_slots, removed_accounts) = self.clean_accounts_older_than_root( - purges_old_accounts, - max_clean_root, - &ancient_account_cleans, - ); + let ((purged_account_slots, removed_accounts), mut pubkeys_removed_from_accounts_index) = + self.clean_accounts_older_than_root( + purges_old_accounts, + max_clean_root, + &ancient_account_cleans, + ); if self.caching_enabled { self.do_reset_uncleaned_roots(max_clean_root); @@ -2752,7 +2767,10 @@ impl AccountsDb { }) .collect(); - let reclaims = self.purge_keys_exact(pubkey_to_slot_set.iter()); + let (reclaims, pubkeys_removed_from_accounts_index2) = + self.purge_keys_exact(pubkey_to_slot_set.iter()); + pubkeys_removed_from_accounts_index + .extend(pubkeys_removed_from_accounts_index2.into_iter()); // Don't reset from clean, since the pubkeys in those stores may need to be unref'ed // and those stores may be used for background hashing. @@ -2763,6 +2781,7 @@ impl AccountsDb { None, Some((&self.clean_accounts_stats.purge_stats, &mut reclaim_result)), reset_accounts, + &pubkeys_removed_from_accounts_index, ); reclaims_time.stop(); @@ -2778,6 +2797,11 @@ impl AccountsDb { i64 ), ("oldest_dirty_slot", key_timings.oldest_dirty_slot, i64), + ( + "pubkeys_removed_from_accounts_index", + pubkeys_removed_from_accounts_index.len(), + i64 + ), ( "dirty_store_processing_us", key_timings.dirty_store_processing_us, @@ -2910,12 +2934,15 @@ impl AccountsDb { /// * `reset_accounts` - Reset the append_vec store when the store is dead (count==0) /// From the clean and shrink paths it should be false since there may be an in-progress /// hash operation and the stores may hold accounts that need to be unref'ed. + /// * `pubkeys_removed_from_accounts_index` - These keys have already been removed from the accounts index + /// and should not be unref'd. If they exist in the accounts index, they are NEW. fn handle_reclaims<'a, I>( &'a self, reclaims: Option, expected_single_dead_slot: Option, purge_stats_and_reclaim_result: Option<(&PurgeStats, &mut ReclaimResult)>, reset_accounts: bool, + pubkeys_removed_from_accounts_index: &PubkeysRemovedFromAccountsIndex, ) where I: Iterator, { @@ -2950,7 +2977,12 @@ impl AccountsDb { } } - self.process_dead_slots(&dead_slots, purged_account_slots, purge_stats); + self.process_dead_slots( + &dead_slots, + purged_account_slots, + purge_stats, + pubkeys_removed_from_accounts_index, + ); } else { // not sure why this fails yet with ancient append vecs if !self.ancient_append_vecs { @@ -3032,17 +3064,24 @@ impl AccountsDb { // Must be kept private!, does sensitive cleanup that should only be called from // supported pipelines in AccountsDb + // pubkeys_removed_from_accounts_index - These keys have already been removed from the accounts index + // and should not be unref'd. If they exist in the accounts index, they are NEW. fn process_dead_slots( &self, dead_slots: &HashSet, purged_account_slots: Option<&mut AccountSlots>, purge_stats: &PurgeStats, + pubkeys_removed_from_accounts_index: &PubkeysRemovedFromAccountsIndex, ) { if dead_slots.is_empty() { return; } let mut clean_dead_slots = Measure::start("reclaims::clean_dead_slots"); - self.clean_stored_dead_slots(dead_slots, purged_account_slots); + self.clean_stored_dead_slots( + dead_slots, + purged_account_slots, + pubkeys_removed_from_accounts_index, + ); clean_dead_slots.stop(); let mut purge_removed_slots = Measure::start("reclaims::purge_removed_slots"); @@ -4284,8 +4323,15 @@ impl AccountsDb { &self, ancestors: &Ancestors, pubkey: &Pubkey, + load_zero_lamports: LoadZeroLamports, ) -> Option<(AccountSharedData, Slot)> { self.load(ancestors, pubkey, LoadHint::FixedMaxRoot) + .filter(|(account, _)| { + matches!( + load_zero_lamports, + LoadZeroLamports::SomeWithZeroLamportAccount + ) || !account.is_zero_lamport() + }) } pub fn load_without_fixed_root( @@ -4563,22 +4609,35 @@ impl AccountsDb { // Notice the subtle `?` at previous line, we bail out pretty early if missing. if new_slot == slot && new_storage_location.is_store_id_equal(&storage_location) { - // Considering that we're failed to get accessor above and further that + inc_new_counter_info!("retry_to_get_account_accessor-panic", 1); + let message = format!( + "Bad index entry detected ({}, {}, {:?}, {:?}, {:?}, {:?})", + pubkey, + slot, + storage_location, + load_hint, + new_storage_location, + self.accounts_index.get_account_read_entry(pubkey) + ); + // Considering that we've failed to get accessor above and further that // the index still returned the same (slot, store_id) tuple, offset must be same // too. - assert!(new_storage_location.is_offset_equal(&storage_location)); + assert!( + new_storage_location.is_offset_equal(&storage_location), + "{message}" + ); // If the entry was missing from the cache, that means it must have been flushed, // and the accounts index is always updated before cache flush, so store_id must // not indicate being cached at this point. - assert!(!new_storage_location.is_cached()); + assert!(!new_storage_location.is_cached(), "{message}"); // If this is not a cache entry, then this was a minor fork slot // that had its storage entries cleaned up by purge_slots() but hasn't been // cleaned yet. That means this must be rpc access and not replay/banking at the // very least. Note that purge shouldn't occur even for RPC as caller must hold all // of ancestor slots.. - assert_eq!(load_hint, LoadHint::Unspecified); + assert_eq!(load_hint, LoadHint::Unspecified, "{message}"); // Everything being assert!()-ed, let's panic!() here as it's an error condition // after all.... @@ -4588,10 +4647,7 @@ impl AccountsDb { // first of all. // For details, see the comment in AccountIndex::do_checked_scan_accounts(), // which is referring back here. - panic!( - "Bad index entry detected ({}, {}, {:?}, {:?})", - pubkey, slot, storage_location, load_hint - ); + panic!("{message}"); } else if fallback_to_slow_path { // the above bad-index-entry check must had been checked first to retain the same // behavior @@ -5161,7 +5217,13 @@ impl AccountsDb { (*account.key(), purged_slot) }) .collect(); - self.purge_slot_cache_pubkeys(purged_slot, purged_slot_pubkeys, pubkey_to_slot_set, true); + self.purge_slot_cache_pubkeys( + purged_slot, + purged_slot_pubkeys, + pubkey_to_slot_set, + true, + &HashSet::default(), + ); } fn purge_slot_cache_pubkeys( @@ -5170,17 +5232,19 @@ impl AccountsDb { purged_slot_pubkeys: HashSet<(Slot, Pubkey)>, pubkey_to_slot_set: Vec<(Pubkey, Slot)>, is_dead: bool, + pubkeys_removed_from_accounts_index: &PubkeysRemovedFromAccountsIndex, ) { // Slot purged from cache should not exist in the backing store assert!(self.storage.get_slot_stores(purged_slot).is_none()); let num_purged_keys = pubkey_to_slot_set.len(); - let reclaims = self.purge_keys_exact(pubkey_to_slot_set.iter()); + let (reclaims, _) = self.purge_keys_exact(pubkey_to_slot_set.iter()); assert_eq!(reclaims.len(), num_purged_keys); if is_dead { self.remove_dead_slots_metadata( std::iter::once(&purged_slot), purged_slot_pubkeys, None, + pubkeys_removed_from_accounts_index, ); } } @@ -5210,7 +5274,7 @@ impl AccountsDb { .fetch_add(scan_storages_elasped.as_us(), Ordering::Relaxed); let mut purge_accounts_index_elapsed = Measure::start("purge_accounts_index_elapsed"); - let reclaims = match scan_result { + let (reclaims, pubkeys_removed_from_accounts_index) = match scan_result { ScanStorageResult::Cached(_) => { panic!("Should not see cached keys in this `else` branch, since we checked this slot did not exist in the cache above"); } @@ -5234,6 +5298,7 @@ impl AccountsDb { expected_dead_slot, Some((purge_stats, &mut ReclaimResult::default())), false, + &pubkeys_removed_from_accounts_index, ); handle_reclaims_elapsed.stop(); purge_stats @@ -5786,7 +5851,13 @@ impl AccountsDb { let is_dead_slot = accounts.is_empty(); // Remove the account index entries from earlier roots that are outdated by later roots. // Safe because queries to the index will be reading updates from later roots. - self.purge_slot_cache_pubkeys(slot, purged_slot_pubkeys, pubkey_to_slot_set, is_dead_slot); + self.purge_slot_cache_pubkeys( + slot, + purged_slot_pubkeys, + pubkey_to_slot_set, + is_dead_slot, + &HashSet::default(), + ); if !is_dead_slot { let aligned_total_size = Self::page_align(total_size); @@ -6165,8 +6236,6 @@ impl AccountsDb { let total_lamports = Mutex::::new(0); let stats = HashStats::default(); - let max_slot_info = SlotInfoInEpoch::new(max_slot, config.epoch_schedule); - let get_hashes = || { keys.par_chunks(chunks) .map(|pubkeys| { @@ -6199,23 +6268,7 @@ impl AccountsDb { .get_loaded_account() .and_then( |loaded_account| { - let find_unskipped_slot = |slot: Slot| { - self.find_unskipped_slot(slot, config.ancestors) - }; let loaded_hash = loaded_account.loaded_hash(); - let new_hash = ExpectedRentCollection::maybe_rehash_skipped_rewrite( - &loaded_account, - &loaded_hash, - pubkey, - *slot, - config.epoch_schedule, - config.rent_collector, - &stats, - &max_slot_info, - find_unskipped_slot, - self.filler_account_suffix.as_ref(), - ); - let loaded_hash = new_hash.unwrap_or(loaded_hash); let balance = loaded_account.lamports(); if config.check_hash && !self.is_filler_account(pubkey) { // this will not be supported anymore let computed_hash = @@ -6428,6 +6481,27 @@ impl AccountsDb { } } + /// if ancient append vecs are enabled, return a slot one epoch old from 'max_slot_inclusive' + /// otherwise, return 0 + fn get_one_epoch_old_slot_for_hash_calc_scan( + &self, + max_slot_inclusive: Slot, + config: &CalcAccountsHashConfig<'_>, + ) -> Slot { + if self.ancient_append_vecs { + // we are going to use a fixed slots per epoch here. + // We are mainly interested in the network at steady state. + let slots_in_epoch = config.epoch_schedule.slots_per_epoch; + // For performance, this is required when ancient appendvecs are enabled + max_slot_inclusive.saturating_sub(slots_in_epoch) + } else { + // This causes the entire range to be chunked together, treating older append vecs just like new ones. + // This performs well if there are many old append vecs that haven't been cleaned yet. + // 0 will have the effect of causing ALL older append vecs to be chunked together, just like every other append vec. + 0 + } + } + /// Scan through all the account storage in parallel fn scan_account_storage_no_bank( &self, @@ -6451,10 +6525,8 @@ impl AccountsDb { // 3. evenly divided full chunks in the middle // 4. unevenly divided chunk of most recent slots (may be empty) let max_slot_inclusive = snapshot_storages.max_slot_inclusive(); - // we are going to use a fixed slots per epoch here. - // We are mainly interested in the network at steady state. - let slots_in_epoch = config.epoch_schedule.slots_per_epoch; - let one_epoch_old_slot = max_slot_inclusive.saturating_sub(slots_in_epoch); + let one_epoch_old_slot = + self.get_one_epoch_old_slot_for_hash_calc_scan(max_slot_inclusive, config); let range = snapshot_storages.range(); let ancient_slots = snapshot_storages @@ -6646,20 +6718,18 @@ impl AccountsDb { } /// storages are sorted by slot and have range info. - /// if we know slots_per_epoch, then add all stores older than slots_per_epoch to dirty_stores so clean visits these slots - fn mark_old_slots_as_dirty(&self, storages: &SortedStorages, slots_per_epoch: Option) { - if let Some(slots_per_epoch) = slots_per_epoch { - let max = storages.max_slot_inclusive(); - let acceptable_straggler_slot_count = 100; // do nothing special for these old stores which will likely get cleaned up shortly - let sub = slots_per_epoch + acceptable_straggler_slot_count; - let in_epoch_range_start = max.saturating_sub(sub); - for (slot, storages) in storages.iter_range(..in_epoch_range_start) { - if let Some(storages) = storages { - storages.iter().for_each(|store| { - self.dirty_stores - .insert((slot, store.append_vec_id()), store.clone()); - }); - } + /// add all stores older than slots_per_epoch to dirty_stores so clean visits these slots + fn mark_old_slots_as_dirty(&self, storages: &SortedStorages, slots_per_epoch: Slot) { + let max = storages.max_slot_inclusive(); + let acceptable_straggler_slot_count = 100; // do nothing special for these old stores which will likely get cleaned up shortly + let sub = slots_per_epoch + acceptable_straggler_slot_count; + let in_epoch_range_start = max.saturating_sub(sub); + for (slot, storages) in storages.iter_range(..in_epoch_range_start) { + if let Some(storages) = storages { + storages.iter().for_each(|store| { + self.dirty_stores + .insert((slot, store.append_vec_id()), store.clone()); + }); } } } @@ -6783,22 +6853,15 @@ impl AccountsDb { let range = bin_range.end - bin_range.start; let sort_time = Arc::new(AtomicU64::new(0)); - let find_unskipped_slot = |slot: Slot| self.find_unskipped_slot(slot, config.ancestors); - - let max_slot_info = - SlotInfoInEpoch::new(storage.max_slot_inclusive(), config.epoch_schedule); let scanner = ScanState { current_slot: Slot::default(), accum: BinnedHashData::default(), bin_calculator: &bin_calculator, config, mismatch_found: mismatch_found.clone(), - max_slot_info, - find_unskipped_slot: &find_unskipped_slot, filler_account_suffix, range, bin_range, - stats, sort_time: sort_time.clone(), pubkey_to_bin_index: 0, }; @@ -6888,7 +6951,7 @@ impl AccountsDb { stats.oldest_root = storages.range().start; - self.mark_old_slots_as_dirty(storages, Some(config.epoch_schedule.slots_per_epoch)); + self.mark_old_slots_as_dirty(storages, config.epoch_schedule.slots_per_epoch); let (num_hash_scan_passes, bins_per_pass) = Self::bins_per_pass(self.num_hash_scan_passes); let use_bg_thread_pool = config.use_bg_thread_pool; @@ -6988,6 +7051,7 @@ impl AccountsDb { epoch_schedule: &EpochSchedule, rent_collector: &RentCollector, can_cached_slot_be_unflushed: bool, + use_bg_thread_pool: bool, ) -> Result<(), BankHashVerificationError> { self.verify_bank_hash_and_lamports_new( slot, @@ -6999,6 +7063,7 @@ impl AccountsDb { can_cached_slot_be_unflushed, false, false, + use_bg_thread_pool, ) } @@ -7015,20 +7080,19 @@ impl AccountsDb { can_cached_slot_be_unflushed: bool, ignore_mismatch: bool, store_hash_raw_data_for_debug: bool, + use_bg_thread_pool: bool, ) -> Result<(), BankHashVerificationError> { use BankHashVerificationError::*; let use_index = false; let check_hash = false; // this will not be supported anymore - // interesting to consider this - let is_startup = true; let (calculated_hash, calculated_lamports) = self .calculate_accounts_hash_helper_with_verify( use_index, test_hash_calculation, slot, CalcAccountsHashConfig { - use_bg_thread_pool: !is_startup, + use_bg_thread_pool, check_hash, ancestors: Some(ancestors), use_write_cache: can_cached_slot_be_unflushed, @@ -7447,18 +7511,22 @@ impl AccountsDb { dead_slots } + // pubkeys_removed_from_accounts_index - These keys have already been removed from the accounts index + // and should not be unref'd. If they exist in the accounts index, they are NEW. fn remove_dead_slots_metadata<'a>( &'a self, dead_slots_iter: impl Iterator + Clone, purged_slot_pubkeys: HashSet<(Slot, Pubkey)>, // Should only be `Some` for non-cached slots purged_stored_account_slots: Option<&mut AccountSlots>, + pubkeys_removed_from_accounts_index: &PubkeysRemovedFromAccountsIndex, ) { let mut measure = Measure::start("remove_dead_slots_metadata-ms"); self.clean_dead_slots_from_accounts_index( dead_slots_iter.clone(), purged_slot_pubkeys, purged_stored_account_slots, + pubkeys_removed_from_accounts_index, ); { let mut bank_hashes = self.bank_hashes.write().unwrap(); @@ -7476,11 +7544,12 @@ impl AccountsDb { purged_slot_pubkeys: HashSet<(Slot, Pubkey)>, // Should only be `Some` for non-cached slots purged_stored_account_slots: Option<&mut AccountSlots>, + pubkeys_removed_from_accounts_index: &PubkeysRemovedFromAccountsIndex, ) { let mut accounts_index_root_stats = AccountsIndexRootsStats::default(); let mut measure = Measure::start("unref_from_storage"); if let Some(purged_stored_account_slots) = purged_stored_account_slots { - let len = purged_stored_account_slots.len(); + let len = purged_slot_pubkeys.len(); const BATCH_SIZE: usize = 10_000; let batches = 1 + (len / BATCH_SIZE); self.thread_pool_clean.install(|| { @@ -7491,7 +7560,12 @@ impl AccountsDb { .iter() .skip(skip) .take(BATCH_SIZE) - .map(|(_slot, pubkey)| pubkey), + .filter_map(|(_slot, pubkey)| { + // filter out pubkeys that have already been removed from the accounts index in a previous step + let already_removed = + pubkeys_removed_from_accounts_index.contains(pubkey); + (!already_removed).then(|| pubkey) + }), |_pubkey, _slots_refs| AccountsIndexScanResult::Unref, ) }) @@ -7525,7 +7599,11 @@ impl AccountsDb { measure.stop(); accounts_index_root_stats.clean_dead_slot_us += measure.as_us(); if self.log_dead_slots.load(Ordering::Relaxed) { - info!("remove_dead_slots_metadata: slots {:?}", dead_slots); + info!( + "remove_dead_slots_metadata: {} dead slots", + dead_slots.len() + ); + trace!("remove_dead_slots_metadata: dead_slots: {:?}", dead_slots); } accounts_index_root_stats.rooted_cleaned_count += rooted_cleaned_count; @@ -7536,10 +7614,13 @@ impl AccountsDb { .update(&accounts_index_root_stats); } + // pubkeys_removed_from_accounts_index - These keys have already been removed from the accounts index + // and should not be unref'd. If they exist in the accounts index, they are NEW. fn clean_stored_dead_slots( &self, dead_slots: &HashSet, purged_account_slots: Option<&mut AccountSlots>, + pubkeys_removed_from_accounts_index: &PubkeysRemovedFromAccountsIndex, ) { let mut measure = Measure::start("clean_stored_dead_slots-ms"); let mut stores: Vec> = vec![]; @@ -7572,6 +7653,7 @@ impl AccountsDb { dead_slots.iter(), purged_slot_pubkeys, purged_account_slots, + pubkeys_removed_from_accounts_index, ); measure.stop(); inc_new_counter_info!("clean_stored_dead_slots-ms", measure.as_ms() as usize); @@ -7640,7 +7722,7 @@ impl AccountsDb { fn report_store_timings(&self) { if self.stats.last_store_report.should_update(1000) { - let (read_only_cache_hits, read_only_cache_misses) = + let (read_only_cache_hits, read_only_cache_misses, read_only_cache_evicts) = self.read_only_accounts_cache.get_and_reset_stats(); datapoint_info!( "accounts_db_store_timings", @@ -7707,6 +7789,11 @@ impl AccountsDb { read_only_cache_misses, i64 ), + ( + "read_only_accounts_cache_evicts", + read_only_cache_evicts, + i64 + ), ( "calc_stored_meta_us", self.stats.calc_stored_meta.swap(0, Ordering::Relaxed), @@ -7905,6 +7992,7 @@ impl AccountsDb { expected_single_dead_slot, None, reset_accounts, + &HashSet::default(), ); handle_reclaims_time.stop(); self.stats @@ -8768,7 +8856,7 @@ impl AccountsDb { #[allow(clippy::stable_sort_primitive)] alive_roots.sort(); info!("{}: accounts_index alive_roots: {:?}", label, alive_roots,); - let full_pubkey_range = Pubkey::new(&[0; 32])..=Pubkey::new(&[0xff; 32]); + let full_pubkey_range = Pubkey::from([0; 32])..=Pubkey::from([0xff; 32]); self.accounts_index.account_maps.iter().for_each(|map| { for (pubkey, account_entry) in map.items(&full_pubkey_range) { @@ -8902,9 +8990,14 @@ impl AccountsDb { if not_sparse || too_small_to_shrink { return 0; } - info!( + trace!( "shrink_stale_slot ({}): not_sparse: {} count: {}/{} byte: {}/{}", - slot, not_sparse, alive_count, stored_count, written_bytes, total_bytes, + slot, + not_sparse, + alive_count, + stored_count, + written_bytes, + total_bytes, ); } @@ -9130,10 +9223,10 @@ pub mod tests { slot: Slot, ) -> (SnapshotStorages, Vec) { let accounts = AccountsDb::new(Vec::new(), &ClusterType::Development); - let pubkey0 = Pubkey::new(&[0u8; 32]); - let pubkey127 = Pubkey::new(&[0x7fu8; 32]); - let pubkey128 = Pubkey::new(&[0x80u8; 32]); - let pubkey255 = Pubkey::new(&[0xffu8; 32]); + let pubkey0 = Pubkey::from([0u8; 32]); + let pubkey127 = Pubkey::from([0x7fu8; 32]); + let pubkey128 = Pubkey::from([0x80u8; 32]); + let pubkey255 = Pubkey::from([0xffu8; 32]); let mut raw_expected = vec![ CalculateHashIntermediate::new(Hash::default(), 1, pubkey0), @@ -11305,6 +11398,7 @@ pub mod tests { &EpochSchedule::default(), &RentCollector::default(), false, + false, ) .unwrap(); } @@ -11430,7 +11524,7 @@ pub mod tests { let slots: HashSet = vec![1].into_iter().collect(); let purge_keys = vec![(key1, slots)]; - db.purge_keys_exact(purge_keys.iter()); + let _ = db.purge_keys_exact(purge_keys.iter()); let account2 = AccountSharedData::new(3, 0, &key); db.store_uncached(2, &[(&key1, &account2)]); @@ -11709,6 +11803,7 @@ pub mod tests { &EpochSchedule::default(), &RentCollector::default(), false, + false, ), Ok(_) ); @@ -11723,6 +11818,7 @@ pub mod tests { &EpochSchedule::default(), &RentCollector::default(), false, + false, ), Err(MissingBankHash) ); @@ -11746,6 +11842,7 @@ pub mod tests { &EpochSchedule::default(), &RentCollector::default(), false, + false, ), Err(MismatchedBankHash) ); @@ -11774,7 +11871,8 @@ pub mod tests { true, &EpochSchedule::default(), &RentCollector::default(), - false + false, + false, ), Ok(_) ); @@ -11796,13 +11894,14 @@ pub mod tests { true, &EpochSchedule::default(), &RentCollector::default(), - false + false, + false, ), Ok(_) ); assert_matches!( - db.verify_bank_hash_and_lamports(some_slot, &ancestors, 10, true, &EpochSchedule::default(), &RentCollector::default(), false), + db.verify_bank_hash_and_lamports(some_slot, &ancestors, 10, true, &EpochSchedule::default(), &RentCollector::default(), false, false), Err(MismatchedTotalLamports(expected, actual)) if expected == 2 && actual == 10 ); } @@ -11829,7 +11928,8 @@ pub mod tests { true, &EpochSchedule::default(), &RentCollector::default(), - false + false, + false, ), Ok(_) ); @@ -11873,7 +11973,8 @@ pub mod tests { true, &EpochSchedule::default(), &RentCollector::default(), - false + false, + false, ), Err(MismatchedBankHash) ); @@ -12337,7 +12438,7 @@ pub mod tests { let accounts = AccountsDb::new_single_for_tests(); let mut dead_slots = HashSet::new(); dead_slots.insert(10); - accounts.clean_stored_dead_slots(&dead_slots, None); + accounts.clean_stored_dead_slots(&dead_slots, None, &HashSet::default()); } #[test] @@ -12493,6 +12594,7 @@ pub mod tests { &EpochSchedule::default(), &RentCollector::default(), false, + false, ) .unwrap(); @@ -12506,6 +12608,7 @@ pub mod tests { &EpochSchedule::default(), &RentCollector::default(), false, + false, ) .unwrap(); @@ -13425,13 +13528,13 @@ pub mod tests { assert_eq!(db.read_only_accounts_cache.cache_len(), 0); let account = db - .load_with_fixed_root(&Ancestors::default(), &account_key) + .load_with_fixed_root(&Ancestors::default(), &account_key, LoadZeroLamports::None) .map(|(account, _)| account) .unwrap(); assert_eq!(account.lamports(), 1); assert_eq!(db.read_only_accounts_cache.cache_len(), 1); let account = db - .load_with_fixed_root(&Ancestors::default(), &account_key) + .load_with_fixed_root(&Ancestors::default(), &account_key, LoadZeroLamports::None) .map(|(account, _)| account) .unwrap(); assert_eq!(account.lamports(), 1); @@ -13439,10 +13542,9 @@ pub mod tests { db.store_cached((2, &[(&account_key, &zero_lamport_account)][..]), None); assert_eq!(db.read_only_accounts_cache.cache_len(), 1); let account = db - .load_with_fixed_root(&Ancestors::default(), &account_key) - .map(|(account, _)| account) - .unwrap(); - assert_eq!(account.lamports(), 0); + .load_with_fixed_root(&Ancestors::default(), &account_key, LoadZeroLamports::None) + .map(|(account, _)| account); + assert!(account.is_none()); assert_eq!(db.read_only_accounts_cache.cache_len(), 1); } @@ -15064,8 +15166,8 @@ pub mod tests { fn test_calculate_storage_count_and_alive_bytes_2_accounts() { let accounts = AccountsDb::new_single_for_tests(); let keys = [ - solana_sdk::pubkey::Pubkey::new(&[0; 32]), - solana_sdk::pubkey::Pubkey::new(&[255; 32]), + solana_sdk::pubkey::Pubkey::from([0; 32]), + solana_sdk::pubkey::Pubkey::from([255; 32]), ]; // make sure accounts are in 2 different bins assert!( @@ -15389,7 +15491,7 @@ pub mod tests { let rewrites = Rewrites::default(); db.extend_hashes_with_skipped_rewrites(&mut hashes, &rewrites); assert!(hashes.is_empty()); - let pubkey = Pubkey::new(&[1; 32]); + let pubkey = Pubkey::from([1; 32]); let hash = Hash::new(&[2; 32]); rewrites.write().unwrap().insert(pubkey, hash); db.extend_hashes_with_skipped_rewrites(&mut hashes, &rewrites); @@ -15397,7 +15499,7 @@ pub mod tests { // pubkey is already in hashes, will not be added a second time db.extend_hashes_with_skipped_rewrites(&mut hashes, &rewrites); assert_eq!(hashes, vec![(pubkey, hash)]); - let pubkey2 = Pubkey::new(&[2; 32]); + let pubkey2 = Pubkey::from([2; 32]); let hash2 = Hash::new(&[3; 32]); rewrites.write().unwrap().insert(pubkey2, hash2); db.extend_hashes_with_skipped_rewrites(&mut hashes, &rewrites); diff --git a/runtime/src/accounts_db/geyser_plugin_utils.rs b/runtime/src/accounts_db/geyser_plugin_utils.rs index 3371472b680f53..701ff99f77f2d6 100644 --- a/runtime/src/accounts_db/geyser_plugin_utils.rs +++ b/runtime/src/accounts_db/geyser_plugin_utils.rs @@ -192,7 +192,7 @@ pub mod tests { ) { self.accounts_notified .entry(meta.pubkey) - .or_insert(Vec::default()) + .or_default() .push((slot, account.clone())); } @@ -201,7 +201,7 @@ pub mod tests { fn notify_account_restore_from_snapshot(&self, slot: Slot, account: &StoredAccountMeta) { self.accounts_notified .entry(account.meta.pubkey) - .or_insert(Vec::default()) + .or_default() .push((slot, account.clone_account())); } diff --git a/runtime/src/accounts_hash.rs b/runtime/src/accounts_hash.rs index c36a95d3e02640..c21fc1f12600bf 100644 --- a/runtime/src/accounts_hash.rs +++ b/runtime/src/accounts_hash.rs @@ -63,7 +63,8 @@ impl<'a> CalcAccountsHashConfig<'a> { /// return true if we should cache accounts hash intermediate data between calls pub fn get_should_cache_hash_data() -> bool { // when we are skipping rewrites, we cannot rely on the cached data from old append vecs, so we have to disable caching for now - false + // skipping rewrites is not enabled in this branch. It requires a cli argument. + true } } @@ -1034,13 +1035,13 @@ pub mod tests { let mut account_maps = Vec::new(); - let key = Pubkey::new(&[11u8; 32]); + let key = Pubkey::from([11u8; 32]); let hash = Hash::new(&[1u8; 32]); let val = CalculateHashIntermediate::new(hash, 88, key); account_maps.push(val); // 2nd key - zero lamports, so will be removed - let key = Pubkey::new(&[12u8; 32]); + let key = Pubkey::from([12u8; 32]); let hash = Hash::new(&[2u8; 32]); let val = CalculateHashIntermediate::new(hash, ZERO_RAW_LAMPORTS_SENTINEL, key); account_maps.push(val); @@ -1057,7 +1058,7 @@ pub mod tests { assert_eq!((result.0, result.1), (expected_hash, 88)); // 3rd key - with pubkey value before 1st key so it will be sorted first - let key = Pubkey::new(&[10u8; 32]); + let key = Pubkey::from([10u8; 32]); let hash = Hash::new(&[2u8; 32]); let val = CalculateHashIntermediate::new(hash, 20, key); account_maps.insert(0, val); @@ -1073,7 +1074,7 @@ pub mod tests { assert_eq!((result.0, result.1), (expected_hash, 108)); // 3rd key - with later slot - let key = Pubkey::new(&[10u8; 32]); + let key = Pubkey::from([10u8; 32]); let hash = Hash::new(&[99u8; 32]); let val = CalculateHashIntermediate::new(hash, 30, key); account_maps.insert(1, val); @@ -1108,13 +1109,13 @@ pub mod tests { for pass in 0..3 { let mut account_maps = Vec::new(); - let key = Pubkey::new(&[11u8; 32]); + let key = Pubkey::from([11u8; 32]); let hash = Hash::new(&[1u8; 32]); let val = CalculateHashIntermediate::new(hash, 88, key); account_maps.push(val); // 2nd key - zero lamports, so will be removed - let key = Pubkey::new(&[12u8; 32]); + let key = Pubkey::from([12u8; 32]); let hash = Hash::new(&[2u8; 32]); let val = CalculateHashIntermediate::new(hash, ZERO_RAW_LAMPORTS_SENTINEL, key); account_maps.push(val); @@ -1195,12 +1196,12 @@ pub mod tests { let mut account_maps = Vec::new(); - let key = Pubkey::new(&[11u8; 32]); + let key = Pubkey::from([11u8; 32]); let hash = Hash::new(&[1u8; 32]); let val = CalculateHashIntermediate::new(hash, 88, key); account_maps.push(val); - let key = Pubkey::new(&[12u8; 32]); + let key = Pubkey::from([12u8; 32]); let hash = Hash::new(&[2u8; 32]); let val = CalculateHashIntermediate::new(hash, 20, key); account_maps.push(val); @@ -1434,9 +1435,9 @@ pub mod tests { fn test_accountsdb_de_dup_accounts_from_stores() { solana_logger::setup(); - let key_a = Pubkey::new(&[1u8; 32]); - let key_b = Pubkey::new(&[2u8; 32]); - let key_c = Pubkey::new(&[3u8; 32]); + let key_a = Pubkey::from([1u8; 32]); + let key_b = Pubkey::from([2u8; 32]); + let key_c = Pubkey::from([3u8; 32]); const COUNT: usize = 6; let hashes = (0..COUNT).into_iter().map(|i| Hash::new(&[i as u8; 32])); // create this vector @@ -1980,7 +1981,7 @@ pub mod tests { for count in start..iterations { let mut input: Vec<_> = (0..count) .map(|i| { - let key = Pubkey::new(&[(pass * iterations + count) as u8; 32]); + let key = Pubkey::from([(pass * iterations + count) as u8; 32]); let hash = Hash::new(&[(pass * iterations + count + i + 1) as u8; 32]); (key, hash) }) diff --git a/runtime/src/accounts_index.rs b/runtime/src/accounts_index.rs index a2bd30d35d4bda..7a23c300db4c69 100644 --- a/runtime/src/accounts_index.rs +++ b/runtime/src/accounts_index.rs @@ -4,7 +4,7 @@ use { ancestors::Ancestors, bucket_map_holder::{Age, BucketMapHolder}, contains::Contains, - in_mem_accounts_index::InMemAccountsIndex, + in_mem_accounts_index::{InMemAccountsIndex, InsertNewEntryResults}, inline_spl_token::{self, GenericTokenAccount}, inline_spl_token_2022, pubkey_bins::PubkeyBinCalculator24, @@ -412,7 +412,7 @@ impl PreAllocatedAccountMapEntry { account_info: T, storage: &Arc>, ) -> AccountMapEntry { - let ref_count = if account_info.is_cached() { 0 } else { 1 }; + let ref_count = u64::from(!account_info.is_cached()); let meta = AccountMapEntryMeta::new_dirty(storage); Arc::new(AccountMapEntryInner::new( vec![(slot, account_info)], @@ -1131,15 +1131,20 @@ impl AccountsIndex { read_lock.slot_list_mut(pubkey, user) } + /// Remove keys from the account index if the key's slot list is empty. + /// Returns the keys that were removed from the index. These keys should not be accessed again in the current code path. + #[must_use] pub fn handle_dead_keys( &self, dead_keys: &[&Pubkey], account_indexes: &AccountSecondaryIndexes, - ) { + ) -> HashSet { + let mut pubkeys_removed_from_accounts_index = HashSet::default(); if !dead_keys.is_empty() { for key in dead_keys.iter() { let w_index = self.get_account_maps_read_lock(key); if w_index.remove_if_slot_list_empty(**key) { + pubkeys_removed_from_accounts_index.insert(**key); // Note it's only safe to remove all the entries for this key // because we have the lock for this key's entry in the AccountsIndex, // so no other thread is also updating the index @@ -1147,6 +1152,7 @@ impl AccountsIndex { } } } + pubkeys_removed_from_accounts_index } /// call func with every pubkey and index visible from a given set of ancestors @@ -1560,7 +1566,7 @@ impl AccountsIndex { (pubkey_bin, Vec::with_capacity(expected_items_per_bin)) }) .collect::>(); - let dirty_pubkeys = items + let mut dirty_pubkeys = items .filter_map(|(pubkey, account_info)| { let pubkey_bin = self.bin_calculator.bin_from_pubkey(&pubkey); let binned_index = (pubkey_bin + random_offset) % bins; @@ -1591,7 +1597,13 @@ impl AccountsIndex { &self.storage.storage, use_disk, ); - r_account_maps.insert_new_entry_if_missing_with_lock(pubkey, new_entry); + match r_account_maps.insert_new_entry_if_missing_with_lock(pubkey, new_entry) { + InsertNewEntryResults::DidNotExist => {} + InsertNewEntryResults::ExistedNewEntryZeroLamports => {} + InsertNewEntryResults::ExistedNewEntryNonZeroLamports => { + dirty_pubkeys.push(pubkey); + } + } }); } insert_time.stop(); @@ -1710,26 +1722,34 @@ impl AccountsIndex { }); } + /// return true if pubkey was removed from the accounts index + /// or does not exist in the accounts index + /// This means it should NOT be unref'd later. + #[must_use] pub fn clean_rooted_entries( &self, pubkey: &Pubkey, reclaims: &mut SlotList, max_clean_root: Option, - ) { + ) -> bool { let mut is_slot_list_empty = false; - self.slot_list_mut(pubkey, |slot_list| { - self.purge_older_root_entries(slot_list, reclaims, max_clean_root); - is_slot_list_empty = slot_list.is_empty(); - }); + let missing_in_accounts_index = self + .slot_list_mut(pubkey, |slot_list| { + self.purge_older_root_entries(slot_list, reclaims, max_clean_root); + is_slot_list_empty = slot_list.is_empty(); + }) + .is_none(); + let mut removed = false; // If the slot list is empty, remove the pubkey from `account_maps`. Make sure to grab the // lock and double check the slot list is still empty, because another writer could have // locked and inserted the pubkey in-between when `is_slot_list_empty=true` and the call to // remove() below. if is_slot_list_empty { let w_maps = self.get_account_maps_read_lock(pubkey); - w_maps.remove_if_slot_list_empty(*pubkey); + removed = w_maps.remove_if_slot_list_empty(*pubkey); } + removed || missing_in_accounts_index } /// When can an entry be purged? @@ -2573,7 +2593,7 @@ pub mod tests { // verify the added entry matches expected { let entry = index.get_account_read_entry(&key).unwrap(); - assert_eq!(entry.ref_count(), if is_cached { 0 } else { 1 }); + assert_eq!(entry.ref_count(), u64::from(!is_cached)); let expected = vec![(slot0, account_infos[0])]; assert_eq!(entry.slot_list().to_vec(), expected); let new_entry: AccountMapEntry<_> = PreAllocatedAccountMapEntry::new( @@ -3501,7 +3521,7 @@ pub mod tests { &mut vec![], ); - index.handle_dead_keys(&[&account_key], secondary_indexes); + let _ = index.handle_dead_keys(&[&account_key], secondary_indexes); assert!(secondary_index.index.is_empty()); assert!(secondary_index.reverse_index.is_empty()); } @@ -3719,7 +3739,7 @@ pub mod tests { index.slot_list_mut(&account_key, |slot_list| slot_list.clear()); // Everything should be deleted - index.handle_dead_keys(&[&account_key], &secondary_indexes); + let _ = index.handle_dead_keys(&[&account_key], &secondary_indexes); assert!(secondary_index.index.is_empty()); assert!(secondary_index.reverse_index.is_empty()); } @@ -3840,7 +3860,7 @@ pub mod tests { // pubkey as dead and finally remove all the secondary indexes let mut reclaims = vec![]; index.purge_exact(&account_key, &later_slot, &mut reclaims); - index.handle_dead_keys(&[&account_key], secondary_indexes); + let _ = index.handle_dead_keys(&[&account_key], secondary_indexes); assert!(secondary_index.index.is_empty()); assert!(secondary_index.reverse_index.is_empty()); } @@ -3911,8 +3931,8 @@ pub mod tests { ); assert_eq!((0, usize::MAX), iter.bin_start_and_range()); - let key_0 = Pubkey::new(&[0; 32]); - let key_ff = Pubkey::new(&[0xff; 32]); + let key_0 = Pubkey::from([0; 32]); + let key_ff = Pubkey::from([0xff; 32]); let iter = AccountsIndexIterator::new( &index, @@ -3950,6 +3970,209 @@ pub mod tests { ); } + #[test] + fn test_get_newest_root_in_slot_list() { + let index = AccountsIndex::::default_for_tests(); + let return_0 = 0; + let slot1 = 1; + let slot2 = 2; + let slot99 = 99; + + // no roots, so always 0 + { + let roots_tracker = &index.roots_tracker.read().unwrap(); + let slot_list = Vec::<(Slot, bool)>::default(); + assert_eq!( + return_0, + AccountsIndex::get_newest_root_in_slot_list( + &roots_tracker.alive_roots, + &slot_list, + Some(slot1), + ) + ); + assert_eq!( + return_0, + AccountsIndex::get_newest_root_in_slot_list( + &roots_tracker.alive_roots, + &slot_list, + Some(slot2), + ) + ); + assert_eq!( + return_0, + AccountsIndex::get_newest_root_in_slot_list( + &roots_tracker.alive_roots, + &slot_list, + Some(slot99), + ) + ); + } + + index.add_root(slot2, true); + + { + let roots_tracker = &index.roots_tracker.read().unwrap(); + let slot_list = vec![(slot2, true)]; + assert_eq!( + slot2, + AccountsIndex::get_newest_root_in_slot_list( + &roots_tracker.alive_roots, + &slot_list, + Some(slot2), + ) + ); + // no newest root + assert_eq!( + return_0, + AccountsIndex::get_newest_root_in_slot_list( + &roots_tracker.alive_roots, + &slot_list, + Some(slot1), + ) + ); + assert_eq!( + slot2, + AccountsIndex::get_newest_root_in_slot_list( + &roots_tracker.alive_roots, + &slot_list, + Some(slot99), + ) + ); + } + } + + impl AccountsIndex { + fn upsert_simple_test(&self, key: &Pubkey, slot: Slot, value: T) { + let mut gc = Vec::new(); + self.upsert( + slot, + slot, + key, + &AccountSharedData::default(), + &AccountSecondaryIndexes::default(), + value, + &mut gc, + UPSERT_PREVIOUS_SLOT_ENTRY_WAS_CACHED_FALSE, + ); + assert!(gc.is_empty()); + } + } + + #[test] + fn test_clean_rooted_entries_return() { + solana_logger::setup(); + let value = true; + let key = solana_sdk::pubkey::new_rand(); + let key_unknown = solana_sdk::pubkey::new_rand(); + let index = AccountsIndex::::default_for_tests(); + let slot1 = 1; + + let mut gc = Vec::new(); + // return true if we don't know anything about 'key_unknown' + // the item did not exist in the accounts index at all, so index is up to date + assert!(index.clean_rooted_entries(&key_unknown, &mut gc, None)); + + index.upsert_simple_test(&key, slot1, value); + + let slot2 = 2; + // none for max root because we don't want to delete the entry yet + assert!(!index.clean_rooted_entries(&key, &mut gc, None)); + // this is because of inclusive vs exclusive in the call to can_purge_older_entries + assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot1))); + // this will delete the entry because it is <= max_root_inclusive and NOT a root + // note this has to be slot2 because of inclusive vs exclusive in the call to can_purge_older_entries + { + let mut gc = Vec::new(); + assert!(index.clean_rooted_entries(&key, &mut gc, Some(slot2))); + assert_eq!(gc, vec![(slot1, value)]); + } + + // re-add it + index.upsert_simple_test(&key, slot1, value); + + index.add_root(slot1, value); + assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot2))); + index.upsert_simple_test(&key, slot2, value); + + assert_eq!( + 2, + index + .get_account_read_entry(&key) + .unwrap() + .slot_list() + .len() + ); + assert_eq!( + &vec![(slot1, value), (slot2, value)], + index.get_account_read_entry(&key).unwrap().slot_list() + ); + assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot2))); + assert_eq!( + 2, + index + .get_account_read_entry(&key) + .unwrap() + .slot_list() + .len() + ); + assert!(gc.is_empty()); + { + { + let roots_tracker = &index.roots_tracker.read().unwrap(); + let slot_list = vec![(slot2, value)]; + assert_eq!( + 0, + AccountsIndex::get_newest_root_in_slot_list( + &roots_tracker.alive_roots, + &slot_list, + None, + ) + ); + } + index.add_root(slot2, true); + { + let roots_tracker = &index.roots_tracker.read().unwrap(); + let slot_list = vec![(slot2, value)]; + assert_eq!( + slot2, + AccountsIndex::get_newest_root_in_slot_list( + &roots_tracker.alive_roots, + &slot_list, + None, + ) + ); + assert_eq!( + 0, + AccountsIndex::get_newest_root_in_slot_list( + &roots_tracker.alive_roots, + &slot_list, + Some(0), + ) + ); + } + } + + assert!(gc.is_empty()); + assert!(!index.clean_rooted_entries(&key, &mut gc, Some(slot2))); + assert_eq!(gc, vec![(slot1, value)]); + gc.clear(); + index.clean_dead_slot(slot2, &mut AccountsIndexRootsStats::default()); + let slot3 = 3; + assert!(index.clean_rooted_entries(&key, &mut gc, Some(slot3))); + assert_eq!(gc, vec![(slot2, value)]); + } + + #[test] + fn test_handle_dead_keys_return() { + let key = solana_sdk::pubkey::new_rand(); + let index = AccountsIndex::::default_for_tests(); + + assert_eq!( + index.handle_dead_keys(&[&key], &AccountSecondaryIndexes::default()), + vec![key].into_iter().collect::>() + ); + } + #[test] fn test_start_end_bin() { let index = AccountsIndex::::default_for_tests(); @@ -3962,7 +4185,7 @@ pub mod tests { assert_eq!(iter.start_bin(), 0); // no range, so 0 assert_eq!(iter.end_bin_inclusive(), usize::MAX); // no range, so max - let key = Pubkey::new(&[0; 32]); + let key = Pubkey::from([0; 32]); let iter = AccountsIndexIterator::new( &index, Some(&RangeInclusive::new(key, key)), @@ -3985,7 +4208,7 @@ pub mod tests { assert_eq!(iter.start_bin(), 0); // start at pubkey 0, so 0 assert_eq!(iter.end_bin_inclusive(), 0); // end at pubkey 0, so 0 - let key = Pubkey::new(&[0xff; 32]); + let key = Pubkey::from([0xff; 32]); let iter = AccountsIndexIterator::new( &index, Some(&RangeInclusive::new(key, key)), diff --git a/runtime/src/accounts_index_storage.rs b/runtime/src/accounts_index_storage.rs index 16fe3b5c0c7038..155adade447e1c 100644 --- a/runtime/src/accounts_index_storage.rs +++ b/runtime/src/accounts_index_storage.rs @@ -72,7 +72,7 @@ impl BgThreads { // note that using rayon here causes us to exhaust # rayon threads and many tests running in parallel deadlock Builder::new() - .name("solana-idx-flusher".to_string()) + .name(format!("solIdxFlusher{:02}", idx)) .spawn(move || { storage_.background(exit_, in_mem_, can_advance_age); }) diff --git a/runtime/src/ancient_append_vecs.rs b/runtime/src/ancient_append_vecs.rs index 85e8fc69e4938d..1fc96fbdf200a0 100644 --- a/runtime/src/ancient_append_vecs.rs +++ b/runtime/src/ancient_append_vecs.rs @@ -135,7 +135,7 @@ pub mod tests { #[test] fn test_accounts_to_store_more() { - let pubkey = Pubkey::new(&[1; 32]); + let pubkey = Pubkey::from([1; 32]); let store_id = AppendVecId::default(); let account_size = 3; @@ -143,7 +143,7 @@ pub mod tests { let account_meta = AccountMeta { lamports: 1, - owner: Pubkey::new(&[2; 32]), + owner: Pubkey::from([2; 32]), executable: false, rent_epoch: 0, }; @@ -238,7 +238,7 @@ pub mod tests { let sm = StoredMeta { write_version: 0, - pubkey: Pubkey::new(&[0; 32]), + pubkey: Pubkey::from([0; 32]), data_len: data_len as u64, }; av.append_accounts(&[(sm, Some(&account))], &[Hash::default()]); diff --git a/runtime/src/append_vec.rs b/runtime/src/append_vec.rs index 4eae2ade86c175..50b7f6719bf832 100644 --- a/runtime/src/append_vec.rs +++ b/runtime/src/append_vec.rs @@ -659,7 +659,7 @@ pub mod tests { .read(true) .write(true) .create(true) - .open(&path) + .open(path) .expect("create a test file for mmap"); let result = AppendVec::new_from_file(path, 0); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index fb107ba32c5fc4..8bfd037012ddd7 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -35,6 +35,7 @@ //! already been signed and verified. #[allow(deprecated)] use solana_sdk::recent_blockhashes_account; +pub use solana_sdk::reward_type::RewardType; use { crate::{ account_overrides::AccountOverrides, @@ -53,7 +54,6 @@ use { builtins::{self, BuiltinAction, BuiltinFeatureTransition, Builtins}, cost_tracker::CostTracker, epoch_stakes::{EpochStakes, NodeVoteAccounts}, - expected_rent_collection::{ExpectedRentCollection, SlotInfoInEpoch}, inline_spl_associated_token_account, inline_spl_token, message_processor::MessageProcessor, rent_collector::{CollectedInfo, RentCollector}, @@ -75,23 +75,23 @@ use { dashmap::{DashMap, DashSet}, itertools::Itertools, log::*, - rand::Rng, rayon::{ iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}, ThreadPool, ThreadPoolBuilder, }, solana_measure::{measure, measure::Measure}, + solana_merkle_tree::MerkleTree, solana_metrics::{inc_new_counter_debug, inc_new_counter_info}, solana_program_runtime::{ accounts_data_meter::MAX_ACCOUNTS_DATA_LEN, compute_budget::{self, ComputeBudget}, - invoke_context::{ - BuiltinProgram, Executor, Executors, ProcessInstructionWithContext, TransactionExecutor, - }, + executor_cache::{CachedExecutors, Executors, TransactionExecutor, MAX_CACHED_EXECUTORS}, + invoke_context::{BuiltinProgram, ProcessInstructionWithContext}, log_collector::LogCollector, sysvar_cache::SysvarCache, timings::{ExecuteTimingType, ExecuteTimings}, }, + // solana_receipt_tree::{fd_bmtree32_commit_append, fd_bmtree32_node, hash_leaf, ReceiptTree}, solana_sdk::{ account::{ create_account_shared_data_with_fields as create_account, from_account, Account, @@ -110,7 +110,7 @@ use { feature_set::{ self, add_set_compute_unit_price_ix, default_units_per_instruction, disable_fee_calculator, enable_early_verification_of_account_modifications, - use_default_units_in_fee_calculation, FeatureSet, + enable_request_heap_frame_ix, use_default_units_in_fee_calculation, FeatureSet, }, fee::FeeStructure, fee_calculator::{FeeCalculator, FeeRateGovernor}, @@ -123,7 +123,7 @@ use { lamports::LamportsError, message::{AccountKeys, SanitizedMessage}, native_loader, - native_token::sol_to_lamports, + native_token::{sol_to_lamports, LAMPORTS_PER_SOL}, nonce::{self, state::DurableNonce, NONCED_TX_MARKER_IX_INDEX}, nonce_account, packet::PACKET_DATA_SIZE, @@ -139,7 +139,7 @@ use { timing::years_as_slots, transaction::{ MessageHash, Result, SanitizedTransaction, Transaction, TransactionError, - TransactionVerificationMode, VersionedTransaction, + TransactionVerificationMode, VersionedTransaction, MAX_TX_ACCOUNT_LOCKS, }, transaction_context::{ ExecutionRecord, InstructionTrace, TransactionAccount, TransactionContext, @@ -156,7 +156,7 @@ use { collections::{HashMap, HashSet}, convert::{TryFrom, TryInto}, fmt, mem, - ops::{Deref, Div, RangeInclusive}, + ops::{Deref, RangeInclusive}, path::PathBuf, rc::Rc, sync::{ @@ -164,12 +164,16 @@ use { AtomicBool, AtomicI64, AtomicU64, AtomicUsize, Ordering::{AcqRel, Acquire, Relaxed}, }, - Arc, LockResult, RwLock, RwLockReadGuard, RwLockWriteGuard, + Arc, LockResult, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, }, thread::Builder, time::{Duration, Instant}, }, }; +use { + rayon::slice::ParallelSliceMut, + std::{borrow::BorrowMut, ops::DerefMut}, +}; /// params to `verify_bank_hash` pub struct VerifyBankHash { @@ -235,6 +239,25 @@ impl RentDebit { } } +/// Incremental snapshots only calculate their accounts hash based on the account changes WITHIN the incremental slot range. +/// So, we need to keep track of the full snapshot expected accounts hash results. +/// We also need to keep track of the hash and capitalization specific to the incremental snapshot slot range. +/// The capitalization we calculate for the incremental slot will NOT be consistent with the bank's capitalization. +/// It is not feasible to calculate a capitalization delta that is correct given just incremental slots account data and the full snapshot's capitalization. +#[derive(Serialize, Deserialize, AbiExample, Clone, Debug, Default, PartialEq, Eq)] +pub struct BankIncrementalSnapshotPersistence { + /// slot of full snapshot + pub full_slot: Slot, + /// accounts hash from the full snapshot + pub full_hash: Hash, + /// capitalization from the full snapshot + pub full_capitalization: u64, + /// hash of the accounts in the incremental snapshot slot range, including zero-lamport accounts + pub incremental_hash: Hash, + /// capitalization of the accounts in the incremental snapshot slot range + pub incremental_capitalization: u64, +} + #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct RentDebits(HashMap); impl RentDebits { @@ -294,272 +317,6 @@ pub struct SquashTiming { type EpochCount = u64; -mod executor_cache { - use {super::*, log}; - - #[derive(Debug, Default)] - pub struct Stats { - pub hits: AtomicU64, - pub misses: AtomicU64, - pub evictions: HashMap, - pub insertions: AtomicU64, - pub replacements: AtomicU64, - pub one_hit_wonders: AtomicU64, - } - - impl Stats { - pub fn submit(&self, slot: Slot) { - let hits = self.hits.load(Relaxed); - let misses = self.misses.load(Relaxed); - let insertions = self.insertions.load(Relaxed); - let replacements = self.replacements.load(Relaxed); - let one_hit_wonders = self.one_hit_wonders.load(Relaxed); - let evictions: u64 = self.evictions.values().sum(); - datapoint_info!( - "bank-executor-cache-stats", - ("slot", slot, i64), - ("hits", hits, i64), - ("misses", misses, i64), - ("evictions", evictions, i64), - ("insertions", insertions, i64), - ("replacements", replacements, i64), - ("one_hit_wonders", one_hit_wonders, i64), - ); - debug!( - "Executor Cache Stats -- Hits: {}, Misses: {}, Evictions: {}, Insertions: {}, Replacements: {}, One-Hit-Wonders: {}", - hits, misses, evictions, insertions, replacements, one_hit_wonders, - ); - if log_enabled!(log::Level::Trace) && !self.evictions.is_empty() { - let mut evictions = self.evictions.iter().collect::>(); - evictions.sort_by_key(|e| e.1); - let evictions = evictions - .into_iter() - .rev() - .map(|(program_id, evictions)| { - format!(" {:<44} {}", program_id.to_string(), evictions) - }) - .collect::>(); - let evictions = evictions.join("\n"); - trace!( - "Eviction Details:\n {:<44} {}\n{}", - "Program", - "Count", - evictions - ); - } - } - } -} - -const MAX_CACHED_EXECUTORS: usize = 256; -#[derive(Debug)] -struct CachedExecutorsEntry { - prev_epoch_count: u64, - epoch_count: AtomicU64, - executor: Arc, - hit_count: AtomicU64, -} - -impl Clone for CachedExecutorsEntry { - fn clone(&self) -> Self { - Self { - prev_epoch_count: self.prev_epoch_count, - epoch_count: AtomicU64::new(self.epoch_count.load(Relaxed)), - executor: self.executor.clone(), - hit_count: AtomicU64::new(self.hit_count.load(Relaxed)), - } - } -} - -/// LFU Cache of executors with single-epoch memory of usage counts -#[derive(Debug)] -struct CachedExecutors { - capacity: usize, - current_epoch: Epoch, - pub(self) executors: HashMap, - stats: executor_cache::Stats, -} - -impl Default for CachedExecutors { - fn default() -> Self { - Self { - capacity: MAX_CACHED_EXECUTORS, - current_epoch: Epoch::default(), - executors: HashMap::default(), - stats: executor_cache::Stats::default(), - } - } -} - -#[cfg(RUSTC_WITH_SPECIALIZATION)] -impl AbiExample for CachedExecutors { - fn example() -> Self { - // Delegate AbiExample impl to Default before going deep and stuck with - // not easily impl-able Arc due to rust's coherence issue - // This is safe because CachedExecutors isn't serializable by definition. - Self::default() - } -} - -impl CachedExecutors { - fn new(max_capacity: usize, current_epoch: Epoch) -> Self { - Self { - capacity: max_capacity, - current_epoch, - executors: HashMap::new(), - stats: executor_cache::Stats::default(), - } - } - - fn new_from_parent_bank_executors( - parent_bank_executors: &CachedExecutors, - current_epoch: Epoch, - ) -> Self { - let executors = if parent_bank_executors.current_epoch == current_epoch { - parent_bank_executors.executors.clone() - } else { - parent_bank_executors - .executors - .iter() - .map(|(&key, entry)| { - let entry = CachedExecutorsEntry { - prev_epoch_count: entry.epoch_count.load(Relaxed), - epoch_count: AtomicU64::default(), - executor: entry.executor.clone(), - hit_count: AtomicU64::new(entry.hit_count.load(Relaxed)), - }; - (key, entry) - }) - .collect() - }; - - Self { - capacity: parent_bank_executors.capacity, - current_epoch, - executors, - stats: executor_cache::Stats::default(), - } - } - - fn get(&self, pubkey: &Pubkey) -> Option> { - if let Some(entry) = self.executors.get(pubkey) { - self.stats.hits.fetch_add(1, Relaxed); - entry.epoch_count.fetch_add(1, Relaxed); - entry.hit_count.fetch_add(1, Relaxed); - Some(entry.executor.clone()) - } else { - self.stats.misses.fetch_add(1, Relaxed); - None - } - } - - fn put(&mut self, executors: &[(&Pubkey, Arc)]) { - let mut new_executors: Vec<_> = executors - .iter() - .filter_map(|(key, executor)| { - if let Some(mut entry) = self.remove(key) { - self.stats.replacements.fetch_add(1, Relaxed); - entry.executor = executor.clone(); - let _ = self.executors.insert(**key, entry); - None - } else { - self.stats.insertions.fetch_add(1, Relaxed); - Some((*key, executor)) - } - }) - .collect(); - - if !new_executors.is_empty() { - let mut counts = self - .executors - .iter() - .map(|(key, entry)| { - let count = entry.prev_epoch_count + entry.epoch_count.load(Relaxed); - (key, count) - }) - .collect::>(); - counts.sort_unstable_by_key(|(_, count)| *count); - - let primer_counts = Self::get_primer_counts(counts.as_slice(), new_executors.len()); - - if self.executors.len() >= self.capacity { - let mut least_keys = counts - .iter() - .take(new_executors.len()) - .map(|least| *least.0) - .collect::>(); - for least_key in least_keys.drain(..) { - let _ = self.remove(&least_key); - self.stats - .evictions - .entry(least_key) - .and_modify(|c| saturating_add_assign!(*c, 1)) - .or_insert(1); - } - } - - for ((key, executor), primer_count) in new_executors.drain(..).zip(primer_counts) { - let entry = CachedExecutorsEntry { - prev_epoch_count: 0, - epoch_count: AtomicU64::new(primer_count), - executor: executor.clone(), - hit_count: AtomicU64::new(1), - }; - let _ = self.executors.insert(*key, entry); - } - } - } - - fn remove(&mut self, pubkey: &Pubkey) -> Option { - let maybe_entry = self.executors.remove(pubkey); - if let Some(entry) = maybe_entry.as_ref() { - if entry.hit_count.load(Relaxed) == 1 { - self.stats.one_hit_wonders.fetch_add(1, Relaxed); - } - } - maybe_entry - } - - fn clear(&mut self) { - *self = CachedExecutors::default(); - } - - fn get_primer_count_upper_bound_inclusive(counts: &[(&Pubkey, u64)]) -> u64 { - const PRIMER_COUNT_TARGET_PERCENTILE: u64 = 85; - #[allow(clippy::assertions_on_constants)] - { - assert!(PRIMER_COUNT_TARGET_PERCENTILE <= 100); - } - // Executor use-frequencies are assumed to fit a Pareto distribution. Choose an - // upper-bound for our primer count as the actual count at the target rank to avoid - // an upward bias - - let target_index = u64::try_from(counts.len().saturating_sub(1)) - .ok() - .and_then(|counts| { - let index = counts - .saturating_mul(PRIMER_COUNT_TARGET_PERCENTILE) - .div(100); // switch to u64::saturating_div once stable - usize::try_from(index).ok() - }) - .unwrap_or(0); - - counts - .get(target_index) - .map(|(_, count)| *count) - .unwrap_or(0) - } - - fn get_primer_counts(counts: &[(&Pubkey, u64)], num_counts: usize) -> Vec { - let max_primer_count = Self::get_primer_count_upper_bound_inclusive(counts); - let mut rng = rand::thread_rng(); - - (0..num_counts) - .map(|_| rng.gen_range(0, max_primer_count.saturating_add(1))) - .collect::>() - } -} - #[derive(Debug)] pub struct BankRc { /// where all the Accounts are stored @@ -603,11 +360,12 @@ impl BankRc { } pub type TransactionCheckResult = (Result<()>, Option); - +#[derive(Debug, Clone)] pub struct TransactionResults { pub fee_collection_results: Vec>, pub execution_results: Vec, pub rent_debits: Vec, + pub receipts: Vec<(Hash, Hash)>, } #[derive(Debug, Clone)] @@ -975,6 +733,8 @@ pub struct BankFieldsToDeserialize { pub(crate) epoch_stakes: HashMap, pub(crate) is_delta: bool, pub(crate) accounts_data_len: u64, + pub(crate) incremental_snapshot_persistence: Option, + pub(crate) epoch_accounts_hash: Option, } // Bank's common fields shared by all supported snapshot versions for serialization. @@ -1082,6 +842,10 @@ impl PartialEq for Bank { accounts_data_size_delta_on_chain: _, accounts_data_size_delta_off_chain: _, fee_structure: _, + incremental_snapshot_persistence: _, + // receipt_tree: _, + receipt_queue: _, + transaction_message_hashes: _, // Ignore new fields explicitly if they do not impact PartialEq. // Adding ".." will remove compile-time checks that if a new field // is added to the struct, this ParitalEq is accordingly updated. @@ -1119,14 +883,6 @@ impl PartialEq for Bank { } } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, AbiExample, AbiEnumVisitor, Clone, Copy)] -pub enum RewardType { - Fee, - Rent, - Staking, - Voting, -} - #[derive(Debug)] pub enum RewardCalculationEvent<'a, 'b> { Staking(&'a Pubkey, &'b InflationPointCalculationEvent), @@ -1136,21 +892,6 @@ fn null_tracer() -> Option { None:: } -impl fmt::Display for RewardType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - RewardType::Fee => "fee", - RewardType::Rent => "rent", - RewardType::Staking => "staking", - RewardType::Voting => "voting", - } - ) - } -} - pub trait DropCallback: fmt::Debug { fn callback(&self, b: &Bank); fn clone_box(&self) -> Box; @@ -1357,6 +1098,15 @@ pub struct Bank { /// Transaction fee structure pub fee_structure: FeeStructure, + + pub incremental_snapshot_persistence: Option, + + // pub receipt_tree: Arc>>, + /// message_hashs + pub transaction_message_hashes: Arc>>, + + /// message_hash, receipt + pub receipt_queue: Arc>>, } struct VoteWithStakeDelegations { @@ -1485,6 +1235,7 @@ impl Bank { fn default_with_accounts(accounts: Accounts) -> Self { let mut bank = Self { + incremental_snapshot_persistence: None, rewrites_skipped_this_slot: Rewrites::default(), rc: BankRc::new(accounts, Slot::default()), status_cache: Arc::>::default(), @@ -1544,6 +1295,9 @@ impl Bank { accounts_data_size_delta_on_chain: AtomicI64::new(0), accounts_data_size_delta_off_chain: AtomicI64::new(0), fee_structure: FeeStructure::default(), + // receipt_tree: Arc::new(RwLock::new(im::HashMap::new())), + receipt_queue: Arc::new(RwLock::new(Vec::new())), + transaction_message_hashes: Arc::new(RwLock::new(Vec::new())), }; let accounts_data_size_initial = bank.get_total_accounts_stats().unwrap().data_len as u64; @@ -1792,6 +1546,7 @@ impl Bank { let accounts_data_size_initial = parent.load_accounts_data_size(); let mut new = Bank { + incremental_snapshot_persistence: None, rewrites_skipped_this_slot: Rewrites::default(), rc, status_cache, @@ -1869,6 +1624,9 @@ impl Bank { accounts_data_size_delta_on_chain: AtomicI64::new(0), accounts_data_size_delta_off_chain: AtomicI64::new(0), fee_structure: parent.fee_structure.clone(), + // receipt_tree: Arc::new(RwLock::new(im::HashMap::new())), + receipt_queue: Arc::new(RwLock::new(Vec::new())), + transaction_message_hashes: Arc::new(RwLock::new(Vec::new())), }; let (_, ancestors_time) = measure!( @@ -1941,7 +1699,7 @@ impl Bank { apply_feature_activations_time.as_us(), i64 ), - ("activate_epoch_Us", activate_epoch_time.as_us(), i64), + ("activate_epoch_us", activate_epoch_time.as_us(), i64), ( "update_epoch_stakes_us", update_epoch_stakes_time.as_us(), @@ -2152,6 +1910,7 @@ impl Bank { } let feature_set = new(); let mut bank = Self { + incremental_snapshot_persistence: fields.incremental_snapshot_persistence, rewrites_skipped_this_slot: Rewrites::default(), rc: bank_rc, status_cache: new(), @@ -2211,6 +1970,9 @@ impl Bank { accounts_data_size_delta_on_chain: AtomicI64::new(0), accounts_data_size_delta_off_chain: AtomicI64::new(0), fee_structure: FeeStructure::default(), + // receipt_tree: Arc::new(RwLock::new(im::HashMap::new())), + receipt_queue: Arc::new(RwLock::new(Vec::new())), + transaction_message_hashes: Arc::new(RwLock::new(Vec::new())), }; bank.finish_init( genesis_config, @@ -2222,6 +1984,11 @@ impl Bank { // Consider removing from serializable bank state // (BankFieldsToSerialize/BankFieldsToDeserialize) and initializing // from the passed in genesis_config instead (as new()/new_with_paths() already do) + assert_eq!( + bank.genesis_creation_time, genesis_config.creation_time, + "Bank snapshot genesis creation time does not match genesis.bin creation time.\ + The snapshot and genesis.bin might pertain to different clusters" + ); assert_eq!( bank.hashes_per_tick, genesis_config.poh_config.hashes_per_tick @@ -2232,7 +1999,6 @@ impl Bank { genesis_config.poh_config.target_tick_duration.as_nanos() * genesis_config.ticks_per_slot as u128 ); - assert_eq!(bank.genesis_creation_time, genesis_config.creation_time); assert_eq!(bank.max_tick_height, (bank.slot + 1) * bank.ticks_per_slot); assert_eq!( bank.slots_per_year, @@ -2727,13 +2493,11 @@ impl Bank { "distributed inflation: {} (rounded from: {})", validator_rewards_paid, validator_rewards ); - // TODO: staked_nodes forces an eager stakes calculation. remove it! - let (num_stake_accounts, num_vote_accounts, num_staked_nodes) = { + let (num_stake_accounts, num_vote_accounts) = { let stakes = self.stakes_cache.stakes(); ( stakes.stake_delegations().len(), stakes.vote_accounts().len(), - stakes.staked_nodes().len(), ) }; self.capitalization @@ -2760,7 +2524,6 @@ impl Bank { ("post_capitalization", self.capitalization(), i64), ("num_stake_accounts", num_stake_accounts as i64, i64), ("num_vote_accounts", num_vote_accounts as i64, i64), - ("num_staked_nodes", num_staked_nodes as i64, i64) ); } @@ -2783,7 +2546,7 @@ impl Bank { let invalid_cached_vote_accounts = AtomicUsize::default(); let invalid_cached_stake_accounts_rent_epoch = AtomicUsize::default(); - let stake_delegations: Vec<_> = stakes.stake_delegations().iter().collect(); + let stake_delegations = self.filter_stake_delegations(&stakes); thread_pool.install(|| { stake_delegations .into_par_iter() @@ -2798,9 +2561,26 @@ impl Bank { None => { invalid_stake_keys .insert(*stake_pubkey, InvalidCacheEntryReason::Missing); + invalid_cached_stake_accounts.fetch_add(1, Relaxed); return; } }; + if cached_stake_account.account() != &stake_account { + invalid_cached_stake_accounts.fetch_add(1, Relaxed); + let cached_stake_account = cached_stake_account.account(); + if cached_stake_account.lamports() == stake_account.lamports() + && cached_stake_account.data() == stake_account.data() + && cached_stake_account.owner() == stake_account.owner() + && cached_stake_account.executable() == stake_account.executable() + { + invalid_cached_stake_accounts_rent_epoch.fetch_add(1, Relaxed); + } else { + debug!( + "cached stake account mismatch: {}: {:?}, {:?}", + stake_pubkey, stake_account, cached_stake_account + ); + } + } let stake_account = match StakeAccount::<()>::try_from(stake_account) { Ok(stake_account) => stake_account, Err(stake_account::Error::InvalidOwner { .. }) => { @@ -2823,33 +2603,6 @@ impl Bank { return; } }; - if cached_stake_account != &stake_account { - invalid_cached_stake_accounts.fetch_add(1, Relaxed); - let mut cached_account = cached_stake_account.account().clone(); - // We could have collected rent on the loaded account already in this new epoch (we could be at partition_index 12, for example). - // So, we may need to adjust the rent_epoch of the cached account. So, update rent_epoch and compare just the accounts. - ExpectedRentCollection::maybe_update_rent_epoch_on_load( - &mut cached_account, - &SlotInfoInEpoch::new_small(self.slot()), - &SlotInfoInEpoch::new_small(self.slot()), - self.epoch_schedule(), - self.rent_collector(), - stake_pubkey, - &self.rewrites_skipped_this_slot, - ); - if &cached_account != stake_account.account() { - info!( - "cached stake account mismatch: {}: {:?}, {:?}", - stake_pubkey, - cached_account, - stake_account.account() - ); - } else { - // track how many of 'invalid_cached_stake_accounts' were due to rent_epoch changes - // subtract these to find real invalid cached accounts - invalid_cached_stake_accounts_rent_epoch.fetch_add(1, Relaxed); - } - } let stake_delegation = (*stake_pubkey, stake_account); let mut vote_delegations = if let Some(vote_delegations) = vote_with_stake_delegations_map.get_mut(vote_pubkey) @@ -2859,16 +2612,12 @@ impl Bank { let cached_vote_account = cached_vote_accounts.get(vote_pubkey); let vote_account = match self.get_account_with_fixed_root(vote_pubkey) { Some(vote_account) => { - match cached_vote_account { - Some(cached_vote_account) - if cached_vote_account == &vote_account => {} - _ => { - invalid_cached_vote_accounts.fetch_add(1, Relaxed); - } - }; if vote_account.owner() != &solana_vote_program::id() { invalid_vote_keys .insert(*vote_pubkey, InvalidCacheEntryReason::WrongOwner); + if cached_vote_account.is_some() { + invalid_cached_vote_accounts.fetch_add(1, Relaxed); + } return; } vote_account @@ -2890,9 +2639,18 @@ impl Bank { } else { invalid_vote_keys .insert(*vote_pubkey, InvalidCacheEntryReason::BadState); + if cached_vote_account.is_some() { + invalid_cached_vote_accounts.fetch_add(1, Relaxed); + } return; }; - + match cached_vote_account { + Some(cached_vote_account) + if cached_vote_account.account() == &vote_account => {} + _ => { + invalid_cached_vote_accounts.fetch_add(1, Relaxed); + } + }; vote_with_stake_delegations_map .entry(*vote_pubkey) .or_insert_with(|| VoteWithStakeDelegations { @@ -2928,6 +2686,39 @@ impl Bank { } } + fn filter_stake_delegations<'a>( + &self, + stakes: &'a Stakes>, + ) -> Vec<(&'a Pubkey, &'a StakeAccount)> { + if self + .feature_set + .is_active(&feature_set::stake_minimum_delegation_for_rewards::id()) + { + let num_stake_delegations = stakes.stake_delegations().len(); + let min_stake_delegation = + solana_stake_program::get_minimum_delegation(&self.feature_set) + .max(LAMPORTS_PER_SOL); + + let (stake_delegations, filter_timer) = measure!(stakes + .stake_delegations() + .iter() + .filter(|(_stake_pubkey, cached_stake_account)| { + cached_stake_account.delegation().stake >= min_stake_delegation + }) + .collect::>()); + + datapoint_info!( + "stake_account_filter_time", + ("filter_time_us", filter_timer.as_us(), i64), + ("num_stake_delegations_before", num_stake_delegations, i64), + ("num_stake_delegations_after", stake_delegations.len(), i64) + ); + stake_delegations + } else { + stakes.stake_delegations().iter().collect() + } + } + fn load_vote_and_stake_accounts( &self, thread_pool: &ThreadPool, @@ -2937,7 +2728,8 @@ impl Bank { F: Fn(&RewardCalculationEvent) + Send + Sync, { let stakes = self.stakes_cache.stakes(); - let stake_delegations: Vec<_> = stakes.stake_delegations().iter().collect(); + let stake_delegations = self.filter_stake_delegations(&stakes); + // Obtain all unique voter pubkeys from stake delegations. fn merge(mut acc: HashSet, other: HashSet) -> HashSet { if acc.len() < other.len() { @@ -3231,19 +3023,15 @@ impl Bank { self.store_account(&vote_pubkey, &vote_account); } - if vote_rewards > 0 { - Some(( - vote_pubkey, - RewardInfo { - reward_type: RewardType::Voting, - lamports: vote_rewards as i64, - post_balance: vote_account.lamports(), - commission: Some(commission), - }, - )) - } else { - None - } + Some(( + vote_pubkey, + RewardInfo { + reward_type: RewardType::Voting, + lamports: vote_rewards as i64, + post_balance: vote_account.lamports(), + commission: Some(commission), + }, + )) }, ) .collect::>(); @@ -3712,6 +3500,7 @@ impl Bank { .is_active(&add_set_compute_unit_price_ix::id()), self.feature_set .is_active(&use_default_units_in_fee_calculation::id()), + self.enable_request_heap_frame_ix(), )) } @@ -3755,6 +3544,7 @@ impl Bank { .is_active(&add_set_compute_unit_price_ix::id()), self.feature_set .is_active(&use_default_units_in_fee_calculation::id()), + self.enable_request_heap_frame_ix(), ) } @@ -3883,13 +3673,28 @@ impl Bank { } } + /// Get the max number of accounts that a transaction may lock in this block + pub fn get_transaction_account_lock_limit(&self) -> usize { + if self + .feature_set + .is_active(&feature_set::increase_tx_account_lock_limit::id()) + { + MAX_TX_ACCOUNT_LOCKS + } else { + 64 + } + } + /// Prepare a transaction batch from a list of legacy transactions. Used for tests only. pub fn prepare_batch_for_tests(&self, txs: Vec) -> TransactionBatch { let sanitized_txs = txs .into_iter() .map(SanitizedTransaction::from_transaction_for_tests) .collect::>(); - let lock_results = self.rc.accounts.lock_accounts(sanitized_txs.iter()); + let lock_results = self.rc.accounts.lock_accounts( + sanitized_txs.iter(), + self.get_transaction_account_lock_limit(), + ); TransactionBatch::new(lock_results, self, Cow::Owned(sanitized_txs)) } @@ -3909,7 +3714,10 @@ impl Bank { ) }) .collect::>>()?; - let lock_results = self.rc.accounts.lock_accounts(sanitized_txs.iter()); + let lock_results = self.rc.accounts.lock_accounts( + sanitized_txs.iter(), + self.get_transaction_account_lock_limit(), + ); Ok(TransactionBatch::new( lock_results, self, @@ -3922,7 +3730,10 @@ impl Bank { &'a self, txs: &'b [SanitizedTransaction], ) -> TransactionBatch<'a, 'b> { - let lock_results = self.rc.accounts.lock_accounts(txs.iter()); + let lock_results = self + .rc + .accounts + .lock_accounts(txs.iter(), self.get_transaction_account_lock_limit()); TransactionBatch::new(lock_results, self, Cow::Borrowed(txs)) } @@ -3934,10 +3745,11 @@ impl Bank { transaction_results: impl Iterator>, ) -> TransactionBatch<'a, 'b> { // this lock_results could be: Ok, AccountInUse, WouldExceedBlockMaxLimit or WouldExceedAccountMaxLimit - let lock_results = self - .rc - .accounts - .lock_accounts_with_results(transactions.iter(), transaction_results); + let lock_results = self.rc.accounts.lock_accounts_with_results( + transactions.iter(), + transaction_results, + self.get_transaction_account_lock_limit(), + ); TransactionBatch::new(lock_results, self, Cow::Borrowed(transactions)) } @@ -3946,7 +3758,9 @@ impl Bank { &'a self, transaction: SanitizedTransaction, ) -> TransactionBatch<'a, '_> { - let lock_result = transaction.get_account_locks().map(|_| ()); + let lock_result = transaction + .get_account_locks(self.get_transaction_account_lock_limit()) + .map(|_| ()); let mut batch = TransactionBatch::new(vec![lock_result], self, Cow::Owned(vec![transaction])); batch.set_needs_unlock(false); @@ -4452,6 +4266,33 @@ impl Bank { None }; + let status_code: u8 = if status.is_ok() { 1 } else { 0 }; + // if let Ok(mut receipt_tree) = self.receipt_tree.write() { + // let mut leaf = fd_bmtree32_node { hash: [0u8; 32] }; + // hash_leaf( + // &mut leaf, + // &mut [ + // tx.signature().as_ref(), + // status_code.clone().to_be_bytes().as_ref(), + // ] + // .clone() + // .as_slice(), + // ); + + // if let Ok(txn_idxs) = self.transaction_indexes.read() { + // let txn_position = txn_idxs + // .iter() + // .position(|&sig| sig == *tx.signature()) + // .unwrap_or_else(|| { + // panic!("Transaction signature not found in transaction_indexes") + // }); + // receipt_tree.insert(txn_position, Hash::new_from_array(leaf.hash.clone())); + // } else { + // info!("lock poisoned"); + // }; + // } + // let mesdsagh_hash = tx.message_hash() + // let receipt_hash = hashv(message_hash,status) TransactionExecutionResult::Executed { details: TransactionExecutionDetails { status, @@ -4466,6 +4307,15 @@ impl Bank { } } + // A cluster specific feature gate, when not activated it keeps v1.13 behavior in mainnet-beta; + // once activated for v1.14+, it allows compute_budget::request_heap_frame and + // compute_budget::set_compute_unit_price co-exist in same transaction. + fn enable_request_heap_frame_ix(&self) -> bool { + self.feature_set + .is_active(&enable_request_heap_frame_ix::id()) + || self.cluster_type() != ClusterType::MainnetBeta + } + #[allow(clippy::type_complexity)] pub fn load_and_execute_transactions( &self, @@ -4488,14 +4338,32 @@ impl Bank { .iter() .enumerate() .filter_map(|(index, res)| match res { + // following are retryable errors Err(TransactionError::AccountInUse) => { error_counters.account_in_use += 1; Some(index) } - Err(TransactionError::WouldExceedMaxBlockCostLimit) - | Err(TransactionError::WouldExceedMaxVoteCostLimit) - | Err(TransactionError::WouldExceedMaxAccountCostLimit) - | Err(TransactionError::WouldExceedAccountDataBlockLimit) => Some(index), + Err(TransactionError::WouldExceedMaxBlockCostLimit) => { + error_counters.would_exceed_max_block_cost_limit += 1; + Some(index) + } + Err(TransactionError::WouldExceedMaxVoteCostLimit) => { + error_counters.would_exceed_max_vote_cost_limit += 1; + Some(index) + } + Err(TransactionError::WouldExceedMaxAccountCostLimit) => { + error_counters.would_exceed_max_account_cost_limit += 1; + Some(index) + } + Err(TransactionError::WouldExceedAccountDataBlockLimit) => { + error_counters.would_exceed_account_data_block_limit += 1; + Some(index) + } + // following are non-retryable errors + Err(TransactionError::TooManyAccountLocks) => { + error_counters.too_many_account_locks += 1; + None + } Err(_) => None, Ok(_) => None, }) @@ -4553,6 +4421,7 @@ impl Bank { tx.message().program_instructions_iter(), feature_set.is_active(&default_units_per_instruction::id()), feature_set.is_active(&add_set_compute_unit_price_ix::id()), + true, // don't reject txs that use request heap size ix ); compute_budget_process_transaction_time.stop(); saturating_add_assign!( @@ -4839,6 +4708,7 @@ impl Bank { fee_structure: &FeeStructure, support_set_compute_unit_price_ix: bool, use_default_units_per_instruction: bool, + enable_request_heap_frame_ix: bool, ) -> u64 { // Fee based on compute units and signatures const BASE_CONGESTION: f64 = 5_000.0; @@ -4855,6 +4725,7 @@ impl Bank { message.program_instructions_iter(), use_default_units_per_instruction, support_set_compute_unit_price_ix, + enable_request_heap_frame_ix, ) .unwrap_or_default(); let prioritization_fee = prioritization_fee_details.get_fee(); @@ -4922,6 +4793,7 @@ impl Bank { .is_active(&add_set_compute_unit_price_ix::id()), self.feature_set .is_active(&use_default_units_in_fee_calculation::id()), + self.enable_request_heap_frame_ix(), ); // In case of instruction error, even though no accounts @@ -4953,6 +4825,7 @@ impl Bank { sanitized_txs: &[SanitizedTransaction], loaded_txs: &mut [TransactionLoadResult], execution_results: Vec, + receipts: Vec<(Hash, Hash)>, last_blockhash: Hash, lamports_per_signature: u64, counts: CommitTransactionCounts, @@ -5069,6 +4942,7 @@ impl Bank { fee_collection_results, execution_results, rent_debits, + receipts, } } @@ -6192,7 +6066,11 @@ impl Bank { } else { vec![] }; - + let msg_hashes: Vec = batch + .sanitized_transactions() + .iter() + .map(|txn| *txn.message_hash()) + .collect(); let LoadAndExecuteTransactionsOutput { mut loaded_transactions, execution_results, @@ -6210,13 +6088,26 @@ impl Bank { None, log_messages_bytes_limit, ); - + let statuses: Vec = execution_results + .iter() + .map(|result| result.was_executed_successfully()) + .collect(); + let receipts: Vec<(Hash, Hash)> = msg_hashes + .into_iter() + .zip(statuses) + .map(|rec_data| { + let status_code: u8 = if rec_data.1 { 1 } else { 0 }; + let hashed = hashv(&[rec_data.0.as_ref(), status_code.to_be_bytes().as_ref()]); + (rec_data.0, hashed) + }) + .collect(); let (last_blockhash, lamports_per_signature) = self.last_blockhash_and_lamports_per_signature(); let results = self.commit_transactions( batch.sanitized_transactions(), &mut loaded_transactions, execution_results, + receipts, last_blockhash, lamports_per_signature, CommitTransactionCounts { @@ -6228,6 +6119,7 @@ impl Bank { }, timings, ); + // bank.receipt_hash_batch_queue.push(results.execution_results.filter(receipt_hash, signature)) let post_balances = if collect_balances { self.collect_balances(batch) } else { @@ -6614,22 +6506,7 @@ impl Bank { ancestors: &Ancestors, pubkey: &Pubkey, ) -> Option<(AccountSharedData, Slot)> { - match self.rc.accounts.load_with_fixed_root(ancestors, pubkey) { - Some((mut account, storage_slot)) => { - ExpectedRentCollection::maybe_update_rent_epoch_on_load( - &mut account, - &SlotInfoInEpoch::new_small(storage_slot), - &SlotInfoInEpoch::new_small(self.slot()), - self.epoch_schedule(), - self.rent_collector(), - pubkey, - &self.rewrites_skipped_this_slot, - ); - - Some((account, storage_slot)) - } - None => None, - } + self.rc.accounts.load_with_fixed_root(ancestors, pubkey) } pub fn get_program_accounts( @@ -6808,14 +6685,39 @@ impl Bank { .bank_hash_info_at(self.slot(), &self.rewrites_skipped_this_slot); let mut signature_count_buf = [0u8; 8]; LittleEndian::write_u64(&mut signature_count_buf[..], self.signature_count() as u64); - + let mut receipt_root = hashv(&[ + (0x80 as u8).to_le_bytes().as_slice(), + &[0u8; 32], + u64::to_le_bytes(0).as_slice(), + ]) + .to_bytes(); + + if let Ok(mut r) = self.receipt_queue.read() { + let mut hashes = r.deref().clone(); + hashes.par_sort_unstable_by(|a, b| a.0.as_ref().cmp(b.0.as_ref())); + let receipt_hashes: Vec = hashes.par_iter().map(|item| item.1).collect(); + let mt = MerkleTree::new_with_leaves(receipt_hashes.clone()); + if let Some(root) = mt.get_root() { + receipt_root = root.to_bytes(); + } + info!("#64 parent_bank_hash: {:?} accounts_delta_hash: {:?} signature_count_buf: {:?} last_blockhash: {:?}",self.parent_hash().as_ref(),accounts_delta_hash.hash.as_ref(),signature_count_buf,self.last_blockhash().as_ref()); + if hashes.len() > 0 { + hashes.clear(); + } + } + if let Ok(mut write_q) = self.receipt_queue.write() { + info!("#64 has cleared queue: {:?}", write_q.len()); + write_q.clear(); + } + info!("#64 receipt root: {:?}", receipt_root); let mut hash = hashv(&[ self.parent_hash.as_ref(), accounts_delta_hash.hash.as_ref(), &signature_count_buf, self.last_blockhash().as_ref(), + receipt_root.as_ref(), ]); - + info!("#64 bankhash: {:?}", hash); let buf = self .hard_forks .read() @@ -6893,7 +6795,7 @@ impl Bank { let accounts_ = Arc::clone(&accounts); accounts.accounts_db.verify_accounts_hash_in_bg.start(|| { Builder::new() - .name("solana-bg-hash-verifier".to_string()) + .name("solBgHashVerify".into()) .spawn(move || { info!( "running initial verification accounts hash calculation in background" @@ -6908,6 +6810,8 @@ impl Bank { config.can_cached_slot_be_unflushed, config.ignore_mismatch, config.store_hash_raw_data_for_debug, + // true to run using bg thread pool + true, ); accounts_ .accounts_db @@ -6929,12 +6833,31 @@ impl Bank { config.can_cached_slot_be_unflushed, config.ignore_mismatch, config.store_hash_raw_data_for_debug, + // fg is waiting for this to run, so we can use the fg thread pool + false, ); self.set_initial_accounts_hash_verification_completed(); result } } + pub fn set_transaction_message_hashes(&self, transaction_message_hashes: Vec) { + if let Ok(mut txn_indexes) = self.transaction_message_hashes.write() { + *txn_indexes = transaction_message_hashes; + } + } + + pub fn append_receipts(&self, multi_batch_receipts: Vec>) { + if let Ok(mut receipt_queue) = self.receipt_queue.write() { + // receipt_queue.clear(); + let mut temp_rec_queue = vec![]; + for mut batch in multi_batch_receipts { + temp_rec_queue.append(&mut batch); + } + // temp_rec_queue.append(&mut receipt_tuples.clone()); + receipt_queue.append(&mut temp_rec_queue); + } + } /// Specify that initial verification has completed. /// Called internally when verification runs in the foreground thread. /// Also has to be called by some tests which don't do verification on startup. @@ -8003,7 +7926,10 @@ impl Drop for Bank { /// utility function used for testing and benchmarking. pub mod test_utils { - use {super::Bank, solana_sdk::hash::hashv}; + use { + super::Bank, + solana_sdk::hash::{hash, hashv}, + }; pub fn goto_end_of_slot(bank: &mut Bank) { let mut tick_hash = bank.last_blockhash(); loop { @@ -8038,8 +7964,10 @@ pub(crate) mod tests { status_cache::MAX_CACHE_ENTRIES, }, crossbeam_channel::{bounded, unbounded}, + rand::Rng, solana_program_runtime::{ compute_budget::MAX_COMPUTE_UNIT_LIMIT, + executor_cache::Executor, invoke_context::InvokeContext, prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType}, }, @@ -8055,7 +7983,6 @@ pub(crate) mod tests { hash, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, message::{Message, MessageHeader}, - native_token::LAMPORTS_PER_SOL, nonce, poh_config::PohConfig, program::MAX_RETURN_DATA, @@ -8068,7 +7995,6 @@ pub(crate) mod tests { system_instruction::{self, SystemError, MAX_PERMITTED_DATA_LENGTH}, system_program, timing::duration_as_s, - transaction::MAX_TX_ACCOUNT_LOCKS, transaction_context::InstructionContext, }, solana_vote_program::{ @@ -8918,11 +8844,7 @@ pub(crate) mod tests { // Since, validator 1 and validator 2 has equal smallest stake, it comes down to comparison // between their pubkey. - let tweak_1 = if validator_1_pubkey > validator_2_pubkey { - 1 - } else { - 0 - }; + let tweak_1 = u64::from(validator_1_pubkey > validator_2_pubkey); let validator_1_portion = ((validator_1_stake_lamports * rent_to_be_distributed) as f64 / 100.0) as u64 + tweak_1; assert_eq!( @@ -8932,11 +8854,7 @@ pub(crate) mod tests { // Since, validator 1 and validator 2 has equal smallest stake, it comes down to comparison // between their pubkey. - let tweak_2 = if validator_2_pubkey > validator_1_pubkey { - 1 - } else { - 0 - }; + let tweak_2 = u64::from(validator_2_pubkey > validator_1_pubkey); let validator_2_portion = ((validator_2_stake_lamports * rent_to_be_distributed) as f64 / 100.0) as u64 + tweak_2; assert_eq!( @@ -9065,7 +8983,7 @@ pub(crate) mod tests { #[allow(clippy::cognitive_complexity)] fn test_rent_complex() { solana_logger::setup(); - let mock_program_id = Pubkey::new(&[2u8; 32]); + let mock_program_id = Pubkey::from([2u8; 32]); #[derive(Serialize, Deserialize)] enum MockInstruction { @@ -10056,7 +9974,7 @@ pub(crate) mod tests { fn test_collect_rent_from_accounts() { solana_logger::setup(); - let zero_lamport_pubkey = Pubkey::new(&[0; 32]); + let zero_lamport_pubkey = Pubkey::from([0; 32]); let genesis_bank = create_simple_test_arc_bank(100000); let first_bank = Arc::new(new_from_parent(&genesis_bank)); @@ -10275,15 +10193,26 @@ pub(crate) mod tests { // verify validator rewards show up in bank1.rewards vector assert_eq!( *bank1.rewards.read().unwrap(), - vec![( - stake_id, - RewardInfo { - reward_type: RewardType::Staking, - lamports: validator_rewards as i64, - post_balance: bank1.get_balance(&stake_id), - commission: Some(0), - } - )] + vec![ + ( + vote_id, + RewardInfo { + reward_type: RewardType::Voting, + lamports: 0, + post_balance: bank1.get_balance(&vote_id), + commission: Some(0), + } + ), + ( + stake_id, + RewardInfo { + reward_type: RewardType::Staking, + lamports: validator_rewards as i64, + post_balance: bank1.get_balance(&stake_id), + commission: Some(0), + } + ) + ] ); bank1.freeze(); assert!(bank1.calculate_and_verify_capitalization(true)); @@ -10326,7 +10255,7 @@ pub(crate) mod tests { let vote_id = solana_sdk::pubkey::new_rand(); let mut vote_account = - vote_state::create_account(&vote_id, &solana_sdk::pubkey::new_rand(), 50, 100); + vote_state::create_account(&vote_id, &solana_sdk::pubkey::new_rand(), 0, 100); let (stake_id1, stake_account1) = crate::stakes::tests::create_stake_account(123, &vote_id); let (stake_id2, stake_account2) = crate::stakes::tests::create_stake_account(456, &vote_id); @@ -10894,6 +10823,7 @@ pub(crate) mod tests { &FeeStructure::default(), true, true, + true, ); let (expected_fee_collected, expected_fee_burned) = @@ -11076,6 +11006,7 @@ pub(crate) mod tests { &FeeStructure::default(), true, true, + true, ); assert_eq!( bank.get_balance(&mint_keypair.pubkey()), @@ -11094,6 +11025,7 @@ pub(crate) mod tests { &FeeStructure::default(), true, true, + true, ); assert_eq!( bank.get_balance(&mint_keypair.pubkey()), @@ -11210,6 +11142,7 @@ pub(crate) mod tests { &FeeStructure::default(), true, true, + true, ) * 2 ) .0 @@ -12498,7 +12431,7 @@ pub(crate) mod tests { let bank0 = Arc::new(new_from_parent(&parent)); let pubkey0 = solana_sdk::pubkey::new_rand(); - let program_id = Pubkey::new(&[2; 32]); + let program_id = Pubkey::from([2; 32]); let account0 = AccountSharedData::new(1, 0, &program_id); bank0.store_account(&pubkey0, &account0); @@ -12683,7 +12616,7 @@ pub(crate) mod tests { let mut bank = Bank::new_for_tests(&genesis_config); fn mock_vote_program_id() -> Pubkey { - Pubkey::new(&[42u8; 32]) + Pubkey::from([42u8; 32]) } fn mock_vote_processor( _first_instruction_account: usize, @@ -13247,7 +13180,7 @@ pub(crate) mod tests { let blockhash = bank.last_blockhash(); bank.store_account(&nonce.pubkey(), &nonce_account); - let ix = system_instruction::assign(&nonce.pubkey(), &Pubkey::new(&[9u8; 32])); + let ix = system_instruction::assign(&nonce.pubkey(), &Pubkey::from([9u8; 32])); let message = Message::new(&[ix], Some(&nonce.pubkey())); let tx = Transaction::new(&[&nonce], message, blockhash); @@ -13941,7 +13874,7 @@ pub(crate) mod tests { let keypair = Keypair::new(); let pubkey0 = solana_sdk::pubkey::new_rand(); let pubkey1 = solana_sdk::pubkey::new_rand(); - let program_id = Pubkey::new(&[2; 32]); + let program_id = Pubkey::from([2; 32]); let keypair_account = AccountSharedData::new(8, 0, &program_id); let account0 = AccountSharedData::new(11, 0, &program_id); let program_account = AccountSharedData::new(1, 10, &Pubkey::default()); @@ -14089,7 +14022,7 @@ pub(crate) mod tests { Ok(()) } - let mock_program_id = Pubkey::new(&[2u8; 32]); + let mock_program_id = Pubkey::from([2u8; 32]); bank.add_builtin("mock_program", &mock_program_id, mock_process_instruction); let from_pubkey = solana_sdk::pubkey::new_rand(); @@ -14133,7 +14066,7 @@ pub(crate) mod tests { Ok(()) } - let mock_program_id = Pubkey::new(&[2u8; 32]); + let mock_program_id = Pubkey::from([2u8; 32]); bank.add_builtin("mock_program", &mock_program_id, mock_process_instruction); let from_pubkey = solana_sdk::pubkey::new_rand(); @@ -14290,7 +14223,8 @@ pub(crate) mod tests { bank.last_blockhash(), ); - while tx.message.account_keys.len() <= MAX_TX_ACCOUNT_LOCKS { + let transaction_account_lock_limit = bank.get_transaction_account_lock_limit(); + while tx.message.account_keys.len() <= transaction_account_lock_limit { tx.message.account_keys.push(solana_sdk::pubkey::new_rand()); } @@ -14552,7 +14486,7 @@ pub(crate) mod tests { let mut genesis_config = GenesisConfig::new( &[( - Pubkey::new(&[42; 32]), + Pubkey::from([42; 32]), AccountSharedData::new(1_000_000_000_000, 0, &system_program::id()), )], &[], @@ -14669,8 +14603,8 @@ pub(crate) mod tests { solana_logger::setup(); let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000_000); - let pubkey0 = Pubkey::new(&[0; 32]); - let pubkey1 = Pubkey::new(&[1; 32]); + let pubkey0 = Pubkey::from([0; 32]); + let pubkey1 = Pubkey::from([1; 32]); info!("pubkey0: {}", pubkey0); info!("pubkey1: {}", pubkey1); @@ -15281,244 +15215,6 @@ pub(crate) mod tests { } } - #[test] - fn test_cached_executors() { - let key1 = solana_sdk::pubkey::new_rand(); - let key2 = solana_sdk::pubkey::new_rand(); - let key3 = solana_sdk::pubkey::new_rand(); - let key4 = solana_sdk::pubkey::new_rand(); - let executor: Arc = Arc::new(TestExecutor {}); - let mut cache = CachedExecutors::new(3, 0); - - cache.put(&[(&key1, executor.clone())]); - cache.put(&[(&key2, executor.clone())]); - cache.put(&[(&key3, executor.clone())]); - assert!(cache.get(&key1).is_some()); - assert!(cache.get(&key2).is_some()); - assert!(cache.get(&key3).is_some()); - - assert!(cache.get(&key1).is_some()); - assert!(cache.get(&key1).is_some()); - assert!(cache.get(&key2).is_some()); - cache.put(&[(&key4, executor.clone())]); - assert!(cache.get(&key4).is_some()); - let num_retained = [&key1, &key2, &key3] - .iter() - .filter_map(|key| cache.get(key)) - .count(); - assert_eq!(num_retained, 2); - - assert!(cache.get(&key4).is_some()); - assert!(cache.get(&key4).is_some()); - assert!(cache.get(&key4).is_some()); - cache.put(&[(&key3, executor.clone())]); - assert!(cache.get(&key3).is_some()); - let num_retained = [&key1, &key2, &key4] - .iter() - .filter_map(|key| cache.get(key)) - .count(); - assert_eq!(num_retained, 2); - } - - #[test] - fn test_cached_executor_eviction() { - let key1 = solana_sdk::pubkey::new_rand(); - let key2 = solana_sdk::pubkey::new_rand(); - let key3 = solana_sdk::pubkey::new_rand(); - let key4 = solana_sdk::pubkey::new_rand(); - let executor: Arc = Arc::new(TestExecutor {}); - let mut cache = CachedExecutors::new(3, 0); - assert!(cache.current_epoch == 0); - - cache.put(&[(&key1, executor.clone())]); - cache.put(&[(&key2, executor.clone())]); - cache.put(&[(&key3, executor.clone())]); - assert!(cache.get(&key1).is_some()); - assert!(cache.get(&key1).is_some()); - assert!(cache.get(&key1).is_some()); - - let mut cache = CachedExecutors::new_from_parent_bank_executors(&cache, 1); - assert!(cache.current_epoch == 1); - - assert!(cache.get(&key2).is_some()); - assert!(cache.get(&key2).is_some()); - assert!(cache.get(&key3).is_some()); - cache.put(&[(&key4, executor.clone())]); - - assert!(cache.get(&key4).is_some()); - let num_retained = [&key1, &key2, &key3] - .iter() - .filter_map(|key| cache.get(key)) - .count(); - assert_eq!(num_retained, 2); - - cache.put(&[(&key1, executor.clone())]); - cache.put(&[(&key3, executor.clone())]); - assert!(cache.get(&key1).is_some()); - assert!(cache.get(&key3).is_some()); - let num_retained = [&key2, &key4] - .iter() - .filter_map(|key| cache.get(key)) - .count(); - assert_eq!(num_retained, 1); - - cache = CachedExecutors::new_from_parent_bank_executors(&cache, 2); - assert!(cache.current_epoch == 2); - - cache.put(&[(&key3, executor.clone())]); - assert!(cache.get(&key3).is_some()); - } - - #[test] - fn test_cached_executors_evicts_smallest() { - let key1 = solana_sdk::pubkey::new_rand(); - let key2 = solana_sdk::pubkey::new_rand(); - let key3 = solana_sdk::pubkey::new_rand(); - let executor: Arc = Arc::new(TestExecutor {}); - let mut cache = CachedExecutors::new(2, 0); - - cache.put(&[(&key1, executor.clone())]); - for _ in 0..5 { - let _ = cache.get(&key1); - } - cache.put(&[(&key2, executor.clone())]); - // make key1's use-count for sure greater than key2's - let _ = cache.get(&key1); - - let mut entries = cache - .executors - .iter() - .map(|(k, v)| (*k, v.epoch_count.load(Relaxed))) - .collect::>(); - entries.sort_by_key(|(_, v)| *v); - assert!(entries[0].1 < entries[1].1); - - cache.put(&[(&key3, executor.clone())]); - assert!(cache.get(&entries[0].0).is_none()); - assert!(cache.get(&entries[1].0).is_some()); - } - - #[test] - fn test_cached_executors_one_hit_wonder_counter() { - let mut cache = CachedExecutors::new(1, 0); - - let one_hit_wonder = Pubkey::new_unique(); - let popular = Pubkey::new_unique(); - let executor: Arc = Arc::new(TestExecutor {}); - - // make sure we're starting from where we think we are - assert_eq!(cache.stats.one_hit_wonders.load(Relaxed), 0); - - // add our one-hit-wonder - cache.put(&[(&one_hit_wonder, executor.clone())]); - assert_eq!(cache.executors[&one_hit_wonder].hit_count.load(Relaxed), 1); - // displace the one-hit-wonder with "popular program" - cache.put(&[(&popular, executor.clone())]); - assert_eq!(cache.executors[&popular].hit_count.load(Relaxed), 1); - - // one-hit-wonder counter incremented - assert_eq!(cache.stats.one_hit_wonders.load(Relaxed), 1); - - // make "popular program" popular - cache.get(&popular).unwrap(); - assert_eq!(cache.executors[&popular].hit_count.load(Relaxed), 2); - - // evict "popular program" - cache.put(&[(&one_hit_wonder, executor.clone())]); - assert_eq!(cache.executors[&one_hit_wonder].hit_count.load(Relaxed), 1); - - // one-hit-wonder counter not incremented - assert_eq!(cache.stats.one_hit_wonders.load(Relaxed), 1); - } - - #[test] - fn test_cached_executors_stats() { - #[derive(Debug, Default, PartialEq)] - struct ComparableStats { - hits: u64, - misses: u64, - evictions: HashMap, - insertions: u64, - replacements: u64, - one_hit_wonders: u64, - } - impl From<&executor_cache::Stats> for ComparableStats { - fn from(stats: &executor_cache::Stats) -> Self { - let executor_cache::Stats { - hits, - misses, - evictions, - insertions, - replacements, - one_hit_wonders, - } = stats; - ComparableStats { - hits: hits.load(Relaxed), - misses: misses.load(Relaxed), - evictions: evictions.clone(), - insertions: insertions.load(Relaxed), - replacements: replacements.load(Relaxed), - one_hit_wonders: one_hit_wonders.load(Relaxed), - } - } - } - - const CURRENT_EPOCH: Epoch = 0; - let mut cache = CachedExecutors::new(2, CURRENT_EPOCH); - let mut expected_stats = ComparableStats::default(); - - let program_id1 = Pubkey::new_unique(); - let program_id2 = Pubkey::new_unique(); - let executor: Arc = Arc::new(TestExecutor {}); - - // make sure we're starting from where we think we are - assert_eq!(ComparableStats::from(&cache.stats), expected_stats,); - - // insert some executors - cache.put(&[(&program_id1, executor.clone())]); - cache.put(&[(&program_id2, executor.clone())]); - expected_stats.insertions += 2; - assert_eq!(ComparableStats::from(&cache.stats), expected_stats); - - // replace a one-hit-wonder executor - cache.put(&[(&program_id1, executor.clone())]); - expected_stats.replacements += 1; - expected_stats.one_hit_wonders += 1; - assert_eq!(ComparableStats::from(&cache.stats), expected_stats); - - // hit some executors - cache.get(&program_id1); - cache.get(&program_id1); - cache.get(&program_id2); - expected_stats.hits += 3; - assert_eq!(ComparableStats::from(&cache.stats), expected_stats); - - // miss an executor - cache.get(&Pubkey::new_unique()); - expected_stats.misses += 1; - assert_eq!(ComparableStats::from(&cache.stats), expected_stats); - - // evict an executor - cache.put(&[(&Pubkey::new_unique(), executor.clone())]); - expected_stats.insertions += 1; - expected_stats.evictions.insert(program_id2, 1); - assert_eq!(ComparableStats::from(&cache.stats), expected_stats); - - // make sure stats are cleared in new_from_parent - assert_eq!( - ComparableStats::from( - &CachedExecutors::new_from_parent_bank_executors(&cache, CURRENT_EPOCH).stats - ), - ComparableStats::default() - ); - assert_eq!( - ComparableStats::from( - &CachedExecutors::new_from_parent_bank_executors(&cache, CURRENT_EPOCH + 1).stats - ), - ComparableStats::default() - ); - } - #[test] fn test_bank_executor_cache() { solana_logger::setup(); @@ -16863,7 +16559,7 @@ pub(crate) mod tests { let GenesisConfigInfo { genesis_config, .. } = create_genesis_config_with_vote_accounts( 1_000_000_000, &validator_keypairs, - vec![10_000; 2], + vec![LAMPORTS_PER_SOL; 2], ); let bank = Arc::new(Bank::new_for_tests(&genesis_config)); let vote_and_stake_accounts = @@ -16910,7 +16606,7 @@ pub(crate) mod tests { load_vote_and_stake_accounts(&bank).vote_with_stake_delegations_map; assert_eq!( vote_and_stake_accounts.len(), - if check_owner_change { 0 } else { 1 } + usize::from(!check_owner_change) ); } @@ -17082,12 +16778,12 @@ pub(crate) mod tests { ); let mut bank = Bank::new_for_tests(&genesis_config); - let mock_program_id = Pubkey::new(&[2u8; 32]); + let mock_program_id = Pubkey::from([2u8; 32]); fn mock_process_instruction( _first_instruction_account: usize, invoke_context: &mut InvokeContext, ) -> result::Result<(), InstructionError> { - let mock_program_id = Pubkey::new(&[2u8; 32]); + let mock_program_id = Pubkey::from([2u8; 32]); let transaction_context = &mut invoke_context.transaction_context; let instruction_context = transaction_context.get_current_instruction_context()?; let instruction_data = instruction_context.get_instruction_data(); @@ -17810,6 +17506,7 @@ pub(crate) mod tests { }, true, true, + true, ), 0 ); @@ -17825,6 +17522,7 @@ pub(crate) mod tests { }, true, true, + true, ), 1 ); @@ -17845,6 +17543,7 @@ pub(crate) mod tests { }, true, true, + true, ), 4 ); @@ -17864,7 +17563,7 @@ pub(crate) mod tests { let message = SanitizedMessage::try_from(Message::new(&[], Some(&Pubkey::new_unique()))).unwrap(); assert_eq!( - Bank::calculate_fee(&message, 1, &fee_structure, true, true,), + Bank::calculate_fee(&message, 1, &fee_structure, true, true, true), max_fee + lamports_per_signature ); @@ -17876,7 +17575,7 @@ pub(crate) mod tests { SanitizedMessage::try_from(Message::new(&[ix0, ix1], Some(&Pubkey::new_unique()))) .unwrap(); assert_eq!( - Bank::calculate_fee(&message, 1, &fee_structure, true, true,), + Bank::calculate_fee(&message, 1, &fee_structure, true, true, true), max_fee + 3 * lamports_per_signature ); @@ -17909,7 +17608,7 @@ pub(crate) mod tests { Some(&Pubkey::new_unique()), )) .unwrap(); - let fee = Bank::calculate_fee(&message, 1, &fee_structure, true, true); + let fee = Bank::calculate_fee(&message, 1, &fee_structure, true, true, true); assert_eq!( fee, lamports_per_signature + prioritization_fee_details.get_fee() @@ -17948,7 +17647,7 @@ pub(crate) mod tests { )) .unwrap(); assert_eq!( - Bank::calculate_fee(&message, 1, &fee_structure, true, true,), + Bank::calculate_fee(&message, 1, &fee_structure, true, true, true), 2 ); @@ -17960,7 +17659,7 @@ pub(crate) mod tests { )) .unwrap(); assert_eq!( - Bank::calculate_fee(&message, 1, &fee_structure, true, true,), + Bank::calculate_fee(&message, 1, &fee_structure, true, true, true), 11 ); } @@ -18130,26 +17829,6 @@ pub(crate) mod tests { ); } - #[test] - fn test_executor_cache_get_primer_count_upper_bound_inclusive() { - let pubkey = Pubkey::default(); - let v = []; - assert_eq!( - CachedExecutors::get_primer_count_upper_bound_inclusive(&v), - 0 - ); - let v = [(&pubkey, 1)]; - assert_eq!( - CachedExecutors::get_primer_count_upper_bound_inclusive(&v), - 1 - ); - let v = (0u64..10).map(|i| (&pubkey, i)).collect::>(); - assert_eq!( - CachedExecutors::get_primer_count_upper_bound_inclusive(v.as_slice()), - 7 - ); - } - #[derive(Serialize, Deserialize)] enum MockTransferInstruction { Transfer(u64), @@ -18941,7 +18620,7 @@ pub(crate) mod tests { assert!(bank.rewrites_skipped_this_slot.read().unwrap().is_empty()); // bank's map is initially empty - let mut test = vec![(Pubkey::new(&[4; 32]), Hash::new(&[5; 32]))]; + let mut test = vec![(Pubkey::from([4; 32]), Hash::new(&[5; 32]))]; bank.remember_skipped_rewrites(test.clone()); assert_eq!( *bank.rewrites_skipped_this_slot.read().unwrap(), @@ -18949,7 +18628,7 @@ pub(crate) mod tests { ); // now there is already some stuff in the bank's map - test.push((Pubkey::new(&[6; 32]), Hash::new(&[7; 32]))); + test.push((Pubkey::from([6; 32]), Hash::new(&[7; 32]))); bank.remember_skipped_rewrites(test[1..].to_vec()); assert_eq!( *bank.rewrites_skipped_this_slot.read().unwrap(), @@ -19466,8 +19145,8 @@ pub(crate) mod tests { assert!(bank.get_rent_paying_pubkeys(&(0, 2, n)).is_none()); assert!(bank.get_rent_paying_pubkeys(&(0, 0, n)).is_none()); - let pk1 = Pubkey::new(&[2; 32]); - let pk2 = Pubkey::new(&[3; 32]); + let pk1 = Pubkey::from([2; 32]); + let pk2 = Pubkey::from([3; 32]); let index1 = Bank::partition_from_pubkey(&pk1, n); let index2 = Bank::partition_from_pubkey(&pk2, n); assert!(index1 > 0, "{}", index1); @@ -19624,4 +19303,58 @@ pub(crate) mod tests { ); } } + + #[test] + fn test_calculate_fee_with_request_heap_frame_flag() { + let key0 = Pubkey::new_unique(); + let key1 = Pubkey::new_unique(); + let lamports_per_signature: u64 = 5_000; + let signature_fee: u64 = 10; + let request_cu: u64 = 1; + let lamports_per_cu: u64 = 5; + let fee_structure = FeeStructure { + lamports_per_signature: signature_fee, + ..FeeStructure::default() + }; + let message = SanitizedMessage::try_from(Message::new( + &[ + system_instruction::transfer(&key0, &key1, 1), + ComputeBudgetInstruction::set_compute_unit_limit(request_cu as u32), + ComputeBudgetInstruction::request_heap_frame(40 * 1024), + ComputeBudgetInstruction::set_compute_unit_price(lamports_per_cu * 1_000_000), + ], + Some(&key0), + )) + .unwrap(); + + // assert when enable_request_heap_frame_ix is enabled, prioritization fee will be counted + // into transaction fee + let mut enable_request_heap_frame_ix = true; + assert_eq!( + Bank::calculate_fee( + &message, + lamports_per_signature, + &fee_structure, + true, + true, + enable_request_heap_frame_ix, + ), + signature_fee + request_cu * lamports_per_cu + ); + + // assert when enable_request_heap_frame_ix is disabled (an v1.13 behavior), prioritization fee will not be counted + // into transaction fee + enable_request_heap_frame_ix = false; + assert_eq!( + Bank::calculate_fee( + &message, + lamports_per_signature, + &fee_structure, + true, + true, + enable_request_heap_frame_ix, + ), + signature_fee + ); + } } diff --git a/runtime/src/bank/address_lookup_table.rs b/runtime/src/bank/address_lookup_table.rs index 3916177afd5668..7da3b125491a19 100644 --- a/runtime/src/bank/address_lookup_table.rs +++ b/runtime/src/bank/address_lookup_table.rs @@ -1,9 +1,14 @@ use { super::Bank, + crate::accounts_db::LoadZeroLamports, solana_address_lookup_table_program::error::AddressLookupError, solana_sdk::{ - message::v0::{LoadedAddresses, MessageAddressTableLookup}, - transaction::{AddressLoader, Result as TransactionResult, TransactionError}, + feature_set::return_none_for_zero_lamport_accounts, + message::{ + v0::{LoadedAddresses, MessageAddressTableLookup}, + AddressLoaderError, + }, + transaction::AddressLoader, }, }; @@ -11,17 +16,26 @@ impl AddressLoader for &Bank { fn load_addresses( self, address_table_lookups: &[MessageAddressTableLookup], - ) -> TransactionResult { + ) -> Result { if !self.versioned_tx_message_enabled() { - return Err(TransactionError::UnsupportedVersion); + return Err(AddressLoaderError::Disabled); } + let load_zero_lamports = if self + .feature_set + .is_active(&return_none_for_zero_lamport_accounts::id()) + { + LoadZeroLamports::None + } else { + LoadZeroLamports::SomeWithZeroLamportAccount + }; + let slot_hashes = self .sysvar_cache .read() .unwrap() .get_slot_hashes() - .map_err(|_| TransactionError::AccountNotFound)?; + .map_err(|_| AddressLoaderError::SlotHashesSysvarNotFound)?; Ok(address_table_lookups .iter() @@ -30,6 +44,7 @@ impl AddressLoader for &Bank { &self.ancestors, address_table_lookup, &slot_hashes, + load_zero_lamports, ) }) .collect::>()?) diff --git a/runtime/src/bank_client.rs b/runtime/src/bank_client.rs index d9eb457126faab..c1cedf62065ba9 100644 --- a/runtime/src/bank_client.rs +++ b/runtime/src/bank_client.rs @@ -312,7 +312,7 @@ impl BankClient { let thread_bank = bank.clone(); let bank = bank.clone(); Builder::new() - .name("solana-bank-client".to_string()) + .name("solBankClient".to_string()) .spawn(move || Self::run(&thread_bank, transaction_receiver)) .unwrap(); Self { diff --git a/runtime/src/bank_utils.rs b/runtime/src/bank_utils.rs index 80246f75047875..ba9d20d2e6166c 100644 --- a/runtime/src/bank_utils.rs +++ b/runtime/src/bank_utils.rs @@ -43,8 +43,8 @@ pub fn find_and_send_votes( sanitized_txs .iter() .zip(execution_results.iter()) - .for_each(|(tx, _result)| { - if tx.is_simple_vote_transaction() { + .for_each(|(tx, result)| { + if tx.is_simple_vote_transaction() && result.was_executed_successfully() { if let Some(parsed_vote) = vote_parser::parse_sanitized_vote_transaction(tx) { if parsed_vote.1.last_voted_slot().is_some() { let _ = vote_sender.send(parsed_vote); diff --git a/runtime/src/cost_tracker.rs b/runtime/src/cost_tracker.rs index a1d779a8a581b3..518ac8fb759df0 100644 --- a/runtime/src/cost_tracker.rs +++ b/runtime/src/cost_tracker.rs @@ -164,16 +164,11 @@ impl CostTracker { } fn find_costliest_account(&self) -> (Pubkey, u64) { - let mut costliest_account = Pubkey::default(); - let mut costliest_account_cost = 0; - for (key, cost) in self.cost_by_writable_accounts.iter() { - if *cost > costliest_account_cost { - costliest_account = *key; - costliest_account_cost = *cost; - } - } - - (costliest_account, costliest_account_cost) + self.cost_by_writable_accounts + .iter() + .max_by_key(|(_, &cost)| cost) + .map(|(&pubkey, &cost)| (pubkey, cost)) + .unwrap_or_default() } fn would_fit(&self, tx_cost: &TransactionCost) -> Result<(), CostTrackerError> { @@ -315,9 +310,9 @@ impl CostTracker { /// count number of none-zero CU accounts fn number_of_accounts(&self) -> usize { self.cost_by_writable_accounts - .iter() - .map(|(_key, units)| if *units > 0 { 1 } else { 0 }) - .sum() + .values() + .filter(|units| **units > 0) + .count() } } diff --git a/runtime/src/execute_cost_table.rs b/runtime/src/execute_cost_table.rs index 922d31c1c0ed7e..0fc76974a204a2 100644 --- a/runtime/src/execute_cost_table.rs +++ b/runtime/src/execute_cost_table.rs @@ -54,7 +54,7 @@ impl ExecuteCostTable { if self.table.is_empty() { self.get_default_compute_unit_limit() } else { - self.table.iter().map(|(_, value)| value).sum::() / self.get_count() as u64 + self.table.values().sum::() / self.get_count() as u64 } } diff --git a/runtime/src/expected_rent_collection.rs b/runtime/src/expected_rent_collection.rs deleted file mode 100644 index d049430933db33..00000000000000 --- a/runtime/src/expected_rent_collection.rs +++ /dev/null @@ -1,1308 +0,0 @@ -//! Logic for determining when rent should have been collected or a rewrite would have occurred for an account. -use { - crate::{ - accounts_db::AccountsDb, - accounts_hash::HashStats, - bank::{Bank, PartitionIndex, Rewrites}, - rent_collector::{RentCollector, RentResult}, - }, - solana_sdk::{ - account::{AccountSharedData, ReadableAccount, WritableAccount}, - clock::{Epoch, Slot}, - epoch_schedule::EpochSchedule, - hash::Hash, - pubkey::Pubkey, - }, - std::sync::atomic::Ordering, -}; - -#[derive(Debug, PartialEq, Eq)] -pub struct ExpectedRentCollection { - partition_from_pubkey: PartitionIndex, - epoch_of_max_storage_slot: Epoch, - partition_index_from_max_slot: PartitionIndex, - first_slot_in_max_epoch: Slot, - // these are the only 2 fields used by downstream calculations at the moment. - // the rest are here for debugging - expected_rent_collection_slot_max_epoch: Slot, - rent_epoch: Epoch, -} - -/* -A goal is to skip rewrites to improve performance. -Reasons this file exists: -A. When we encounter skipped-rewrite account data as part of a load, we may need to update rent_epoch. -B. When we encounter skipped-rewrite account data during accounts hash calc, the saved hash will be incorrect if we skipped a rewrite. - We need slot and rent_epoch to recalculate a new hash. - -cases of rent collection: - -setup assumptions: -pubkey = abc -slots_per_epoch = 432,000 -pubkey_partition_index of 'abc' = 80 - -So, rent will be collected or a rewrite is expected to occur: - each time a slot's pubkey_partition is == [footnote1] pubkey_partition_index within an epoch. [footnote2] - - If we skip rewrites, then pubkey's account data will not be rewritten when its rent collecion partition index occurs. - However, later we need to get a hash value for the most recent update to this account. - That leads us to the purpose of this file. - To calculate a hash for account data, we need to know: - 1. the slot the account was written in - 2. the rent_epoch field of the account, telling us which epoch is the next time we should evaluate rent on this account - If we did not perform a rewrite, then the account in the append vec it was last written in has: - 1. The wrong slot, since the append vec is not associated with the slot that the rewrite would have occurred. - 2. The wrong rent_epoch since the account was not rewritten with a newer rent_epoch [footnote3] - -Reason A for this file's existence: -When we encounter the skipped-rewrite account data as part of a load, we may need to update rent_epoch. -Many operations work like this: - 1. read account - 2. add lamports (ex: reward was paid) - 3. write account -If the account is written with the WRONG rent_epoch field, then we will store an 'incorrect' rent_epoch field and the hash will be incorrect. -So, at (1. read account), we must FIX the rent_epoch to be as it would be if the rewrite would have occurred. - -Reason B for this file's existence: -When we encounter the skipped-rewrite account data during accounts hash calc, the saved hash will be incorrect if we skipped a rewrite. -We must compute the correct rent_epoch and slot and recompute the hash that we would have gotten if we would have done the rewrite. - -Both reasons result in the need for the same answer. -Given -1. pubkey -2. pubkey's account data -3. the slot the data was loaded from (storage_slot) -4. the highest_root caller cares about - -We also need a RentCollector and EpochSchedule for the highest root the caller cares about. -With these: -1. can calculate partition_index of -1.a. highest_root (slot) -1.b. storage_slot - -We also need : -fn find_unskipped_slot(slot) -> root -which can return the lowest root >= slot - 1 (this is why 'historical_roots' is necessary) Also see [footnote1]. - -Given these inputs, we can determine the correct slot and rent_epoch where we SHOULD have found this account's data and also compute the hash that we SHOULD have stored at that slot. -Note that a slot could be (-432k..infinite) slots and (-1..infinite) epochs relative to the expected rent collection slot. -Examples: -1. -storage_slot: 1 -highest_root: 1 -since pubkey_partition_index is 80 and hasn't been reached, the current account's data is CORRECT -Every highest_root < 80 is this same result. - -2. -storage_slot: 1 -highest_root: 79 -since pubkey_partition_index is 80 and hasn't been reached, the current account's data is CORRECT - -3. -storage_slot: 1 (partition index = 1) -highest_root: 80 (partition index = 80) -since pubkey_partition_index is 80 and it HAS been reached, but the account data is from slot 1, then the account's data is INcorrect -the account's hash is incorrect and needs to be recalculated as of slot 80 -rent_epoch will be 0 -Every highest_root >= 80 and < 432k + 80 is this same result - -4. -storage_slot: 1 (partition index = 1) -find_unskipped_slot(80) returns 81 since slot 80 was SKIPPED -highest_root: 81 (partition index = 81) -since pubkey_partition_index is 80 and it HAS been reached, but the account data is from slot 1, then the account's data is INcorrect -the account's hash is incorrect and needs to be recalculated as of slot 81 (note 81 because 80 was skipped) -rent_epoch will be 0 -Every highest_root >= 80 and < 432k + 80 is this same result - -5. -storage_slot: 1 (partition index = 1) (epoch 0) -highest_root: 432k + 1 (partition index = 1) (epoch 1) -since pubkey_partition_index is 80 and it HAS been reached, but the account data is from slot 1, then the account's data is INcorrect -the account's hash is incorrect and needs to be recalculated as of slot 80 in epoch 0 -partition_index 80 has NOT been reached in epoch 1 -so, rent_epoch will be 0 -Every highest_root >= 80 and < 432k + 80 is this same result - -6. -storage_slot: 1 (partition index = 1) (epoch 0) -highest_root: 432k + 80 (partition index = 80) (epoch 1) -since pubkey_partition_index is 80 and it HAS been reached, but the account data is from slot 1, then the account's data is INcorrect -the account's hash is incorrect and needs to be recalculated as of slot 432k + 80 in epoch 1 -partition_index 80 HAS been reached in epoch 1 -so, rent_epoch will be 1 -Every highest_root >= 432k + 80 and < 432k * 2 + 80 is this same result - -7. -storage_slot: 1 (partition index = 1) (epoch 0) -find_unskipped_slot(432k + 80) returns 432k + 81 since slot 432k + 80 was SKIPPED -highest_root: 432k + 81 (partition index = 81) (epoch 1) -since pubkey_partition_index is 80 and it HAS been reached, but the account data is from slot 1, then the account's data is INcorrect -the account's hash is incorrect and needs to be recalculated as of slot 432k + 81 in epoch 1 (slot 432k + 80 was skipped) -partition_index 80 HAS been reached in epoch 1 -so, rent_epoch will be 1 -Every highest_root >= 432k + 81 and < 432k * 2 + 80 is this same result - -8. -storage_slot: 1 (partition index = 1) (epoch 0) -highest_root: 432k * 2 + 1 (partition index = 1) (epoch 2) -since pubkey_partition_index is 80 and it HAS been reached, but the account data is from slot 1, then the account's data is INcorrect -the account's hash is incorrect and needs to be recalculated as of slot 432k + 80 in epoch 1 -partition_index 80 has NOT been reached in epoch 2 -so, rent_epoch will 1 -Every highest_root >= 432k + 80 and < 432k * 2 + 80 is this same result - -9. -storage_slot: 1 (partition index = 1) (epoch 0) -highest_root: 432k * 2 + 80 (partition index = 80) (epoch 2) -since pubkey_partition_index is 80 and it HAS been reached, but the account data is from slot 1, then the account's data is INcorrect -the account's hash is incorrect and needs to be recalculated as of slot 432k * 2 + 80 in epoch 2 -partition_index 80 HAS been reached in epoch 2 -so, rent_epoch will be 2 -Every highest_root >= 432k * 2 + 80 and < 432k * 3 + 80 is this same result - -[footnote1:] - "each time a slot's pubkey_partition is == [footnote1] pubkey_partition_index within an epoch." - Due to skipped slots, this is not true. - In reality, it is: - the first slot where a slot's pubkey_partition >= pubkey_partition_index - 1. - So, simply, if the pubkey_partition for our rent is 80, then that slot occurs at these slot #s: - Slot:........... Epoch: - 0 + 80 for epoch 0 - 432,000 + 80 for epoch 1 - 432,000 * 2 + 80 for epoch 2 - ... - However, sometimes we skip slots. So, just considering epoch 0: - Normal, not skipping any slots: - slot 78 is a root - slot 79 is a root - slot 80 is a root (account is rewritten/rent collected here) - Scenario 1: - slot 78 is a root - slot 79 is skipped/not a root - slot 80 is a root (account is rewritten/rent collected here along with accounts from slot 79) - Scenario 2: - slot 78 is a root - slot 79 is skipped/not a root - slot 80 is skipped/not a root - slot 81 is a root (account is rewritten/rent collected here because of slot 80, along with accounts from slots 79 and 81) - This gets us to looking for: - the first slot where a slot's pubkey_partition >= pubkey_partition_index - 1. - -[footnote2:] - Describing partition_index within an epoch: - example: - slot=0 is epoch=0, partition_index=0 - slot=1 is epoch=0, partition_index=1 - slot=431,999 is epoch=0, partition_index=431,999 - slot=432,000 is epoch=1, partition_index=432,000 - This is NOT technically accurate because of first_normal_slot, but we'll ignore that. - EpochSchedule::get_epoch_and_slot_index(slot) calculates epoch and partition_index from slot. - -[footnote3:] - when we do a rewrite of account data, only this data changes: - 1. rent_epoch - 2. computed hash value (which is a function of (data, lamports, executable, owner, rent_epoch, pubkey) + slot - 3. into a new append vec that is associated with the slot# - -*/ - -/// specify a slot -/// and keep track of epoch info for that slot -/// The idea is that algorithms often iterate over a storage slot or over a max/bank slot. -/// The epoch info for that slot will be fixed for many pubkeys, so save the calculated results. -/// There are 2 slots required for rent collection algorithms. Some callers have storage slot fixed -/// while others have max/bank slot fixed. 'epoch_info' isn't always needed, so it is optionally -/// specified by caller and only used by callee if necessary. -#[derive(Default, Copy, Clone)] -pub struct SlotInfoInEpoch { - /// the slot - slot: Slot, - /// possible info about this slot - epoch_info: Option, -} - -/// epoch info for a slot -#[derive(Default, Copy, Clone)] -pub struct SlotInfoInEpochInner { - /// epoch of the slot - epoch: Epoch, - /// partition index of the slot within the epoch - partition_index: PartitionIndex, - /// number of slots in this epoch - slots_in_epoch: Slot, -} - -impl SlotInfoInEpoch { - /// create, populating epoch info - pub fn new(slot: Slot, epoch_schedule: &EpochSchedule) -> Self { - let mut result = Self::new_small(slot); - result.epoch_info = Some(result.get_epoch_info(epoch_schedule)); - result - } - /// create, without populating epoch info - pub fn new_small(slot: Slot) -> Self { - SlotInfoInEpoch { - slot, - ..SlotInfoInEpoch::default() - } - } - /// get epoch info by returning already calculated or by calculating it now - pub fn get_epoch_info(&self, epoch_schedule: &EpochSchedule) -> SlotInfoInEpochInner { - if let Some(inner) = &self.epoch_info { - *inner - } else { - let (epoch, partition_index) = epoch_schedule.get_epoch_and_slot_index(self.slot); - SlotInfoInEpochInner { - epoch, - partition_index, - slots_in_epoch: epoch_schedule.get_slots_in_epoch(epoch), - } - } - } -} - -impl ExpectedRentCollection { - /// 'account' is being loaded from 'storage_slot' in 'bank_slot' - /// adjusts 'account.rent_epoch' if we skipped the last rewrite on this account - pub(crate) fn maybe_update_rent_epoch_on_load( - account: &mut AccountSharedData, - storage_slot: &SlotInfoInEpoch, - bank_slot: &SlotInfoInEpoch, - epoch_schedule: &EpochSchedule, - rent_collector: &RentCollector, - pubkey: &Pubkey, - rewrites_skipped_this_slot: &Rewrites, - ) { - let result = Self::get_corrected_rent_epoch_on_load( - account, - storage_slot, - bank_slot, - epoch_schedule, - rent_collector, - pubkey, - rewrites_skipped_this_slot, - ); - if let Some(rent_epoch) = result { - account.set_rent_epoch(rent_epoch); - } - } - - /// 'account' is being loaded - /// we may need to adjust 'account.rent_epoch' if we skipped the last rewrite on this account - /// returns Some(rent_epoch) if an adjustment needs to be made - /// returns None if the account is up to date - fn get_corrected_rent_epoch_on_load( - account: &AccountSharedData, - storage_slot: &SlotInfoInEpoch, - bank_slot: &SlotInfoInEpoch, - epoch_schedule: &EpochSchedule, - rent_collector: &RentCollector, - pubkey: &Pubkey, - rewrites_skipped_this_slot: &Rewrites, - ) -> Option { - let next_epoch = match rent_collector.calculate_rent_result( - pubkey, account, None, // filler_account_suffix - // Skipping rewrites is not compatible with the below feature. - // We will not skip rewrites until the feature is activated. - false, // preserve_rent_epoch_for_rent_exempt_accounts - ) { - RentResult::LeaveAloneNoRent => return None, - RentResult::CollectRent { - new_rent_epoch, - rent_due: 0, - } => new_rent_epoch, - // Rent is due on this account in this epoch, - // so we did not skip a rewrite. - RentResult::CollectRent { .. } => return None, - }; - { - // grab epoch infno for bank slot and storage slot - let bank_info = bank_slot.get_epoch_info(epoch_schedule); - let (current_epoch, partition_from_current_slot) = - (bank_info.epoch, bank_info.partition_index); - let storage_info = storage_slot.get_epoch_info(epoch_schedule); - let (storage_epoch, storage_slot_partition) = - (storage_info.epoch, storage_info.partition_index); - let partition_from_pubkey = - Bank::partition_from_pubkey(pubkey, bank_info.slots_in_epoch); - let mut possibly_update = true; - if current_epoch == storage_epoch { - // storage is in same epoch as bank - if partition_from_pubkey > partition_from_current_slot { - // we haven't hit the slot's rent collection slot yet, and the storage was within this slot, so do not update - possibly_update = false; - } - } else if current_epoch == storage_epoch + 1 { - // storage is in the previous epoch - if storage_slot_partition >= partition_from_pubkey - && partition_from_pubkey > partition_from_current_slot - { - // we did a rewrite in last epoch and we have not yet hit the rent collection slot in THIS epoch - possibly_update = false; - } - } // if more than 1 epoch old, then we need to collect rent because we clearly skipped it. - - let rewrites_skipped_this_pubkey_this_slot = || { - rewrites_skipped_this_slot - .read() - .unwrap() - .contains_key(pubkey) - }; - let rent_epoch = account.rent_epoch(); - if possibly_update && rent_epoch == 0 && current_epoch > 1 { - if rewrites_skipped_this_pubkey_this_slot() { - return Some(next_epoch); - } else { - // we know we're done - return None; - } - } - - // if an account was written >= its rent collection slot within the last epoch worth of slots, then we don't want to update it here - if possibly_update && rent_epoch < current_epoch { - let new_rent_epoch = if partition_from_pubkey < partition_from_current_slot - || (partition_from_pubkey == partition_from_current_slot - && rewrites_skipped_this_pubkey_this_slot()) - { - // partition_from_pubkey < partition_from_current_slot: - // we already would have done a rewrite on this account IN this epoch - next_epoch - } else { - // should have done rewrite up to last epoch - // we have not passed THIS epoch's rewrite slot yet, so the correct 'rent_epoch' is previous - next_epoch.saturating_sub(1) - }; - if rent_epoch != new_rent_epoch { - // the point of this function: - // 'new_rent_epoch' is the correct rent_epoch that the account would have if we had done rewrites - return Some(new_rent_epoch); - } - } else if !possibly_update { - // This is a non-trivial lookup. Would be nice to skip this. - assert!(!rewrites_skipped_this_pubkey_this_slot(), "did not update rent_epoch: {}, new value for rent_epoch: {}, old: {}, current epoch: {}", pubkey, rent_epoch, next_epoch, current_epoch); - } - } - None - } - - #[allow(clippy::too_many_arguments)] - /// it is possible 0.. rewrites were skipped on this account - /// if so, return Some(correct hash as of 'storage_slot') - /// if 'loaded_hash' is CORRECT, return None - pub fn maybe_rehash_skipped_rewrite( - loaded_account: &impl ReadableAccount, - loaded_hash: &Hash, - pubkey: &Pubkey, - storage_slot: Slot, - epoch_schedule: &EpochSchedule, - rent_collector: &RentCollector, - stats: &HashStats, - max_slot_in_storages_inclusive: &SlotInfoInEpoch, - find_unskipped_slot: impl Fn(Slot) -> Option, - filler_account_suffix: Option<&Pubkey>, - ) -> Option { - use solana_measure::measure::Measure; - let mut m = Measure::start("rehash_calc_us"); - let expected = ExpectedRentCollection::new( - pubkey, - loaded_account, - storage_slot, - epoch_schedule, - rent_collector, - max_slot_in_storages_inclusive, - find_unskipped_slot, - filler_account_suffix, - ); - - m.stop(); - stats.rehash_calc_us.fetch_add(m.as_us(), Ordering::Relaxed); - let expected = match expected { - None => { - // use the previously calculated hash - return None; - } - Some(expected) => expected, - }; - let mut m = Measure::start("rehash_hash_us"); - let recalc_hash = AccountsDb::hash_account_with_rent_epoch( - expected.expected_rent_collection_slot_max_epoch, - loaded_account, - pubkey, - expected.rent_epoch, - ); - m.stop(); - stats.rehash_hash_us.fetch_add(m.as_us(), Ordering::Relaxed); - if &recalc_hash == loaded_hash { - // unnecessary calculation occurred - stats.rehash_unnecessary.fetch_add(1, Ordering::Relaxed); - return None; - } - stats.rehash_required.fetch_add(1, Ordering::Relaxed); - - // recomputed based on rent collection/rewrite slot - // Rent would have been collected AT 'expected_rent_collection_slot', so hash according to that slot. - // Note that a later storage (and slot) may contain this same pubkey. In that case, that newer hash will make this one irrelevant. - Some(recalc_hash) - } - - /// figure out whether the account stored at 'storage_slot' would have normally been rewritten at a slot that has already occurred: after 'storage_slot' but <= 'max_slot_in_storages_inclusive' - /// returns Some(...) if the account would have normally been rewritten - /// returns None if the account was updated wrt rent already or if it is known that there must exist a future rewrite of this account (for example, non-zero rent is due) - fn new( - pubkey: &Pubkey, - loaded_account: &impl ReadableAccount, - storage_slot: Slot, - epoch_schedule: &EpochSchedule, - rent_collector_max_epoch: &RentCollector, - max_slot_in_storages_inclusive: &SlotInfoInEpoch, - find_unskipped_slot: impl Fn(Slot) -> Option, - filler_account_suffix: Option<&Pubkey>, - ) -> Option { - let mut rent_collector = rent_collector_max_epoch; - let SlotInfoInEpochInner { - epoch: epoch_of_max_storage_slot, - partition_index: partition_index_from_max_slot, - slots_in_epoch: slots_per_epoch_max_epoch, - } = max_slot_in_storages_inclusive.get_epoch_info(epoch_schedule); - let mut partition_from_pubkey = - crate::bank::Bank::partition_from_pubkey(pubkey, slots_per_epoch_max_epoch); - // now, we have to find the root that is >= the slot where this pubkey's rent would have been collected - let first_slot_in_max_epoch = - max_slot_in_storages_inclusive.slot - partition_index_from_max_slot; - let mut expected_rent_collection_slot_max_epoch = - first_slot_in_max_epoch + partition_from_pubkey; - let calculated_from_index_expected_rent_collection_slot_max_epoch = - expected_rent_collection_slot_max_epoch; - if expected_rent_collection_slot_max_epoch <= max_slot_in_storages_inclusive.slot { - // may need to find a valid root - if let Some(find) = - find_unskipped_slot(calculated_from_index_expected_rent_collection_slot_max_epoch) - { - // found a root that is >= expected_rent_collection_slot. - expected_rent_collection_slot_max_epoch = find; - } - } - let mut use_previous_epoch_rent_collector = false; - if expected_rent_collection_slot_max_epoch > max_slot_in_storages_inclusive.slot { - // max slot has not hit the slot in the max epoch where we would have collected rent yet, so the most recent rent-collected rewrite slot for this pubkey would be in the previous epoch - let previous_epoch = epoch_of_max_storage_slot.saturating_sub(1); - let slots_per_epoch_previous_epoch = epoch_schedule.get_slots_in_epoch(previous_epoch); - expected_rent_collection_slot_max_epoch = - if slots_per_epoch_previous_epoch == slots_per_epoch_max_epoch { - // partition index remains the same - calculated_from_index_expected_rent_collection_slot_max_epoch - .saturating_sub(slots_per_epoch_max_epoch) - } else { - // the newer epoch has a different # of slots, so the partition index will be different in the prior epoch - partition_from_pubkey = crate::bank::Bank::partition_from_pubkey( - pubkey, - slots_per_epoch_previous_epoch, - ); - first_slot_in_max_epoch - .saturating_sub(slots_per_epoch_previous_epoch) - .saturating_add(partition_from_pubkey) - }; - // since we are looking a different root, we have to call this again - if let Some(find) = find_unskipped_slot(expected_rent_collection_slot_max_epoch) { - // found a root (because we have a storage) that is >= expected_rent_collection_slot. - expected_rent_collection_slot_max_epoch = find; - } - - // since we have not hit the slot in the rent collector's epoch yet, we need to collect rent according to the previous epoch's rent collector. - use_previous_epoch_rent_collector = true; - } - - // the slot we're dealing with is where we expected the rent to be collected for this pubkey, so use what is in this slot - // however, there are cases, such as adjusting the clock, where we store the account IN the same slot, but we do so BEFORE we collect rent. We later store the account AGAIN for rewrite/rent collection. - // So, if storage_slot == expected_rent_collection_slot..., then we MAY have collected rent or may not have. So, it has to be > - // rent_epoch=0 is a special case - if storage_slot > expected_rent_collection_slot_max_epoch - || loaded_account.rent_epoch() == 0 - { - // no need to update hash - return None; - } - - let rent_collector_previous; - if use_previous_epoch_rent_collector { - // keep in mind the storage slot could be 0..inf epochs in the past - // we want to swap the rent collector for one whose epoch is the previous epoch - let mut rent_collector_temp = rent_collector.clone(); - rent_collector_temp.epoch = rent_collector.epoch.saturating_sub(1); // previous epoch - rent_collector_previous = Some(rent_collector_temp); - rent_collector = rent_collector_previous.as_ref().unwrap(); - } - - // ask the rent collector what rent should be collected. - // Rent collector knows the current epoch. - let rent_result = rent_collector.calculate_rent_result( - pubkey, - loaded_account, - filler_account_suffix, - // Skipping rewrites is not compatible with the below feature. - // We will not skip rewrites until the feature is activated. - false, // preserve_rent_epoch_for_rent_exempt_accounts - ); - let current_rent_epoch = loaded_account.rent_epoch(); - let new_rent_epoch = match rent_result { - RentResult::CollectRent { - new_rent_epoch: next_epoch, - rent_due, - } => { - if next_epoch > current_rent_epoch && rent_due != 0 { - // this is an account that would have had rent collected since this storage slot, so just use the hash we have since there must be a newer version of this account already in a newer slot - // It would be a waste of time to recalcluate a hash. - return None; - } - std::cmp::max(next_epoch, current_rent_epoch) - } - RentResult::LeaveAloneNoRent => { - // rent_epoch is not updated for this condition - // But, a rewrite WOULD HAVE occured at the expected slot. - // So, fall through with same rent_epoch, but we will have already calculated 'expected_rent_collection_slot_max_epoch' - current_rent_epoch - } - }; - - if expected_rent_collection_slot_max_epoch == storage_slot - && new_rent_epoch == loaded_account.rent_epoch() - { - // no rewrite would have occurred - return None; - } - - Some(Self { - partition_from_pubkey, - epoch_of_max_storage_slot, - partition_index_from_max_slot, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch, - rent_epoch: new_rent_epoch, - }) - } -} - -#[cfg(test)] -pub mod tests { - use { - super::*, - solana_sdk::{ - account::{AccountSharedData, WritableAccount}, - genesis_config::GenesisConfig, - }, - }; - - #[test] - fn test_expected_rent_collection() { - solana_logger::setup(); - let pubkey = Pubkey::new(&[5; 32]); - let owner = solana_sdk::pubkey::new_rand(); - let mut account = AccountSharedData::new(1, 0, &owner); - let max_slot_in_storages_inclusive = 0; - let epoch_schedule = EpochSchedule::default(); - let first_normal_slot = epoch_schedule.first_normal_slot; - let storage_slot = first_normal_slot; - let epoch = epoch_schedule.get_epoch(storage_slot); - assert_eq!( - (epoch, 0), - epoch_schedule.get_epoch_and_slot_index(storage_slot) - ); - let genesis_config = GenesisConfig::default(); - let mut rent_collector = RentCollector::new( - epoch, - epoch_schedule, - genesis_config.slots_per_year(), - genesis_config.rent, - ); - rent_collector.rent.lamports_per_byte_year = 0; // temporarily disable rent - let find_unskipped_slot = Some; - // slot in current epoch - let result = ExpectedRentCollection::new( - &pubkey, - &account, - storage_slot, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - assert!(result.is_none()); - - let slots_per_epoch = 432_000; - assert_eq!( - slots_per_epoch, - epoch_schedule.get_slots_in_epoch(epoch_schedule.get_epoch(storage_slot)) - ); - let partition_index_max_inclusive = slots_per_epoch - 1; - account.set_rent_epoch(rent_collector.epoch); - // several epochs ahead of now - // first slot of new epoch is max slot EXclusive - // so last slot of prior epoch is max slot INclusive - let max_slot_in_storages_inclusive = slots_per_epoch * 3 + first_normal_slot - 1; - rent_collector.epoch = epoch_schedule.get_epoch(max_slot_in_storages_inclusive); - let partition_from_pubkey = 8470; // function of 432k slots and 'pubkey' above - let first_slot_in_max_epoch = 1388256; - let expected_rent_collection_slot_max_epoch = - first_slot_in_max_epoch + partition_from_pubkey; - let result = ExpectedRentCollection::new( - &pubkey, - &account, - storage_slot, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - assert_eq!( - result, - Some(ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch, - partition_index_from_max_slot: partition_index_max_inclusive, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch, - rent_epoch: rent_collector.epoch, - }) - ); - - // LeaveAloneNoRent - for leave_alone in [true, false] { - account.set_executable(leave_alone); - let result = ExpectedRentCollection::new( - &pubkey, - &account, - expected_rent_collection_slot_max_epoch, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - assert_eq!( - result, - (!leave_alone).then(|| ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch, - partition_index_from_max_slot: partition_index_max_inclusive, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch, - rent_epoch: rent_collector.epoch, - }), - "leave_alone: {}", - leave_alone - ); - } - - // storage_slot > expected_rent_collection_slot_max_epoch - // if greater, we return None - for greater in [false, true] { - let result = ExpectedRentCollection::new( - &pubkey, - &account, - expected_rent_collection_slot_max_epoch + if greater { 1 } else { 0 }, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - assert_eq!( - result, - (!greater).then(|| ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch, - partition_index_from_max_slot: partition_index_max_inclusive, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch, - rent_epoch: rent_collector.epoch, - }) - ); - } - - // test rewrite would have occurred in previous epoch from max_slot_in_storages_inclusive's epoch - // the change is in 'rent_epoch' returned in 'expected' - for previous_epoch in [false, true] { - let result = ExpectedRentCollection::new( - &pubkey, - &account, - expected_rent_collection_slot_max_epoch, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new( - max_slot_in_storages_inclusive - + if previous_epoch { slots_per_epoch } else { 0 }, - &epoch_schedule, - ), - find_unskipped_slot, - None, - ); - let epoch_delta = if previous_epoch { 1 } else { 0 }; - let slot_delta = epoch_delta * slots_per_epoch; - assert_eq!( - result, - Some(ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch + epoch_delta, - partition_index_from_max_slot: partition_index_max_inclusive, - first_slot_in_max_epoch: first_slot_in_max_epoch + slot_delta, - expected_rent_collection_slot_max_epoch: expected_rent_collection_slot_max_epoch - + slot_delta, - rent_epoch: rent_collector.epoch, - }), - "previous_epoch: {}", - previous_epoch, - ); - } - - // if account's rent_epoch is already > our rent epoch, rent was collected already - // if greater, we return None - let original_rent_epoch = account.rent_epoch(); - for already_collected in [true, false] { - // to consider: maybe if we already collected rent_epoch IN this slot and slot matches what we need, then we should return None here - account.set_rent_epoch(original_rent_epoch + if already_collected { 1 } else { 0 }); - let result = ExpectedRentCollection::new( - &pubkey, - &account, - expected_rent_collection_slot_max_epoch, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - assert_eq!( - result, - Some(ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch, - partition_index_from_max_slot: partition_index_max_inclusive, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch, - rent_epoch: std::cmp::max(rent_collector.epoch, account.rent_epoch()), - }), - "rent_collector.epoch: {}, already_collected: {}", - rent_collector.epoch, - already_collected - ); - } - account.set_rent_epoch(original_rent_epoch); - - let storage_slot = max_slot_in_storages_inclusive - slots_per_epoch; - // check partition from pubkey code - for end_partition_index in [0, 1, 2, 100, slots_per_epoch - 2, slots_per_epoch - 1] { - // generate a pubkey range - let range = crate::bank::Bank::pubkey_range_from_partition(( - // start_index: - end_partition_index.saturating_sub(1), // this can end up at start=0, end=0 (this is a desired test case) - // end_index: - end_partition_index, - epoch_schedule.get_slots_in_epoch(rent_collector.epoch), - )); - // use both start and end from INclusive range separately - for pubkey in [&range.start(), &range.end()] { - let result = ExpectedRentCollection::new( - pubkey, - &account, - storage_slot, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - assert_eq!( - result, - Some(ExpectedRentCollection { - partition_from_pubkey: end_partition_index, - epoch_of_max_storage_slot: rent_collector.epoch, - partition_index_from_max_slot: partition_index_max_inclusive, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch: first_slot_in_max_epoch + end_partition_index, - rent_epoch: rent_collector.epoch, - }), - "range: {:?}, pubkey: {:?}, end_partition_index: {}, max_slot_in_storages_inclusive: {}", - range, - pubkey, - end_partition_index, - max_slot_in_storages_inclusive, - ); - } - } - - // check max_slot_in_storages_inclusive related code - // so sweep through max_slot_in_storages_inclusive values within an epoch - let first_slot_in_max_epoch = first_normal_slot + slots_per_epoch; - rent_collector.epoch = epoch_schedule.get_epoch(first_slot_in_max_epoch); - // an epoch in the past so we always collect rent - let storage_slot = first_normal_slot; - for partition_index in [ - 0, - 1, - 2, - partition_from_pubkey - 1, - partition_from_pubkey, - partition_from_pubkey + 1, - 100, - slots_per_epoch - 2, - slots_per_epoch - 1, - ] { - // partition_index=0 means first slot of second normal epoch - // second normal epoch because we want to deal with accounts stored in the first normal epoch - // + 1 because of exclusive - let max_slot_in_storages_inclusive = first_slot_in_max_epoch + partition_index; - let result = ExpectedRentCollection::new( - &pubkey, - &account, - storage_slot, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - let partition_index_passed_pubkey = partition_from_pubkey <= partition_index; - let expected_rent_epoch = - rent_collector.epoch - if partition_index_passed_pubkey { 0 } else { 1 }; - let expected_rent_collection_slot_max_epoch = first_slot_in_max_epoch - + partition_from_pubkey - - if partition_index_passed_pubkey { - 0 - } else { - slots_per_epoch - }; - - assert_eq!( - result, - Some(ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch, - partition_index_from_max_slot: partition_index, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch, - rent_epoch: expected_rent_epoch, - }), - "partition_index: {}, max_slot_in_storages_inclusive: {}, storage_slot: {}, first_normal_slot: {}", - partition_index, - max_slot_in_storages_inclusive, - storage_slot, - first_normal_slot, - ); - } - - // test account.rent_epoch = 0 - let first_slot_in_max_epoch = 1388256; - for account_rent_epoch in [0, epoch] { - account.set_rent_epoch(account_rent_epoch); - let result = ExpectedRentCollection::new( - &pubkey, - &account, - storage_slot, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - assert_eq!( - result, - (account_rent_epoch != 0).then(|| ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch + 1, - partition_index_from_max_slot: partition_index_max_inclusive, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch, - rent_epoch: rent_collector.epoch, - }) - ); - } - - // test find_unskipped_slot - for find_unskipped_slot in [ - |_| None, - Some, // identity - |slot| Some(slot + 1), // increment - |_| Some(Slot::MAX), // max - ] { - let test_value = 10; - let find_result = find_unskipped_slot(test_value); - let increment = find_result.unwrap_or_default() == test_value + 1; - let result = ExpectedRentCollection::new( - &pubkey, - &account, - storage_slot, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - // the test case of max is hacky - let prior_epoch = (partition_from_pubkey > partition_index_max_inclusive) - || find_unskipped_slot(0) == Some(Slot::MAX); - assert_eq!( - result, - Some(ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch + 1, - partition_index_from_max_slot: partition_index_max_inclusive, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch: if find_result.unwrap_or_default() - == Slot::MAX - { - Slot::MAX - } else if increment { - expected_rent_collection_slot_max_epoch + 1 - } else { - expected_rent_collection_slot_max_epoch - }, - rent_epoch: rent_collector.epoch - if prior_epoch { 1 } else { 0 }, - }), - "find_unskipped_slot(0): {:?}, rent_collector.epoch: {}, prior_epoch: {}", - find_unskipped_slot(0), - rent_collector.epoch, - prior_epoch, - ); - } - } - - #[test] - fn test_simplified_rent_collection() { - solana_logger::setup(); - let pubkey = Pubkey::new(&[5; 32]); - let owner = solana_sdk::pubkey::new_rand(); - let mut account = AccountSharedData::new(1, 0, &owner); - let mut epoch_schedule = EpochSchedule { - first_normal_epoch: 0, - ..EpochSchedule::default() - }; - epoch_schedule.first_normal_slot = 0; - let first_normal_slot = epoch_schedule.first_normal_slot; - let slots_per_epoch = 432_000; - let partition_from_pubkey = 8470; // function of 432k slots and 'pubkey' above - // start in epoch=1 because of issues at rent_epoch=1 - let storage_slot = first_normal_slot + partition_from_pubkey + slots_per_epoch; - let epoch = epoch_schedule.get_epoch(storage_slot); - assert_eq!( - (epoch, partition_from_pubkey), - epoch_schedule.get_epoch_and_slot_index(storage_slot) - ); - let genesis_config = GenesisConfig::default(); - let mut rent_collector = RentCollector::new( - epoch, - epoch_schedule, - genesis_config.slots_per_year(), - genesis_config.rent, - ); - rent_collector.rent.lamports_per_byte_year = 0; // temporarily disable rent - - assert_eq!( - slots_per_epoch, - epoch_schedule.get_slots_in_epoch(epoch_schedule.get_epoch(storage_slot)) - ); - account.set_rent_epoch(1); // has to be not 0 - - /* - test this: - pubkey_partition_index: 8470 - storage_slot: 8470 - account.rent_epoch: 1 (has to be not 0) - - max_slot: 8469 + 432k * 1 - max_slot: 8470 + 432k * 1 - max_slot: 8471 + 432k * 1 - max_slot: 8472 + 432k * 1 - max_slot: 8469 + 432k * 2 - max_slot: 8470 + 432k * 2 - max_slot: 8471 + 432k * 2 - max_slot: 8472 + 432k * 2 - max_slot: 8469 + 432k * 3 - max_slot: 8470 + 432k * 3 - max_slot: 8471 + 432k * 3 - max_slot: 8472 + 432k * 3 - - one run without skipping slot 8470, once WITH skipping slot 8470 - */ - - for skipped_slot in [false, true] { - let find_unskipped_slot = if skipped_slot { - |slot| Some(slot + 1) - } else { - Some - }; - - // starting at epoch = 0 has issues because of rent_epoch=0 special casing - for epoch in 1..4 { - for partition_index_from_max_slot in - partition_from_pubkey - 1..=partition_from_pubkey + 2 - { - let max_slot_in_storages_inclusive = - slots_per_epoch * epoch + first_normal_slot + partition_index_from_max_slot; - if storage_slot > max_slot_in_storages_inclusive { - continue; // illegal combination - } - rent_collector.epoch = epoch_schedule.get_epoch(max_slot_in_storages_inclusive); - let first_slot_in_max_epoch = max_slot_in_storages_inclusive - - max_slot_in_storages_inclusive % slots_per_epoch; - let skip_offset = if skipped_slot { 1 } else { 0 }; - let mut expected_rent_collection_slot_max_epoch = - first_slot_in_max_epoch + partition_from_pubkey + skip_offset; - let hit_this_epoch = - expected_rent_collection_slot_max_epoch <= max_slot_in_storages_inclusive; - if !hit_this_epoch { - expected_rent_collection_slot_max_epoch -= slots_per_epoch; - } - - assert_eq!( - (epoch, partition_index_from_max_slot), - epoch_schedule.get_epoch_and_slot_index(max_slot_in_storages_inclusive) - ); - assert_eq!( - (epoch, 0), - epoch_schedule.get_epoch_and_slot_index(first_slot_in_max_epoch) - ); - account.set_rent_epoch(1); - let result = ExpectedRentCollection::new( - &pubkey, - &account, - storage_slot, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - let some_expected = if epoch == 1 { - skipped_slot && partition_index_from_max_slot > partition_from_pubkey - } else if epoch == 2 { - partition_index_from_max_slot >= partition_from_pubkey - skip_offset - } else { - true - }; - assert_eq!( - result, - some_expected.then(|| ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch, - partition_index_from_max_slot, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch, - rent_epoch: rent_collector.epoch - if hit_this_epoch { 0 } else {1}, - }), - "partition_index_from_max_slot: {}, epoch: {}, hit_this_epoch: {}, skipped_slot: {}", - partition_index_from_max_slot, - epoch, - hit_this_epoch, - skipped_slot, - ); - - // test RentResult::LeaveAloneNoRent - { - let result = ExpectedRentCollection::new( - &pubkey, - &account, - storage_slot, - &epoch_schedule, - &rent_collector, - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - // treat this pubkey like a filler account so we get a 'LeaveAloneNoRent' result - Some(&pubkey), - ); - assert_eq!( - result, - some_expected.then(|| ExpectedRentCollection { - partition_from_pubkey, - epoch_of_max_storage_slot: rent_collector.epoch, - partition_index_from_max_slot, - first_slot_in_max_epoch, - expected_rent_collection_slot_max_epoch, - // this will not be adjusted for 'LeaveAloneNoRent' - rent_epoch: account.rent_epoch(), - }), - "partition_index_from_max_slot: {}, epoch: {}", - partition_index_from_max_slot, - epoch, - ); - } - - // test maybe_rehash_skipped_rewrite - let hash = AccountsDb::hash_account(storage_slot, &account, &pubkey); - let maybe_rehash = ExpectedRentCollection::maybe_rehash_skipped_rewrite( - &account, - &hash, - &pubkey, - storage_slot, - &epoch_schedule, - &rent_collector, - &HashStats::default(), - &SlotInfoInEpoch::new(max_slot_in_storages_inclusive, &epoch_schedule), - find_unskipped_slot, - None, - ); - assert_eq!( - maybe_rehash, - some_expected.then(|| { - AccountsDb::hash_account_with_rent_epoch( - result - .as_ref() - .unwrap() - .expected_rent_collection_slot_max_epoch, - &account, - &pubkey, - result.as_ref().unwrap().rent_epoch, - ) - }) - ); - } - } - } - } - - #[test] - fn test_get_corrected_rent_epoch_on_load() { - solana_logger::setup(); - let pubkey = Pubkey::new(&[5; 32]); - let owner = solana_sdk::pubkey::new_rand(); - let mut account = AccountSharedData::new(1, 0, &owner); - let mut epoch_schedule = EpochSchedule { - first_normal_epoch: 0, - ..EpochSchedule::default() - }; - epoch_schedule.first_normal_slot = 0; - let first_normal_slot = epoch_schedule.first_normal_slot; - let slots_per_epoch = 432_000; - let partition_from_pubkey = 8470; // function of 432k slots and 'pubkey' above - // start in epoch=1 because of issues at rent_epoch=1 - let storage_slot = first_normal_slot + partition_from_pubkey + slots_per_epoch; - let epoch = epoch_schedule.get_epoch(storage_slot); - assert_eq!( - (epoch, partition_from_pubkey), - epoch_schedule.get_epoch_and_slot_index(storage_slot) - ); - let genesis_config = GenesisConfig::default(); - let mut rent_collector = RentCollector::new( - epoch, - epoch_schedule, - genesis_config.slots_per_year(), - genesis_config.rent, - ); - rent_collector.rent.lamports_per_byte_year = 0; // temporarily disable rent - - assert_eq!( - slots_per_epoch, - epoch_schedule.get_slots_in_epoch(epoch_schedule.get_epoch(storage_slot)) - ); - account.set_rent_epoch(1); // has to be not 0 - - /* - test this: - pubkey_partition_index: 8470 - storage_slot: 8470 - account.rent_epoch: 1 (has to be not 0) - - max_slot: 8469 + 432k * 1 - max_slot: 8470 + 432k * 1 - max_slot: 8471 + 432k * 1 - max_slot: 8472 + 432k * 1 - max_slot: 8469 + 432k * 2 - max_slot: 8470 + 432k * 2 - max_slot: 8471 + 432k * 2 - max_slot: 8472 + 432k * 2 - max_slot: 8469 + 432k * 3 - max_slot: 8470 + 432k * 3 - max_slot: 8471 + 432k * 3 - max_slot: 8472 + 432k * 3 - - one run without skipping slot 8470, once WITH skipping slot 8470 - */ - - for new_small in [false, true] { - for rewrite_already in [false, true] { - // starting at epoch = 0 has issues because of rent_epoch=0 special casing - for epoch in 1..4 { - for partition_index_bank_slot in - partition_from_pubkey - 1..=partition_from_pubkey + 2 - { - let bank_slot = - slots_per_epoch * epoch + first_normal_slot + partition_index_bank_slot; - if storage_slot > bank_slot { - continue; // illegal combination - } - rent_collector.epoch = epoch_schedule.get_epoch(bank_slot); - let first_slot_in_max_epoch = bank_slot - bank_slot % slots_per_epoch; - - assert_eq!( - (epoch, partition_index_bank_slot), - epoch_schedule.get_epoch_and_slot_index(bank_slot) - ); - assert_eq!( - (epoch, 0), - epoch_schedule.get_epoch_and_slot_index(first_slot_in_max_epoch) - ); - account.set_rent_epoch(1); - let rewrites = Rewrites::default(); - if rewrite_already { - if partition_index_bank_slot != partition_from_pubkey { - // this is an invalid test occurrence. - // we wouldn't have inserted pubkey into 'rewrite_already' for this slot if the current partition index wasn't at the pubkey's partition idnex yet. - continue; - } - - rewrites.write().unwrap().insert(pubkey, Hash::default()); - } - let expected_new_rent_epoch = - if partition_index_bank_slot > partition_from_pubkey { - if epoch > account.rent_epoch() { - Some(rent_collector.epoch) - } else { - None - } - } else if partition_index_bank_slot == partition_from_pubkey - && rewrite_already - { - let expected_rent_epoch = rent_collector.epoch; - if expected_rent_epoch == account.rent_epoch() { - None - } else { - Some(expected_rent_epoch) - } - } else if partition_index_bank_slot <= partition_from_pubkey - && epoch > account.rent_epoch() - { - let expected_rent_epoch = rent_collector.epoch.saturating_sub(1); - if expected_rent_epoch == account.rent_epoch() { - None - } else { - Some(expected_rent_epoch) - } - } else { - None - }; - let get_slot_info = |slot| { - if new_small { - SlotInfoInEpoch::new_small(slot) - } else { - SlotInfoInEpoch::new(slot, &epoch_schedule) - } - }; - let new_rent_epoch = - ExpectedRentCollection::get_corrected_rent_epoch_on_load( - &account, - &get_slot_info(storage_slot), - &get_slot_info(bank_slot), - &epoch_schedule, - &rent_collector, - &pubkey, - &rewrites, - ); - assert_eq!(new_rent_epoch, expected_new_rent_epoch); - } - } - } - } - } -} diff --git a/runtime/src/hardened_unpack.rs b/runtime/src/hardened_unpack.rs index e3af855216e409..558e2d428d4648 100644 --- a/runtime/src/hardened_unpack.rs +++ b/runtime/src/hardened_unpack.rs @@ -482,7 +482,7 @@ pub fn unpack_genesis_archive( let extract_start = Instant::now(); fs::create_dir_all(destination_dir)?; - let tar_bz2 = File::open(&archive_filename)?; + let tar_bz2 = File::open(archive_filename)?; let tar = BzDecoder::new(BufReader::new(tar_bz2)); let mut archive = Archive::new(tar); unpack_genesis( diff --git a/runtime/src/in_mem_accounts_index.rs b/runtime/src/in_mem_accounts_index.rs index b252499267ba4b..50898da7fed5a8 100644 --- a/runtime/src/in_mem_accounts_index.rs +++ b/runtime/src/in_mem_accounts_index.rs @@ -319,7 +319,7 @@ impl InMemAccountsIndex { callback(Some(entry)).1 } else { // not in cache, look on disk - let stats = &self.stats(); + let stats = self.stats(); let disk_entry = self.load_account_entry_from_disk(pubkey); if disk_entry.is_none() { return callback(None).1; @@ -358,6 +358,8 @@ impl InMemAccountsIndex { } } + /// return false if the entry is in the index (disk or memory) and has a slot list len > 0 + /// return true in all other cases, including if the entry is NOT in the index at all fn remove_if_slot_list_empty_entry(&self, entry: Entry>) -> bool { match entry { Entry::Occupied(occupied) => { @@ -391,7 +393,7 @@ impl InMemAccountsIndex { false } } - None => false, // not in cache or on disk + None => true, // not in cache or on disk, but slot list is 'empty' and entry is not in index, so return true } } } @@ -527,7 +529,7 @@ impl InMemAccountsIndex { } fn update_entry_stats(&self, stopped_measure: Measure, found: bool) { - let stats = &self.stats(); + let stats = self.stats(); let (count, time) = if found { (&stats.entries_from_mem, &stats.entry_mem_us) } else { @@ -868,12 +870,12 @@ impl InMemAccountsIndex { assert!(!only_add_if_already_held || start_holding); let start = match range.start_bound() { Bound::Included(bound) | Bound::Excluded(bound) => *bound, - Bound::Unbounded => Pubkey::new(&[0; 32]), + Bound::Unbounded => Pubkey::from([0; 32]), }; let end = match range.end_bound() { Bound::Included(bound) | Bound::Excluded(bound) => *bound, - Bound::Unbounded => Pubkey::new(&[0xff; 32]), + Bound::Unbounded => Pubkey::from([0xff; 32]), }; // this becomes inclusive - that is ok - we are just roughly holding a range of items. @@ -1110,11 +1112,10 @@ impl InMemAccountsIndex { // merge all items into the disk index now let disk = self.bucket.as_ref().unwrap(); - let mut duplicate = vec![]; let mut count = 0; insert.into_iter().for_each(|(slot, k, v)| { let entry = (slot, v); - let new_ref_count = if v.is_cached() { 0 } else { 1 }; + let new_ref_count = u64::from(!v.is_cached()); disk.update(&k, |current| { match current { Some((current_slot_list, mut ref_count)) => { @@ -1123,7 +1124,7 @@ impl InMemAccountsIndex { slot_list.extend_from_slice(current_slot_list); slot_list.push(entry); // will never be from the same slot that already exists in the list ref_count += new_ref_count; - duplicate.push((slot, k)); + duplicates.push((slot, k)); Some((slot_list, ref_count)) } None => { @@ -1665,10 +1666,10 @@ mod tests { fn test_hold_range_in_memory() { let bucket = new_disk_buckets_for_test::(); // 0x81 is just some other range - let all = Pubkey::new(&[0; 32])..=Pubkey::new(&[0xff; 32]); + let all = Pubkey::from([0; 32])..=Pubkey::from([0xff; 32]); let ranges = [ all.clone(), - Pubkey::new(&[0x81; 32])..=Pubkey::new(&[0xff; 32]), + Pubkey::from([0x81; 32])..=Pubkey::from([0xff; 32]), ]; for range in ranges.clone() { assert!(bucket.cache_ranges_held.read().unwrap().is_empty()); @@ -1950,4 +1951,52 @@ mod tests { // After the FlushGuard is dropped, the flag will be cleared. assert!(!flushing_active.load(Ordering::Acquire)); } + + #[test] + fn test_remove_if_slot_list_empty_entry() { + let key = solana_sdk::pubkey::new_rand(); + let unknown_key = solana_sdk::pubkey::new_rand(); + + let test = new_for_test::(); + + let mut map = test.map_internal.write().unwrap(); + + { + // item is NOT in index at all, still return true from remove_if_slot_list_empty_entry + // make sure not initially in index + let entry = map.entry(unknown_key); + assert!(matches!(entry, Entry::Vacant(_))); + let entry = map.entry(unknown_key); + assert!(test.remove_if_slot_list_empty_entry(entry)); + // make sure still not in index + let entry = map.entry(unknown_key); + assert!(matches!(entry, Entry::Vacant(_))); + } + + { + // add an entry with an empty slot list + let val = Arc::new(AccountMapEntryInner::::default()); + map.insert(key, val); + let entry = map.entry(key); + assert!(matches!(entry, Entry::Occupied(_))); + // should have removed it since it had an empty slot list + assert!(test.remove_if_slot_list_empty_entry(entry)); + let entry = map.entry(key); + assert!(matches!(entry, Entry::Vacant(_))); + // return true - item is not in index at all now + assert!(test.remove_if_slot_list_empty_entry(entry)); + } + + { + // add an entry with a NON empty slot list - it will NOT get removed + let val = Arc::new(AccountMapEntryInner::::default()); + val.slot_list.write().unwrap().push((1, 1)); + map.insert(key, val); + // does NOT remove it since it has a non-empty slot list + let entry = map.entry(key); + assert!(!test.remove_if_slot_list_empty_entry(entry)); + let entry = map.entry(key); + assert!(matches!(entry, Entry::Occupied(_))); + } + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 16a3f78e6d8163..30a7aea527dc15 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -36,7 +36,6 @@ pub mod cost_model; pub mod cost_tracker; pub mod epoch_stakes; pub mod execute_cost_table; -mod expected_rent_collection; pub mod genesis_utils; pub mod hardened_unpack; pub mod in_mem_accounts_index; @@ -47,6 +46,8 @@ pub mod loader_utils; pub mod message_processor; pub mod non_circulating_supply; mod nonce_keyed_account; +pub mod prioritization_fee; +pub mod prioritization_fee_cache; mod pubkey_bins; mod read_only_accounts_cache; pub mod rent_collector; @@ -73,6 +74,7 @@ mod storable_accounts; mod system_instruction_processor; pub mod transaction_batch; pub mod transaction_error_metrics; +pub mod transaction_priority_details; mod verify_accounts_hash_in_background; pub mod vote_account; pub mod vote_parser; diff --git a/runtime/src/loader_utils.rs b/runtime/src/loader_utils.rs index d8ead330452b88..23bb5d56e9527e 100644 --- a/runtime/src/loader_utils.rs +++ b/runtime/src/loader_utils.rs @@ -10,65 +10,92 @@ use { signature::{Keypair, Signer}, system_instruction, }, + std::{env, fs::File, io::Read, path::PathBuf}, }; const CHUNK_SIZE: usize = 512; // Size of chunk just needs to fit into tx -pub fn load_program( - bank_client: &T, - from_keypair: &Keypair, - loader_pubkey: &Pubkey, - program: Vec, -) -> Pubkey { - let program_keypair = Keypair::new(); - let program_pubkey = program_keypair.pubkey(); - - let instruction = system_instruction::create_account( - &from_keypair.pubkey(), - &program_pubkey, - 1.max( - bank_client - .get_minimum_balance_for_rent_exemption(program.len()) - .unwrap(), - ), - program.len() as u64, - loader_pubkey, - ); - bank_client - .send_and_confirm_message( - &[from_keypair, &program_keypair], - Message::new(&[instruction], Some(&from_keypair.pubkey())), - ) - .unwrap(); +pub fn load_program_from_file(name: &str) -> Vec { + let mut pathbuf = { + let current_exe = env::current_exe().unwrap(); + PathBuf::from(current_exe.parent().unwrap().parent().unwrap()) + }; + pathbuf.push("bpf/"); + pathbuf.push(name); + pathbuf.set_extension("so"); + let mut file = File::open(&pathbuf).unwrap_or_else(|err| { + panic!("Failed to open {}: {}", pathbuf.display(), err); + }); + let mut program = Vec::new(); + file.read_to_end(&mut program).unwrap(); + program +} +pub fn load_and_finalize_deprecated_program( + bank_client: &T, + loader_id: &Pubkey, + program_keypair: Option, + payer_keypair: &Keypair, + name: &str, +) -> (Keypair, Instruction) { + let program = load_program_from_file(name); + let program_keypair = program_keypair.unwrap_or_else(|| { + let program_keypair = Keypair::new(); + let instruction = system_instruction::create_account( + &payer_keypair.pubkey(), + &program_keypair.pubkey(), + 1.max( + bank_client + .get_minimum_balance_for_rent_exemption(program.len()) + .unwrap(), + ), + program.len() as u64, + loader_id, + ); + let message = Message::new(&[instruction], Some(&payer_keypair.pubkey())); + bank_client + .send_and_confirm_message(&[payer_keypair, &program_keypair], message) + .unwrap(); + program_keypair + }); let chunk_size = CHUNK_SIZE; let mut offset = 0; for chunk in program.chunks(chunk_size) { let instruction = - loader_instruction::write(&program_pubkey, loader_pubkey, offset, chunk.to_vec()); - let message = Message::new(&[instruction], Some(&from_keypair.pubkey())); + loader_instruction::write(&program_keypair.pubkey(), loader_id, offset, chunk.to_vec()); + let message = Message::new(&[instruction], Some(&payer_keypair.pubkey())); bank_client - .send_and_confirm_message(&[from_keypair, &program_keypair], message) + .send_and_confirm_message(&[payer_keypair, &program_keypair], message) .unwrap(); offset += chunk_size as u32; } + let instruction = loader_instruction::finalize(&program_keypair.pubkey(), loader_id); + (program_keypair, instruction) +} - let instruction = loader_instruction::finalize(&program_pubkey, loader_pubkey); - let message = Message::new(&[instruction], Some(&from_keypair.pubkey())); +pub fn create_deprecated_program( + bank_client: &T, + loader_id: &Pubkey, + payer_keypair: &Keypair, + name: &str, +) -> Pubkey { + let (program_keypair, instruction) = + load_and_finalize_deprecated_program(bank_client, loader_id, None, payer_keypair, name); + let message = Message::new(&[instruction], Some(&payer_keypair.pubkey())); bank_client - .send_and_confirm_message(&[from_keypair, &program_keypair], message) + .send_and_confirm_message(&[payer_keypair, &program_keypair], message) .unwrap(); - - program_pubkey + program_keypair.pubkey() } -pub fn load_buffer_account( +pub fn load_upgradeable_buffer( bank_client: &T, from_keypair: &Keypair, buffer_keypair: &Keypair, buffer_authority_keypair: &Keypair, - program: &[u8], -) { + name: &str, +) -> Vec { + let program = load_program_from_file(name); let buffer_pubkey = buffer_keypair.pubkey(); let buffer_authority_pubkey = buffer_authority_keypair.pubkey(); @@ -110,6 +137,8 @@ pub fn load_buffer_account( .unwrap(); offset += chunk_size as u32; } + + program } pub fn load_upgradeable_program( @@ -118,25 +147,22 @@ pub fn load_upgradeable_program( buffer_keypair: &Keypair, executable_keypair: &Keypair, authority_keypair: &Keypair, - program: Vec, + name: &str, ) { - let program_pubkey = executable_keypair.pubkey(); - let authority_pubkey = authority_keypair.pubkey(); - - load_buffer_account( + let program = load_upgradeable_buffer( bank_client, from_keypair, buffer_keypair, authority_keypair, - &program, + name, ); let message = Message::new( &bpf_loader_upgradeable::deploy_with_max_program_len( &from_keypair.pubkey(), - &program_pubkey, + &executable_keypair.pubkey(), &buffer_keypair.pubkey(), - &authority_pubkey, + &authority_keypair.pubkey(), 1.max( bank_client .get_minimum_balance_for_rent_exemption( @@ -159,23 +185,30 @@ pub fn load_upgradeable_program( pub fn upgrade_program( bank_client: &T, - from_keypair: &Keypair, - program_pubkey: &Pubkey, - buffer_pubkey: &Pubkey, + payer_keypair: &Keypair, + buffer_keypair: &Keypair, + executable_pubkey: &Pubkey, authority_keypair: &Keypair, - spill_pubkey: &Pubkey, + name: &str, ) { + load_upgradeable_buffer( + bank_client, + payer_keypair, + buffer_keypair, + authority_keypair, + name, + ); let message = Message::new( &[bpf_loader_upgradeable::upgrade( - program_pubkey, - buffer_pubkey, + executable_pubkey, + &buffer_keypair.pubkey(), &authority_keypair.pubkey(), - spill_pubkey, + &payer_keypair.pubkey(), )], - Some(&from_keypair.pubkey()), + Some(&payer_keypair.pubkey()), ); bank_client - .send_and_confirm_message(&[from_keypair, authority_keypair], message) + .send_and_confirm_message(&[payer_keypair, authority_keypair], message) .unwrap(); } diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index c1b06c141dbc98..55ae052b6a074a 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -3,7 +3,8 @@ use { solana_measure::measure::Measure, solana_program_runtime::{ compute_budget::ComputeBudget, - invoke_context::{BuiltinProgram, Executors, InvokeContext}, + executor_cache::Executors, + invoke_context::{BuiltinProgram, InvokeContext}, log_collector::LogCollector, sysvar_cache::SysvarCache, timings::{ExecuteDetailsTimings, ExecuteTimings}, @@ -181,7 +182,7 @@ mod tests { solana_sdk::{ account::{AccountSharedData, ReadableAccount}, instruction::{AccountMeta, Instruction, InstructionError}, - message::{AccountKeys, Message}, + message::{AccountKeys, LegacyMessage, Message}, native_loader::{self, create_loadable_account_for_test}, pubkey::Pubkey, secp256k1_instruction::new_secp256k1_instruction, @@ -272,20 +273,21 @@ mod tests { AccountMeta::new_readonly(readonly_pubkey, false), ]; - let message = SanitizedMessage::Legacy(Message::new_with_compiled_instructions( - 1, - 0, - 2, - account_keys.clone(), - Hash::default(), - AccountKeys::new(&account_keys, None).compile_instructions(&[ - Instruction::new_with_bincode( - mock_system_program_id, - &MockSystemInstruction::Correct, - account_metas.clone(), - ), - ]), - )); + let message = + SanitizedMessage::Legacy(LegacyMessage::new(Message::new_with_compiled_instructions( + 1, + 0, + 2, + account_keys.clone(), + Hash::default(), + AccountKeys::new(&account_keys, None).compile_instructions(&[ + Instruction::new_with_bincode( + mock_system_program_id, + &MockSystemInstruction::Correct, + account_metas.clone(), + ), + ]), + ))); let sysvar_cache = SysvarCache::default(); let result = MessageProcessor::process_message( builtin_programs, @@ -322,20 +324,21 @@ mod tests { 0 ); - let message = SanitizedMessage::Legacy(Message::new_with_compiled_instructions( - 1, - 0, - 2, - account_keys.clone(), - Hash::default(), - AccountKeys::new(&account_keys, None).compile_instructions(&[ - Instruction::new_with_bincode( - mock_system_program_id, - &MockSystemInstruction::TransferLamports { lamports: 50 }, - account_metas.clone(), - ), - ]), - )); + let message = + SanitizedMessage::Legacy(LegacyMessage::new(Message::new_with_compiled_instructions( + 1, + 0, + 2, + account_keys.clone(), + Hash::default(), + AccountKeys::new(&account_keys, None).compile_instructions(&[ + Instruction::new_with_bincode( + mock_system_program_id, + &MockSystemInstruction::TransferLamports { lamports: 50 }, + account_metas.clone(), + ), + ]), + ))); let result = MessageProcessor::process_message( builtin_programs, &message, @@ -361,20 +364,21 @@ mod tests { )) ); - let message = SanitizedMessage::Legacy(Message::new_with_compiled_instructions( - 1, - 0, - 2, - account_keys.clone(), - Hash::default(), - AccountKeys::new(&account_keys, None).compile_instructions(&[ - Instruction::new_with_bincode( - mock_system_program_id, - &MockSystemInstruction::ChangeData { data: 50 }, - account_metas, - ), - ]), - )); + let message = + SanitizedMessage::Legacy(LegacyMessage::new(Message::new_with_compiled_instructions( + 1, + 0, + 2, + account_keys.clone(), + Hash::default(), + AccountKeys::new(&account_keys, None).compile_instructions(&[ + Instruction::new_with_bincode( + mock_system_program_id, + &MockSystemInstruction::ChangeData { data: 50 }, + account_metas, + ), + ]), + ))); let result = MessageProcessor::process_message( builtin_programs, &message, @@ -462,7 +466,7 @@ mod tests { } } - let mock_program_id = Pubkey::new(&[2u8; 32]); + let mock_program_id = Pubkey::from([2u8; 32]); let rent_collector = RentCollector::default(); let builtin_programs = &[BuiltinProgram { program_id: mock_program_id, @@ -503,14 +507,14 @@ mod tests { ]; // Try to borrow mut the same account - let message = SanitizedMessage::Legacy(Message::new( + let message = SanitizedMessage::Legacy(LegacyMessage::new(Message::new( &[Instruction::new_with_bincode( mock_program_id, &MockSystemInstruction::BorrowFail, account_metas.clone(), )], Some(transaction_context.get_key_of_account_at_index(0).unwrap()), - )); + ))); let sysvar_cache = SysvarCache::default(); let result = MessageProcessor::process_message( builtin_programs, @@ -538,14 +542,14 @@ mod tests { ); // Try to borrow mut the same account in a safe way - let message = SanitizedMessage::Legacy(Message::new( + let message = SanitizedMessage::Legacy(LegacyMessage::new(Message::new( &[Instruction::new_with_bincode( mock_program_id, &MockSystemInstruction::MultiBorrowMut, account_metas.clone(), )], Some(transaction_context.get_key_of_account_at_index(0).unwrap()), - )); + ))); let result = MessageProcessor::process_message( builtin_programs, &message, @@ -566,7 +570,7 @@ mod tests { assert!(result.is_ok()); // Do work on the same transaction account but at different instruction accounts - let message = SanitizedMessage::Legacy(Message::new( + let message = SanitizedMessage::Legacy(LegacyMessage::new(Message::new( &[Instruction::new_with_bincode( mock_program_id, &MockSystemInstruction::DoWork { @@ -576,7 +580,7 @@ mod tests { account_metas, )], Some(transaction_context.get_key_of_account_at_index(0).unwrap()), - )); + ))); let result = MessageProcessor::process_message( builtin_programs, &message, @@ -646,7 +650,7 @@ mod tests { let mut transaction_context = TransactionContext::new(accounts, Some(Rent::default()), 1, 2); - let message = SanitizedMessage::Legacy(Message::new( + let message = SanitizedMessage::Legacy(LegacyMessage::new(Message::new( &[ new_secp256k1_instruction( &libsecp256k1::SecretKey::random(&mut rand::thread_rng()), @@ -655,7 +659,7 @@ mod tests { Instruction::new_with_bytes(mock_program_id, &[], vec![]), ], None, - )); + ))); let sysvar_cache = SysvarCache::default(); let result = MessageProcessor::process_message( builtin_programs, diff --git a/runtime/src/prioritization_fee.rs b/runtime/src/prioritization_fee.rs new file mode 100644 index 00000000000000..8a75d0f4749c11 --- /dev/null +++ b/runtime/src/prioritization_fee.rs @@ -0,0 +1,328 @@ +use { + solana_measure::measure, + solana_sdk::{clock::Slot, pubkey::Pubkey, saturating_add_assign}, + std::collections::HashMap, +}; + +#[derive(Debug, Default)] +struct PrioritizationFeeMetrics { + // Count of writable accounts in slot + total_writable_accounts_count: u64, + + // Count of writeable accounts with a minimum prioritization fee higher than the minimum transaction + // fee for this slot. + relevant_writable_accounts_count: u64, + + // Total prioritization fees included in this slot. + total_prioritization_fee: u64, + + // Accumulated time spent on tracking prioritization fee for each slot. + total_update_elapsed_us: u64, +} + +impl PrioritizationFeeMetrics { + fn accumulate_total_prioritization_fee(&mut self, val: u64) { + saturating_add_assign!(self.total_prioritization_fee, val); + } + + fn accumulate_total_update_elapsed_us(&mut self, val: u64) { + saturating_add_assign!(self.total_update_elapsed_us, val); + } + + fn report(&self, slot: Slot) { + datapoint_info!( + "block_prioritization_fee", + ("slot", slot as i64, i64), + ( + "total_writable_accounts_count", + self.total_writable_accounts_count as i64, + i64 + ), + ( + "relevant_writable_accounts_count", + self.relevant_writable_accounts_count as i64, + i64 + ), + ( + "total_prioritization_fee", + self.total_prioritization_fee as i64, + i64 + ), + ( + "total_update_elapsed_us", + self.total_update_elapsed_us as i64, + i64 + ), + ); + } +} + +pub enum PrioritizationFeeError { + // Not able to get account locks from sanitized transaction, which is required to update block + // minimum fees. + FailGetTransactionAccountLocks, + + // Not able to read priority details, including compute-unit price, from transaction. + // Compute-unit price is required to update block minimum fees. + FailGetTransactionPriorityDetails, + + // Block is already finalized, trying to finalize it again is usually unexpected + BlockIsAlreadyFinalized, +} + +/// Block minimum prioritization fee stats, includes the minimum prioritization fee for a transaction in this +/// block; and the minimum fee for each writable account in all transactions in this block. The only relevant +/// write account minimum fees are those greater than the block minimum transaction fee, because the minimum fee needed to land +/// a transaction is determined by Max( min_transaction_fee, min_writable_account_fees(key), ...) +#[derive(Debug)] +pub struct PrioritizationFee { + // The minimum prioritization fee of transactions that landed in this block. + min_transaction_fee: u64, + + // The minimum prioritization fee of each writable account in transactions in this block. + min_writable_account_fees: HashMap, + + // Default to `false`, set to `true` when a block is completed, therefore the minimum fees recorded + // are finalized, and can be made available for use (e.g., RPC query) + is_finalized: bool, + + // slot prioritization fee metrics + metrics: PrioritizationFeeMetrics, +} + +impl Default for PrioritizationFee { + fn default() -> Self { + PrioritizationFee { + min_transaction_fee: u64::MAX, + min_writable_account_fees: HashMap::new(), + is_finalized: false, + metrics: PrioritizationFeeMetrics::default(), + } + } +} + +impl PrioritizationFee { + /// Update self for minimum transaction fee in the block and minimum fee for each writable account. + pub fn update( + &mut self, + transaction_fee: u64, + writable_accounts: &[Pubkey], + ) -> Result<(), PrioritizationFeeError> { + let (_, update_time) = measure!( + { + if transaction_fee < self.min_transaction_fee { + self.min_transaction_fee = transaction_fee; + } + + for write_account in writable_accounts.iter() { + self.min_writable_account_fees + .entry(*write_account) + .and_modify(|write_lock_fee| { + *write_lock_fee = std::cmp::min(*write_lock_fee, transaction_fee) + }) + .or_insert(transaction_fee); + } + + self.metrics + .accumulate_total_prioritization_fee(transaction_fee); + }, + "update_time", + ); + + self.metrics + .accumulate_total_update_elapsed_us(update_time.as_us()); + Ok(()) + } + + /// Accounts that have minimum fees lesser or equal to the minimum fee in the block are redundant, they are + /// removed to reduce memory footprint when mark_block_completed() is called. + fn prune_irrelevant_writable_accounts(&mut self) { + self.metrics.total_writable_accounts_count = self.get_writable_accounts_count() as u64; + self.min_writable_account_fees + .retain(|_, account_fee| account_fee > &mut self.min_transaction_fee); + self.metrics.relevant_writable_accounts_count = self.get_writable_accounts_count() as u64; + } + + pub fn mark_block_completed(&mut self) -> Result<(), PrioritizationFeeError> { + if self.is_finalized { + return Err(PrioritizationFeeError::BlockIsAlreadyFinalized); + } + self.prune_irrelevant_writable_accounts(); + self.is_finalized = true; + Ok(()) + } + + pub fn get_min_transaction_fee(&self) -> Option { + if self.min_transaction_fee != u64::MAX { + Some(self.min_transaction_fee) + } else { + None + } + } + + pub fn get_writable_account_fee(&self, key: &Pubkey) -> Option { + self.min_writable_account_fees.get(key).copied() + } + + pub fn get_writable_account_fees(&self) -> impl Iterator { + self.min_writable_account_fees.iter() + } + + pub fn get_writable_accounts_count(&self) -> usize { + self.min_writable_account_fees.len() + } + + pub fn is_finalized(&self) -> bool { + self.is_finalized + } + + pub fn report_metrics(&self, slot: Slot) { + self.metrics.report(slot); + + // report this slot's min_transaction_fee and top 10 min_writable_account_fees + let min_transaction_fee = self.get_min_transaction_fee().unwrap_or(0); + let mut accounts_fees: Vec<_> = self.get_writable_account_fees().collect(); + accounts_fees.sort_by(|lh, rh| rh.1.cmp(lh.1)); + datapoint_info!( + "block_min_prioritization_fee", + ("slot", slot as i64, i64), + ("entity", "block", String), + ("min_prioritization_fee", min_transaction_fee as i64, i64), + ); + for (account_key, fee) in accounts_fees.iter().take(10) { + datapoint_trace!( + "block_min_prioritization_fee", + ("slot", slot as i64, i64), + ("entity", account_key.to_string(), String), + ("min_prioritization_fee", **fee as i64, i64), + ); + } + } +} + +#[cfg(test)] +mod tests { + use {super::*, solana_sdk::pubkey::Pubkey}; + + #[test] + fn test_update_prioritization_fee() { + solana_logger::setup(); + let write_account_a = Pubkey::new_unique(); + let write_account_b = Pubkey::new_unique(); + let write_account_c = Pubkey::new_unique(); + + let mut prioritization_fee = PrioritizationFee::default(); + assert!(prioritization_fee.get_min_transaction_fee().is_none()); + + // Assert for 1st transaction + // [fee, write_accounts...] --> [block, account_a, account_b, account_c] + // ----------------------------------------------------------------------- + // [5, a, b ] --> [5, 5, 5, nil ] + { + assert!(prioritization_fee + .update(5, &[write_account_a, write_account_b]) + .is_ok()); + assert_eq!(5, prioritization_fee.get_min_transaction_fee().unwrap()); + assert_eq!( + 5, + prioritization_fee + .get_writable_account_fee(&write_account_a) + .unwrap() + ); + assert_eq!( + 5, + prioritization_fee + .get_writable_account_fee(&write_account_b) + .unwrap() + ); + assert!(prioritization_fee + .get_writable_account_fee(&write_account_c) + .is_none()); + } + + // Assert for second transaction: + // [fee, write_accounts...] --> [block, account_a, account_b, account_c] + // ----------------------------------------------------------------------- + // [9, b, c ] --> [5, 5, 5, 9 ] + { + assert!(prioritization_fee + .update(9, &[write_account_b, write_account_c]) + .is_ok()); + assert_eq!(5, prioritization_fee.get_min_transaction_fee().unwrap()); + assert_eq!( + 5, + prioritization_fee + .get_writable_account_fee(&write_account_a) + .unwrap() + ); + assert_eq!( + 5, + prioritization_fee + .get_writable_account_fee(&write_account_b) + .unwrap() + ); + assert_eq!( + 9, + prioritization_fee + .get_writable_account_fee(&write_account_c) + .unwrap() + ); + } + + // Assert for third transaction: + // [fee, write_accounts...] --> [block, account_a, account_b, account_c] + // ----------------------------------------------------------------------- + // [2, a, c ] --> [2, 2, 5, 2 ] + { + assert!(prioritization_fee + .update(2, &[write_account_a, write_account_c]) + .is_ok()); + assert_eq!(2, prioritization_fee.get_min_transaction_fee().unwrap()); + assert_eq!( + 2, + prioritization_fee + .get_writable_account_fee(&write_account_a) + .unwrap() + ); + assert_eq!( + 5, + prioritization_fee + .get_writable_account_fee(&write_account_b) + .unwrap() + ); + assert_eq!( + 2, + prioritization_fee + .get_writable_account_fee(&write_account_c) + .unwrap() + ); + } + + // assert after prune, account a and c should be removed from cache to save space + { + prioritization_fee.prune_irrelevant_writable_accounts(); + assert_eq!(1, prioritization_fee.min_writable_account_fees.len()); + assert_eq!(2, prioritization_fee.get_min_transaction_fee().unwrap()); + assert!(prioritization_fee + .get_writable_account_fee(&write_account_a) + .is_none()); + assert_eq!( + 5, + prioritization_fee + .get_writable_account_fee(&write_account_b) + .unwrap() + ); + assert!(prioritization_fee + .get_writable_account_fee(&write_account_c) + .is_none()); + } + } + + #[test] + fn test_mark_block_completed() { + let mut prioritization_fee = PrioritizationFee::default(); + + assert!(prioritization_fee.mark_block_completed().is_ok()); + assert!(prioritization_fee.mark_block_completed().is_err()); + } +} diff --git a/runtime/src/prioritization_fee_cache.rs b/runtime/src/prioritization_fee_cache.rs new file mode 100644 index 00000000000000..1740b3a76f72f7 --- /dev/null +++ b/runtime/src/prioritization_fee_cache.rs @@ -0,0 +1,799 @@ +use { + crate::{ + bank::Bank, prioritization_fee::*, + transaction_priority_details::GetTransactionPriorityDetails, + }, + crossbeam_channel::{unbounded, Receiver, Sender}, + log::*, + lru::LruCache, + solana_measure::measure, + solana_sdk::{ + clock::Slot, pubkey::Pubkey, saturating_add_assign, transaction::SanitizedTransaction, + }, + std::{ + collections::HashMap, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, Mutex, RwLock, + }, + thread::{Builder, JoinHandle}, + }, +}; + +/// The maximum number of blocks to keep in `PrioritizationFeeCache`, ie. +/// the amount of history generally desired to estimate the prioritization fee needed to +/// land a transaction in the current block. +const MAX_NUM_RECENT_BLOCKS: u64 = 150; + +#[derive(Debug, Default)] +struct PrioritizationFeeCacheMetrics { + // Count of transactions that successfully updated each slot's prioritization fee cache. + successful_transaction_update_count: AtomicU64, + + // Accumulated time spent on tracking prioritization fee for each slot. + total_update_elapsed_us: AtomicU64, + + // Accumulated time spent on acquiring cache write lock. + total_cache_lock_elapsed_us: AtomicU64, + + // Accumulated time spent on acquiring each block entry's lock.. + total_entry_lock_elapsed_us: AtomicU64, + + // Accumulated time spent on updating block prioritization fees. + total_entry_update_elapsed_us: AtomicU64, + + // Accumulated time spent on finalizing block prioritization fees. + total_block_finalize_elapsed_us: AtomicU64, +} + +impl PrioritizationFeeCacheMetrics { + fn accumulate_successful_transaction_update_count(&self, val: u64) { + self.successful_transaction_update_count + .fetch_add(val, Ordering::Relaxed); + } + + fn accumulate_total_update_elapsed_us(&self, val: u64) { + self.total_update_elapsed_us + .fetch_add(val, Ordering::Relaxed); + } + + fn accumulate_total_cache_lock_elapsed_us(&self, val: u64) { + self.total_cache_lock_elapsed_us + .fetch_add(val, Ordering::Relaxed); + } + + fn accumulate_total_entry_lock_elapsed_us(&self, val: u64) { + self.total_entry_lock_elapsed_us + .fetch_add(val, Ordering::Relaxed); + } + + fn accumulate_total_entry_update_elapsed_us(&self, val: u64) { + self.total_entry_update_elapsed_us + .fetch_add(val, Ordering::Relaxed); + } + + fn accumulate_total_block_finalize_elapsed_us(&self, val: u64) { + self.total_block_finalize_elapsed_us + .fetch_add(val, Ordering::Relaxed); + } + + fn report(&self, slot: Slot) { + datapoint_info!( + "block_prioritization_fee_counters", + ("slot", slot as i64, i64), + ( + "successful_transaction_update_count", + self.successful_transaction_update_count + .swap(0, Ordering::Relaxed) as i64, + i64 + ), + ( + "total_update_elapsed_us", + self.total_update_elapsed_us.swap(0, Ordering::Relaxed) as i64, + i64 + ), + ( + "total_cache_lock_elapsed_us", + self.total_cache_lock_elapsed_us.swap(0, Ordering::Relaxed) as i64, + i64 + ), + ( + "total_entry_lock_elapsed_us", + self.total_entry_lock_elapsed_us.swap(0, Ordering::Relaxed) as i64, + i64 + ), + ( + "total_entry_update_elapsed_us", + self.total_entry_update_elapsed_us + .swap(0, Ordering::Relaxed) as i64, + i64 + ), + ( + "total_block_finalize_elapsed_us", + self.total_block_finalize_elapsed_us + .swap(0, Ordering::Relaxed) as i64, + i64 + ), + ); + } +} + +enum CacheServiceUpdate { + TransactionUpdate { + slot: Slot, + transaction_fee: u64, + writable_accounts: Arc>, + }, + BankFrozen { + slot: Slot, + }, + Exit, +} + +/// Stores up to MAX_NUM_RECENT_BLOCKS recent block's prioritization fee, +/// A separate internal thread `service_thread` handles additional tasks when a bank is frozen, +/// and collecting stats and reporting metrics. +pub struct PrioritizationFeeCache { + cache: Arc>>>>, + service_thread: Option>, + sender: Sender, + metrics: Arc, +} + +impl Default for PrioritizationFeeCache { + fn default() -> Self { + Self::new(MAX_NUM_RECENT_BLOCKS) + } +} + +impl Drop for PrioritizationFeeCache { + fn drop(&mut self) { + let _ = self.sender.send(CacheServiceUpdate::Exit); + self.service_thread + .take() + .unwrap() + .join() + .expect("Prioritization fee cache servicing thread failed to join"); + } +} + +impl PrioritizationFeeCache { + pub fn new(capacity: u64) -> Self { + let metrics = Arc::new(PrioritizationFeeCacheMetrics::default()); + let (sender, receiver) = unbounded(); + let cache = Arc::new(RwLock::new(LruCache::new(capacity as usize))); + + let cache_clone = cache.clone(); + let metrics_clone = metrics.clone(); + let service_thread = Some( + Builder::new() + .name("prioritization-fee-cache-servicing-thread".to_string()) + .spawn(move || { + Self::service_loop(cache_clone, receiver, metrics_clone); + }) + .unwrap(), + ); + + PrioritizationFeeCache { + cache, + service_thread, + sender, + metrics, + } + } + + /// Get prioritization fee entry, create new entry if necessary + fn get_prioritization_fee( + cache: Arc>>>>, + slot: &Slot, + ) -> Arc> { + let mut cache = cache.write().unwrap(); + match cache.get(slot) { + Some(entry) => Arc::clone(entry), + None => { + let entry = Arc::new(Mutex::new(PrioritizationFee::default())); + cache.put(*slot, Arc::clone(&entry)); + entry + } + } + } + + /// Update with a list of transactions' tx_priority_details and tx_account_locks; Only + /// transactions have both valid priority_detail and account_locks will be used to update + /// fee_cache asynchronously. + pub fn update<'a>(&self, bank: Arc, txs: impl Iterator) { + let mut successful_transaction_update_count: u64 = 0; + let (_, send_updates_time) = measure!( + { + for sanitized_transaction in txs { + let priority_details = sanitized_transaction.get_transaction_priority_details(); + let account_locks = sanitized_transaction + .get_account_locks(bank.get_transaction_account_lock_limit()); + + if priority_details.is_none() || account_locks.is_err() { + continue; + } + let priority_details = priority_details.unwrap(); + + // filter out any transaction that requests zero compute_unit_limit + // since its priority fee amount is not instructive + if priority_details.compute_unit_limit == 0 { + continue; + } + + let writable_accounts = Arc::new( + account_locks + .unwrap() + .writable + .iter() + .map(|key| **key) + .collect::>(), + ); + + self.sender + .send(CacheServiceUpdate::TransactionUpdate { + slot: bank.slot(), + transaction_fee: priority_details.priority, + writable_accounts, + }) + .unwrap_or_else(|err| { + warn!( + "prioritization fee cache transaction updates failed: {:?}", + err + ); + }); + saturating_add_assign!(successful_transaction_update_count, 1) + } + }, + "send_updates", + ); + + self.metrics + .accumulate_total_update_elapsed_us(send_updates_time.as_us()); + self.metrics + .accumulate_successful_transaction_update_count(successful_transaction_update_count); + } + + /// Finalize prioritization fee when it's bank is completely replayed from blockstore, + /// by pruning irrelevant accounts to save space, and marking its availability for queries. + pub fn finalize_priority_fee(&self, slot: Slot) { + self.sender + .send(CacheServiceUpdate::BankFrozen { slot }) + .unwrap_or_else(|err| { + warn!( + "prioritization fee cache signalling bank frozen failed: {:?}", + err + ) + }); + } + + /// Internal function is invoked by worker thread to update slot's minimum prioritization fee, + /// Cache lock contends here. + fn update_cache( + cache: Arc>>>>, + slot: &Slot, + transaction_fee: u64, + writable_accounts: Arc>, + metrics: Arc, + ) { + let (block_prioritization_fee, cache_lock_time) = + measure!(Self::get_prioritization_fee(cache, slot), "cache_lock_time"); + + let (mut block_prioritization_fee, entry_lock_time) = + measure!(block_prioritization_fee.lock().unwrap(), "entry_lock_time"); + + let (_, entry_update_time) = measure!( + block_prioritization_fee.update(transaction_fee, &writable_accounts), + "entry_update_time" + ); + metrics.accumulate_total_cache_lock_elapsed_us(cache_lock_time.as_us()); + metrics.accumulate_total_entry_lock_elapsed_us(entry_lock_time.as_us()); + metrics.accumulate_total_entry_update_elapsed_us(entry_update_time.as_us()); + } + + fn finalize_slot( + cache: Arc>>>>, + slot: &Slot, + metrics: Arc, + ) { + let (block_prioritization_fee, cache_lock_time) = + measure!(Self::get_prioritization_fee(cache, slot), "cache_lock_time"); + + let (mut block_prioritization_fee, entry_lock_time) = + measure!(block_prioritization_fee.lock().unwrap(), "entry_lock_time"); + + // prune cache by evicting write account entry from prioritization fee if its fee is less + // or equal to block's minimum transaction fee, because they are irrelevant in calculating + // block minimum fee. + let (_, slot_finalize_time) = measure!( + block_prioritization_fee.mark_block_completed(), + "slot_finalize_time" + ); + block_prioritization_fee.report_metrics(*slot); + metrics.accumulate_total_cache_lock_elapsed_us(cache_lock_time.as_us()); + metrics.accumulate_total_entry_lock_elapsed_us(entry_lock_time.as_us()); + metrics.accumulate_total_block_finalize_elapsed_us(slot_finalize_time.as_us()); + } + + fn service_loop( + cache: Arc>>>>, + receiver: Receiver, + metrics: Arc, + ) { + for update in receiver.iter() { + match update { + CacheServiceUpdate::TransactionUpdate { + slot, + transaction_fee, + writable_accounts, + } => Self::update_cache( + cache.clone(), + &slot, + transaction_fee, + writable_accounts, + metrics.clone(), + ), + CacheServiceUpdate::BankFrozen { slot } => { + Self::finalize_slot(cache.clone(), &slot, metrics.clone()); + + metrics.report(slot); + } + CacheServiceUpdate::Exit => { + break; + } + } + } + } + + /// Returns number of blocks that have finalized minimum fees collection + pub fn available_block_count(&self) -> usize { + self.cache + .read() + .unwrap() + .iter() + .filter(|(_slot, prioritization_fee)| prioritization_fee.lock().unwrap().is_finalized()) + .count() + } + + pub fn get_prioritization_fees(&self, account_keys: &[Pubkey]) -> HashMap { + self.cache + .read() + .unwrap() + .iter() + .filter_map(|(slot, prioritization_fee)| { + let prioritization_fee_read = prioritization_fee.lock().unwrap(); + prioritization_fee_read.is_finalized().then(|| { + let mut fee = prioritization_fee_read + .get_min_transaction_fee() + .unwrap_or_default(); + for account_key in account_keys { + if let Some(account_fee) = + prioritization_fee_read.get_writable_account_fee(account_key) + { + fee = std::cmp::max(fee, account_fee); + } + } + Some((*slot, fee)) + }) + }) + .flatten() + .collect() + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + crate::{ + bank::Bank, + bank_forks::BankForks, + genesis_utils::{create_genesis_config, GenesisConfigInfo}, + }, + solana_sdk::{ + compute_budget::ComputeBudgetInstruction, + message::Message, + pubkey::Pubkey, + system_instruction, + transaction::{SanitizedTransaction, Transaction}, + }, + }; + + fn build_sanitized_transaction_for_test( + compute_unit_price: u64, + signer_account: &Pubkey, + write_account: &Pubkey, + ) -> SanitizedTransaction { + let transaction = Transaction::new_unsigned(Message::new( + &[ + system_instruction::transfer(signer_account, write_account, 1), + ComputeBudgetInstruction::set_compute_unit_price(compute_unit_price), + ], + Some(signer_account), + )); + + SanitizedTransaction::try_from_legacy_transaction(transaction).unwrap() + } + + // update fee cache is asynchronous, this test helper blocks until update is completed. + fn sync_update<'a>( + prioritization_fee_cache: &mut PrioritizationFeeCache, + bank: Arc, + txs: impl Iterator, + ) { + prioritization_fee_cache.update(bank.clone(), txs); + + let block_fee = PrioritizationFeeCache::get_prioritization_fee( + prioritization_fee_cache.cache.clone(), + &bank.slot(), + ); + + // wait till update is done + while block_fee + .lock() + .unwrap() + .get_min_transaction_fee() + .is_none() + { + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + + // finalization is asynchronous, this test helper blocks until finalization is completed. + fn sync_finalize_priority_fee_for_test( + prioritization_fee_cache: &mut PrioritizationFeeCache, + slot: Slot, + ) { + prioritization_fee_cache.finalize_priority_fee(slot); + let fee = PrioritizationFeeCache::get_prioritization_fee( + prioritization_fee_cache.cache.clone(), + &slot, + ); + + // wait till finalization is done + while !fee.lock().unwrap().is_finalized() { + std::thread::sleep(std::time::Duration::from_millis(100)); + } + } + + #[test] + fn test_prioritization_fee_cache_update() { + solana_logger::setup(); + let write_account_a = Pubkey::new_unique(); + let write_account_b = Pubkey::new_unique(); + let write_account_c = Pubkey::new_unique(); + + // Set up test with 3 transactions, in format of [fee, write-accounts...], + // Shall expect fee cache is updated in following sequence: + // transaction block minimum prioritization fee cache + // [fee, write_accounts...] --> [block, account_a, account_b, account_c] + // ----------------------------------------------------------------------- + // [5, a, b ] --> [5, 5, 5, nil ] + // [9, b, c ] --> [5, 5, 5, 9 ] + // [2, a, c ] --> [2, 2, 5, 2 ] + // + let txs = vec![ + build_sanitized_transaction_for_test(5, &write_account_a, &write_account_b), + build_sanitized_transaction_for_test(9, &write_account_b, &write_account_c), + build_sanitized_transaction_for_test(2, &write_account_a, &write_account_c), + ]; + + let bank = Arc::new(Bank::default_for_tests()); + let slot = bank.slot(); + + let mut prioritization_fee_cache = PrioritizationFeeCache::default(); + sync_update(&mut prioritization_fee_cache, bank, txs.iter()); + + // assert block minimum fee and account a, b, c fee accordingly + { + let fee = PrioritizationFeeCache::get_prioritization_fee( + prioritization_fee_cache.cache.clone(), + &slot, + ); + let fee = fee.lock().unwrap(); + assert_eq!(2, fee.get_min_transaction_fee().unwrap()); + assert_eq!(2, fee.get_writable_account_fee(&write_account_a).unwrap()); + assert_eq!(5, fee.get_writable_account_fee(&write_account_b).unwrap()); + assert_eq!(2, fee.get_writable_account_fee(&write_account_c).unwrap()); + // assert unknown account d fee + assert!(fee + .get_writable_account_fee(&Pubkey::new_unique()) + .is_none()); + } + + // assert after prune, account a and c should be removed from cache to save space + { + sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, slot); + let fee = PrioritizationFeeCache::get_prioritization_fee( + prioritization_fee_cache.cache.clone(), + &slot, + ); + let fee = fee.lock().unwrap(); + assert_eq!(2, fee.get_min_transaction_fee().unwrap()); + assert!(fee.get_writable_account_fee(&write_account_a).is_none()); + assert_eq!(5, fee.get_writable_account_fee(&write_account_b).unwrap()); + assert!(fee.get_writable_account_fee(&write_account_c).is_none()); + } + } + + #[test] + fn test_available_block_count() { + let prioritization_fee_cache = PrioritizationFeeCache::default(); + + assert!(PrioritizationFeeCache::get_prioritization_fee( + prioritization_fee_cache.cache.clone(), + &1 + ) + .lock() + .unwrap() + .mark_block_completed() + .is_ok()); + assert!(PrioritizationFeeCache::get_prioritization_fee( + prioritization_fee_cache.cache.clone(), + &2 + ) + .lock() + .unwrap() + .mark_block_completed() + .is_ok()); + // add slot 3 entry to cache, but not finalize it + PrioritizationFeeCache::get_prioritization_fee(prioritization_fee_cache.cache.clone(), &3); + + // assert available block count should be 2 finalized blocks + assert_eq!(2, prioritization_fee_cache.available_block_count()); + } + + fn hashmap_of(vec: Vec<(Slot, u64)>) -> HashMap { + vec.into_iter().collect() + } + + #[test] + fn test_get_prioritization_fees() { + solana_logger::setup(); + let write_account_a = Pubkey::new_unique(); + let write_account_b = Pubkey::new_unique(); + let write_account_c = Pubkey::new_unique(); + + let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000); + let bank0 = Bank::new_for_benches(&genesis_config); + let bank_forks = BankForks::new(bank0); + let bank = bank_forks.working_bank(); + let collector = solana_sdk::pubkey::new_rand(); + let bank1 = Arc::new(Bank::new_from_parent(&bank, &collector, 1)); + let bank2 = Arc::new(Bank::new_from_parent(&bank, &collector, 2)); + let bank3 = Arc::new(Bank::new_from_parent(&bank, &collector, 3)); + + let mut prioritization_fee_cache = PrioritizationFeeCache::default(); + + // Assert no minimum fee from empty cache + assert!(prioritization_fee_cache + .get_prioritization_fees(&[]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_a]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_b]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_c]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_a, write_account_b]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c]) + .is_empty()); + + // Assert after add one transaction for slot 1 + { + let txs = vec![ + build_sanitized_transaction_for_test(2, &write_account_a, &write_account_b), + build_sanitized_transaction_for_test( + 1, + &Pubkey::new_unique(), + &Pubkey::new_unique(), + ), + ]; + sync_update(&mut prioritization_fee_cache, bank1, txs.iter()); + // before block is marked as completed + assert!(prioritization_fee_cache + .get_prioritization_fees(&[]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_a]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_b]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_c]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_a, write_account_b]) + .is_empty()); + assert!(prioritization_fee_cache + .get_prioritization_fees(&[write_account_a, write_account_b, write_account_c]) + .is_empty()); + // after block is completed + sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, 1); + assert_eq!( + hashmap_of(vec![(1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[]) + ); + assert_eq!( + hashmap_of(vec![(1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_a]) + ); + assert_eq!( + hashmap_of(vec![(1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_b]) + ); + assert_eq!( + hashmap_of(vec![(1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_c]) + ); + assert_eq!( + hashmap_of(vec![(1, 2)]), + prioritization_fee_cache + .get_prioritization_fees(&[write_account_a, write_account_b]) + ); + assert_eq!( + hashmap_of(vec![(1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[ + write_account_a, + write_account_b, + write_account_c + ]) + ); + } + + // Assert after add one transaction for slot 2 + { + let txs = vec![ + build_sanitized_transaction_for_test(4, &write_account_b, &write_account_c), + build_sanitized_transaction_for_test( + 3, + &Pubkey::new_unique(), + &Pubkey::new_unique(), + ), + ]; + sync_update(&mut prioritization_fee_cache, bank2, txs.iter()); + // before block is marked as completed + assert_eq!( + hashmap_of(vec![(1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[]) + ); + assert_eq!( + hashmap_of(vec![(1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_a]) + ); + assert_eq!( + hashmap_of(vec![(1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_b]) + ); + assert_eq!( + hashmap_of(vec![(1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_c]) + ); + assert_eq!( + hashmap_of(vec![(1, 2)]), + prioritization_fee_cache + .get_prioritization_fees(&[write_account_a, write_account_b]) + ); + assert_eq!( + hashmap_of(vec![(1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[ + write_account_a, + write_account_b, + write_account_c + ]) + ); + // after block is completed + sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, 2); + assert_eq!( + hashmap_of(vec![(2, 3), (1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[]), + ); + assert_eq!( + hashmap_of(vec![(2, 3), (1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_a]), + ); + assert_eq!( + hashmap_of(vec![(2, 4), (1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_b]), + ); + assert_eq!( + hashmap_of(vec![(2, 4), (1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_c]), + ); + assert_eq!( + hashmap_of(vec![(2, 4), (1, 2)]), + prioritization_fee_cache + .get_prioritization_fees(&[write_account_a, write_account_b]), + ); + assert_eq!( + hashmap_of(vec![(2, 4), (1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[ + write_account_a, + write_account_b, + write_account_c, + ]), + ); + } + + // Assert after add one transaction for slot 3 + { + let txs = vec![ + build_sanitized_transaction_for_test(6, &write_account_a, &write_account_c), + build_sanitized_transaction_for_test( + 5, + &Pubkey::new_unique(), + &Pubkey::new_unique(), + ), + ]; + sync_update(&mut prioritization_fee_cache, bank3, txs.iter()); + // before block is marked as completed + assert_eq!( + hashmap_of(vec![(2, 3), (1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[]), + ); + assert_eq!( + hashmap_of(vec![(2, 3), (1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_a]), + ); + assert_eq!( + hashmap_of(vec![(2, 4), (1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_b]), + ); + assert_eq!( + hashmap_of(vec![(2, 4), (1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_c]), + ); + assert_eq!( + hashmap_of(vec![(2, 4), (1, 2)]), + prioritization_fee_cache + .get_prioritization_fees(&[write_account_a, write_account_b]), + ); + assert_eq!( + hashmap_of(vec![(2, 4), (1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[ + write_account_a, + write_account_b, + write_account_c, + ]), + ); + // after block is completed + sync_finalize_priority_fee_for_test(&mut prioritization_fee_cache, 3); + assert_eq!( + hashmap_of(vec![(3, 5), (2, 3), (1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[]), + ); + assert_eq!( + hashmap_of(vec![(3, 6), (2, 3), (1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_a]), + ); + assert_eq!( + hashmap_of(vec![(3, 5), (2, 4), (1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_b]), + ); + assert_eq!( + hashmap_of(vec![(3, 6), (2, 4), (1, 1)]), + prioritization_fee_cache.get_prioritization_fees(&[write_account_c]), + ); + assert_eq!( + hashmap_of(vec![(3, 6), (2, 4), (1, 2)]), + prioritization_fee_cache + .get_prioritization_fees(&[write_account_a, write_account_b]), + ); + assert_eq!( + hashmap_of(vec![(3, 6), (2, 4), (1, 2)]), + prioritization_fee_cache.get_prioritization_fees(&[ + write_account_a, + write_account_b, + write_account_c, + ]), + ); + } + } +} diff --git a/runtime/src/pubkey_bins.rs b/runtime/src/pubkey_bins.rs index 9d1938758dcfe0..40206829cf3cf5 100644 --- a/runtime/src/pubkey_bins.rs +++ b/runtime/src/pubkey_bins.rs @@ -37,7 +37,7 @@ impl PubkeyBinCalculator24 { pub fn lowest_pubkey_from_bin(&self, mut bin: usize, bins: usize) -> Pubkey { assert!(bin < bins); bin <<= self.shift_bits; - let mut pubkey = Pubkey::new(&[0; 32]); + let mut pubkey = Pubkey::from([0; 32]); pubkey.as_mut()[0] = ((bin / 256 / 256) & 0xff) as u8; pubkey.as_mut()[1] = ((bin / 256) & 0xff) as u8; pubkey.as_mut()[2] = (bin & 0xff) as u8; @@ -75,7 +75,7 @@ pub mod tests { #[test] fn test_pubkey_bins_pubkeys() { - let mut pk = Pubkey::new(&[0; 32]); + let mut pk = Pubkey::from([0; 32]); for i in 0..=8 { let bins = 2usize.pow(i); let calc = PubkeyBinCalculator24::new(bins); @@ -106,7 +106,7 @@ pub mod tests { } for i in 9..=16 { - let mut pk = Pubkey::new(&[0; 32]); + let mut pk = Pubkey::from([0; 32]); let bins = 2usize.pow(i); let calc = PubkeyBinCalculator24::new(bins); @@ -118,7 +118,7 @@ pub mod tests { pk.as_mut()[1] = 0xff; assert_eq!(bins - 1, calc.bin_from_pubkey(&pk)); - let mut pk = Pubkey::new(&[0; 32]); + let mut pk = Pubkey::from([0; 32]); for bin in 0..bins { let mut target = (bin << shift_bits) as u16; pk.as_mut()[0] = (target / 256) as u8; @@ -142,7 +142,7 @@ pub mod tests { } for i in 17..=24 { - let mut pk = Pubkey::new(&[0; 32]); + let mut pk = Pubkey::from([0; 32]); let bins = 2usize.pow(i); let calc = PubkeyBinCalculator24::new(bins); @@ -155,7 +155,7 @@ pub mod tests { pk.as_mut()[2] = 0xff; assert_eq!(bins - 1, calc.bin_from_pubkey(&pk)); - let mut pk = Pubkey::new(&[0; 32]); + let mut pk = Pubkey::from([0; 32]); for bin in 0..bins { let mut target = (bin << shift_bits) as u32; pk.as_mut()[0] = (target / 256 / 256) as u8; diff --git a/runtime/src/read_only_accounts_cache.rs b/runtime/src/read_only_accounts_cache.rs index f58eb70a9ef382..ed35f24fbfbd2d 100644 --- a/runtime/src/read_only_accounts_cache.rs +++ b/runtime/src/read_only_accounts_cache.rs @@ -38,6 +38,7 @@ pub(crate) struct ReadOnlyAccountsCache { data_size: AtomicUsize, hits: AtomicU64, misses: AtomicU64, + evicts: AtomicU64, } impl ReadOnlyAccountsCache { @@ -49,6 +50,7 @@ impl ReadOnlyAccountsCache { data_size: AtomicUsize::default(), hits: AtomicU64::default(), misses: AtomicU64::default(), + evicts: AtomicU64::default(), } } @@ -107,13 +109,16 @@ impl ReadOnlyAccountsCache { } }; // Evict entries from the front of the queue. + let mut num_evicts = 0; while self.data_size.load(Ordering::Relaxed) > self.max_data_size { let (pubkey, slot) = match self.queue.lock().unwrap().get_first() { None => break, Some(key) => *key, }; + num_evicts += 1; self.remove(pubkey, slot); } + self.evicts.fetch_add(num_evicts, Ordering::Relaxed); } pub(crate) fn remove(&self, pubkey: Pubkey, slot: Slot) -> Option { @@ -135,10 +140,12 @@ impl ReadOnlyAccountsCache { self.data_size.load(Ordering::Relaxed) } - pub(crate) fn get_and_reset_stats(&self) -> (u64, u64) { + pub(crate) fn get_and_reset_stats(&self) -> (u64, u64, u64) { let hits = self.hits.swap(0, Ordering::Relaxed); let misses = self.misses.swap(0, Ordering::Relaxed); - (hits, misses) + let evicts = self.evicts.swap(0, Ordering::Relaxed); + + (hits, misses, evicts) } } diff --git a/runtime/src/rent_paying_accounts_by_partition.rs b/runtime/src/rent_paying_accounts_by_partition.rs index 286e5eebd5483b..ffb3bf4de35f75 100644 --- a/runtime/src/rent_paying_accounts_by_partition.rs +++ b/runtime/src/rent_paying_accounts_by_partition.rs @@ -5,6 +5,10 @@ use { std::collections::HashSet, }; +lazy_static! { + pub static ref EMPTY_HASHSET: HashSet = HashSet::default(); +} + /// populated at startup with the accounts that were found that are rent paying. /// These are the 'possible' rent paying accounts. /// This set can never grow during runtime since it is not possible to create rent paying accounts now. @@ -45,7 +49,9 @@ impl RentPayingAccountsByPartition { &self, partition_end_index: PartitionIndex, ) -> &HashSet { - &self.accounts[partition_end_index as usize] + self.accounts + .get(partition_end_index as usize) + .unwrap_or(&EMPTY_HASHSET) } pub fn is_initialized(&self) -> bool { self.partition_count != 0 @@ -59,7 +65,7 @@ pub(crate) mod tests { #[test] fn test_add() { let mut test = RentPayingAccountsByPartition::new(&EpochSchedule::custom(32, 0, false)); - let pk = Pubkey::new(&[1; 32]); + let pk = Pubkey::from([1; 32]); test.add_account(&pk); // make sure duplicate adds only result in a single item test.add_account(&pk); diff --git a/runtime/src/secondary_index.rs b/runtime/src/secondary_index.rs index c1b5d4336876a5..2987f066776a0e 100644 --- a/runtime/src/secondary_index.rs +++ b/runtime/src/secondary_index.rs @@ -120,12 +120,10 @@ impl pub fn insert(&self, key: &Pubkey, inner_key: &Pubkey) { { - let pubkeys_map = self.index.get(key).unwrap_or_else(|| { - self.index - .entry(*key) - .or_insert(SecondaryIndexEntryType::default()) - .downgrade() - }); + let pubkeys_map = self + .index + .get(key) + .unwrap_or_else(|| self.index.entry(*key).or_default().downgrade()); pubkeys_map.insert_if_not_exists(inner_key, &self.stats.num_inner_keys); } diff --git a/runtime/src/serde_snapshot.rs b/runtime/src/serde_snapshot.rs index e1afe5fec9f4ab..96bdd95fb27b74 100644 --- a/runtime/src/serde_snapshot.rs +++ b/runtime/src/serde_snapshot.rs @@ -8,7 +8,7 @@ use { accounts_index::AccountSecondaryIndexes, accounts_update_notifier_interface::AccountsUpdateNotifier, append_vec::{AppendVec, StoredMetaWriteVersion}, - bank::{Bank, BankFieldsToDeserialize, BankRc}, + bank::{Bank, BankFieldsToDeserialize, BankIncrementalSnapshotPersistence, BankRc}, blockhash_queue::BlockhashQueue, builtins::Builtins, epoch_stakes::EpochStakes, @@ -76,6 +76,7 @@ pub struct AccountsDbFields( /// slots that were roots within the last epoch for which we care about the hash value #[serde(deserialize_with = "default_on_eof")] Vec<(Slot, Hash)>, + // here? ); /// Helper type to wrap BufReader streams when deserializing and reconstructing from either just a @@ -192,6 +193,7 @@ trait TypeContext<'a>: PartialEq { stream_reader: &mut BufReader, stream_writer: &mut BufWriter, accounts_hash: &Hash, + incremental_snapshot_persistence: Option<&BankIncrementalSnapshotPersistence>, ) -> std::result::Result<(), Box> where R: Read, @@ -367,12 +369,18 @@ fn reserialize_bank_fields_with_new_hash( stream_reader: &mut BufReader, stream_writer: &mut BufWriter, accounts_hash: &Hash, + incremental_snapshot_persistence: Option<&BankIncrementalSnapshotPersistence>, ) -> Result<(), Error> where W: Write, R: Read, { - newer::Context::reserialize_bank_fields_with_hash(stream_reader, stream_writer, accounts_hash) + newer::Context::reserialize_bank_fields_with_hash( + stream_reader, + stream_writer, + accounts_hash, + incremental_snapshot_persistence, + ) } /// effectively updates the accounts hash in the serialized bank file on disk @@ -384,6 +392,7 @@ pub fn reserialize_bank_with_new_accounts_hash( bank_snapshots_dir: impl AsRef, slot: Slot, accounts_hash: &Hash, + incremental_snapshot_persistence: Option<&BankIncrementalSnapshotPersistence>, ) -> bool { let bank_post = snapshot_utils::get_bank_snapshots_dir(bank_snapshots_dir, slot); let bank_post = bank_post.join(snapshot_utils::get_snapshot_file_name(slot)); @@ -401,6 +410,7 @@ pub fn reserialize_bank_with_new_accounts_hash( &mut BufReader::new(file), &mut BufWriter::new(file_out), accounts_hash, + incremental_snapshot_persistence, ) .unwrap(); } @@ -775,7 +785,7 @@ where let accounts_db = Arc::new(accounts_db); let accounts_db_clone = accounts_db.clone(); let handle = Builder::new() - .name("notify_account_restore_from_snapshot".to_string()) + .name("solNfyAccRestor".to_string()) .spawn(move || { accounts_db_clone.notify_account_restore_from_snapshot(); }) diff --git a/runtime/src/serde_snapshot/newer.rs b/runtime/src/serde_snapshot/newer.rs index 3dd73803cf3010..f8a2b5755890cb 100644 --- a/runtime/src/serde_snapshot/newer.rs +++ b/runtime/src/serde_snapshot/newer.rs @@ -96,6 +96,8 @@ impl From for BankFieldsToDeserialize { stakes: dvb.stakes, epoch_stakes: dvb.epoch_stakes, is_delta: dvb.is_delta, + incremental_snapshot_persistence: None, + epoch_accounts_hash: None, } } } @@ -209,6 +211,7 @@ impl<'a> TypeContext<'a> for Context { // we can grab it on restart. // TODO: if we do a snapshot version bump, consider moving this out. lamports_per_signature, + // None::, this will be saved starting in 1.12 ) .serialize(serializer) } @@ -314,6 +317,13 @@ impl<'a> TypeContext<'a> for Context { bank_fields.fee_rate_governor = bank_fields .fee_rate_governor .clone_with_lamports_per_signature(lamports_per_signature); + + let incremental_snapshot_persistence = ignore_eof_error(deserialize_from(&mut stream))?; + bank_fields.incremental_snapshot_persistence = incremental_snapshot_persistence; + + let epoch_accounts_hash = ignore_eof_error(deserialize_from(stream))?; + bank_fields.epoch_accounts_hash = epoch_accounts_hash; + Ok((bank_fields, accounts_db_fields)) } @@ -327,12 +337,13 @@ impl<'a> TypeContext<'a> for Context { } /// deserialize the bank from 'stream_reader' - /// modify the accounts_hash + /// modify the accounts_hash and incremental_snapshot_persistence /// reserialize the bank to 'stream_writer' fn reserialize_bank_fields_with_hash( stream_reader: &mut BufReader, stream_writer: &mut BufWriter, accounts_hash: &Hash, + _incremental_snapshot_persistence: Option<&BankIncrementalSnapshotPersistence>, ) -> std::result::Result<(), Box> where R: Read, @@ -345,6 +356,7 @@ impl<'a> TypeContext<'a> for Context { let blockhash_queue = RwLock::new(rhs.blockhash_queue.clone()); let hard_forks = RwLock::new(rhs.hard_forks.clone()); let lamports_per_signature = rhs.fee_rate_governor.lamports_per_signature; + let bank = SerializableVersionedBank { blockhash_queue: &blockhash_queue, ancestors: &rhs.ancestors, @@ -382,7 +394,12 @@ impl<'a> TypeContext<'a> for Context { bincode::serialize_into( stream_writer, - &(bank, accounts_db_fields, lamports_per_signature), + &( + bank, + accounts_db_fields, + lamports_per_signature, + // incremental_snapshot_persistence, this will be saved starting in 1.12 + ), ) } } diff --git a/runtime/src/serde_snapshot/tests.rs b/runtime/src/serde_snapshot/tests.rs index 8b5e82526993ed..2d0fceeddddbf5 100644 --- a/runtime/src/serde_snapshot/tests.rs +++ b/runtime/src/serde_snapshot/tests.rs @@ -190,8 +190,12 @@ fn test_bank_serialize_style( serde_style: SerdeStyle, reserialize_accounts_hash: bool, update_accounts_hash: bool, + _incremental_snapshot_persistence: bool, ) { solana_logger::setup(); + + // in 1.11, don't test incremental snapshot persistence because we are not writing it - only reading it and ignoring it + let incremental_snapshot_persistence = false; let (genesis_config, _) = create_genesis_config(500); let bank0 = Arc::new(Bank::new_for_tests(&genesis_config)); let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1); @@ -236,8 +240,18 @@ fn test_bank_serialize_style( } else { bank2.get_accounts_hash() }; - if reserialize_accounts_hash { - let slot = bank2.slot(); + + let slot = bank2.slot(); + let incremental = + incremental_snapshot_persistence.then(|| BankIncrementalSnapshotPersistence { + full_slot: slot + 1, + full_hash: Hash::new(&[1; 32]), + full_capitalization: 31, + incremental_hash: Hash::new(&[2; 32]), + incremental_capitalization: 32, + }); + + if reserialize_accounts_hash || incremental_snapshot_persistence { let temp_dir = TempDir::new().unwrap(); let slot_dir = temp_dir.path().join(slot.to_string()); let post_path = slot_dir.join(slot.to_string()); @@ -248,21 +262,32 @@ fn test_bank_serialize_style( let mut f = std::fs::File::create(&pre_path).unwrap(); f.write_all(&buf).unwrap(); } + assert!(reserialize_bank_with_new_accounts_hash( temp_dir.path(), slot, - &accounts_hash + &accounts_hash, + incremental.as_ref(), )); let previous_len = buf.len(); // larger buffer than expected to make sure the file isn't larger than expected - let mut buf_reserialized = vec![0; previous_len + 1]; + let sizeof_none = std::mem::size_of::(); + let sizeof_incremental_snapshot_persistence = + std::mem::size_of::>(); + let mut buf_reserialized = + vec![0; previous_len + sizeof_incremental_snapshot_persistence + 1]; { let mut f = std::fs::File::open(post_path).unwrap(); let size = f.read(&mut buf_reserialized).unwrap(); - assert_eq!(size, previous_len); + let expected = if !incremental_snapshot_persistence { + previous_len + } else { + previous_len + sizeof_incremental_snapshot_persistence - sizeof_none + }; + assert_eq!(size, expected); buf_reserialized.truncate(size); } - if update_accounts_hash { + if update_accounts_hash || incremental_snapshot_persistence { // We cannot guarantee buffer contents are exactly the same if hash is the same. // Things like hashsets/maps have randomness in their in-mem representations. // This make serialized bytes not deterministic. @@ -310,6 +335,7 @@ fn test_bank_serialize_style( assert_eq!(dbank.get_balance(&key3.pubkey()), 0); assert_eq!(dbank.get_accounts_hash(), accounts_hash); assert!(bank2 == dbank); + assert_eq!(dbank.incremental_snapshot_persistence, incremental); } pub(crate) fn reconstruct_accounts_db_via_serialization( @@ -358,11 +384,18 @@ fn test_bank_serialize_newer() { for (reserialize_accounts_hash, update_accounts_hash) in [(false, false), (true, false), (true, true)] { - test_bank_serialize_style( - SerdeStyle::Newer, - reserialize_accounts_hash, - update_accounts_hash, - ) + for incremental_snapshot_persistence in if reserialize_accounts_hash { + [false, true].to_vec() + } else { + [false].to_vec() + } { + test_bank_serialize_style( + SerdeStyle::Newer, + reserialize_accounts_hash, + update_accounts_hash, + incremental_snapshot_persistence, + ) + } } } diff --git a/runtime/src/shared_buffer_reader.rs b/runtime/src/shared_buffer_reader.rs index 819b4bcb3f2a01..41c57b0d484ce3 100644 --- a/runtime/src/shared_buffer_reader.rs +++ b/runtime/src/shared_buffer_reader.rs @@ -75,7 +75,7 @@ impl SharedBuffer { let bg_reader_data = instance.bg_reader_data.clone(); let handle = Builder::new() - .name("solana-compressed_file_reader".to_string()) + .name("solCompFileRead".to_string()) .spawn(move || { // importantly, this thread does NOT hold a refcount on the arc of 'instance' bg_reader_data.read_entire_file_in_bg(reader, total_buffer_budget, chunk_size); diff --git a/runtime/src/snapshot_minimizer.rs b/runtime/src/snapshot_minimizer.rs index 69e7a99e8e7601..c4b5c3ab7b85f9 100644 --- a/runtime/src/snapshot_minimizer.rs +++ b/runtime/src/snapshot_minimizer.rs @@ -353,7 +353,7 @@ impl<'a> SnapshotMinimizer<'a> { .into_iter() .map(|pubkey| (*pubkey, slot)) .collect(); - self.accounts_db().purge_keys_exact(purge_pubkeys.iter()); + let _ = self.accounts_db().purge_keys_exact(purge_pubkeys.iter()); let aligned_total: u64 = AccountsDb::page_align(total_bytes as u64); if aligned_total > 0 { diff --git a/runtime/src/snapshot_utils.rs b/runtime/src/snapshot_utils.rs index 602df50dd9abb1..e11f7a65d1ca86 100644 --- a/runtime/src/snapshot_utils.rs +++ b/runtime/src/snapshot_utils.rs @@ -18,6 +18,7 @@ use { snapshot_package::{ AccountsPackage, PendingAccountsPackage, SnapshotPackage, SnapshotType, }, + status_cache, }, bincode::{config::Options, serialize_into}, bzip2::bufread::BzDecoder, @@ -27,7 +28,13 @@ use { rayon::prelude::*, regex::Regex, solana_measure::measure::Measure, - solana_sdk::{clock::Slot, genesis_config::GenesisConfig, hash::Hash, pubkey::Pubkey}, + solana_sdk::{ + clock::Slot, + genesis_config::GenesisConfig, + hash::Hash, + pubkey::Pubkey, + slot_history::{Check, SlotHistory}, + }, std::{ cmp::Ordering, collections::{HashMap, HashSet}, @@ -222,9 +229,37 @@ pub enum SnapshotError { #[error("snapshot has mismatch: deserialized bank: {:?}, snapshot archive info: {:?}", .0, .1)] MismatchedSlotHash((Slot, Hash), (Slot, Hash)), + + #[error("snapshot slot deltas are invalid: {0}")] + VerifySlotDeltas(#[from] VerifySlotDeltasError), } pub type Result = std::result::Result; +/// Errors that can happen in `verify_slot_deltas()` +#[derive(Error, Debug, PartialEq, Eq)] +pub enum VerifySlotDeltasError { + #[error("too many entries: {0} (max: {1})")] + TooManyEntries(usize, usize), + + #[error("slot {0} is not a root")] + SlotIsNotRoot(Slot), + + #[error("slot {0} is greater than bank slot {1}")] + SlotGreaterThanMaxRoot(Slot, Slot), + + #[error("slot {0} has multiple entries")] + SlotHasMultipleEntries(Slot), + + #[error("slot {0} was not found in slot history")] + SlotNotFoundInHistory(Slot), + + #[error("slot {0} was in history but missing from slot deltas")] + SlotNotFoundInDeltas(Slot), + + #[error("slot history is bad and cannot be used to verify slot deltas")] + BadSlotHistory, +} + /// If the validator halts in the middle of `archive_snapshot_package()`, the temporary staging /// directory won't be cleaned up. Call this function to clean them up. pub fn remove_tmp_snapshot_archives(snapshot_archives_dir: impl AsRef) { @@ -344,10 +379,12 @@ pub fn archive_snapshot_package( let do_archive_files = |encoder: &mut dyn Write| -> Result<()> { let mut archive = tar::Builder::new(encoder); + // Serialize the version and snapshots files before accounts so we can quickly determine the version + // and other bank fields. This is necessary if we want to interleave unpacking with reconstruction + archive.append_path_with_name(staging_dir.as_ref().join("version"), "version")?; for dir in ["snapshots", "accounts"] { archive.append_dir_all(dir, staging_dir.as_ref().join(dir))?; } - archive.append_path_with_name(staging_dir.as_ref().join("version"), "version")?; archive.into_inner()?; Ok(()) }; @@ -385,7 +422,7 @@ pub fn archive_snapshot_package( // Atomically move the archive into position for other validators to find let metadata = fs::metadata(&archive_path) .map_err(|e| SnapshotError::IoWithSource(e, "archive path stat"))?; - fs::rename(&archive_path, &snapshot_package.path()) + fs::rename(&archive_path, snapshot_package.path()) .map_err(|e| SnapshotError::IoWithSource(e, "archive path rename"))?; purge_old_snapshot_archives( @@ -1469,10 +1506,8 @@ pub fn purge_old_snapshot_archives( incremental_snapshot_archives.sort_unstable(); let num_to_retain = if Some(base_slot) == highest_full_snapshot_slot { maximum_incremental_snapshot_archives_to_retain - } else if retained_full_snapshot_slots.contains(&base_slot) { - 1 } else { - 0 + usize::from(retained_full_snapshot_slots.contains(&base_slot)) }; trace!( "There are {} incremental snapshot archives for base slot {}, removing {} of them", @@ -1532,7 +1567,7 @@ fn untar_snapshot_create_shared_buffer( snapshot_tar: &Path, archive_format: ArchiveFormat, ) -> SharedBuffer { - let open_file = || File::open(&snapshot_tar).unwrap(); + let open_file = || File::open(snapshot_tar).unwrap(); match archive_format { ArchiveFormat::TarBzip2 => SharedBuffer::new(BzDecoder::new(BufReader::new(open_file()))), ArchiveFormat::TarGzip => SharedBuffer::new(GzDecoder::new(BufReader::new(open_file()))), @@ -1729,6 +1764,8 @@ fn rebuild_bank_from_snapshots( Ok(slot_deltas) })?; + verify_slot_deltas(slot_deltas.as_slice(), &bank)?; + bank.status_cache.write().unwrap().append(&slot_deltas); bank.prepare_rewrites_for_hash(); @@ -1737,6 +1774,106 @@ fn rebuild_bank_from_snapshots( Ok(bank) } +/// Verify that the snapshot's slot deltas are not corrupt/invalid +fn verify_slot_deltas( + slot_deltas: &[BankSlotDelta], + bank: &Bank, +) -> std::result::Result<(), VerifySlotDeltasError> { + let info = verify_slot_deltas_structural(slot_deltas, bank.slot())?; + verify_slot_deltas_with_history(&info.slots, &bank.get_slot_history(), bank.slot()) +} + +/// Verify that the snapshot's slot deltas are not corrupt/invalid +/// These checks are simple/structural +fn verify_slot_deltas_structural( + slot_deltas: &[BankSlotDelta], + bank_slot: Slot, +) -> std::result::Result { + // there should not be more entries than that status cache's max + let num_entries = slot_deltas.len(); + if num_entries > status_cache::MAX_CACHE_ENTRIES { + return Err(VerifySlotDeltasError::TooManyEntries( + num_entries, + status_cache::MAX_CACHE_ENTRIES, + )); + } + + let mut slots_seen_so_far = HashSet::new(); + for &(slot, is_root, ..) in slot_deltas { + // all entries should be roots + if !is_root { + return Err(VerifySlotDeltasError::SlotIsNotRoot(slot)); + } + + // all entries should be for slots less than or equal to the bank's slot + if slot > bank_slot { + return Err(VerifySlotDeltasError::SlotGreaterThanMaxRoot( + slot, bank_slot, + )); + } + + // there should only be one entry per slot + let is_duplicate = !slots_seen_so_far.insert(slot); + if is_duplicate { + return Err(VerifySlotDeltasError::SlotHasMultipleEntries(slot)); + } + } + + // detect serious logic error for future careless changes. :) + assert_eq!(slots_seen_so_far.len(), slot_deltas.len()); + + Ok(VerifySlotDeltasStructuralInfo { + slots: slots_seen_so_far, + }) +} + +/// Computed information from `verify_slot_deltas_structural()`, that may be reused/useful later. +#[derive(Debug, PartialEq, Eq)] +struct VerifySlotDeltasStructuralInfo { + /// All the slots in the slot deltas + slots: HashSet, +} + +/// Verify that the snapshot's slot deltas are not corrupt/invalid +/// These checks use the slot history for verification +fn verify_slot_deltas_with_history( + slots_from_slot_deltas: &HashSet, + slot_history: &SlotHistory, + bank_slot: Slot, +) -> std::result::Result<(), VerifySlotDeltasError> { + // ensure the slot history is valid (as much as possible), since we're using it to verify the + // slot deltas + if slot_history.newest() != bank_slot { + return Err(VerifySlotDeltasError::BadSlotHistory); + } + + // all slots in the slot deltas should be in the bank's slot history + let slot_missing_from_history = slots_from_slot_deltas + .iter() + .find(|slot| slot_history.check(**slot) != Check::Found); + if let Some(slot) = slot_missing_from_history { + return Err(VerifySlotDeltasError::SlotNotFoundInHistory(*slot)); + } + + // all slots in the history should be in the slot deltas (up to MAX_CACHE_ENTRIES) + // this ensures nothing was removed from the status cache + // + // go through the slot history and make sure there's an entry for each slot + // note: it's important to go highest-to-lowest since the status cache removes + // older entries first + // note: we already checked above that `bank_slot == slot_history.newest()` + let slot_missing_from_deltas = (slot_history.oldest()..=slot_history.newest()) + .rev() + .filter(|slot| slot_history.check(*slot) == Check::Found) + .take(status_cache::MAX_CACHE_ENTRIES) + .find(|slot| !slots_from_slot_deltas.contains(slot)); + if let Some(slot) = slot_missing_from_deltas { + return Err(VerifySlotDeltasError::SlotNotFoundInDeltas(slot)); + } + + Ok(()) +} + pub(crate) fn get_snapshot_file_name(slot: Slot) -> String { slot.to_string() } @@ -2036,6 +2173,7 @@ pub fn package_and_archive_full_snapshot( accounts_package.snapshot_links.path(), accounts_package.slot, &bank.get_accounts_hash(), + None, ); let snapshot_package = SnapshotPackage::new(accounts_package, bank.get_accounts_hash()); @@ -2088,6 +2226,7 @@ pub fn package_and_archive_incremental_snapshot( accounts_package.snapshot_links.path(), accounts_package.slot, &bank.get_accounts_hash(), + None, ); let snapshot_package = SnapshotPackage::new(accounts_package, bank.get_accounts_hash()); @@ -2156,13 +2295,14 @@ fn can_submit_accounts_package( mod tests { use { super::*, - crate::accounts_db::ACCOUNTS_DB_CONFIG_FOR_TESTING, + crate::{accounts_db::ACCOUNTS_DB_CONFIG_FOR_TESTING, status_cache::Status}, assert_matches::assert_matches, bincode::{deserialize_from, serialize_into}, solana_sdk::{ genesis_config::create_genesis_config, native_token::sol_to_lamports, signature::{Keypair, Signer}, + slot_history::SlotHistory, system_transaction, transaction::SanitizedTransaction, }, @@ -2769,7 +2909,7 @@ mod tests { let temp_snap_dir = tempfile::TempDir::new().unwrap(); for snap_name in snapshot_names { - let snap_path = temp_snap_dir.path().join(&snap_name); + let snap_path = temp_snap_dir.path().join(snap_name); let mut _snap_file = File::create(snap_path); } purge_old_snapshot_archives( @@ -3817,4 +3957,155 @@ mod tests { assert_eq!(expected_result, actual_result); } } + + #[test] + fn test_verify_slot_deltas_structural_good() { + // NOTE: slot deltas do not need to be sorted + let slot_deltas = vec![ + (222, true, Status::default()), + (333, true, Status::default()), + (111, true, Status::default()), + ]; + + let bank_slot = 333; + let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot); + assert_eq!( + result, + Ok(VerifySlotDeltasStructuralInfo { + slots: HashSet::from([111, 222, 333]) + }) + ); + } + + #[test] + fn test_verify_slot_deltas_structural_bad_too_many_entries() { + let bank_slot = status_cache::MAX_CACHE_ENTRIES as Slot + 1; + let slot_deltas: Vec<_> = (0..bank_slot) + .map(|slot| (slot, true, Status::default())) + .collect(); + + let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot); + assert_eq!( + result, + Err(VerifySlotDeltasError::TooManyEntries( + status_cache::MAX_CACHE_ENTRIES + 1, + status_cache::MAX_CACHE_ENTRIES + )), + ); + } + + #[test] + fn test_verify_slot_deltas_structural_bad_slot_not_root() { + let slot_deltas = vec![ + (111, true, Status::default()), + (222, false, Status::default()), // <-- slot is not a root + (333, true, Status::default()), + ]; + + let bank_slot = 333; + let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot); + assert_eq!(result, Err(VerifySlotDeltasError::SlotIsNotRoot(222))); + } + + #[test] + fn test_verify_slot_deltas_structural_bad_slot_greater_than_bank() { + let slot_deltas = vec![ + (222, true, Status::default()), + (111, true, Status::default()), + (555, true, Status::default()), // <-- slot is greater than the bank slot + ]; + + let bank_slot = 444; + let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot); + assert_eq!( + result, + Err(VerifySlotDeltasError::SlotGreaterThanMaxRoot( + 555, bank_slot + )), + ); + } + + #[test] + fn test_verify_slot_deltas_structural_bad_slot_has_multiple_entries() { + let slot_deltas = vec![ + (111, true, Status::default()), + (222, true, Status::default()), + (111, true, Status::default()), // <-- slot is a duplicate + ]; + + let bank_slot = 222; + let result = verify_slot_deltas_structural(slot_deltas.as_slice(), bank_slot); + assert_eq!( + result, + Err(VerifySlotDeltasError::SlotHasMultipleEntries(111)), + ); + } + + #[test] + fn test_verify_slot_deltas_with_history_good() { + let mut slots_from_slot_deltas = HashSet::default(); + let mut slot_history = SlotHistory::default(); + // note: slot history expects slots to be added in numeric order + for slot in [0, 111, 222, 333, 444] { + slots_from_slot_deltas.insert(slot); + slot_history.add(slot); + } + + let bank_slot = 444; + let result = + verify_slot_deltas_with_history(&slots_from_slot_deltas, &slot_history, bank_slot); + assert_eq!(result, Ok(())); + } + + #[test] + fn test_verify_slot_deltas_with_history_bad_slot_history() { + let bank_slot = 444; + let result = verify_slot_deltas_with_history( + &HashSet::default(), + &SlotHistory::default(), // <-- will only have an entry for slot 0 + bank_slot, + ); + assert_eq!(result, Err(VerifySlotDeltasError::BadSlotHistory)); + } + + #[test] + fn test_verify_slot_deltas_with_history_bad_slot_not_in_history() { + let slots_from_slot_deltas = HashSet::from([ + 0, // slot history has slot 0 added by default + 444, 222, + ]); + let mut slot_history = SlotHistory::default(); + slot_history.add(444); // <-- slot history is missing slot 222 + + let bank_slot = 444; + let result = + verify_slot_deltas_with_history(&slots_from_slot_deltas, &slot_history, bank_slot); + + assert_eq!( + result, + Err(VerifySlotDeltasError::SlotNotFoundInHistory(222)), + ); + } + + #[test] + fn test_verify_slot_deltas_with_history_bad_slot_not_in_deltas() { + let slots_from_slot_deltas = HashSet::from([ + 0, // slot history has slot 0 added by default + 444, 222, + // <-- slot deltas is missing slot 333 + ]); + let mut slot_history = SlotHistory::default(); + slot_history.add(222); + slot_history.add(333); + slot_history.add(444); + + let bank_slot = 444; + let result = + verify_slot_deltas_with_history(&slots_from_slot_deltas, &slot_history, bank_slot); + + assert_eq!( + result, + Err(VerifySlotDeltasError::SlotNotFoundInDeltas(333)), + ); + } } diff --git a/runtime/src/stake_account.rs b/runtime/src/stake_account.rs index b335255d7ec680..8421e3783211ab 100644 --- a/runtime/src/stake_account.rs +++ b/runtime/src/stake_account.rs @@ -29,7 +29,7 @@ pub enum Error { #[error(transparent)] InstructionError(#[from] InstructionError), #[error("Invalid delegation: {0:?}")] - InvalidDelegation(StakeState), + InvalidDelegation(Box), #[error("Invalid stake account owner: {0}")] InvalidOwner(/*owner:*/ Pubkey), } @@ -79,7 +79,9 @@ impl TryFrom for StakeAccount { fn try_from(account: AccountSharedData) -> Result { let stake_account = StakeAccount::<()>::try_from(account)?; if stake_account.stake_state.delegation().is_none() { - return Err(Error::InvalidDelegation(stake_account.stake_state)); + return Err(Error::InvalidDelegation(Box::new( + stake_account.stake_state, + ))); } Ok(Self { account: stake_account.account, diff --git a/runtime/src/stakes.rs b/runtime/src/stakes.rs index 1eefe07c3edc43..f8ea7fb007bef3 100644 --- a/runtime/src/stakes.rs +++ b/runtime/src/stakes.rs @@ -8,6 +8,7 @@ use { }, dashmap::DashMap, im::HashMap as ImHashMap, + log::error, num_derive::ToPrimitive, num_traits::ToPrimitive, rayon::{prelude::*, ThreadPool}, @@ -19,7 +20,7 @@ use { }, solana_vote_program::vote_state::VoteState, std::{ - collections::HashMap, + collections::{HashMap, HashSet}, ops::Add, sync::{Arc, RwLock, RwLockReadGuard}, }, @@ -34,6 +35,12 @@ pub enum Error { InvalidStakeAccount(#[from] stake_account::Error), #[error("Stake account not found: {0}")] StakeAccountNotFound(Pubkey), + #[error("Vote account mismatch: {0}")] + VoteAccountMismatch(Pubkey), + #[error("Vote account not cached: {0}")] + VoteAccountNotCached(Pubkey), + #[error("Vote account not found: {0}")] + VoteAccountNotFound(Pubkey), } #[derive(Debug, Clone, PartialEq, Eq, ToPrimitive)] @@ -222,6 +229,38 @@ impl Stakes { Err(Error::InvalidDelegation(*pubkey)) } }); + // Assert that cached vote accounts are consistent with accounts-db. + for (pubkey, vote_account) in stakes.vote_accounts.iter() { + let account = match get_account(pubkey) { + None => return Err(Error::VoteAccountNotFound(*pubkey)), + Some(account) => account, + }; + let vote_account = vote_account.account(); + if vote_account != &account { + error!("vote account mismatch: {pubkey}, {vote_account:?}, {account:?}"); + return Err(Error::VoteAccountMismatch(*pubkey)); + } + } + // Assert that all valid vote-accounts referenced in + // stake delegations are already cached. + let voter_pubkeys: HashSet = stakes + .stake_delegations + .values() + .map(|delegation| delegation.voter_pubkey) + .filter(|voter_pubkey| stakes.vote_accounts.get(voter_pubkey).is_none()) + .collect(); + for pubkey in voter_pubkeys { + let account = match get_account(&pubkey) { + None => continue, + Some(account) => account, + }; + if VoteState::is_correct_size_and_initialized(account.data()) + && VoteAccount::try_from(account.clone()).is_ok() + { + error!("vote account not cached: {pubkey}, {account:?}"); + return Err(Error::VoteAccountNotCached(pubkey)); + } + } Ok(Self { vote_accounts: stakes.vote_accounts.clone(), stake_delegations: stake_delegations.collect::>()?, diff --git a/runtime/src/status_cache.rs b/runtime/src/status_cache.rs index f9d48941e8f3e1..bb7cfc98db5c99 100644 --- a/runtime/src/status_cache.rs +++ b/runtime/src/status_cache.rs @@ -154,9 +154,7 @@ impl StatusCache { key: K, ancestors: &Ancestors, ) -> Option<(Slot, T)> { - let mut keys = vec![]; - let mut val: Vec<_> = self.cache.iter().map(|(k, _)| *k).collect(); - keys.append(&mut val); + let keys: Vec<_> = self.cache.keys().copied().collect(); for blockhash in keys.iter() { trace!("get_status_any_blockhash: trying {}", blockhash); @@ -315,11 +313,11 @@ mod tests { let blockhash = hash(Hash::default().as_ref()); let status_cache = BankStatusCache::default(); assert_eq!( - status_cache.get_status(&sig, &blockhash, &Ancestors::default()), + status_cache.get_status(sig, &blockhash, &Ancestors::default()), None ); assert_eq!( - status_cache.get_status_any_blockhash(&sig, &Ancestors::default()), + status_cache.get_status_any_blockhash(sig, &Ancestors::default()), None ); } @@ -330,13 +328,13 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); let ancestors = vec![(0, 1)].into_iter().collect(); - status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.insert(&blockhash, sig, 0, ()); assert_eq!( - status_cache.get_status(&sig, &blockhash, &ancestors), + status_cache.get_status(sig, &blockhash, &ancestors), Some((0, ())) ); assert_eq!( - status_cache.get_status_any_blockhash(&sig, &ancestors), + status_cache.get_status_any_blockhash(sig, &ancestors), Some((0, ())) ); } @@ -347,12 +345,9 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); let ancestors = Ancestors::default(); - status_cache.insert(&blockhash, &sig, 1, ()); - assert_eq!(status_cache.get_status(&sig, &blockhash, &ancestors), None); - assert_eq!( - status_cache.get_status_any_blockhash(&sig, &ancestors), - None - ); + status_cache.insert(&blockhash, sig, 1, ()); + assert_eq!(status_cache.get_status(sig, &blockhash, &ancestors), None); + assert_eq!(status_cache.get_status_any_blockhash(sig, &ancestors), None); } #[test] @@ -361,10 +356,10 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); let ancestors = Ancestors::default(); - status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.insert(&blockhash, sig, 0, ()); status_cache.add_root(0); assert_eq!( - status_cache.get_status(&sig, &blockhash, &ancestors), + status_cache.get_status(sig, &blockhash, &ancestors), Some((0, ())) ); } @@ -375,13 +370,13 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); let ancestors = vec![(0, 0)].into_iter().collect(); - status_cache.insert(&blockhash, &sig, 0, ()); - status_cache.insert(&blockhash, &sig, 1, ()); + status_cache.insert(&blockhash, sig, 0, ()); + status_cache.insert(&blockhash, sig, 1, ()); for i in 0..(MAX_CACHE_ENTRIES + 1) { status_cache.add_root(i as u64); } assert!(status_cache - .get_status(&sig, &blockhash, &ancestors) + .get_status(sig, &blockhash, &ancestors) .is_some()); } @@ -391,11 +386,11 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); let ancestors = Ancestors::default(); - status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.insert(&blockhash, sig, 0, ()); for i in 0..(MAX_CACHE_ENTRIES + 1) { status_cache.add_root(i as u64); } - assert_eq!(status_cache.get_status(&sig, &blockhash, &ancestors), None); + assert_eq!(status_cache.get_status(sig, &blockhash, &ancestors), None); } #[test] @@ -404,10 +399,10 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); let ancestors = Ancestors::default(); - status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.insert(&blockhash, sig, 0, ()); status_cache.add_root(0); status_cache.clear(); - assert_eq!(status_cache.get_status(&sig, &blockhash, &ancestors), None); + assert_eq!(status_cache.get_status(sig, &blockhash, &ancestors), None); } #[test] @@ -418,9 +413,9 @@ mod tests { let ancestors = Ancestors::default(); status_cache.add_root(0); status_cache.clear(); - status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.insert(&blockhash, sig, 0, ()); assert!(status_cache - .get_status(&sig, &blockhash, &ancestors) + .get_status(sig, &blockhash, &ancestors) .is_some()); } @@ -430,7 +425,7 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); status_cache.clear(); - status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.insert(&blockhash, sig, 0, ()); let (_, index, sig_map) = status_cache.cache.get(&blockhash).unwrap(); let sig_slice: &[u8; CACHED_KEY_SIZE] = arrayref::array_ref![sig.as_ref(), *index, CACHED_KEY_SIZE]; @@ -443,7 +438,7 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); status_cache.clear(); - status_cache.insert(&blockhash, &sig, 0, ()); + status_cache.insert(&blockhash, sig, 0, ()); let slot_deltas = status_cache.slot_deltas(&[0]); let cache = StatusCache::from_slot_deltas(&slot_deltas); assert_eq!(cache, status_cache); @@ -458,9 +453,9 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); let blockhash2 = hash(blockhash.as_ref()); - status_cache.insert(&blockhash, &sig, 0, ()); - status_cache.insert(&blockhash, &sig, 1, ()); - status_cache.insert(&blockhash2, &sig, 1, ()); + status_cache.insert(&blockhash, sig, 0, ()); + status_cache.insert(&blockhash, sig, 1, ()); + status_cache.insert(&blockhash2, sig, 1, ()); for i in 0..(MAX_CACHE_ENTRIES + 1) { status_cache.add_root(i as u64); } @@ -484,9 +479,9 @@ mod tests { let mut status_cache = BankStatusCache::default(); let blockhash = hash(Hash::default().as_ref()); let blockhash2 = hash(blockhash.as_ref()); - status_cache.insert(&blockhash, &sig, 0, ()); - status_cache.insert(&blockhash, &sig, 1, ()); - status_cache.insert(&blockhash2, &sig, 1, ()); + status_cache.insert(&blockhash, sig, 0, ()); + status_cache.insert(&blockhash, sig, 1, ()); + status_cache.insert(&blockhash2, sig, 1, ()); let mut ancestors0 = Ancestors::default(); ancestors0.insert(0, 0); @@ -495,17 +490,17 @@ mod tests { // Clear slot 0 related data assert!(status_cache - .get_status(&sig, &blockhash, &ancestors0) + .get_status(sig, &blockhash, &ancestors0) .is_some()); status_cache.clear_slot_entries(0); assert!(status_cache - .get_status(&sig, &blockhash, &ancestors0) + .get_status(sig, &blockhash, &ancestors0) .is_none()); assert!(status_cache - .get_status(&sig, &blockhash, &ancestors1) + .get_status(sig, &blockhash, &ancestors1) .is_some()); assert!(status_cache - .get_status(&sig, &blockhash2, &ancestors1) + .get_status(sig, &blockhash2, &ancestors1) .is_some()); // Check that the slot delta for slot 0 is gone, but slot 1 still @@ -517,10 +512,10 @@ mod tests { status_cache.clear_slot_entries(1); assert!(status_cache.slot_deltas.is_empty()); assert!(status_cache - .get_status(&sig, &blockhash, &ancestors1) + .get_status(sig, &blockhash, &ancestors1) .is_none()); assert!(status_cache - .get_status(&sig, &blockhash2, &ancestors1) + .get_status(sig, &blockhash2, &ancestors1) .is_none()); assert!(status_cache.cache.is_empty()); } @@ -536,13 +531,13 @@ mod tests { let blockhash = hash(blockhash.as_ref()); let sig_key = Signature::default(); let hash_key = Hash::new_unique(); - status_cache.insert(&blockhash, &sig_key, 0, ()); - status_cache.insert(&blockhash, &hash_key, 0, ()); + status_cache.insert(&blockhash, sig_key, 0, ()); + status_cache.insert(&blockhash, hash_key, 0, ()); assert!(status_cache - .get_status(&sig_key, &blockhash, &ancestors) + .get_status(sig_key, &blockhash, &ancestors) .is_some()); assert!(status_cache - .get_status(&hash_key, &blockhash, &ancestors) + .get_status(hash_key, &blockhash, &ancestors) .is_some()); } } diff --git a/runtime/src/storable_accounts.rs b/runtime/src/storable_accounts.rs index 8d79c0f78c5fe4..4c46fb77315746 100644 --- a/runtime/src/storable_accounts.rs +++ b/runtime/src/storable_accounts.rs @@ -136,7 +136,7 @@ pub mod tests { #[test] fn test_contains_multiple_slots() { - let pk = Pubkey::new(&[1; 32]); + let pk = Pubkey::from([1; 32]); let account = AccountSharedData::create(1, Vec::default(), Pubkey::default(), false, 0); let slot = 0; let test3 = ( @@ -159,7 +159,7 @@ pub mod tests { for starting_slot in 0..max_slots { let mut raw = Vec::new(); for entry in 0..entries { - let pk = Pubkey::new(&[entry; 32]); + let pk = Pubkey::from([entry; 32]); raw.push(( pk, AccountSharedData::create( diff --git a/runtime/src/system_instruction_processor.rs b/runtime/src/system_instruction_processor.rs index 67f1f931147cef..66bcf145829e06 100644 --- a/runtime/src/system_instruction_processor.rs +++ b/runtime/src/system_instruction_processor.rs @@ -662,7 +662,7 @@ mod tests { #[test] fn test_create_account() { - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let from = Pubkey::new_unique(); let to = Pubkey::new_unique(); let from_account = AccountSharedData::new(100, 0, &system_program::id()); @@ -699,7 +699,7 @@ mod tests { #[test] fn test_create_account_with_seed() { - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let from = Pubkey::new_unique(); let seed = "shiny pepper"; let to = Pubkey::create_with_seed(&from, seed, &new_owner).unwrap(); @@ -739,7 +739,7 @@ mod tests { #[test] fn test_create_account_with_seed_separate_base_account() { - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let from = Pubkey::new_unique(); let base = Pubkey::new_unique(); let seed = "shiny pepper"; @@ -802,7 +802,7 @@ mod tests { #[test] fn test_create_account_with_seed_missing_sig() { - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let from = Pubkey::new_unique(); let seed = "dull boy"; let to = Pubkey::create_with_seed(&from, seed, &new_owner).unwrap(); @@ -839,7 +839,7 @@ mod tests { #[test] fn test_create_with_zero_lamports() { // create account with zero lamports transferred - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let from = Pubkey::new_unique(); let from_account = AccountSharedData::new(100, 0, &Pubkey::new_unique()); // not from system account let to = Pubkey::new_unique(); @@ -877,7 +877,7 @@ mod tests { #[test] fn test_create_negative_lamports() { // Attempt to create account with more lamports than from_account has - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let from = Pubkey::new_unique(); let from_account = AccountSharedData::new(100, 0, &Pubkey::new_unique()); let to = Pubkey::new_unique(); @@ -960,13 +960,13 @@ mod tests { #[test] fn test_create_already_in_use() { - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let from = Pubkey::new_unique(); let from_account = AccountSharedData::new(100, 0, &system_program::id()); let owned_key = Pubkey::new_unique(); // Attempt to create system account in account already owned by another program - let original_program_owner = Pubkey::new(&[5; 32]); + let original_program_owner = Pubkey::from([5; 32]); let owned_account = AccountSharedData::new(0, 0, &original_program_owner); let unchanged_account = owned_account.clone(); let accounts = process_instruction( @@ -1057,7 +1057,7 @@ mod tests { #[test] fn test_create_unsigned() { // Attempt to create an account without signing the transfer - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let from = Pubkey::new_unique(); let from_account = AccountSharedData::new(100, 0, &system_program::id()); let owned_key = Pubkey::new_unique(); @@ -1180,7 +1180,7 @@ mod tests { #[test] fn test_create_data_populated() { // Attempt to create system account in account with populated data - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let from = Pubkey::new_unique(); let from_account = AccountSharedData::new(100, 0, &system_program::id()); let populated_key = Pubkey::new_unique(); @@ -1253,7 +1253,7 @@ mod tests { #[test] fn test_assign() { - let new_owner = Pubkey::new(&[9; 32]); + let new_owner = Pubkey::from([9; 32]); let pubkey = Pubkey::new_unique(); let account = AccountSharedData::new(100, 0, &system_program::id()); @@ -1352,7 +1352,7 @@ mod tests { fn test_transfer_lamports() { let from = Pubkey::new_unique(); let from_account = AccountSharedData::new(100, 0, &system_program::id()); - let to = Pubkey::new(&[3; 32]); + let to = Pubkey::from([3; 32]); let to_account = AccountSharedData::new(1, 0, &to); // account owner should not matter let transaction_accounts = vec![(from, from_account), (to, to_account)]; let instruction_accounts = vec![ @@ -1427,12 +1427,12 @@ mod tests { #[test] fn test_transfer_with_seed() { let base = Pubkey::new_unique(); - let base_account = AccountSharedData::new(100, 0, &Pubkey::new(&[2; 32])); // account owner should not matter + let base_account = AccountSharedData::new(100, 0, &Pubkey::from([2; 32])); // account owner should not matter let from_seed = "42".to_string(); let from_owner = system_program::id(); let from = Pubkey::create_with_seed(&base, from_seed.as_str(), &from_owner).unwrap(); let from_account = AccountSharedData::new(100, 0, &system_program::id()); - let to = Pubkey::new(&[3; 32]); + let to = Pubkey::from([3; 32]); let to_account = AccountSharedData::new(1, 0, &to); // account owner should not matter let transaction_accounts = vec![(from, from_account), (base, base_account), (to, to_account)]; @@ -1519,7 +1519,7 @@ mod tests { get_system_account_kind(&from_account), Some(SystemAccountKind::Nonce) ); - let to = Pubkey::new(&[3; 32]); + let to = Pubkey::from([3; 32]); let to_account = AccountSharedData::new(1, 0, &to); // account owner should not matter process_instruction( diff --git a/runtime/src/transaction_error_metrics.rs b/runtime/src/transaction_error_metrics.rs index deabd2cdc54f25..baa25a07364839 100644 --- a/runtime/src/transaction_error_metrics.rs +++ b/runtime/src/transaction_error_metrics.rs @@ -4,6 +4,7 @@ use solana_sdk::{clock::Slot, saturating_add_assign}; pub struct TransactionErrorMetrics { pub total: usize, pub account_in_use: usize, + pub too_many_account_locks: usize, pub account_loaded_twice: usize, pub account_not_found: usize, pub blockhash_not_found: usize, @@ -18,6 +19,10 @@ pub struct TransactionErrorMetrics { pub not_allowed_during_cluster_maintenance: usize, pub invalid_writable_account: usize, pub invalid_rent_paying_account: usize, + pub would_exceed_max_block_cost_limit: usize, + pub would_exceed_max_account_cost_limit: usize, + pub would_exceed_max_vote_cost_limit: usize, + pub would_exceed_account_data_block_limit: usize, } impl TransactionErrorMetrics { @@ -28,6 +33,7 @@ impl TransactionErrorMetrics { pub fn accumulate(&mut self, other: &TransactionErrorMetrics) { saturating_add_assign!(self.total, other.total); saturating_add_assign!(self.account_in_use, other.account_in_use); + saturating_add_assign!(self.too_many_account_locks, other.too_many_account_locks); saturating_add_assign!(self.account_loaded_twice, other.account_loaded_twice); saturating_add_assign!(self.account_not_found, other.account_not_found); saturating_add_assign!(self.blockhash_not_found, other.blockhash_not_found); @@ -54,6 +60,22 @@ impl TransactionErrorMetrics { self.invalid_rent_paying_account, other.invalid_rent_paying_account ); + saturating_add_assign!( + self.would_exceed_max_block_cost_limit, + other.would_exceed_max_block_cost_limit + ); + saturating_add_assign!( + self.would_exceed_max_account_cost_limit, + other.would_exceed_max_account_cost_limit + ); + saturating_add_assign!( + self.would_exceed_max_vote_cost_limit, + other.would_exceed_max_vote_cost_limit + ); + saturating_add_assign!( + self.would_exceed_account_data_block_limit, + other.would_exceed_account_data_block_limit + ); } pub fn report(&self, id: u32, slot: Slot) { @@ -63,6 +85,11 @@ impl TransactionErrorMetrics { ("slot", slot as i64, i64), ("total", self.total as i64, i64), ("account_in_use", self.account_in_use as i64, i64), + ( + "too_many_account_locks", + self.too_many_account_locks as i64, + i64 + ), ( "account_loaded_twice", self.account_loaded_twice as i64, @@ -105,6 +132,26 @@ impl TransactionErrorMetrics { self.invalid_rent_paying_account as i64, i64 ), + ( + "would_exceed_max_block_cost_limit", + self.would_exceed_max_block_cost_limit as i64, + i64 + ), + ( + "would_exceed_max_account_cost_limit", + self.would_exceed_max_account_cost_limit as i64, + i64 + ), + ( + "would_exceed_max_vote_cost_limit", + self.would_exceed_max_vote_cost_limit as i64, + i64 + ), + ( + "would_exceed_account_data_block_limit", + self.would_exceed_account_data_block_limit as i64, + i64 + ), ); } } diff --git a/core/src/transaction_priority_details.rs b/runtime/src/transaction_priority_details.rs similarity index 99% rename from core/src/transaction_priority_details.rs rename to runtime/src/transaction_priority_details.rs index 7f816cad2a879a..99bd7a8352c81d 100644 --- a/core/src/transaction_priority_details.rs +++ b/runtime/src/transaction_priority_details.rs @@ -25,6 +25,7 @@ pub trait GetTransactionPriorityDetails { instructions, true, // use default units per instruction true, // don't reject txs that use set compute unit price ix + true, // supports request heap frame instruction ) .ok()?; Some(TransactionPriorityDetails { diff --git a/runtime/src/verify_accounts_hash_in_background.rs b/runtime/src/verify_accounts_hash_in_background.rs index 90266e36a61a94..63a7eeed97495b 100644 --- a/runtime/src/verify_accounts_hash_in_background.rs +++ b/runtime/src/verify_accounts_hash_in_background.rs @@ -117,7 +117,7 @@ pub(crate) mod tests { let verify_ = Arc::clone(verify); verify.start(|| { Builder::new() - .name("solana-bg-hash-verifier".to_string()) + .name("solBgHashVerfy".to_string()) .spawn(move || { // should have been marked not complete before thread started assert!(!verify_.check_complete()); diff --git a/runtime/src/vote_account.rs b/runtime/src/vote_account.rs index 1feefb6ffaee46..c37dd7b2487c4b 100644 --- a/runtime/src/vote_account.rs +++ b/runtime/src/vote_account.rs @@ -3,7 +3,7 @@ use { once_cell::sync::OnceCell, serde::ser::{Serialize, Serializer}, solana_sdk::{ - account::{accounts_equal, AccountSharedData, ReadableAccount}, + account::{AccountSharedData, ReadableAccount}, instruction::InstructionError, pubkey::Pubkey, }, @@ -53,6 +53,10 @@ pub struct VoteAccounts { } impl VoteAccount { + pub(crate) fn account(&self) -> &AccountSharedData { + &self.0.account + } + pub(crate) fn lamports(&self) -> u64 { self.0.account.lamports() } @@ -255,12 +259,6 @@ impl PartialEq for VoteAccountInner { } } -impl PartialEq for VoteAccount { - fn eq(&self, other: &AccountSharedData) -> bool { - accounts_equal(&self.0.account, other) - } -} - impl Default for VoteAccounts { fn default() -> Self { Self { diff --git a/runtime/src/vote_parser.rs b/runtime/src/vote_parser.rs index c67045b0827533..9505345edbe7f5 100644 --- a/runtime/src/vote_parser.rs +++ b/runtime/src/vote_parser.rs @@ -7,7 +7,7 @@ use { signature::Signature, transaction::{SanitizedTransaction, Transaction}, }, - solana_vote_program::{vote_instruction::VoteInstruction, vote_state::VoteStateUpdate}, + solana_vote_program::vote_instruction::VoteInstruction, }; pub type ParsedVote = (Pubkey, VoteTransaction, Option, Signature); @@ -82,14 +82,12 @@ fn parse_vote_instruction_data( VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash) => { Some((VoteTransaction::from(vote_state_update), Some(hash))) } - VoteInstruction::CompactUpdateVoteState(compact_vote_state_update) => Some(( - VoteTransaction::from(VoteStateUpdate::from(compact_vote_state_update)), - None, - )), - VoteInstruction::CompactUpdateVoteStateSwitch(compact_vote_state_update, hash) => Some(( - VoteTransaction::from(VoteStateUpdate::from(compact_vote_state_update)), - Some(hash), - )), + VoteInstruction::CompactUpdateVoteState(vote_state_update) => { + Some((VoteTransaction::from(vote_state_update), None)) + } + VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, hash) => { + Some((VoteTransaction::from(vote_state_update), Some(hash))) + } VoteInstruction::Authorize(_, _) | VoteInstruction::AuthorizeChecked(_) | VoteInstruction::AuthorizeWithSeed(_) diff --git a/runtime/store-tool/Cargo.toml b/runtime/store-tool/Cargo.toml index f5ee2272c45be4..ec1b015ad36242 100644 --- a/runtime/store-tool/Cargo.toml +++ b/runtime/store-tool/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-store-tool" description = "Tool to inspect append vecs" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -12,9 +12,9 @@ publish = false [dependencies] clap = "2.33.1" log = { version = "0.4.17" } -solana-logger = { path = "../../logger", version = "=1.11.6" } -solana-runtime = { path = "..", version = "=1.11.6" } -solana-version = { path = "../../version", version = "=1.11.6" } +solana-logger = { path = "../../logger", version = "=1.14.24" } +solana-runtime = { path = "..", version = "=1.14.24" } +solana-version = { path = "../../version", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/scripts/build-downstream-projects.sh b/scripts/build-downstream-projects.sh index 465c6d239a736b..0186baf65378d9 100755 --- a/scripts/build-downstream-projects.sh +++ b/scripts/build-downstream-projects.sh @@ -6,6 +6,7 @@ set -e cd "$(dirname "$0")"/.. source ci/_ +source ci/semver_bash/semver.sh source scripts/patch-crates.sh source scripts/read-cargo-variable.sh @@ -40,22 +41,32 @@ spl() { ( # Mind the order! PROGRAMS=( + instruction-padding/program token/program token/program-2022 token/program-2022-test associated-token-account/program + token-upgrade/program feature-proposal/program governance/addin-mock/program governance/program memo/program name-service/program stake-pool/program - ) + stake-pool/single-pool + ) set -x rm -rf spl git clone https://github.com/solana-labs/solana-program-library.git spl cd spl + project_used_solana_version=$(sed -nE 's/solana-sdk = \"[>=<~]*(.*)\"/\1/p' <"token/program/Cargo.toml") + echo "used solana version: $project_used_solana_version" + if semverGT "$project_used_solana_version" "$solana_ver"; then + echo "skip" + return + fi + ./patch.crates-io.sh "$solana_dir" for program in "${PROGRAMS[@]}"; do diff --git a/scripts/cargo-install-all.sh b/scripts/cargo-install-all.sh index eae5006c6f95c1..4d677138f8b775 100755 --- a/scripts/cargo-install-all.sh +++ b/scripts/cargo-install-all.sh @@ -144,7 +144,7 @@ mkdir -p "$installDir/bin" # Exclude `spl-token` binary for net.sh builds if [[ -z "$validatorOnly" ]]; then # shellcheck disable=SC2086 # Don't want to double quote $rust_version - "$cargo" $maybeRustVersion install --locked spl-token-cli --root "$installDir" + "$cargo" $maybeRustVersion install --locked spl-token-cli --version 2.3.0 --root "$installDir" fi ) diff --git a/scripts/coverage.sh b/scripts/coverage.sh index d06bfcdb1a7cb6..059ddc2e20c654 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -79,7 +79,7 @@ NPROC=$(nproc) JOBS=$((JOBS>NPROC ? NPROC : JOBS)) RUST_LOG=solana=trace _ "$cargo" nightly test --jobs "$JOBS" --target-dir target/cov --no-run "${packages[@]}" -if RUST_LOG=solana=trace _ "$cargo" nightly test --jobs "$JOBS" --target-dir target/cov "${packages[@]}" 2> target/cov/coverage-stderr.log; then +if RUST_LOG=solana=trace _ "$cargo" nightly test --jobs "$JOBS" --target-dir target/cov "${packages[@]}" -- --nocapture 2> >(tee target/cov/coverage-stderr.log >&2); then test_status=0 else test_status=$? diff --git a/scripts/increment-cargo-version.sh b/scripts/increment-cargo-version.sh index 386b2b21239a05..7a5f48960360d4 100755 --- a/scripts/increment-cargo-version.sh +++ b/scripts/increment-cargo-version.sh @@ -33,8 +33,6 @@ done # shellcheck disable=2207 Cargo_tomls=($(find . -mindepth 2 -name Cargo.toml "${not_paths[@]}")) -# shellcheck disable=2207 -markdownFiles=($(find . -name "*.md" "${not_paths[@]}")) # Collect the name of all the internal crates crates=() @@ -138,15 +136,6 @@ for Cargo_toml in "${Cargo_tomls[@]}"; do done done -# Update all the documentation references -for file in "${markdownFiles[@]}"; do - # Set new crate version - ( - set -x - sed -i "$file" -e "s/$currentVersion/$newVersion/g" - ) -done - # Update cargo lock files scripts/cargo-for-all-lock-files.sh tree >/dev/null diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 4428b1a1b781fd..56b9536342d7a4 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-sdk" -version = "1.11.6" +version = "1.14.24" description = "Solana SDK" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -71,11 +71,11 @@ serde_derive = "1.0.103" serde_json = { version = "1.0.81", optional = true } sha2 = "0.10.2" sha3 = { version = "0.10.1", optional = true } -solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6", optional = true } -solana-program = { path = "program", version = "=1.11.6" } -solana-sdk-macro = { path = "macro", version = "=1.11.6" } +solana-frozen-abi = { path = "../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24", optional = true } +solana-program = { path = "program", version = "=1.14.24" } +solana-sdk-macro = { path = "macro", version = "=1.14.24" } thiserror = "1.0" uriparse = "0.6.4" wasm-bindgen = "0.2" @@ -86,6 +86,7 @@ js-sys = "0.3.58" [dev-dependencies] anyhow = "1.0.58" curve25519-dalek = "3.2.1" +hex = "0.4.3" static_assertions = "1.1.0" tiny-bip39 = "0.8.2" diff --git a/sdk/README.md b/sdk/README.md index c8d6b77bd47e28..f12b3bfc19186b 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -12,4 +12,4 @@ More information about Solana is available in the [Solana documentation](https:/ The [Solana Program Library](https://github.com/solana-labs/solana-program-library) provides examples of how to use this crate. -Still have questions? Ask us on [Discord](https://discordapp.com/invite/pquxPsq) +Still have questions? Ask us on [Stack Exchange](https://sola.na/sse) diff --git a/sdk/bpf/c/bpf.mk b/sdk/bpf/c/bpf.mk index 541629ad49fcfe..bf0b5fbee4fa61 100644 --- a/sdk/bpf/c/bpf.mk +++ b/sdk/bpf/c/bpf.mk @@ -15,7 +15,7 @@ OUT_DIR ?= ./out OS := $(shell uname) LLVM_DIR = $(LOCAL_PATH)../dependencies/bpf-tools/llvm -LLVM_SYSTEM_INC_DIRS := $(LLVM_DIR)/lib/clang/13.0.0/include +LLVM_SYSTEM_INC_DIRS := $(LLVM_DIR)/lib/clang/14.0.0/include COMPILER_RT_DIR = $(LOCAL_PATH)../dependencies/bpf-tools/rust/lib/rustlib/bpfel-unknown-unknown/lib STD_INC_DIRS := $(LLVM_DIR)/include STD_LIB_DIRS := $(LLVM_DIR)/lib diff --git a/sdk/bpf/c/inc/sol/cpi.h b/sdk/bpf/c/inc/sol/cpi.h index a1c4c21de5e7f7..b3748cff2240f9 100644 --- a/sdk/bpf/c/inc/sol/cpi.h +++ b/sdk/bpf/c/inc/sol/cpi.h @@ -28,10 +28,10 @@ static const uint8_t MAX_CPI_INSTRUCTION_ACCOUNTS = 255; /** * Maximum number of account info structs that can be used in a single CPI * invocation. A limit on account info structs is effectively the same as - * limiting the number of unique accounts. 64 was chosen to match the max + * limiting the number of unique accounts. 128 was chosen to match the max * number of locked accounts per transaction (MAX_TX_ACCOUNT_LOCKS). */ -static const uint8_t MAX_CPI_ACCOUNT_INFOS = 64; +static const uint16_t MAX_CPI_ACCOUNT_INFOS = 128; /** * Account Meta diff --git a/sdk/bpf/c/inc/sol/inc/cpi.inc b/sdk/bpf/c/inc/sol/inc/cpi.inc index ce615e90b84f4d..41ce4fb01a691b 100644 --- a/sdk/bpf/c/inc/sol/inc/cpi.inc +++ b/sdk/bpf/c/inc/sol/inc/cpi.inc @@ -28,10 +28,10 @@ static const uint8_t MAX_CPI_INSTRUCTION_ACCOUNTS = 255; /** * Maximum number of account info structs that can be used in a single CPI * invocation. A limit on account info structs is effectively the same as - * limiting the number of unique accounts. 64 was chosen to match the max + * limiting the number of unique accounts. 128 was chosen to match the max * number of locked accounts per transaction (MAX_TX_ACCOUNT_LOCKS). */ -static const uint8_t MAX_CPI_ACCOUNT_INFOS = 64; +static const uint16_t MAX_CPI_ACCOUNT_INFOS = 128; /** * Account Meta diff --git a/sdk/bpf/scripts/install.sh b/sdk/bpf/scripts/install.sh index 1f1a3c2c5b7a5c..205d98407fcb92 100755 --- a/sdk/bpf/scripts/install.sh +++ b/sdk/bpf/scripts/install.sh @@ -102,7 +102,7 @@ if [[ ! -e criterion-$version.md || ! -e criterion ]]; then fi # Install Rust-BPF -version=v1.28 +version=v1.29 if [[ ! -e bpf-tools-$version.md || ! -e bpf-tools ]]; then ( set -e diff --git a/sdk/cargo-build-bpf/Cargo.toml b/sdk/cargo-build-bpf/Cargo.toml index 41bc61945c363d..51741684664c8b 100644 --- a/sdk/cargo-build-bpf/Cargo.toml +++ b/sdk/cargo-build-bpf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-cargo-build-bpf" -version = "1.11.6" +version = "1.14.24" description = "Compile a local package and all of its dependencies using the Solana SBF SDK" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -12,7 +12,7 @@ publish = false [dependencies] cargo_metadata = "0.15.0" clap = { version = "3.1.5", features = ["cargo", "env"] } -solana-sdk = { path = "..", version = "=1.11.6" } +solana-sdk = { path = "..", version = "=1.14.24" } [features] program = [] diff --git a/sdk/cargo-build-bpf/src/main.rs b/sdk/cargo-build-bpf/src/main.rs index 5de2742c2add32..0ea34f68c6cb74 100644 --- a/sdk/cargo-build-bpf/src/main.rs +++ b/sdk/cargo-build-bpf/src/main.rs @@ -26,8 +26,9 @@ fn main() { args.remove(0); } } - args.push("--arch".to_string()); - args.push("bpf".to_string()); + let index = args.iter().position(|x| x == "--").unwrap_or(args.len()); + args.insert(index, "bpf".to_string()); + args.insert(index, "--arch".to_string()); print!("cargo-build-bpf child: {}", program.display()); for a in &args { print!(" {}", a); diff --git a/sdk/cargo-build-sbf/Cargo.toml b/sdk/cargo-build-sbf/Cargo.toml index 86e3446267de65..44eb3a43860c5e 100644 --- a/sdk/cargo-build-sbf/Cargo.toml +++ b/sdk/cargo-build-sbf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-cargo-build-sbf" -version = "1.11.6" +version = "1.14.24" description = "Compile a local package and all of its dependencies using the Solana SBF SDK" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,14 +10,14 @@ edition = "2021" publish = false [dependencies] -bzip2 = "0.4.3" +bzip2 = "0.4.4" cargo_metadata = "0.15.0" clap = { version = "3.1.5", features = ["cargo", "env"] } log = { version = "0.4.14", features = ["std"] } regex = "1.5.6" -solana-download-utils = { path = "../../download-utils", version = "=1.11.6" } -solana-logger = { path = "../../logger", version = "=1.11.6" } -solana-sdk = { path = "..", version = "=1.11.6" } +solana-download-utils = { path = "../../download-utils", version = "=1.14.24" } +solana-logger = { path = "../../logger", version = "=1.14.24" } +solana-sdk = { path = "..", version = "=1.14.24" } tar = "0.4.38" [dev-dependencies] diff --git a/sdk/cargo-build-sbf/src/main.rs b/sdk/cargo-build-sbf/src/main.rs index 70f1f1796a4e2b..c97eabedbe730e 100644 --- a/sdk/cargo-build-sbf/src/main.rs +++ b/sdk/cargo-build-sbf/src/main.rs @@ -141,7 +141,7 @@ fn install_if_missing( .next() .is_none() { - fs::remove_dir(&target_path).map_err(|err| err.to_string())?; + fs::remove_dir(target_path).map_err(|err| err.to_string())?; } // Check whether the package is already in ~/.cache/solana. @@ -153,9 +153,9 @@ fn install_if_missing( .unwrap_or(false) { if target_path.exists() { - fs::remove_file(&target_path).map_err(|err| err.to_string())?; + fs::remove_file(target_path).map_err(|err| err.to_string())?; } - fs::create_dir_all(&target_path).map_err(|err| err.to_string())?; + fs::create_dir_all(target_path).map_err(|err| err.to_string())?; let mut url = String::from(url); url.push('/'); url.push_str(config.sbf_tools_version); @@ -169,9 +169,7 @@ fn install_if_missing( let zip = File::open(&download_file_path).map_err(|err| err.to_string())?; let tar = BzDecoder::new(BufReader::new(zip)); let mut archive = Archive::new(tar); - archive - .unpack(&target_path) - .map_err(|err| err.to_string())?; + archive.unpack(target_path).map_err(|err| err.to_string())?; fs::remove_file(download_file_path).map_err(|err| err.to_string())?; } // Make a symbolic link source_path -> target_path in the @@ -475,7 +473,7 @@ fn build_sbf_package(config: &Config, target_directory: &Path, package: &cargo_m }) .join("release"); - env::set_current_dir(&root_package_dir).unwrap_or_else(|err| { + env::set_current_dir(root_package_dir).unwrap_or_else(|err| { error!( "Unable to set current directory to {}: {}", root_package_dir, err @@ -536,7 +534,7 @@ fn build_sbf_package(config: &Config, target_directory: &Path, package: &cargo_m // The package version directory doesn't contain a valid // installation, and it should be removed. let target_path_parent = target_path.parent().expect("Invalid package path"); - fs::remove_dir_all(&target_path_parent).unwrap_or_else(|err| { + fs::remove_dir_all(target_path_parent).unwrap_or_else(|err| { error!( "Failed to remove {} while recovering from installation failure: {}", target_path_parent.to_string_lossy(), @@ -705,7 +703,7 @@ fn build_sbf_package(config: &Config, target_directory: &Path, package: &cargo_m #[cfg(not(windows))] let output = spawn( &config.sbf_sdk.join("scripts").join("strip.sh"), - &[&program_unstripped_so, &program_so], + [&program_unstripped_so, &program_so], config.generate_child_script_on_failure, ); if config.verbose { @@ -728,7 +726,7 @@ fn build_sbf_package(config: &Config, target_directory: &Path, package: &cargo_m { let output = spawn( &dump_script, - &[&program_unstripped_so, &program_dump], + [&program_unstripped_so, &program_dump], config.generate_child_script_on_failure, ); if config.verbose { @@ -746,7 +744,7 @@ fn build_sbf_package(config: &Config, target_directory: &Path, package: &cargo_m let output = spawn( llvm_objcopy, - &[ + [ "--only-keep-debug".as_ref(), program_unstripped_so.as_os_str(), program_debug.as_os_str(), @@ -826,7 +824,7 @@ fn main() { // The following line is scanned by CI configuration script to // separate cargo caches according to the version of sbf-tools. - let sbf_tools_version = "v1.28"; + let sbf_tools_version = "v1.29"; let version = format!("{}\nsbf-tools {}", crate_version!(), sbf_tools_version); let matches = clap::Command::new(crate_name!()) .about(crate_description!()) @@ -933,7 +931,7 @@ fn main() { .arg( Arg::new("arch") .long("arch") - .possible_values(&["bpf", "sbf", "sbfv2"]) + .possible_values(["bpf", "sbf", "sbfv2"]) .default_value("sbf") .help("Build for the given SBF version"), ) diff --git a/sdk/cargo-build-sbf/tests/crates.rs b/sdk/cargo-build-sbf/tests/crates.rs index 845928e8aa6735..6fb6ad625a8a27 100644 --- a/sdk/cargo-build-sbf/tests/crates.rs +++ b/sdk/cargo-build-sbf/tests/crates.rs @@ -7,7 +7,7 @@ use std::{ #[macro_use] extern crate serial_test; -fn run_cargo_build(crate_name: &str, extra_args: &[&str]) -> Output { +fn run_cargo_build(crate_name: &str, extra_args: &[&str], test_name: &str) -> Output { let cwd = env::current_dir().expect("Unable to get current working directory"); let root = cwd .parent() @@ -20,30 +20,44 @@ fn run_cargo_build(crate_name: &str, extra_args: &[&str]) -> Output { .join(crate_name) .join("Cargo.toml"); let toml = format!("{}", toml.display()); - let mut args = vec!["--sbf-sdk", "../bpf", "--manifest-path", &toml]; + let mut args = vec!["-v", "--sbf-sdk", "../bpf", "--manifest-path", &toml]; for arg in extra_args { args.push(arg); } + args.push("--"); + args.push("-vv"); let cargo_build_sbf = root.join("target").join("debug").join("cargo-build-sbf"); + env::set_var("RUST_LOG", "debug"); let output = Command::new(cargo_build_sbf) .args(&args) .output() .expect("Error running cargo-build-sbf"); if !output.status.success() { - eprintln!("--- stdout ---"); + eprintln!("--- stdout of {} ---", test_name); io::stderr().write_all(&output.stdout).unwrap(); - eprintln!("--- stderr ---"); + eprintln!("--- stderr of {} ---", test_name); io::stderr().write_all(&output.stderr).unwrap(); eprintln!("--------------"); } output } +fn clean_target(crate_name: &str) { + let cwd = env::current_dir().expect("Unable to get current working directory"); + let target = cwd + .join("tests") + .join("crates") + .join(crate_name) + .join("target"); + fs::remove_dir_all(target).expect("Failed to remove target dir"); +} + #[test] #[serial] fn test_build() { - let output = run_cargo_build("noop", &[]); + let output = run_cargo_build("noop", &[], "test_build"); assert!(output.status.success()); + clean_target("noop"); } #[test] @@ -51,11 +65,11 @@ fn test_build() { fn test_dump() { // This test requires rustfilt. assert!(Command::new("cargo") - .args(&["install", "-f", "rustfilt"]) + .args(["install", "-f", "rustfilt"]) .status() .expect("Unable to install rustfilt required for --dump option") .success()); - let output = run_cargo_build("noop", &["--dump"]); + let output = run_cargo_build("noop", &["--dump"], "test_dump"); assert!(output.status.success()); let cwd = env::current_dir().expect("Unable to get current working directory"); let dump = cwd @@ -66,23 +80,29 @@ fn test_dump() { .join("deploy") .join("noop-dump.txt"); assert!(dump.exists()); + clean_target("noop"); } #[test] #[serial] fn test_out_dir() { - let output = run_cargo_build("noop", &["--sbf-out-dir", "tmp_out"]); + let output = run_cargo_build("noop", &["--sbf-out-dir", "tmp_out"], "test_out_dir"); assert!(output.status.success()); let cwd = env::current_dir().expect("Unable to get current working directory"); let dir = cwd.join("tmp_out"); assert!(dir.exists()); fs::remove_dir_all("tmp_out").expect("Failed to remove tmp_out dir"); + clean_target("noop"); } #[test] #[serial] fn test_generate_child_script_on_failre() { - let output = run_cargo_build("fail", &["--generate-child-script-on-failure"]); + let output = run_cargo_build( + "fail", + &["--generate-child-script-on-failure"], + "test_generate_child_script_on_failre", + ); assert!(!output.status.success()); let cwd = env::current_dir().expect("Unable to get current working directory"); let scr = cwd @@ -92,4 +112,5 @@ fn test_generate_child_script_on_failre() { .join("cargo-build-sbf-child-script-cargo.sh"); assert!(scr.exists()); fs::remove_file(scr).expect("Failed to remove script"); + clean_target("fail"); } diff --git a/sdk/cargo-build-sbf/tests/crates/fail/Cargo.toml b/sdk/cargo-build-sbf/tests/crates/fail/Cargo.toml index 82da7b8f9e63b2..3c07fe09a27581 100644 --- a/sdk/cargo-build-sbf/tests/crates/fail/Cargo.toml +++ b/sdk/cargo-build-sbf/tests/crates/fail/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fail" -version = "1.11.6" +version = "1.14.24" description = "Solana SBF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ edition = "2021" publish = false [dependencies] -solana-program = { path = "../../../../program", version = "=1.11.6" } +solana-program = { path = "../../../../program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/sdk/cargo-build-sbf/tests/crates/noop/Cargo.toml b/sdk/cargo-build-sbf/tests/crates/noop/Cargo.toml index 5e41eafed0baa2..c0ff469b386e93 100644 --- a/sdk/cargo-build-sbf/tests/crates/noop/Cargo.toml +++ b/sdk/cargo-build-sbf/tests/crates/noop/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "noop" -version = "1.11.6" +version = "1.14.24" description = "Solana SBF test program written in Rust" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -10,7 +10,7 @@ edition = "2021" publish = false [dependencies] -solana-program = { path = "../../../../program", version = "=1.11.6" } +solana-program = { path = "../../../../program", version = "=1.14.24" } [lib] crate-type = ["cdylib"] diff --git a/sdk/cargo-test-bpf/Cargo.toml b/sdk/cargo-test-bpf/Cargo.toml index eeea87fc84860a..e381dfe5df732f 100644 --- a/sdk/cargo-test-bpf/Cargo.toml +++ b/sdk/cargo-test-bpf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-cargo-test-bpf" -version = "1.11.6" +version = "1.14.24" description = "Execute all unit and integration tests after building with the Solana SBF SDK" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" diff --git a/sdk/cargo-test-bpf/src/main.rs b/sdk/cargo-test-bpf/src/main.rs index fee4dc73811fe5..af5a382fdd4ec9 100644 --- a/sdk/cargo-test-bpf/src/main.rs +++ b/sdk/cargo-test-bpf/src/main.rs @@ -32,8 +32,9 @@ fn main() { args.remove(0); } } - args.push("--arch".to_string()); - args.push("bpf".to_string()); + let index = args.iter().position(|x| x == "--").unwrap_or(args.len()); + args.insert(index, "bpf".to_string()); + args.insert(index, "--arch".to_string()); print!("cargo-test-bpf child: {}", program.display()); for a in &args { print!(" {}", a); diff --git a/sdk/cargo-test-sbf/Cargo.toml b/sdk/cargo-test-sbf/Cargo.toml index e156c82799f810..dee3901a51c418 100644 --- a/sdk/cargo-test-sbf/Cargo.toml +++ b/sdk/cargo-test-sbf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-cargo-test-sbf" -version = "1.11.6" +version = "1.14.24" description = "Execute all unit and integration tests after building with the Solana SBF SDK" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" diff --git a/sdk/cargo-test-sbf/src/main.rs b/sdk/cargo-test-sbf/src/main.rs index 01ea5848ef5a0c..483d5b81cc12d1 100644 --- a/sdk/cargo-test-sbf/src/main.rs +++ b/sdk/cargo-test-sbf/src/main.rs @@ -319,7 +319,7 @@ fn main() { .arg( Arg::new("arch") .long("arch") - .possible_values(&["bpf", "sbf", "sbfv2"]) + .possible_values(["bpf", "sbf", "sbfv2"]) .default_value("sbf") .help("Build for the given SBF version"), ) diff --git a/sdk/gen-headers/Cargo.toml b/sdk/gen-headers/Cargo.toml index 751f305a4ddfa1..880e4a2c5f640c 100644 --- a/sdk/gen-headers/Cargo.toml +++ b/sdk/gen-headers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gen-headers" -version = "1.11.6" +version = "1.14.24" edition = "2021" license = "Apache-2.0" publish = false diff --git a/sdk/macro/Cargo.toml b/sdk/macro/Cargo.toml index 03769729c98873..6c718387c08e8c 100644 --- a/sdk/macro/Cargo.toml +++ b/sdk/macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-sdk-macro" -version = "1.11.6" +version = "1.14.24" description = "Solana SDK Macro" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" diff --git a/sdk/macro/src/lib.rs b/sdk/macro/src/lib.rs index 723a5729c1b1e5..3e8859366443ad 100644 --- a/sdk/macro/src/lib.rs +++ b/sdk/macro/src/lib.rs @@ -291,10 +291,10 @@ fn parse_pubkey( ) -> Result { let id_vec = bs58::decode(id_literal.value()) .into_vec() - .map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?; + .map_err(|_| syn::Error::new_spanned(id_literal, "failed to decode base58 string"))?; let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| { syn::Error::new_spanned( - &id_literal, + id_literal, format!("pubkey array is not 32 bytes long: len={}", id_vec.len()), ) })?; diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index fd359f7dc7d167..3f583f16eed0fa 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-program" -version = "1.11.6" +version = "1.14.24" description = "Solana Program" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -12,7 +12,7 @@ edition = "2021" [dependencies] bincode = "1.3.1" -blake3 = { version = "1.3.1", features = ["digest", "traits-preview"] } +blake3 = { version = "=1.3.1", features = ["digest", "traits-preview"] } borsh = "0.9.1" borsh-derive = "0.9.1" bs58 = "0.4.0" @@ -31,9 +31,9 @@ serde_derive = "1.0" serde_json = "1.0" sha2 = "0.10.0" sha3 = "0.10.0" -solana-frozen-abi = { path = "../../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "=1.11.6" } -solana-sdk-macro = { path = "../macro", version = "=1.11.6" } +solana-frozen-abi = { path = "../../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../../frozen-abi/macro", version = "=1.14.24" } +solana-sdk-macro = { path = "../macro", version = "=1.14.24" } thiserror = "1.0" [target.'cfg(not(target_os = "solana"))'.dependencies] @@ -50,7 +50,7 @@ wasm-bindgen = "0.2" zeroize = { version = "1.3", default-features = true, features = ["zeroize_derive"] } [target.'cfg(not(target_os = "solana"))'.dev-dependencies] -solana-logger = { path = "../../logger", version = "=1.11.6" } +solana-logger = { path = "../../logger", version = "=1.14.24" } [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.7" diff --git a/sdk/program/README.md b/sdk/program/README.md index f15457eb7908c5..e5120a2780bce1 100644 --- a/sdk/program/README.md +++ b/sdk/program/README.md @@ -12,4 +12,4 @@ More information about Solana is available in the [Solana documentation](https:/ [Helloworld](https://github.com/solana-labs/example-helloworld) and the [Solana Program Library](https://github.com/solana-labs/solana-program-library) provide examples of how to use this crate. -Still have questions? Ask us on [Discord](https://discordapp.com/invite/pquxPsq) +Still have questions? Ask us on [Stack Exchange](https://sola.na/sse) diff --git a/sdk/program/src/bpf_loader_upgradeable.rs b/sdk/program/src/bpf_loader_upgradeable.rs index 19264adb05750d..a2d2aa49119cc0 100644 --- a/sdk/program/src/bpf_loader_upgradeable.rs +++ b/sdk/program/src/bpf_loader_upgradeable.rs @@ -300,13 +300,19 @@ pub fn close_any( Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas) } -/// Returns the instruction required to extend the size of a program data account -pub fn extend_program_data( - program_data_address: &Pubkey, +/// Returns the instruction required to extend the size of a program's +/// executable data account +pub fn extend_program( + program_address: &Pubkey, payer_address: Option<&Pubkey>, additional_bytes: u32, ) -> Instruction { - let mut metas = vec![AccountMeta::new(*program_data_address, false)]; + let (program_data_address, _) = + Pubkey::find_program_address(&[program_address.as_ref()], &id()); + let mut metas = vec![ + AccountMeta::new(program_data_address, false), + AccountMeta::new(*program_address, false), + ]; if let Some(payer_address) = payer_address { metas.push(AccountMeta::new_readonly( crate::system_program::id(), @@ -316,7 +322,7 @@ pub fn extend_program_data( } Instruction::new_with_bincode( id(), - &UpgradeableLoaderInstruction::ExtendProgramData { additional_bytes }, + &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes }, metas, ) } diff --git a/sdk/program/src/example_mocks.rs b/sdk/program/src/example_mocks.rs index cacc800ef0c640..97b5f8b5c6f873 100644 --- a/sdk/program/src/example_mocks.rs +++ b/sdk/program/src/example_mocks.rs @@ -113,7 +113,7 @@ pub mod solana_client { /// programs. pub mod solana_sdk { pub use crate::{ - address_lookup_table_account, hash, instruction, message, nonce, + address_lookup_table_account, hash, instruction, keccak, message, nonce, pubkey::{self, Pubkey}, system_instruction, system_program, }; @@ -178,6 +178,7 @@ pub mod solana_sdk { pub trait Signers {} + impl Signers for [&T] {} impl Signers for [&T; 1] {} impl Signers for [&T; 2] {} } diff --git a/sdk/program/src/fee_calculator.rs b/sdk/program/src/fee_calculator.rs index 69ec783bc0590c..182f82ed160458 100644 --- a/sdk/program/src/fee_calculator.rs +++ b/sdk/program/src/fee_calculator.rs @@ -214,8 +214,8 @@ mod tests { assert_eq!(FeeCalculator::new(1).calculate_fee(&message), 0); // One signature, a fee. - let pubkey0 = Pubkey::new(&[0; 32]); - let pubkey1 = Pubkey::new(&[1; 32]); + let pubkey0 = Pubkey::from([0; 32]); + let pubkey1 = Pubkey::from([1; 32]); let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1); let message = Message::new(&[ix0], Some(&pubkey0)); assert_eq!(FeeCalculator::new(2).calculate_fee(&message), 2); @@ -231,8 +231,8 @@ mod tests { #[allow(deprecated)] fn test_fee_calculator_calculate_fee_secp256k1() { use crate::instruction::Instruction; - let pubkey0 = Pubkey::new(&[0; 32]); - let pubkey1 = Pubkey::new(&[1; 32]); + let pubkey0 = Pubkey::from([0; 32]); + let pubkey1 = Pubkey::from([1; 32]); let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1); let mut secp_instruction = Instruction { program_id: crate::secp256k1_program::id(), diff --git a/sdk/program/src/instruction.rs b/sdk/program/src/instruction.rs index 7b2931847b463c..3a6ff0e0bbb7a1 100644 --- a/sdk/program/src/instruction.rs +++ b/sdk/program/src/instruction.rs @@ -664,9 +664,9 @@ impl CompiledInstruction { #[derive(Default, Debug, Clone, Copy)] pub struct ProcessedSiblingInstruction { /// Length of the instruction data - pub data_len: usize, + pub data_len: u64, /// Number of AccountMeta structures - pub accounts_len: usize, + pub accounts_len: u64, } /// Returns a sibling instruction from the processed sibling instruction list. @@ -698,8 +698,8 @@ pub fn get_processed_sibling_instruction(index: usize) -> Option { } { let mut data = Vec::new(); let mut accounts = Vec::new(); - data.resize_with(meta.data_len, u8::default); - accounts.resize_with(meta.accounts_len, AccountMeta::default); + data.resize_with(meta.data_len as usize, u8::default); + accounts.resize_with(meta.accounts_len as usize, AccountMeta::default); let _ = unsafe { crate::syscalls::sol_get_processed_sibling_instruction( diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 65b951014cfe84..e224f2abd4248e 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -597,6 +597,7 @@ pub mod rent; pub mod sanitize; pub mod secp256k1_program; pub mod secp256k1_recover; +pub mod serde_varint; pub mod serialize_utils; pub mod short_vec; pub mod slot_hashes; diff --git a/sdk/program/src/loader_upgradeable_instruction.rs b/sdk/program/src/loader_upgradeable_instruction.rs index e751c40a1369fd..b8832c5329e9de 100644 --- a/sdk/program/src/loader_upgradeable_instruction.rs +++ b/sdk/program/src/loader_upgradeable_instruction.rs @@ -128,7 +128,7 @@ pub enum UpgradeableLoaderInstruction { /// is a ProgramData account. Close, - /// Extend a ProgramData account by the specified number of bytes. + /// Extend a program's ProgramData account by the specified number of bytes. /// Only upgradeable program's can be extended. /// /// The payer account must contain sufficient lamports to fund the @@ -138,11 +138,12 @@ pub enum UpgradeableLoaderInstruction { /// /// # Account references /// 0. `[writable]` The ProgramData account. - /// 1. `[]` System program (`solana_sdk::system_program::id()`), optional, used to transfer + /// 1. `[writable]` The ProgramData account's associated Program account. + /// 2. `[]` System program (`solana_sdk::system_program::id()`), optional, used to transfer /// lamports from the payer to the ProgramData account. - /// 2. `[signer]` The payer account, optional, that will pay necessary rent exemption costs + /// 3. `[signer]` The payer account, optional, that will pay necessary rent exemption costs /// for the increased storage size. - ExtendProgramData { + ExtendProgram { /// Number of bytes to extend the program data. additional_bytes: u32, }, diff --git a/sdk/program/src/message/address_loader.rs b/sdk/program/src/message/address_loader.rs new file mode 100644 index 00000000000000..83e514cd0bd63b --- /dev/null +++ b/sdk/program/src/message/address_loader.rs @@ -0,0 +1,56 @@ +use { + super::v0::{LoadedAddresses, MessageAddressTableLookup}, + thiserror::Error, +}; + +#[derive(Debug, Error, PartialEq, Eq, Clone)] +pub enum AddressLoaderError { + /// Address loading from lookup tables is disabled + #[error("Address loading from lookup tables is disabled")] + Disabled, + + /// Failed to load slot hashes sysvar + #[error("Failed to load slot hashes sysvar")] + SlotHashesSysvarNotFound, + + /// Attempted to lookup addresses from a table that does not exist + #[error("Attempted to lookup addresses from a table that does not exist")] + LookupTableAccountNotFound, + + /// Attempted to lookup addresses from an account owned by the wrong program + #[error("Attempted to lookup addresses from an account owned by the wrong program")] + InvalidAccountOwner, + + /// Attempted to lookup addresses from an invalid account + #[error("Attempted to lookup addresses from an invalid account")] + InvalidAccountData, + + /// Address lookup contains an invalid index + #[error("Address lookup contains an invalid index")] + InvalidLookupIndex, +} + +pub trait AddressLoader: Clone { + fn load_addresses( + self, + lookups: &[MessageAddressTableLookup], + ) -> Result; +} + +#[derive(Clone)] +pub enum SimpleAddressLoader { + Disabled, + Enabled(LoadedAddresses), +} + +impl AddressLoader for SimpleAddressLoader { + fn load_addresses( + self, + _lookups: &[MessageAddressTableLookup], + ) -> Result { + match self { + Self::Disabled => Err(AddressLoaderError::Disabled), + Self::Enabled(loaded_addresses) => Ok(loaded_addresses), + } + } +} diff --git a/sdk/program/src/message/legacy.rs b/sdk/program/src/message/legacy.rs index 8b90322400253f..cff7eee56c650d 100644 --- a/sdk/program/src/message/legacy.rs +++ b/sdk/program/src/message/legacy.rs @@ -16,7 +16,7 @@ use { bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, hash::Hash, instruction::{CompiledInstruction, Instruction}, - message::{CompiledKeys, MessageHeader}, + message::{compiled_keys::CompiledKeys, MessageHeader}, pubkey::Pubkey, sanitize::{Sanitize, SanitizeError}, short_vec, system_instruction, system_program, sysvar, wasm_bindgen, diff --git a/sdk/program/src/message/mod.rs b/sdk/program/src/message/mod.rs index 376b81e9590d0b..c950852021a50d 100644 --- a/sdk/program/src/message/mod.rs +++ b/sdk/program/src/message/mod.rs @@ -44,16 +44,16 @@ pub mod legacy; #[path = ""] mod non_bpf_modules { mod account_keys; + mod address_loader; mod sanitized; mod versions; - pub use {account_keys::*, sanitized::*, versions::*}; + pub use {account_keys::*, address_loader::*, sanitized::*, versions::*}; } -use compiled_keys::*; -pub use legacy::Message; #[cfg(not(target_os = "solana"))] pub use non_bpf_modules::*; +pub use {compiled_keys::CompileError, legacy::Message}; /// The length of a message header in bytes. pub const MESSAGE_HEADER_LENGTH: usize = 3; diff --git a/sdk/program/src/message/sanitized.rs b/sdk/program/src/message/sanitized.rs index 5fc64cd05f8f58..a71164193c6954 100644 --- a/sdk/program/src/message/sanitized.rs +++ b/sdk/program/src/message/sanitized.rs @@ -3,9 +3,10 @@ use { hash::Hash, instruction::CompiledInstruction, message::{ - legacy::Message as LegacyMessage, + legacy, v0::{self, LoadedAddresses}, - AccountKeys, MessageHeader, + AccountKeys, AddressLoader, AddressLoaderError, MessageHeader, + SanitizedVersionedMessage, VersionedMessage, }, nonce::NONCED_TX_MARKER_IX_INDEX, program_utils::limited_deserialize, @@ -14,15 +15,61 @@ use { solana_program::{system_instruction::SystemInstruction, system_program}, sysvar::instructions::{BorrowedAccountMeta, BorrowedInstruction}, }, - std::convert::TryFrom, + std::{borrow::Cow, convert::TryFrom}, thiserror::Error, }; +#[derive(Debug, Clone)] +pub struct LegacyMessage<'a> { + /// Legacy message + pub message: Cow<'a, legacy::Message>, + /// List of boolean with same length as account_keys(), each boolean value indicates if + /// corresponding account key is writable or not. + pub is_writable_account_cache: Vec, +} + +impl<'a> LegacyMessage<'a> { + pub fn new(message: legacy::Message) -> Self { + let is_writable_account_cache = message + .account_keys + .iter() + .enumerate() + .map(|(i, _key)| message.is_writable(i)) + .collect::>(); + Self { + message: Cow::Owned(message), + is_writable_account_cache, + } + } + + pub fn has_duplicates(&self) -> bool { + self.message.has_duplicates() + } + + pub fn is_key_called_as_program(&self, key_index: usize) -> bool { + self.message.is_key_called_as_program(key_index) + } + + /// Inspect all message keys for the bpf upgradeable loader + pub fn is_upgradeable_loader_present(&self) -> bool { + self.message.is_upgradeable_loader_present() + } + + /// Returns the full list of account keys. + pub fn account_keys(&self) -> AccountKeys { + AccountKeys::new(&self.message.account_keys, None) + } + + pub fn is_writable(&self, index: usize) -> bool { + *self.is_writable_account_cache.get(index).unwrap_or(&false) + } +} + /// Sanitized message of a transaction. #[derive(Debug, Clone)] pub enum SanitizedMessage { /// Sanitized legacy message - Legacy(LegacyMessage), + Legacy(LegacyMessage<'static>), /// Sanitized version #0 message with dynamically loaded addresses V0(v0::LoadedMessage<'static>), } @@ -35,6 +82,8 @@ pub enum SanitizeMessageError { ValueOutOfBounds, #[error("invalid value")] InvalidValue, + #[error("{0}")] + AddressLoaderError(#[from] AddressLoaderError), } impl From for SanitizeMessageError { @@ -47,15 +96,34 @@ impl From for SanitizeMessageError { } } -impl TryFrom for SanitizedMessage { +impl TryFrom for SanitizedMessage { type Error = SanitizeMessageError; - fn try_from(message: LegacyMessage) -> Result { + fn try_from(message: legacy::Message) -> Result { message.sanitize()?; - Ok(Self::Legacy(message)) + Ok(Self::Legacy(LegacyMessage::new(message))) } } impl SanitizedMessage { + /// Create a sanitized message from a sanitized versioned message. + /// If the input message uses address tables, attempt to look up the + /// address for each table index. + pub fn try_new( + sanitized_msg: SanitizedVersionedMessage, + address_loader: impl AddressLoader, + ) -> Result { + Ok(match sanitized_msg.message { + VersionedMessage::Legacy(message) => { + SanitizedMessage::Legacy(LegacyMessage::new(message)) + } + VersionedMessage::V0(message) => { + let loaded_addresses = + address_loader.load_addresses(&message.address_table_lookups)?; + SanitizedMessage::V0(v0::LoadedMessage::new(message, loaded_addresses)) + } + }) + } + /// Return true if this message contains duplicate account keys pub fn has_duplicates(&self) -> bool { match self { @@ -68,15 +136,15 @@ impl SanitizedMessage { /// readonly accounts pub fn header(&self) -> &MessageHeader { match self { - Self::Legacy(message) => &message.header, + Self::Legacy(legacy_message) => &legacy_message.message.header, Self::V0(loaded_msg) => &loaded_msg.message.header, } } /// Returns a legacy message if this sanitized message wraps one - pub fn legacy_message(&self) -> Option<&LegacyMessage> { - if let Self::Legacy(message) = &self { - Some(message) + pub fn legacy_message(&self) -> Option<&legacy::Message> { + if let Self::Legacy(legacy_message) = &self { + Some(&legacy_message.message) } else { None } @@ -92,7 +160,7 @@ impl SanitizedMessage { /// The hash of a recent block, used for timing out a transaction pub fn recent_blockhash(&self) -> &Hash { match self { - Self::Legacy(message) => &message.recent_blockhash, + Self::Legacy(legacy_message) => &legacy_message.message.recent_blockhash, Self::V0(loaded_msg) => &loaded_msg.message.recent_blockhash, } } @@ -101,7 +169,7 @@ impl SanitizedMessage { /// one atomic transaction if all succeed. pub fn instructions(&self) -> &[CompiledInstruction] { match self { - Self::Legacy(message) => &message.instructions, + Self::Legacy(legacy_message) => &legacy_message.message.instructions, Self::V0(loaded_msg) => &loaded_msg.message.instructions, } } @@ -124,7 +192,7 @@ impl SanitizedMessage { /// Returns the list of account keys that are loaded for this message. pub fn account_keys(&self) -> AccountKeys { match self { - Self::Legacy(message) => AccountKeys::new(&message.account_keys, None), + Self::Legacy(message) => message.account_keys(), Self::V0(message) => message.account_keys(), } } @@ -275,9 +343,9 @@ mod tests { #[test] fn test_try_from_message() { - let legacy_message_with_no_signers = LegacyMessage { + let legacy_message_with_no_signers = legacy::Message { account_keys: vec![Pubkey::new_unique()], - ..LegacyMessage::default() + ..legacy::Message::default() }; assert_eq!( @@ -296,7 +364,7 @@ mod tests { CompiledInstruction::new(2, &(), vec![0, 1]), ]; - let message = SanitizedMessage::try_from(LegacyMessage::new_with_compiled_instructions( + let message = SanitizedMessage::try_from(legacy::Message::new_with_compiled_instructions( 1, 0, 2, @@ -320,14 +388,14 @@ mod tests { let key4 = Pubkey::new_unique(); let key5 = Pubkey::new_unique(); - let legacy_message = SanitizedMessage::try_from(LegacyMessage { + let legacy_message = SanitizedMessage::try_from(legacy::Message { header: MessageHeader { num_required_signatures: 2, num_readonly_signed_accounts: 1, num_readonly_unsigned_accounts: 1, }, account_keys: vec![key0, key1, key2, key3], - ..LegacyMessage::default() + ..legacy::Message::default() }) .unwrap(); @@ -364,7 +432,7 @@ mod tests { CompiledInstruction::new(3, &(), vec![0, 0]), ]; - let message = SanitizedMessage::try_from(LegacyMessage::new_with_compiled_instructions( + let message = SanitizedMessage::try_from(legacy::Message::new_with_compiled_instructions( 2, 1, 2, @@ -391,4 +459,73 @@ mod tests { HashSet::default() ); } + + #[test] + fn test_is_writable_account_cache() { + let key0 = Pubkey::new_unique(); + let key1 = Pubkey::new_unique(); + let key2 = Pubkey::new_unique(); + let key3 = Pubkey::new_unique(); + let key4 = Pubkey::new_unique(); + let key5 = Pubkey::new_unique(); + + let legacy_message = SanitizedMessage::try_from(legacy::Message { + header: MessageHeader { + num_required_signatures: 2, + num_readonly_signed_accounts: 1, + num_readonly_unsigned_accounts: 1, + }, + account_keys: vec![key0, key1, key2, key3], + ..legacy::Message::default() + }) + .unwrap(); + match legacy_message { + SanitizedMessage::Legacy(message) => { + assert_eq!( + message.is_writable_account_cache.len(), + message.account_keys().len() + ); + assert!(message.is_writable_account_cache.get(0).unwrap()); + assert!(!message.is_writable_account_cache.get(1).unwrap()); + assert!(message.is_writable_account_cache.get(2).unwrap()); + assert!(!message.is_writable_account_cache.get(3).unwrap()); + } + _ => { + panic!("Expect to be SanitizedMessage::LegacyMessage") + } + } + + let v0_message = SanitizedMessage::V0(v0::LoadedMessage::new( + v0::Message { + header: MessageHeader { + num_required_signatures: 2, + num_readonly_signed_accounts: 1, + num_readonly_unsigned_accounts: 1, + }, + account_keys: vec![key0, key1, key2, key3], + ..v0::Message::default() + }, + LoadedAddresses { + writable: vec![key4], + readonly: vec![key5], + }, + )); + match v0_message { + SanitizedMessage::V0(message) => { + assert_eq!( + message.is_writable_account_cache.len(), + message.account_keys().len() + ); + assert!(message.is_writable_account_cache.get(0).unwrap()); + assert!(!message.is_writable_account_cache.get(1).unwrap()); + assert!(message.is_writable_account_cache.get(2).unwrap()); + assert!(!message.is_writable_account_cache.get(3).unwrap()); + assert!(message.is_writable_account_cache.get(4).unwrap()); + assert!(!message.is_writable_account_cache.get(5).unwrap()); + } + _ => { + panic!("Expect to be SanitizedMessage::V0") + } + } + } } diff --git a/sdk/program/src/message/versions/v0/loaded.rs b/sdk/program/src/message/versions/v0/loaded.rs index c1a7adb260be43..dc3a12f661b4d0 100644 --- a/sdk/program/src/message/versions/v0/loaded.rs +++ b/sdk/program/src/message/versions/v0/loaded.rs @@ -14,6 +14,9 @@ pub struct LoadedMessage<'a> { pub message: Cow<'a, v0::Message>, /// Addresses loaded with on-chain address lookup tables pub loaded_addresses: Cow<'a, LoadedAddresses>, + /// List of boolean with same length as account_keys(), each boolean value indicates if + /// corresponding account key is writable or not. + pub is_writable_account_cache: Vec, } /// Collection of addresses loaded from on-chain lookup tables, split @@ -53,17 +56,36 @@ impl LoadedAddresses { impl<'a> LoadedMessage<'a> { pub fn new(message: v0::Message, loaded_addresses: LoadedAddresses) -> Self { - Self { + let mut loaded_message = Self { message: Cow::Owned(message), loaded_addresses: Cow::Owned(loaded_addresses), - } + is_writable_account_cache: Vec::default(), + }; + loaded_message.set_is_writable_account_cache(); + loaded_message } pub fn new_borrowed(message: &'a v0::Message, loaded_addresses: &'a LoadedAddresses) -> Self { - Self { + let mut loaded_message = Self { message: Cow::Borrowed(message), loaded_addresses: Cow::Borrowed(loaded_addresses), - } + is_writable_account_cache: Vec::default(), + }; + loaded_message.set_is_writable_account_cache(); + loaded_message + } + + fn set_is_writable_account_cache(&mut self) { + let is_writable_account_cache = self + .account_keys() + .iter() + .enumerate() + .map(|(i, _key)| self.is_writable_internal(i)) + .collect::>(); + let _ = std::mem::replace( + &mut self.is_writable_account_cache, + is_writable_account_cache, + ); } /// Returns the full list of static and dynamic account keys that are loaded for this message. @@ -105,7 +127,7 @@ impl<'a> LoadedMessage<'a> { } /// Returns true if the account at the specified index was loaded as writable - pub fn is_writable(&self, key_index: usize) -> bool { + fn is_writable_internal(&self, key_index: usize) -> bool { if self.is_writable_index(key_index) { if let Some(key) = self.account_keys().get(key_index) { return !(is_builtin_key_or_sysvar(key) || self.demote_program_id(key_index)); @@ -114,6 +136,13 @@ impl<'a> LoadedMessage<'a> { false } + pub fn is_writable(&self, key_index: usize) -> bool { + *self + .is_writable_account_cache + .get(key_index) + .unwrap_or(&false) + } + pub fn is_signer(&self, i: usize) -> bool { i < self.message.header.num_required_signatures as usize } @@ -227,15 +256,45 @@ mod tests { #[test] fn test_is_writable() { - let mut message = check_test_loaded_message().0; + solana_logger::setup(); + let create_message_with_keys = |keys: Vec| { + LoadedMessage::new( + v0::Message { + header: MessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 1, + }, + account_keys: keys[..2].to_vec(), + ..v0::Message::default() + }, + LoadedAddresses { + writable: keys[2..=2].to_vec(), + readonly: keys[3..].to_vec(), + }, + ) + }; - message.message.to_mut().account_keys[0] = sysvar::clock::id(); - assert!(message.is_writable_index(0)); - assert!(!message.is_writable(0)); + let key0 = Pubkey::new_unique(); + let key1 = Pubkey::new_unique(); + let key2 = Pubkey::new_unique(); + { + let message = create_message_with_keys(vec![sysvar::clock::id(), key0, key1, key2]); + assert!(message.is_writable_index(0)); + assert!(!message.is_writable(0)); + } - message.message.to_mut().account_keys[0] = system_program::id(); - assert!(message.is_writable_index(0)); - assert!(!message.is_writable(0)); + { + let message = create_message_with_keys(vec![system_program::id(), key0, key1, key2]); + assert!(message.is_writable_index(0)); + assert!(!message.is_writable(0)); + } + + { + let message = create_message_with_keys(vec![key0, key1, system_program::id(), key2]); + assert!(message.is_writable_index(2)); + assert!(!message.is_writable(2)); + } } #[test] diff --git a/sdk/program/src/message/versions/v0/mod.rs b/sdk/program/src/message/versions/v0/mod.rs index 61d12ce5eafb48..b63907b27ab3bd 100644 --- a/sdk/program/src/message/versions/v0/mod.rs +++ b/sdk/program/src/message/versions/v0/mod.rs @@ -15,8 +15,9 @@ use crate::{ hash::Hash, instruction::{CompiledInstruction, Instruction}, message::{ - compiled_keys::CompileError, legacy::is_builtin_key_or_sysvar, AccountKeys, CompiledKeys, - MessageHeader, MESSAGE_VERSION_PREFIX, + compiled_keys::{CompileError, CompiledKeys}, + legacy::is_builtin_key_or_sysvar, + AccountKeys, MessageHeader, MESSAGE_VERSION_PREFIX, }, pubkey::Pubkey, sanitize::SanitizeError, diff --git a/sdk/program/src/program_stubs.rs b/sdk/program/src/program_stubs.rs index a0265255849318..28eff37b688eea 100644 --- a/sdk/program/src/program_stubs.rs +++ b/sdk/program/src/program_stubs.rs @@ -54,7 +54,7 @@ pub trait SyscallStubs: Sync + Send { unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) { // cannot be overlapping assert!( - is_nonoverlapping(src as usize, dst as usize, n), + is_nonoverlapping(src as usize, n, dst as usize, n), "memcpy does not support overlapping regions" ); std::ptr::copy_nonoverlapping(src, dst, n as usize); @@ -196,18 +196,20 @@ pub(crate) fn sol_get_stack_height() -> u64 { /// Check that two regions do not overlap. /// -/// Adapted from libcore, hidden to share with bpf_loader without being part of -/// the API surface. +/// Hidden to share with bpf_loader without being part of the API surface. #[doc(hidden)] -pub fn is_nonoverlapping(src: N, dst: N, count: N) -> bool +pub fn is_nonoverlapping(src: N, src_len: N, dst: N, dst_len: N) -> bool where N: Ord + std::ops::Sub, ::Output: Ord, { - let diff = if src > dst { src - dst } else { dst - src }; - // If the absolute distance between the ptrs is at least as big as the size of the buffer, + // If the absolute distance between the ptrs is at least as big as the size of the other, // they do not overlap. - diff >= count + if src > dst { + src - dst >= dst_len + } else { + dst - src >= src_len + } } #[cfg(test)] @@ -216,12 +218,16 @@ mod tests { #[test] fn test_is_nonoverlapping() { - assert!(is_nonoverlapping(10, 7, 3)); - assert!(!is_nonoverlapping(10, 8, 3)); - assert!(!is_nonoverlapping(10, 9, 3)); - assert!(!is_nonoverlapping(10, 10, 3)); - assert!(!is_nonoverlapping(10, 11, 3)); - assert!(!is_nonoverlapping(10, 12, 3)); - assert!(is_nonoverlapping(10, 13, 3)); + for dst in 0..8 { + assert!(is_nonoverlapping(10, 3, dst, 3)); + } + for dst in 8..13 { + assert!(!is_nonoverlapping(10, 3, dst, 3)); + } + for dst in 13..20 { + assert!(is_nonoverlapping(10, 3, dst, 3)); + } + assert!(is_nonoverlapping::(255, 3, 254, 1)); + assert!(!is_nonoverlapping::(255, 2, 254, 3)); } } diff --git a/sdk/program/src/pubkey.rs b/sdk/program/src/pubkey.rs index a8992c3672a3e0..7bbaf4ffef336f 100644 --- a/sdk/program/src/pubkey.rs +++ b/sdk/program/src/pubkey.rs @@ -121,17 +121,36 @@ impl FromStr for Pubkey { if pubkey_vec.len() != mem::size_of::() { Err(ParsePubkeyError::WrongSize) } else { - Ok(Pubkey::new(&pubkey_vec)) + Pubkey::try_from(pubkey_vec).map_err(|_| ParsePubkeyError::Invalid) } } } impl From<[u8; 32]> for Pubkey { + #[inline] fn from(from: [u8; 32]) -> Self { Self(from) } } +impl TryFrom<&[u8]> for Pubkey { + type Error = std::array::TryFromSliceError; + + #[inline] + fn try_from(pubkey: &[u8]) -> Result { + <[u8; 32]>::try_from(pubkey).map(Self::from) + } +} + +impl TryFrom> for Pubkey { + type Error = Vec; + + #[inline] + fn try_from(pubkey: Vec) -> Result { + <[u8; 32]>::try_from(pubkey).map(Self::from) + } +} + impl TryFrom<&str> for Pubkey { type Error = ParsePubkeyError; fn try_from(s: &str) -> Result { @@ -151,11 +170,12 @@ pub fn bytes_are_curve_point>(_bytes: T) -> bool { } impl Pubkey { + #[deprecated( + since = "1.14.14", + note = "Please use 'Pubkey::from' or 'Pubkey::try_from' instead" + )] pub fn new(pubkey_vec: &[u8]) -> Self { - Self( - <[u8; 32]>::try_from(<&[u8]>::clone(&pubkey_vec)) - .expect("Slice must be the same length as a Pubkey"), - ) + Self::try_from(pubkey_vec).expect("Slice must be the same length as a Pubkey") } pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self { @@ -166,7 +186,7 @@ impl Pubkey { #[cfg(not(target_os = "solana"))] pub fn new_rand() -> Self { // Consider removing Pubkey::new_rand() entirely in the v1.5 or v1.6 timeframe - Pubkey::new(&rand::random::<[u8; 32]>()) + Pubkey::from(rand::random::<[u8; 32]>()) } /// unique Pubkey for tests and benchmarks. @@ -179,7 +199,7 @@ impl Pubkey { // use big endian representation to ensure that recent unique pubkeys // are always greater than less recent unique pubkeys b[0..8].copy_from_slice(&i.to_be_bytes()); - Self::new(&b) + Self::from(b) } pub fn create_with_seed( @@ -198,10 +218,8 @@ impl Pubkey { return Err(PubkeyError::IllegalOwner); } } - - Ok(Pubkey::new( - hashv(&[base.as_ref(), seed.as_ref(), owner]).as_ref(), - )) + let hash = hashv(&[base.as_ref(), seed.as_ref(), owner]); + Ok(Pubkey::from(hash.to_bytes())) } /// Find a valid [program derived address][pda] and its corresponding bump seed. @@ -508,7 +526,7 @@ impl Pubkey { ) }; match result { - crate::entrypoint::SUCCESS => Some((Pubkey::new(&bytes), bump_seed)), + crate::entrypoint::SUCCESS => Some((Pubkey::from(bytes), bump_seed)), _ => None, } } @@ -584,7 +602,7 @@ impl Pubkey { return Err(PubkeyError::InvalidSeeds); } - Ok(Pubkey::new(hash.as_ref())) + Ok(Pubkey::from(hash.to_bytes())) } // Call via a system call to perform the calculation #[cfg(target_os = "solana")] @@ -599,7 +617,7 @@ impl Pubkey { ) }; match result { - crate::entrypoint::SUCCESS => Ok(Pubkey::new(&bytes)), + crate::entrypoint::SUCCESS => Ok(Pubkey::from(bytes)), _ => Err(result.into()), } } diff --git a/sdk/program/src/secp256k1_program.rs b/sdk/program/src/secp256k1_program.rs index 5517fcb2e8f491..4bc3de2c71596e 100644 --- a/sdk/program/src/secp256k1_program.rs +++ b/sdk/program/src/secp256k1_program.rs @@ -1,5 +1,10 @@ //! The [secp256k1 native program][np]. //! //! [np]: https://docs.solana.com/developing/runtime-facilities/programs#secp256k1-program +//! +//! Constructors for secp256k1 program instructions, and documentation on the +//! program's usage can be found in [`solana_sdk::secp256k1_instruction`]. +//! +//! [`solana_sdk::secp256k1_instruction`]: https://docs.rs/solana-sdk/latest/solana_sdk/secp256k1_instruction/index.html crate::declare_id!("KeccakSecp256k11111111111111111111111111111"); diff --git a/sdk/program/src/secp256k1_recover.rs b/sdk/program/src/secp256k1_recover.rs index 16451b149da850..408d41b77295c0 100644 --- a/sdk/program/src/secp256k1_recover.rs +++ b/sdk/program/src/secp256k1_recover.rs @@ -1,4 +1,28 @@ -//! Key recovery from secp256k1 signatures. +//! Public key recovery from [secp256k1] ECDSA signatures. +//! +//! [secp256k1]: https://en.bitcoin.it/wiki/Secp256k1 +//! +//! _This module provides low-level cryptographic building blocks that must be +//! used carefully to ensure proper security. Read this documentation and +//! accompanying links thoroughly._ +//! +//! The [`secp256k1_recover`] syscall allows a secp256k1 public key that has +//! previously signed a message to be recovered from the combination of the +//! message, the signature, and a recovery ID. The recovery ID is generated +//! during signing. +//! +//! Use cases for `secp256k1_recover` include: +//! +//! - Implementing the Ethereum [`ecrecover`] builtin contract. +//! - Performing secp256k1 public key recovery generally. +//! - Verifying a single secp256k1 signature. +//! +//! While `secp256k1_recover` can be used to verify secp256k1 signatures, Solana +//! also provides the [secp256k1 program][sp], which is more flexible, has lower CPU +//! cost, and can validate many signatures at once. +//! +//! [sp]: crate::secp256k1_program +//! [`ecrecover`]: https://docs.soliditylang.org/en/v0.8.14/units-and-global-variables.html?highlight=ecrecover#mathematical-and-cryptographic-functions use { borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, @@ -69,6 +93,309 @@ impl Secp256k1Pubkey { } } +/// Recover the public key from a [secp256k1] ECDSA signature and +/// cryptographically-hashed message. +/// +/// [secp256k1]: https://en.bitcoin.it/wiki/Secp256k1 +/// +/// This function is specifically intended for efficiently implementing +/// Ethereum's [`ecrecover`] builtin contract, for use by Ethereum integrators. +/// It may be useful for other purposes. +/// +/// [`ecrecover`]: https://docs.soliditylang.org/en/v0.8.14/units-and-global-variables.html?highlight=ecrecover#mathematical-and-cryptographic-functions +/// +/// `hash` is the 32-byte cryptographic hash (typically [`keccak`]) of an +/// arbitrary message, signed by some public key. +/// +/// The recovery ID is a value in the range [0, 3] that is generated during +/// signing, and allows the recovery process to be more efficent. Note that the +/// `recovery_id` here does not directly correspond to an Ethereum recovery ID +/// as used in `ecrecover`. This function accepts recovery IDs in the range of +/// [0, 3], while Ethereum's recovery IDs have a value of 27 or 28. To convert +/// an Ethereum recovery ID to a value this function will accept subtract 27 +/// from it, checking for underflow. In practice this function will not succeed +/// if given a recovery ID of 2 or 3, as these values represent an +/// "overflowing" signature, and this function returns an error when parsing +/// overflowing signatures. +/// +/// [`keccak`]: crate::keccak +/// [`wrapping_sub`]: https://doc.rust-lang.org/std/primitive.u8.html#method.wrapping_sub +/// +/// On success this function returns a [`Secp256k1Pubkey`], a wrapper around a +/// 64-byte secp256k1 public key. This public key corresponds to the secret key +/// that previously signed the message `hash` to produce the provided +/// `signature`. +/// +/// While `secp256k1_recover` can be used to verify secp256k1 signatures by +/// comparing the recovered key against an expected key, Solana also provides +/// the [secp256k1 program][sp], which is more flexible, has lower CPU cost, and +/// can validate many signatures at once. +/// +/// [sp]: crate::secp256k1_program +/// +/// The `secp256k1_recover` syscall is implemented with the [`libsecp256k1`] +/// crate, which clients may also want to use. +/// +/// [`libsecp256k1`]: https://docs.rs/libsecp256k1/latest/libsecp256k1 +/// +/// # Hashing messages +/// +/// In ECDSA signing and key recovery the signed "message" is always a +/// crytographic hash, not the original message itself. If not a cryptographic +/// hash, then an adversary can craft signatures that recover to arbitrary +/// public keys. This means the caller of this function generally must hash the +/// original message themselves and not rely on another party to provide the +/// hash. +/// +/// Ethereum uses the [`keccak`] hash. +/// +/// # Signature malleability +/// +/// With the ECDSA signature algorithm it is possible for any party, given a +/// valid signature of some message, to create a second signature that is +/// equally valid. This is known as _signature malleability_. In many cases this +/// is not a concern, but in cases where applications rely on signatures to have +/// a unique representation this can be the source of bugs, potentially with +/// security implications. +/// +/// **The solana `secp256k1_recover` function does not prevent signature +/// malleability**. This is in contrast to the Bitcoin secp256k1 library, which +/// does prevent malleability by default. Solana accepts signatures with `S` +/// values that are either in the _high order_ or in the _low order_, and it +/// is trivial to produce one from the other. +/// +/// To prevent signature malleability, it is common for secp256k1 signature +/// validators to only accept signatures with low-order `S` values, and reject +/// signatures with high-order `S` values. The following code will accomplish +/// this: +/// +/// ```rust +/// # use solana_program::program_error::ProgramError; +/// # let signature_bytes = [ +/// # 0x83, 0x55, 0x81, 0xDF, 0xB1, 0x02, 0xA7, 0xD2, +/// # 0x2D, 0x33, 0xA4, 0x07, 0xDD, 0x7E, 0xFA, 0x9A, +/// # 0xE8, 0x5F, 0x42, 0x6B, 0x2A, 0x05, 0xBB, 0xFB, +/// # 0xA1, 0xAE, 0x93, 0x84, 0x46, 0x48, 0xE3, 0x35, +/// # 0x74, 0xE1, 0x6D, 0xB4, 0xD0, 0x2D, 0xB2, 0x0B, +/// # 0x3C, 0x89, 0x8D, 0x0A, 0x44, 0xDF, 0x73, 0x9C, +/// # 0x1E, 0xBF, 0x06, 0x8E, 0x8A, 0x9F, 0xA9, 0xC3, +/// # 0xA5, 0xEA, 0x21, 0xAC, 0xED, 0x5B, 0x22, 0x13, +/// # ]; +/// let signature = libsecp256k1::Signature::parse_standard_slice(&signature_bytes) +/// .map_err(|_| ProgramError::InvalidArgument)?; +/// +/// if signature.s.is_high() { +/// return Err(ProgramError::InvalidArgument); +/// } +/// # Ok::<_, ProgramError>(()) +/// ``` +/// +/// This has the downside that the program must link to the [`libsecp256k1`] +/// crate and parse the signature just for this check. Note that `libsecp256k1` +/// version 0.7.0 or greater is required for running on the Solana BPF target. +/// +/// [`libsecp256k1`]: https://docs.rs/libsecp256k1/latest/libsecp256k1 +/// +/// For the most accurate description of signature malleability, and its +/// prevention in secp256k1, refer to comments in [`secp256k1.h`] in the Bitcoin +/// Core secp256k1 library, the documentation of the [OpenZeppelin `recover` +/// method for Solidity][ozr], and [this description of the problem on +/// StackExchange][sxr]. +/// +/// [`secp256k1.h`]: https://github.com/bitcoin-core/secp256k1/blob/44c2452fd387f7ca604ab42d73746e7d3a44d8a2/include/secp256k1.h +/// [ozr]: https://docs.openzeppelin.com/contracts/2.x/api/cryptography#ECDSA-recover-bytes32-bytes- +/// [sxr]: https://bitcoin.stackexchange.com/questions/81115/if-someone-wanted-to-pretend-to-be-satoshi-by-posting-a-fake-signature-to-defrau/81116#81116 +/// +/// # Errors +/// +/// If `hash` is not 32 bytes in length this function returns +/// [`Secp256k1RecoverError::InvalidHash`], though see notes +/// on BPF-specific behavior below. +/// +/// If `recovery_id` is not in the range [0, 3] this function returns +/// [`Secp256k1RecoverError::InvalidRecoveryId`]. +/// +/// If `signature` is not 64 bytes in length this function returns +/// [`Secp256k1RecoverError::InvalidSignature`], though see notes +/// on BPF-specific behavior below. +/// +/// If `signature` represents an "overflowing" signature this function returns +/// [`Secp256k1RecoverError::InvalidSignature`]. Overflowing signatures are +/// non-standard and should not be encountered in practice. +/// +/// If `signature` is otherwise invalid this function returns +/// [`Secp256k1RecoverError::InvalidSignature`]. +/// +/// # BPF-specific behavior +/// +/// When calling this function on-chain the caller must verify the correct +/// lengths of `hash` and `signature` beforehand. +/// +/// When run on-chain this function will not directly validate the lengths of +/// `hash` and `signature`. It will assume they are the the correct lengths and +/// pass their pointers to the runtime, which will interpret them as 32-byte and +/// 64-byte buffers. If the provided slices are too short, the runtime will read +/// invalid data and attempt to interpret it, most likely returning an error, +/// though in some scenarios it may be possible to incorrectly return +/// successfully, or the transaction will abort if the syscall reads data +/// outside of the program's memory space. If the provided slices are too long +/// then they may be used to "smuggle" uninterpreted data. +/// +/// # Examples +/// +/// This example demonstrates recovering a public key and using it to very a +/// signature with the `secp256k1_recover` syscall. It has three parts: a Solana +/// program, an RPC client to call the program, and common definitions shared +/// between the two. +/// +/// Common definitions: +/// +/// ``` +/// use borsh::{BorshDeserialize, BorshSerialize}; +/// +/// #[derive(BorshSerialize, BorshDeserialize, Debug)] +/// pub struct DemoSecp256k1RecoverInstruction { +/// pub message: Vec, +/// pub signature: [u8; 64], +/// pub recovery_id: u8, +/// } +/// ``` +/// +/// The Solana program. Note that it uses `libsecp256k1` version 0.7.0 to parse +/// the secp256k1 signature to prevent malleability. +/// +/// ```no_run +/// use solana_program::{ +/// entrypoint::ProgramResult, +/// keccak, msg, +/// program_error::ProgramError, +/// secp256k1_recover::secp256k1_recover, +/// }; +/// +/// /// The key we expect to sign secp256k1 messages, +/// /// as serialized by `libsecp256k1::PublicKey::serialize`. +/// const AUTHORIZED_PUBLIC_KEY: [u8; 64] = [ +/// 0x8C, 0xD6, 0x47, 0xF8, 0xA5, 0xBF, 0x59, 0xA0, 0x4F, 0x77, 0xFA, 0xFA, 0x6C, 0xA0, 0xE6, 0x4D, +/// 0x94, 0x5B, 0x46, 0x55, 0xA6, 0x2B, 0xB0, 0x6F, 0x10, 0x4C, 0x9E, 0x2C, 0x6F, 0x42, 0x0A, 0xBE, +/// 0x18, 0xDF, 0x0B, 0xF0, 0x87, 0x42, 0xBA, 0x88, 0xB4, 0xCF, 0x87, 0x5A, 0x35, 0x27, 0xBE, 0x0F, +/// 0x45, 0xAE, 0xFC, 0x66, 0x9C, 0x2C, 0x6B, 0xF3, 0xEF, 0xCA, 0x5C, 0x32, 0x11, 0xF7, 0x2A, 0xC7, +/// ]; +/// # pub struct DemoSecp256k1RecoverInstruction { +/// # pub message: Vec, +/// # pub signature: [u8; 64], +/// # pub recovery_id: u8, +/// # } +/// +/// pub fn process_secp256k1_recover( +/// instruction: DemoSecp256k1RecoverInstruction, +/// ) -> ProgramResult { +/// // The secp256k1 recovery operation accepts a cryptographically-hashed +/// // message only. Passing it anything else is insecure and allows signatures +/// // to be forged. +/// // +/// // This means that the code calling `secp256k1_recover` must perform the hash +/// // itself, and not assume that data passed to it has been properly hashed. +/// let message_hash = { +/// let mut hasher = keccak::Hasher::default(); +/// hasher.hash(&instruction.message); +/// hasher.result() +/// }; +/// +/// // Reject high-s value signatures to prevent malleability. +/// // Solana does not do this itself. +/// // This may or may not be necessary depending on use case. +/// { +/// let signature = libsecp256k1::Signature::parse_standard_slice(&instruction.signature) +/// .map_err(|_| ProgramError::InvalidArgument)?; +/// +/// if signature.s.is_high() { +/// msg!("signature with high-s value"); +/// return Err(ProgramError::InvalidArgument); +/// } +/// } +/// +/// let recovered_pubkey = secp256k1_recover( +/// &message_hash.0, +/// instruction.recovery_id, +/// &instruction.signature, +/// ) +/// .map_err(|_| ProgramError::InvalidArgument)?; +/// +/// // If we're using this function for signature verification then we +/// // need to check the pubkey is an expected value. +/// // Here we are checking the secp256k1 pubkey against a known authorized pubkey. +/// if recovered_pubkey.0 != AUTHORIZED_PUBLIC_KEY { +/// return Err(ProgramError::InvalidArgument); +/// } +/// +/// Ok(()) +/// } +/// ``` +/// +/// The RPC client program: +/// +/// ```no_run +/// # use solana_program::example_mocks::solana_client; +/// # use solana_program::example_mocks::solana_sdk; +/// use anyhow::Result; +/// use solana_client::rpc_client::RpcClient; +/// use solana_sdk::{ +/// instruction::Instruction, +/// keccak, +/// pubkey::Pubkey, +/// signature::{Keypair, Signer}, +/// transaction::Transaction, +/// }; +/// # use borsh::{BorshDeserialize, BorshSerialize}; +/// # #[derive(BorshSerialize, BorshDeserialize, Debug)] +/// # pub struct DemoSecp256k1RecoverInstruction { +/// # pub message: Vec, +/// # pub signature: [u8; 64], +/// # pub recovery_id: u8, +/// # } +/// +/// pub fn demo_secp256k1_recover( +/// payer_keypair: &Keypair, +/// secp256k1_secret_key: &libsecp256k1::SecretKey, +/// client: &RpcClient, +/// program_keypair: &Keypair, +/// ) -> Result<()> { +/// let message = b"hello world"; +/// let message_hash = { +/// let mut hasher = keccak::Hasher::default(); +/// hasher.hash(message); +/// hasher.result() +/// }; +/// +/// let secp_message = libsecp256k1::Message::parse(&message_hash.0); +/// let (signature, recovery_id) = libsecp256k1::sign(&secp_message, &secp256k1_secret_key); +/// +/// let signature = signature.serialize(); +/// +/// let instr = DemoSecp256k1RecoverInstruction { +/// message: message.to_vec(), +/// signature, +/// recovery_id: recovery_id.serialize(), +/// }; +/// let instr = Instruction::new_with_borsh( +/// program_keypair.pubkey(), +/// &instr, +/// vec![], +/// ); +/// +/// let blockhash = client.get_latest_blockhash()?; +/// let tx = Transaction::new_signed_with_payer( +/// &[instr], +/// Some(&payer_keypair.pubkey()), +/// &[payer_keypair], +/// blockhash, +/// ); +/// +/// client.send_and_confirm_transaction(&tx)?; +/// +/// Ok(()) +/// } +/// ``` pub fn secp256k1_recover( hash: &[u8], recovery_id: u8, diff --git a/sdk/program/src/serde_varint.rs b/sdk/program/src/serde_varint.rs new file mode 100644 index 00000000000000..80324bbe084700 --- /dev/null +++ b/sdk/program/src/serde_varint.rs @@ -0,0 +1,260 @@ +#![allow(clippy::integer_arithmetic)] +use { + serde::{ + de::{Error as _, SeqAccess, Visitor}, + ser::SerializeTuple, + Deserializer, Serializer, + }, + std::{fmt, marker::PhantomData}, +}; + +pub trait VarInt: Sized { + fn visit_seq<'de, A>(seq: A) -> Result + where + A: SeqAccess<'de>; + + fn serialize(self, serializer: S) -> Result + where + S: Serializer; +} + +struct VarIntVisitor { + phantom: PhantomData, +} + +impl<'de, T> Visitor<'de> for VarIntVisitor +where + T: VarInt, +{ + type Value = T; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a VarInt") + } + + fn visit_seq(self, seq: A) -> Result + where + A: SeqAccess<'de>, + { + T::visit_seq(seq) + } +} + +pub fn serialize(value: &T, serializer: S) -> Result +where + T: Copy + VarInt, + S: Serializer, +{ + (*value).serialize(serializer) +} + +pub fn deserialize<'de, D, T>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: VarInt, +{ + deserializer.deserialize_tuple( + (std::mem::size_of::() * 8 + 6) / 7, + VarIntVisitor { + phantom: PhantomData::default(), + }, + ) +} + +macro_rules! impl_var_int { + ($type:ty) => { + impl VarInt for $type { + fn visit_seq<'de, A>(mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut out = 0; + let mut shift = 0u32; + while shift < <$type>::BITS { + let byte = match seq.next_element::()? { + None => return Err(A::Error::custom("Invalid Sequence")), + Some(byte) => byte, + }; + out |= ((byte & 0x7F) as Self) << shift; + if byte & 0x80 == 0 { + // Last byte should not have been truncated when it was + // shifted to the left above. + if (out >> shift) as u8 != byte { + return Err(A::Error::custom("Last Byte Truncated")); + } + // Last byte can be zero only if there was only one + // byte and the output is also zero. + if byte == 0u8 && (shift != 0 || out != 0) { + return Err(A::Error::custom("Invalid Trailing Zeros")); + } + return Ok(out); + } + shift += 7; + } + Err(A::Error::custom("Left Shift Overflows")) + } + + fn serialize(mut self, serializer: S) -> Result + where + S: Serializer, + { + let bits = <$type>::BITS - self.leading_zeros(); + let num_bytes = ((bits + 6) / 7).max(1) as usize; + let mut seq = serializer.serialize_tuple(num_bytes)?; + while self >= 0x80 { + let byte = ((self & 0x7F) | 0x80) as u8; + seq.serialize_element(&byte)?; + self >>= 7; + } + seq.serialize_element(&(self as u8))?; + seq.end() + } + } + }; +} + +impl_var_int!(u32); +impl_var_int!(u64); + +#[cfg(test)] +mod tests { + use rand::Rng; + + #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] + struct Dummy { + #[serde(with = "super")] + a: u32, + b: u64, + #[serde(with = "super")] + c: u64, + d: u32, + } + + #[test] + fn test_serde_varint() { + assert_eq!((std::mem::size_of::() * 8 + 6) / 7, 5); + assert_eq!((std::mem::size_of::() * 8 + 6) / 7, 10); + let dummy = Dummy { + a: 698, + b: 370, + c: 146, + d: 796, + }; + let bytes = bincode::serialize(&dummy).unwrap(); + assert_eq!(bytes.len(), 16); + let other: Dummy = bincode::deserialize(&bytes).unwrap(); + assert_eq!(other, dummy); + } + + #[test] + fn test_serde_varint_zero() { + let dummy = Dummy { + a: 0, + b: 0, + c: 0, + d: 0, + }; + let bytes = bincode::serialize(&dummy).unwrap(); + assert_eq!(bytes.len(), 14); + let other: Dummy = bincode::deserialize(&bytes).unwrap(); + assert_eq!(other, dummy); + } + + #[test] + fn test_serde_varint_max() { + let dummy = Dummy { + a: u32::MAX, + b: u64::MAX, + c: u64::MAX, + d: u32::MAX, + }; + let bytes = bincode::serialize(&dummy).unwrap(); + assert_eq!(bytes.len(), 27); + let other: Dummy = bincode::deserialize(&bytes).unwrap(); + assert_eq!(other, dummy); + } + + #[test] + fn test_serde_varint_rand() { + let mut rng = rand::thread_rng(); + for _ in 0..100_000 { + let dummy = Dummy { + a: rng.gen::() >> rng.gen_range(0, u32::BITS), + b: rng.gen::() >> rng.gen_range(0, u64::BITS), + c: rng.gen::() >> rng.gen_range(0, u64::BITS), + d: rng.gen::() >> rng.gen_range(0, u32::BITS), + }; + let bytes = bincode::serialize(&dummy).unwrap(); + let other: Dummy = bincode::deserialize(&bytes).unwrap(); + assert_eq!(other, dummy); + } + } + + #[test] + fn test_serde_varint_trailing_zeros() { + let buffer = [0x93, 0xc2, 0xa9, 0x8d, 0x0]; + let out = bincode::deserialize::(&buffer); + assert!(out.is_err()); + assert_eq!( + format!("{:?}", out), + r#"Err(Custom("Invalid Trailing Zeros"))"# + ); + let buffer = [0x80, 0x0]; + let out = bincode::deserialize::(&buffer); + assert!(out.is_err()); + assert_eq!( + format!("{:?}", out), + r#"Err(Custom("Invalid Trailing Zeros"))"# + ); + } + + #[test] + fn test_serde_varint_last_byte_truncated() { + let buffer = [0xe4, 0xd7, 0x88, 0xf6, 0x6f, 0xd4, 0xb9, 0x59]; + let out = bincode::deserialize::(&buffer); + assert!(out.is_err()); + assert_eq!( + format!("{:?}", out), + r#"Err(Custom("Last Byte Truncated"))"# + ); + } + + #[test] + fn test_serde_varint_shift_overflow() { + let buffer = [0x84, 0xdf, 0x96, 0xfa, 0xef]; + let out = bincode::deserialize::(&buffer); + assert!(out.is_err()); + assert_eq!( + format!("{:?}", out), + r#"Err(Custom("Left Shift Overflows"))"# + ); + } + + #[test] + fn test_serde_varint_short_buffer() { + let buffer = [0x84, 0xdf, 0x96, 0xfa]; + let out = bincode::deserialize::(&buffer); + assert!(out.is_err()); + assert_eq!(format!("{:?}", out), r#"Err(Io(Kind(UnexpectedEof)))"#); + } + + #[test] + fn test_serde_varint_fuzz() { + let mut rng = rand::thread_rng(); + let mut buffer = [0u8; 36]; + let mut num_errors = 0; + for _ in 0..200_000 { + rng.fill(&mut buffer[..]); + match bincode::deserialize::(&buffer) { + Err(_) => { + num_errors += 1; + } + Ok(dummy) => { + let bytes = bincode::serialize(&dummy).unwrap(); + assert_eq!(bytes, &buffer[..bytes.len()]); + } + } + } + assert!(num_errors > 2_000); + } +} diff --git a/sdk/program/src/serialize_utils.rs b/sdk/program/src/serialize_utils.rs index 78003f85777901..a2b61f75d06de9 100644 --- a/sdk/program/src/serialize_utils.rs +++ b/sdk/program/src/serialize_utils.rs @@ -37,7 +37,8 @@ pub fn read_pubkey(current: &mut usize, data: &[u8]) -> Result u64); define_syscall!(fn sol_get_stack_height() -> u64); -define_syscall!(fn sol_curve_validate_point(curve_id: u64, point: *const u8, result: *mut u8) -> u64); -define_syscall!(fn sol_curve_group_op(curve_id: u64, op_id: u64, left_point: *const u8, right_point: *const u8, result: *mut u8) -> u64); -define_syscall!(fn sol_curve_multiscalar_mul(curve_id: u64, scalars: *const u8, points: *const u8, result: *mut u8) -> u64); +define_syscall!(fn sol_curve_validate_point(curve_id: u64, point_addr: *const u8, result: *mut u8) -> u64); +define_syscall!(fn sol_curve_group_op(curve_id: u64, group_op: u64, left_input_addr: *const u8, right_input_addr: *const u8, result_point_addr: *mut u8) -> u64); +define_syscall!(fn sol_curve_multiscalar_mul(curve_id: u64, scalars_addr: *const u8, points_addr: *const u8, points_len: u64, result_point_addr: *mut u8) -> u64); define_syscall!(fn sol_curve_pairing_map(curve_id: u64, point: *const u8, result: *mut u8) -> u64); #[cfg(target_feature = "static-syscalls")] diff --git a/sdk/program/src/syscalls/mod.rs b/sdk/program/src/syscalls/mod.rs index aa0a85c23f4590..d66c9361e95792 100644 --- a/sdk/program/src/syscalls/mod.rs +++ b/sdk/program/src/syscalls/mod.rs @@ -16,6 +16,6 @@ pub const MAX_CPI_INSTRUCTION_ACCOUNTS: u8 = u8::MAX; /// Maximum number of account info structs that can be used in a single CPI /// invocation. A limit on account info structs is effectively the same as -/// limiting the number of unique accounts. 64 was chosen to match the max +/// limiting the number of unique accounts. 128 was chosen to match the max /// number of locked accounts per transaction (MAX_TX_ACCOUNT_LOCKS). -pub const MAX_CPI_ACCOUNT_INFOS: usize = 64; +pub const MAX_CPI_ACCOUNT_INFOS: usize = 128; diff --git a/sdk/program/src/wasm/pubkey.rs b/sdk/program/src/wasm/pubkey.rs index a3aa27941927eb..5f8733b88a6eac 100644 --- a/sdk/program/src/wasm/pubkey.rs +++ b/sdk/program/src/wasm/pubkey.rs @@ -34,7 +34,8 @@ impl Pubkey { if let Some(base58_str) = value.as_string() { base58_str.parse::().map_err(display_to_jsvalue) } else if let Some(uint8_array) = value.dyn_ref::() { - Ok(Pubkey::new(&uint8_array.to_vec())) + Pubkey::try_from(uint8_array.to_vec()) + .map_err(|err| JsValue::from(format!("Invalid Uint8Array pubkey: {err:?}"))) } else if let Some(array) = value.dyn_ref::() { let mut bytes = vec![]; let iterator = js_sys::try_iter(&array.values())?.expect("array to be iterable"); @@ -49,7 +50,8 @@ impl Pubkey { } return Err(format!("Invalid array argument: {:?}", x).into()); } - Ok(Pubkey::new(&bytes)) + Pubkey::try_from(bytes) + .map_err(|err| JsValue::from(format!("Invalid Array pubkey: {err:?}"))) } else if value.is_undefined() { Ok(Pubkey::default()) } else { diff --git a/sdk/sbf/syscalls.txt b/sdk/sbf/syscalls.txt new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 0d85d3e3ab161f..e2a9835ab92862 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -137,10 +137,6 @@ pub mod stake_merge_with_unmatched_credits_observed { solana_sdk::declare_id!("meRgp4ArRPhD3KtCY9c5yAf2med7mBLsjKTPeVUHqBL"); } -pub mod gate_large_block { - solana_sdk::declare_id!("2ry7ygxiYURULZCrypHhveanvP5tzZ4toRwVp89oCNSj"); -} - pub mod zk_token_sdk_enabled { solana_sdk::declare_id!("zk1snxsc6Fh3wsGNbbHAJNHiJoYgF29mMnTSusGx5EJ"); } @@ -396,8 +392,12 @@ pub mod require_static_program_ids_in_transaction { } pub mod stake_raise_minimum_delegation_to_1_sol { - // This is a feature-proposal *feature id*. The feature keypair address is `3YHAo6wWw5rDbQxb59BmJkQ3XwVhX3m8tdBVbtxnJmma`. - solana_sdk::declare_id!("4xmyBuR2VCXzy9H6qYpH9ckfgnTuMDQFPFBfTs4eBCY1"); + // This is a feature-proposal *feature id*. The feature keypair address is `GQXzC7YiSNkje6FFUk6sc2p53XRvKoaZ9VMktYzUMnpL`. + solana_sdk::declare_id!("9onWzzvCzNC2jfhxxeqRgs5q7nFAAKpCUvkj6T6GJK9i"); +} + +pub mod stake_minimum_delegation_for_rewards { + solana_sdk::declare_id!("ELjxSXwNsyXGfAh8TqX8ih22xeT8huF6UngQirbLKYKH"); } pub mod add_set_compute_unit_price_ix { @@ -456,7 +456,7 @@ pub mod preserve_rent_epoch_for_rent_exempt_accounts { solana_sdk::declare_id!("HH3MUYReL2BvqqA3oEcAa7txju5GY6G4nxJ51zvsEjEZ"); } -pub mod enable_bpf_loader_extend_program_data_ix { +pub mod enable_bpf_loader_extend_program_ix { solana_sdk::declare_id!("8Zs9W7D9MpSEtUWSQdGniZk2cNmV22y6FLJwCx53asme"); } @@ -484,10 +484,6 @@ pub mod compact_vote_state_updates { solana_sdk::declare_id!("86HpNqzutEZwLcPxS6EHDcMNYWk6ikhteg9un7Y2PBKE"); } -pub mod sign_repair_requests { - solana_sdk::declare_id!("sigrs6u1EWeHuoKFkY8RR7qcSsPmrAeBBPESyf5pnYe"); -} - pub mod concurrent_replay_of_forks { solana_sdk::declare_id!("9F2Dcu8xkBPKxiiy65XKPZYdCG3VZDpjDTuSmeYLozJe"); } @@ -496,6 +492,66 @@ pub mod incremental_snapshot_only_incremental_hash_calculation { solana_sdk::declare_id!("25vqsfjk7Nv1prsQJmA4Xu1bN61s8LXCBGUPp8Rfy1UF"); } +pub mod vote_state_update_root_fix { + solana_sdk::declare_id!("G74BkWBzmsByZ1kxHy44H3wjwp5hp7JbrGRuDpco22tY"); +} + +pub mod return_none_for_zero_lamport_accounts { + solana_sdk::declare_id!("7K5HFrS1WAq6ND7RQbShXZXbtAookyTfaDQPTJNuZpze"); +} + +pub mod disable_rehash_for_rent_epoch { + solana_sdk::declare_id!("DTVTkmw3JSofd8CJVJte8PXEbxNQ2yZijvVr3pe2APPj"); +} + +pub mod on_load_preserve_rent_epoch_for_rent_exempt_accounts { + solana_sdk::declare_id!("CpkdQmspsaZZ8FVAouQTtTWZkc8eeQ7V3uj7dWz543rZ"); +} + +pub mod increase_tx_account_lock_limit { + solana_sdk::declare_id!("9LZdXeKGeBV6hRLdxS1rHbHoEUsKqesCC2ZAPTPKJAbK"); +} + +pub mod check_syscall_outputs_do_not_overlap { + solana_sdk::declare_id!("3uRVPBpyEJRo1emLCrq38eLRFGcu6uKSpUXqGvU8T7SZ"); +} + +pub mod commission_updates_only_allowed_in_first_half_of_epoch { + solana_sdk::declare_id!("noRuG2kzACwgaY7TVmLRnUNPLKNVQE1fb7X55YWBehp"); +} + +pub mod enable_turbine_fanout_experiments { + solana_sdk::declare_id!("D31EFnLgdiysi84Woo3of4JMu7VmasUS3Z7j9HYXCeLY"); +} + +pub mod disable_turbine_fanout_experiments { + solana_sdk::declare_id!("Gz1aLrbeQ4Q6PTSafCZcGWZXz91yVRi7ASFzFEr1U4sa"); +} + +pub mod drop_merkle_shreds { + solana_sdk::declare_id!("84zy5N23Q9vTZuLc9h1HWUtyM9yCFV2SCmyP9W9C3yHZ"); +} + +pub mod keep_merkle_shreds { + solana_sdk::declare_id!("HyNQzc7TMNmRhpVHXqDGjpsHzeQie82mDQXSF9hj7nAH"); +} + +pub mod move_serialized_len_ptr_in_cpi { + solana_sdk::declare_id!("74CoWuBmt3rUVUrCb2JiSTvh6nXyBWUsK4SaMj3CtE3T"); +} + +pub mod disable_builtin_loader_ownership_chains { + solana_sdk::declare_id!("4UDcAfQ6EcA6bdcadkeHpkarkhZGJ7Bpq7wTAiRMjkoi"); +} + +pub mod enable_request_heap_frame_ix { + solana_sdk::declare_id!("Hr1nUA9b7NJ6eChS26o7Vi8gYYDDwWD3YeBfzJkTbU86"); +} + +pub mod clean_up_delegation_errors { + solana_sdk::declare_id!("Bj2jmUsM2iRhfdLLDSTkhM5UQRQvQHm57HSmPibPtEyu"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -525,7 +581,6 @@ lazy_static! { (merge_nonce_error_into_system_error::id(), "merge NonceError into SystemError"), (disable_fees_sysvar::id(), "disable fees sysvar"), (stake_merge_with_unmatched_credits_observed::id(), "allow merging active stakes with unmatched credits_observed #18985"), - (gate_large_block::id(), "validator checks block cost against max limit in realtime, reject if exceeds."), (zk_token_sdk_enabled::id(), "enable Zk Token proof program and syscalls"), (curve25519_syscall_enabled::id(), "enable curve25519 syscalls"), (versioned_tx_message_enabled::id(), "enable versioned transaction message processing"), @@ -589,6 +644,7 @@ lazy_static! { (stake_allow_zero_undelegated_amount::id(), "Allow zero-lamport undelegated amount for initialized stakes #24670"), (require_static_program_ids_in_transaction::id(), "require static program ids in versioned transactions"), (stake_raise_minimum_delegation_to_1_sol::id(), "Raise minimum stake delegation to 1.0 SOL #24357"), + (stake_minimum_delegation_for_rewards::id(), "stakes must be at least the minimum delegation to earn rewards"), (add_set_compute_unit_price_ix::id(), "add compute budget ix for setting a compute unit price"), (disable_deploy_of_alloc_free_syscall::id(), "disable new deployments of deprecated sol_alloc_free_ syscall"), (include_account_index_in_rent_error::id(), "include account index in rent tx error #25190"), @@ -604,16 +660,30 @@ lazy_static! { (cap_accounts_data_size_per_block::id(), "cap the accounts data size per block #25517"), (stake_redelegate_instruction::id(), "enable the redelegate stake instruction #26294"), (preserve_rent_epoch_for_rent_exempt_accounts::id(), "preserve rent epoch for rent exempt accounts #26479"), - (enable_bpf_loader_extend_program_data_ix::id(), "enable bpf upgradeable loader ExtendProgramData instruction #25234"), + (enable_bpf_loader_extend_program_ix::id(), "enable bpf upgradeable loader ExtendProgram instruction #25234"), (enable_early_verification_of_account_modifications::id(), "enable early verification of account modifications #25899"), + (disable_rehash_for_rent_epoch::id(), "on accounts hash calculation, do not try to rehash accounts #28934"), + (on_load_preserve_rent_epoch_for_rent_exempt_accounts::id(), "on bank load account, do not try to fix up rent_epoch #28541"), (prevent_crediting_accounts_that_end_rent_paying::id(), "prevent crediting rent paying accounts #26606"), (cap_bpf_program_instruction_accounts::id(), "enforce max number of accounts per bpf program instruction #26628"), (loosen_cpi_size_restriction::id(), "loosen cpi size restrictions #26641"), (use_default_units_in_fee_calculation::id(), "use default units per instruction in fee calculation #26785"), (compact_vote_state_updates::id(), "Compact vote state updates to lower block size"), - (sign_repair_requests::id(), "sign repair requests #26834"), (concurrent_replay_of_forks::id(), "Allow slots from different forks to be replayed concurrently #26465"), (incremental_snapshot_only_incremental_hash_calculation::id(), "only hash accounts in incremental snapshot during incremental snapshot creation #26799"), + (vote_state_update_root_fix::id(), "fix root in vote state updates #27361"), + (return_none_for_zero_lamport_accounts::id(), "return none for zero lamport accounts #27800"), + (increase_tx_account_lock_limit::id(), "increase tx account lock limit to 128 #27241"), + (check_syscall_outputs_do_not_overlap::id(), "check syscall outputs do_not overlap #28600"), + (commission_updates_only_allowed_in_first_half_of_epoch::id(), "validator commission updates are only allowed in the first half of an epoch #29362"), + (enable_turbine_fanout_experiments::id(), "enable turbine fanout experiments #29393"), + (disable_turbine_fanout_experiments::id(), "disable turbine fanout experiments #29393"), + (drop_merkle_shreds::id(), "drop merkle shreds #29711"), + (keep_merkle_shreds::id(), "keep merkle shreds #29711"), + (move_serialized_len_ptr_in_cpi::id(), "cpi ignore serialized_len_ptr #29592"), + (disable_builtin_loader_ownership_chains::id(), "disable builtin loader ownership chains #29956"), + (enable_request_heap_frame_ix::id(), "Enable transaction to request heap frame using compute budget instruction #30076"), + (clean_up_delegation_errors::id(), "Return InsufficientDelegation instead of InsufficientFunds or InsufficientStake where applicable #31206"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter() diff --git a/sdk/src/genesis_config.rs b/sdk/src/genesis_config.rs index 7b6b02457f9503..9bcf995386e9a9 100644 --- a/sdk/src/genesis_config.rs +++ b/sdk/src/genesis_config.rs @@ -197,7 +197,7 @@ impl GenesisConfig { ) })?; - std::fs::create_dir_all(&ledger_path)?; + std::fs::create_dir_all(ledger_path)?; let mut file = File::create(Self::genesis_filename(ledger_path))?; file.write_all(&serialized) @@ -343,6 +343,6 @@ mod tests { config.write(path).expect("write"); let loaded_config = GenesisConfig::load(path).expect("load"); assert_eq!(config.hash(), loaded_config.hash()); - let _ignored = std::fs::remove_file(&path); + let _ignored = std::fs::remove_file(path); } } diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index bc3c65dcbd289a..08da2ce8245570 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -41,6 +41,7 @@ pub mod program_utils; pub mod pubkey; pub mod quic; pub mod recent_blockhashes_account; +pub mod reward_type; pub mod rpc_port; pub mod secp256k1_instruction; pub mod shred_version; diff --git a/sdk/src/packet.rs b/sdk/src/packet.rs index 412375e35fec81..2befff66be94ce 100644 --- a/sdk/src/packet.rs +++ b/sdk/src/packet.rs @@ -130,8 +130,9 @@ impl fmt::Debug for Packet { #[allow(clippy::uninit_assumed_init)] impl Default for Packet { fn default() -> Packet { + let buffer = std::mem::MaybeUninit::<[u8; PACKET_DATA_SIZE]>::uninit(); Packet { - buffer: unsafe { std::mem::MaybeUninit::uninit().assume_init() }, + buffer: unsafe { buffer.assume_init() }, meta: Meta::default(), } } diff --git a/sdk/src/pubkey.rs b/sdk/src/pubkey.rs index b28672b7f36226..fe3c9488e9107c 100644 --- a/sdk/src/pubkey.rs +++ b/sdk/src/pubkey.rs @@ -3,7 +3,7 @@ pub use solana_program::pubkey::*; /// New random Pubkey for tests and benchmarks. #[cfg(feature = "full")] pub fn new_rand() -> Pubkey { - Pubkey::new(&rand::random::<[u8; PUBKEY_BYTES]>()) + Pubkey::from(rand::random::<[u8; PUBKEY_BYTES]>()) } #[cfg(feature = "full")] diff --git a/sdk/src/reward_type.rs b/sdk/src/reward_type.rs new file mode 100644 index 00000000000000..feba7459937ab5 --- /dev/null +++ b/sdk/src/reward_type.rs @@ -0,0 +1,24 @@ +use std::fmt; + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, AbiExample, AbiEnumVisitor, Clone, Copy)] +pub enum RewardType { + Fee, + Rent, + Staking, + Voting, +} + +impl fmt::Display for RewardType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + RewardType::Fee => "fee", + RewardType::Rent => "rent", + RewardType::Staking => "staking", + RewardType::Voting => "voting", + } + ) + } +} diff --git a/sdk/src/secp256k1_instruction.rs b/sdk/src/secp256k1_instruction.rs index de9bb89fca1c1f..767b91127cfc8b 100644 --- a/sdk/src/secp256k1_instruction.rs +++ b/sdk/src/secp256k1_instruction.rs @@ -1,3 +1,790 @@ +//! Instructions for the [secp256k1 native program][np]. +//! +//! [np]: https://docs.solana.com/developing/runtime-facilities/programs#secp256k1-program +//! +//! _This module provides low-level cryptographic building blocks that must be +//! used carefully to ensure proper security. Read this documentation and +//! accompanying links thoroughly._ +//! +//! The secp26k1 native program peforms flexible verification of [secp256k1] +//! ECDSA signatures, as used by Ethereum. It can verify up to 255 signatures on +//! up to 255 messages, with those signatures, messages, and their public keys +//! arbitrarily distributed across the instruction data of any instructions in +//! the same transaction as the secp256k1 instruction. +//! +//! The secp256k1 native program ID is located in the [`secp256k1_program`] module. +//! +//! The instruction is designed for Ethereum interoperability, but may be useful +//! for other purposes. It operates on Ethereum addresses, which are [`keccak`] +//! hashes of secp256k1 public keys, and internally is implemented using the +//! secp256k1 key recovery algorithm. Ethereum address can be created for +//! secp256k1 public keys with the [`construct_eth_pubkey`] function. +//! +//! [`keccak`]: crate::keccak +//! +//! This instruction does not directly allow for key recovery as in Ethereum's +//! [`ecrecover`] precompile. For that Solana provides the [`secp256k1_recover`] +//! syscall. +//! +//! [secp256k1]: https://en.bitcoin.it/wiki/Secp256k1 +//! [`secp256k1_program`]: solana_program::secp256k1_program +//! [`secp256k1_recover`]: solana_program::secp256k1_recover +//! [`ecrecover`]: https://docs.soliditylang.org/en/v0.8.14/units-and-global-variables.html?highlight=ecrecover#mathematical-and-cryptographic-functions +//! +//! Use cases for the secp256k1 instruction include: +//! +//! - Verifying Ethereum transaction signatures. +//! - Verifying Ethereum [EIP-712] signatures. +//! - Verifying arbitrary secp256k1 signatures. +//! - Signing a single message with multiple signatures. +//! +//! [EIP-712]: https://eips.ethereum.org/EIPS/eip-712 +//! +//! The [`new_secp256k1_instruction`] function is suitable for building a +//! secp256k1 program instruction for basic use cases were a single message must +//! be signed by a known secret key. For other uses cases, including many +//! Ethereum-integration use cases, construction of the secp256k1 instruction +//! must be done manually. +//! +//! # How to use this program +//! +//! Transactions that uses the secp256k1 native program will typically include +//! at least two instructions: one for the secp256k1 program to verify the +//! signatures, and one for a custom program that will check that the secp256k1 +//! instruction data matches what the program expects (using +//! [`load_instruction_at_checked`] or [`get_instruction_relative`]). The +//! signatures, messages, and Ethereum addresses being verified may reside in the +//! instruction data of either of these instructions, or in the instruction data +//! of one or more additional instructions, as long as those instructions are in +//! the same transaction. +//! +//! [`load_instruction_at_checked`]: crate::sysvar::instructions::load_instruction_at_checked +//! [`get_instruction_relative`]: crate::sysvar::instructions::get_instruction_relative +//! +//! Correct use of this program involves multiple steps, in client code and +//! program code: +//! +//! - In the client: +//! - Sign the [`keccak`]-hashed messages with a secp256k1 ECDSA library, +//! like the [`libsecp256k1`] crate. +//! - Build any custom instruction data that contain signature, message, or +//! Ethereum address data that will be used by the secp256k1 instruction. +//! - Build the secp256k1 program instruction data, specifying the number of +//! signatures to verify, the instruction indexes within the transaction, +//! and offsets within those instruction's data, where the signatures, +//! messages, and Ethereum addresses are located. +//! - Build the custom instruction for the program that will check the results +//! of the secp256k1 native program. +//! - Package all instructions into a single transaction and submit them. +//! - In the program: +//! - Load the secp256k1 instruction data with +//! [`load_instruction_at_checked`]. or [`get_instruction_relative`]. +//! - Check that the secp256k1 program ID is equal to +//! [`secp256k1_program::ID`], so that the signature verification cannot be +//! faked with a malicious program. +//! - Check that the public keys and messages are the expected values per +//! the program's requirements. +//! +//! [`secp256k1_program::ID`]: crate::secp256k1_program::ID +//! +//! The signature, message, or Ethereum addresses may reside in the secp256k1 +//! instruction data itself as additional data, their bytes following the bytes +//! of the protocol required by the secp256k1 instruction to locate the +//! signature, message, and Ethereum address data. This is the technique used by +//! `new_secp256k1_instruction` for simple signature verification. +//! +//! The `solana_sdk` crate provides few APIs for building the instructions and +//! transactions necessary for properly using the secp256k1 native program. +//! Many steps must be done manually. +//! +//! The `solana_program` crate provides no APIs to assist in interpreting +//! the the secp256k1 instruction data. It must be done manually. +//! +//! The secp256k1 program is implemented with the [`libsecp256k1`] crate, +//! which clients may also want to use. +//! +//! [`libsecp256k1`]: https://docs.rs/libsecp256k1/latest/libsecp256k1 +//! +//! # Layout and interpretation of the secp256k1 instruction data +//! +//! The secp256k1 instruction data contains: +//! +//! - 1 byte indicating the number of signatures to verify, 0 - 255, +//! - A number of _signature offset_ structures that indicate where in the +//! transaction to locate each signature, message, and Ethereum address. +//! - 0 or more bytes of arbitrary data, which may contain signatures, +//! messages or Ethereum addresses. +//! +//! The signature offset structure is defined by [`SecpSignatureOffsets`], +//! and can be serialized to the correct format with [`bincode::serialize_into`]. +//! Note that the bincode format may not be stable, +//! and callers should ensure they use the same version of `bincode` as the Solana SDK. +//! This data structure is not provided to Solana programs, +//! which are expected to interpret the signature offsets manually. +//! +//! [`bincode::serialize_into`]: https://docs.rs/bincode/1.3.3/bincode/fn.serialize_into.html +//! +//! The serialized signature offset structure has the following 11-byte layout, +//! with data types in little-endian encoding. +//! +//! | index | bytes | type | description | +//! |--------|-------|-------|-------------| +//! | 0 | 2 | `u16` | `signature_offset` - offset to 64-byte signature plus 1-byte recovery ID. | +//! | 2 | 1 | `u8` | `signature_offset_instruction_index` - within the transaction, the index of the transaction whose instruction data contains the signature. | +//! | 3 | 2 | `u16` | `eth_address_offset` - offset to 20-byte Ethereum address. | +//! | 5 | 1 | `u8` | `eth_address_instruction_index` - within the transaction, the index of the instruction whose instruction data contains the Ethereum address. | +//! | 6 | 2 | `u16` | `message_data_offset` - Offset to start of message data. | +//! | 8 | 2 | `u16` | `message_data_size` - Size of message data in bytes. | +//! | 10 | 1 | `u8` | `message_instruction_index` - Within the transaction, the index of the instruction whose instruction data contains the message data. | +//! +//! # Signature malleability +//! +//! With the ECDSA signature algorithm it is possible for any party, given a +//! valid signature of some message, to create a second signature that is +//! equally valid. This is known as _signature malleability_. In many cases this +//! is not a concern, but in cases where applications rely on signatures to have +//! a unique representation this can be the source of bugs, potentially with +//! security implications. +//! +//! **The solana `secp256k1_recover` function does not prevent signature +//! malleability**. This is in contrast to the Bitcoin secp256k1 library, which +//! does prevent malleability by default. Solana accepts signatures with `S` +//! values that are either in the _high order_ or in the _low order_, and it +//! is trivial to produce one from the other. +//! +//! For more complete documentation of the subject, and techniques to prevent +//! malleability, see the documentation for the [`secp256k1_recover`] syscall. +//! +//! # Additional security considerations +//! +//! Most programs will want to be conservative about the layout of the secp256k1 instruction +//! to prevent unforeseen bugs. The following checks may be desirable: +//! +//! - That there are exactly the expected number of signatures. +//! - That the three indexes, `signature_offset_instruction_index`, +//! `eth_address_instruction_index`, and `message_instruction_index` are as +//! expected, placing the signature, message and Ethereum address in the +//! expected instruction. +//! +//! Loading the secp256k1 instruction data within a program requires access to +//! the [instructions sysvar][is], which must be passed to the program by its +//! caller. Programs must verify the ID of this program to avoid calling an +//! imposter program. This does not need to be done manually though, as long as +//! it is only used through the [`load_instruction_at_checked`] or +//! [`get_instruction_relative`] functions. Both of these functions check their +//! sysvar argument to ensure it is the known instruction sysvar. +//! +//! [is]: crate::sysvar::instructions +//! +//! Programs should _always_ verify that the secp256k1 program ID loaded through +//! the instructions sysvar has the same value as in the [`secp256k1_program`] +//! module. Again this prevents imposter programs. +//! +//! [`secp256k1_program`]: crate::secp256k1_program +//! +//! # Errors +//! +//! The transaction will fail if any of the following are true: +//! +//! - Any signature was not created by the secret key corresponding to the +//! specified public key. +//! - Any signature is invalid. +//! - Any signature is "overflowing", a non-standard condition. +//! - The instruction data is empty. +//! - The first byte of instruction data is equal to 0 (indicating no signatures), +//! but the instruction data's length is greater than 1. +//! - The instruction data is not long enough to hold the number of signature +//! offsets specified in the first byte. +//! - Any instruction indexes specified in the signature offsets are greater or +//! equal to the number of instructions in the transaction. +//! - Any bounds specified in the signature offsets exceed the bounds of the +//! instruction data to which they are indexed. +//! +//! # Examples +//! +//! Both of the following examples make use of the following module definition +//! to parse the secp256k1 instruction data from within a Solana program. +//! +//! ```no_run +//! mod secp256k1_defs { +//! use solana_program::program_error::ProgramError; +//! use std::iter::Iterator; +//! +//! pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20; +//! pub const SIGNATURE_SERIALIZED_SIZE: usize = 64; +//! pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11; +//! +//! /// The structure encoded in the secp2256k1 instruction data. +//! pub struct SecpSignatureOffsets { +//! pub signature_offset: u16, +//! pub signature_instruction_index: u8, +//! pub eth_address_offset: u16, +//! pub eth_address_instruction_index: u8, +//! pub message_data_offset: u16, +//! pub message_data_size: u16, +//! pub message_instruction_index: u8, +//! } +//! +//! pub fn iter_signature_offsets( +//! secp256k1_instr_data: &[u8], +//! ) -> Result + '_, ProgramError> { +//! // First element is the number of `SecpSignatureOffsets`. +//! let num_structs = *secp256k1_instr_data +//! .get(0) +//! .ok_or(ProgramError::InvalidArgument)?; +//! +//! let all_structs_size = SIGNATURE_OFFSETS_SERIALIZED_SIZE * num_structs as usize; +//! let all_structs_slice = secp256k1_instr_data +//! .get(1..all_structs_size + 1) +//! .ok_or(ProgramError::InvalidArgument)?; +//! +//! fn decode_u16(chunk: &[u8], index: usize) -> u16 { +//! u16::from_le_bytes(<[u8; 2]>::try_from(&chunk[index..index + 2]).unwrap()) +//! } +//! +//! Ok(all_structs_slice +//! .chunks(SIGNATURE_OFFSETS_SERIALIZED_SIZE) +//! .map(|chunk| SecpSignatureOffsets { +//! signature_offset: decode_u16(chunk, 0), +//! signature_instruction_index: chunk[2], +//! eth_address_offset: decode_u16(chunk, 3), +//! eth_address_instruction_index: chunk[5], +//! message_data_offset: decode_u16(chunk, 6), +//! message_data_size: decode_u16(chunk, 8), +//! message_instruction_index: chunk[10], +//! })) +//! } +//! } +//! ``` +//! +//! ## Example: Signing and verifying with `new_secp256k1_instruction` +//! +//! This example demonstrates the simplest way to use the secp256k1 program, by +//! calling [`new_secp256k1_instruction`] to sign a single message and build the +//! corresponding secp256k1 instruction. +//! +//! This example has two components: a Solana program, and an RPC client that +//! sends a transaction to call it. The RPC client will sign a single message, +//! and the Solana program will introspect the secp256k1 instruction to verify +//! that the signer matches a known authorized public key. +//! +//! The Solana program. Note that it uses `libsecp256k1` version 0.7.0 to parse +//! the secp256k1 signature to prevent malleability. +//! +//! ```no_run +//! # mod secp256k1_defs { +//! # use solana_program::program_error::ProgramError; +//! # use std::iter::Iterator; +//! # +//! # pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20; +//! # pub const SIGNATURE_SERIALIZED_SIZE: usize = 64; +//! # pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11; +//! # +//! # /// The structure encoded in the secp2256k1 instruction data. +//! # pub struct SecpSignatureOffsets { +//! # pub signature_offset: u16, +//! # pub signature_instruction_index: u8, +//! # pub eth_address_offset: u16, +//! # pub eth_address_instruction_index: u8, +//! # pub message_data_offset: u16, +//! # pub message_data_size: u16, +//! # pub message_instruction_index: u8, +//! # } +//! # +//! # pub fn iter_signature_offsets( +//! # secp256k1_instr_data: &[u8], +//! # ) -> Result + '_, ProgramError> { +//! # // First element is the number of `SecpSignatureOffsets`. +//! # let num_structs = *secp256k1_instr_data +//! # .get(0) +//! # .ok_or(ProgramError::InvalidArgument)?; +//! # +//! # let all_structs_size = SIGNATURE_OFFSETS_SERIALIZED_SIZE * num_structs as usize; +//! # let all_structs_slice = secp256k1_instr_data +//! # .get(1..all_structs_size + 1) +//! # .ok_or(ProgramError::InvalidArgument)?; +//! # +//! # fn decode_u16(chunk: &[u8], index: usize) -> u16 { +//! # u16::from_le_bytes(<[u8; 2]>::try_from(&chunk[index..index + 2]).unwrap()) +//! # } +//! # +//! # Ok(all_structs_slice +//! # .chunks(SIGNATURE_OFFSETS_SERIALIZED_SIZE) +//! # .map(|chunk| SecpSignatureOffsets { +//! # signature_offset: decode_u16(chunk, 0), +//! # signature_instruction_index: chunk[2], +//! # eth_address_offset: decode_u16(chunk, 3), +//! # eth_address_instruction_index: chunk[5], +//! # message_data_offset: decode_u16(chunk, 6), +//! # message_data_size: decode_u16(chunk, 8), +//! # message_instruction_index: chunk[10], +//! # })) +//! # } +//! # } +//! use solana_program::{ +//! account_info::{next_account_info, AccountInfo}, +//! entrypoint::ProgramResult, +//! msg, +//! program_error::ProgramError, +//! secp256k1_program, +//! sysvar, +//! }; +//! +//! /// An Ethereum address corresponding to a secp256k1 secret key that is +//! /// authorized to sign our messages. +//! const AUTHORIZED_ETH_ADDRESS: [u8; 20] = [ +//! 0x18, 0x8a, 0x5c, 0xf2, 0x3b, 0x0e, 0xff, 0xe9, 0xa8, 0xe1, 0x42, 0x64, 0x5b, 0x82, 0x2f, 0x3a, +//! 0x6b, 0x8b, 0x52, 0x35, +//! ]; +//! +//! /// Check the secp256k1 instruction to ensure it was signed by +//! /// `AUTHORIZED_ETH_ADDRESS`s key. +//! /// +//! /// `accounts` is the slice of all accounts passed to the program +//! /// entrypoint. The only account it should contain is the instructions sysvar. +//! fn demo_secp256k1_verify_basic( +//! accounts: &[AccountInfo], +//! ) -> ProgramResult { +//! let account_info_iter = &mut accounts.iter(); +//! +//! // The instructions sysvar gives access to the instructions in the transaction. +//! let instructions_sysvar_account = next_account_info(account_info_iter)?; +//! assert!(sysvar::instructions::check_id( +//! instructions_sysvar_account.key +//! )); +//! +//! // Load the secp256k1 instruction. +//! // `new_secp256k1_instruction` generates an instruction that must be at index 0. +//! let secp256k1_instr = +//! sysvar::instructions::load_instruction_at_checked(0, instructions_sysvar_account)?; +//! +//! // Verify it is a secp256k1 instruction. +//! // This is security-critical - what if the transaction uses an imposter secp256k1 program? +//! assert!(secp256k1_program::check_id(&secp256k1_instr.program_id)); +//! +//! // There must be at least one byte. This is also verified by the runtime, +//! // and doesn't strictly need to be checked. +//! assert!(secp256k1_instr.data.len() > 1); +//! +//! let num_signatures = secp256k1_instr.data[0]; +//! // `new_secp256k1_instruction` generates an instruction that contains one signature. +//! assert_eq!(1, num_signatures); +//! +//! // Load the first and only set of signature offsets. +//! let offsets: secp256k1_defs::SecpSignatureOffsets = +//! secp256k1_defs::iter_signature_offsets(&secp256k1_instr.data)? +//! .next() +//! .ok_or(ProgramError::InvalidArgument)?; +//! +//! // `new_secp256k1_instruction` generates an instruction that only uses instruction index 0. +//! assert_eq!(0, offsets.signature_instruction_index); +//! assert_eq!(0, offsets.eth_address_instruction_index); +//! assert_eq!(0, offsets.message_instruction_index); +//! +//! // Reject high-s value signatures to prevent malleability. +//! // Solana does not do this itself. +//! // This may or may not be necessary depending on use case. +//! { +//! let signature = &secp256k1_instr.data[offsets.signature_offset as usize +//! ..offsets.signature_offset as usize + secp256k1_defs::SIGNATURE_SERIALIZED_SIZE]; +//! let signature = libsecp256k1::Signature::parse_standard_slice(signature) +//! .map_err(|_| ProgramError::InvalidArgument)?; +//! +//! if signature.s.is_high() { +//! msg!("signature with high-s value"); +//! return Err(ProgramError::InvalidArgument); +//! } +//! } +//! +//! // There is likely at least one more verification step a real program needs +//! // to do here to ensure it trusts the secp256k1 instruction, e.g.: +//! // +//! // - verify the tx signer is authorized +//! // - verify the secp256k1 signer is authorized +//! +//! // Here we are checking the secp256k1 pubkey against a known authorized pubkey. +//! let eth_address = &secp256k1_instr.data[offsets.eth_address_offset as usize +//! ..offsets.eth_address_offset as usize + secp256k1_defs::HASHED_PUBKEY_SERIALIZED_SIZE]; +//! +//! if eth_address != AUTHORIZED_ETH_ADDRESS { +//! return Err(ProgramError::InvalidArgument); +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! The client program: +//! +//! ```no_run +//! # use solana_sdk::example_mocks::solana_client; +//! use anyhow::Result; +//! use solana_client::rpc_client::RpcClient; +//! use solana_sdk::{ +//! instruction::{AccountMeta, Instruction}, +//! secp256k1_instruction, +//! signature::{Keypair, Signer}, +//! sysvar, +//! transaction::Transaction, +//! }; +//! +//! fn demo_secp256k1_verify_basic( +//! payer_keypair: &Keypair, +//! secp256k1_secret_key: &libsecp256k1::SecretKey, +//! client: &RpcClient, +//! program_keypair: &Keypair, +//! ) -> Result<()> { +//! // Internally to `new_secp256k1_instruction` and +//! // `secp256k_instruction::verify` (the secp256k1 program), this message is +//! // keccak-hashed before signing. +//! let msg = b"hello world"; +//! let secp256k1_instr = secp256k1_instruction::new_secp256k1_instruction(&secp256k1_secret_key, msg); +//! +//! let program_instr = Instruction::new_with_bytes( +//! program_keypair.pubkey(), +//! &[], +//! vec![ +//! AccountMeta::new_readonly(sysvar::instructions::ID, false) +//! ], +//! ); +//! +//! let blockhash = client.get_latest_blockhash()?; +//! let tx = Transaction::new_signed_with_payer( +//! &[secp256k1_instr, program_instr], +//! Some(&payer_keypair.pubkey()), +//! &[payer_keypair], +//! blockhash, +//! ); +//! +//! client.send_and_confirm_transaction(&tx)?; +//! +//! Ok(()) +//! } +//! ``` +//! +//! ## Example: Verifying multiple signatures in one instruction +//! +//! This examples demonstrates manually creating a secp256k1 instruction +//! containing many signatures, and a Solana program that parses them all. This +//! example on its own has no practical purpose. It simply demonstrates advanced +//! use of the secp256k1 program. +//! +//! Recall that the secp256k1 program will accept signatures, messages, and +//! Ethereum addresses that reside in any instruction contained in the same +//! transaction. In the _previous_ example, the Solana program asserted that all +//! signatures, messages, and addresses were stored in the instruction at 0. In +//! this next example the Solana program supports signatures, messages, and +//! addresses stored in any instruction. For simplicity the client still only +//! stores signatures, messages, and addresses in a single instruction, the +//! secp256k1 instruction. The code for storing this data across multiple +//! instructions would be complex, and may not be necessary in practice. +//! +//! This example has two components: a Solana program, and an RPC client that +//! sends a transaction to call it. +//! +//! The Solana program: +//! +//! ```no_run +//! # mod secp256k1_defs { +//! # use solana_program::program_error::ProgramError; +//! # use std::iter::Iterator; +//! # +//! # pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20; +//! # pub const SIGNATURE_SERIALIZED_SIZE: usize = 64; +//! # pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11; +//! # +//! # /// The structure encoded in the secp2256k1 instruction data. +//! # pub struct SecpSignatureOffsets { +//! # pub signature_offset: u16, +//! # pub signature_instruction_index: u8, +//! # pub eth_address_offset: u16, +//! # pub eth_address_instruction_index: u8, +//! # pub message_data_offset: u16, +//! # pub message_data_size: u16, +//! # pub message_instruction_index: u8, +//! # } +//! # +//! # pub fn iter_signature_offsets( +//! # secp256k1_instr_data: &[u8], +//! # ) -> Result + '_, ProgramError> { +//! # // First element is the number of `SecpSignatureOffsets`. +//! # let num_structs = *secp256k1_instr_data +//! # .get(0) +//! # .ok_or(ProgramError::InvalidArgument)?; +//! # +//! # let all_structs_size = SIGNATURE_OFFSETS_SERIALIZED_SIZE * num_structs as usize; +//! # let all_structs_slice = secp256k1_instr_data +//! # .get(1..all_structs_size + 1) +//! # .ok_or(ProgramError::InvalidArgument)?; +//! # +//! # fn decode_u16(chunk: &[u8], index: usize) -> u16 { +//! # u16::from_le_bytes(<[u8; 2]>::try_from(&chunk[index..index + 2]).unwrap()) +//! # } +//! # +//! # Ok(all_structs_slice +//! # .chunks(SIGNATURE_OFFSETS_SERIALIZED_SIZE) +//! # .map(|chunk| SecpSignatureOffsets { +//! # signature_offset: decode_u16(chunk, 0), +//! # signature_instruction_index: chunk[2], +//! # eth_address_offset: decode_u16(chunk, 3), +//! # eth_address_instruction_index: chunk[5], +//! # message_data_offset: decode_u16(chunk, 6), +//! # message_data_size: decode_u16(chunk, 8), +//! # message_instruction_index: chunk[10], +//! # })) +//! # } +//! # } +//! use solana_program::{ +//! account_info::{next_account_info, AccountInfo}, +//! entrypoint::ProgramResult, +//! msg, +//! program_error::ProgramError, +//! secp256k1_program, +//! sysvar, +//! }; +//! +//! /// A struct to hold the values specified in the `SecpSignatureOffsets` struct. +//! struct SecpSignature { +//! signature: [u8; secp256k1_defs::SIGNATURE_SERIALIZED_SIZE], +//! recovery_id: u8, +//! eth_address: [u8; secp256k1_defs::HASHED_PUBKEY_SERIALIZED_SIZE], +//! message: Vec, +//! } +//! +//! /// Load all signatures indicated in the secp256k1 instruction. +//! /// +//! /// This function is quite inefficient for reloading the same instructions +//! /// repeatedly and making copies and allocations. +//! fn load_signatures( +//! secp256k1_instr_data: &[u8], +//! instructions_sysvar_account: &AccountInfo, +//! ) -> Result, ProgramError> { +//! let mut sigs = vec![]; +//! for offsets in secp256k1_defs::iter_signature_offsets(secp256k1_instr_data)? { +//! let signature_instr = sysvar::instructions::load_instruction_at_checked( +//! offsets.signature_instruction_index as usize, +//! instructions_sysvar_account, +//! )?; +//! let eth_address_instr = sysvar::instructions::load_instruction_at_checked( +//! offsets.eth_address_instruction_index as usize, +//! instructions_sysvar_account, +//! )?; +//! let message_instr = sysvar::instructions::load_instruction_at_checked( +//! offsets.message_instruction_index as usize, +//! instructions_sysvar_account, +//! )?; +//! +//! // These indexes must all be valid because the runtime already verified them. +//! let signature = &signature_instr.data[offsets.signature_offset as usize +//! ..offsets.signature_offset as usize + secp256k1_defs::SIGNATURE_SERIALIZED_SIZE]; +//! let recovery_id = signature_instr.data +//! [offsets.signature_offset as usize + secp256k1_defs::SIGNATURE_SERIALIZED_SIZE]; +//! let eth_address = ð_address_instr.data[offsets.eth_address_offset as usize +//! ..offsets.eth_address_offset as usize + secp256k1_defs::HASHED_PUBKEY_SERIALIZED_SIZE]; +//! let message = &message_instr.data[offsets.message_data_offset as usize +//! ..offsets.message_data_offset as usize + offsets.message_data_size as usize]; +//! +//! let signature = +//! <[u8; secp256k1_defs::SIGNATURE_SERIALIZED_SIZE]>::try_from(signature).unwrap(); +//! let eth_address = +//! <[u8; secp256k1_defs::HASHED_PUBKEY_SERIALIZED_SIZE]>::try_from(eth_address).unwrap(); +//! let message = Vec::from(message); +//! +//! sigs.push(SecpSignature { +//! signature, +//! recovery_id, +//! eth_address, +//! message, +//! }) +//! } +//! Ok(sigs) +//! } +//! +//! fn demo_secp256k1_custom_many( +//! accounts: &[AccountInfo], +//! ) -> ProgramResult { +//! let account_info_iter = &mut accounts.iter(); +//! +//! let instructions_sysvar_account = next_account_info(account_info_iter)?; +//! assert!(sysvar::instructions::check_id( +//! instructions_sysvar_account.key +//! )); +//! +//! let secp256k1_instr = +//! sysvar::instructions::get_instruction_relative(-1, instructions_sysvar_account)?; +//! +//! assert!(secp256k1_program::check_id(&secp256k1_instr.program_id)); +//! +//! let signatures = load_signatures(&secp256k1_instr.data, instructions_sysvar_account)?; +//! for (idx, signature_bundle) in signatures.iter().enumerate() { +//! let signature = hex::encode(&signature_bundle.signature); +//! let eth_address = hex::encode(&signature_bundle.eth_address); +//! let message = hex::encode(&signature_bundle.message); +//! msg!("sig {}: {:?}", idx, signature); +//! msg!("recid: {}: {}", idx, signature_bundle.recovery_id); +//! msg!("eth address {}: {}", idx, eth_address); +//! msg!("message {}: {}", idx, message); +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! The client program: +//! +//! ```no_run +//! # use solana_sdk::example_mocks::solana_client; +//! use anyhow::Result; +//! use solana_client::rpc_client::RpcClient; +//! use solana_sdk::{ +//! instruction::{AccountMeta, Instruction}, +//! keccak, +//! secp256k1_instruction::{ +//! self, SecpSignatureOffsets, HASHED_PUBKEY_SERIALIZED_SIZE, +//! SIGNATURE_OFFSETS_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE, +//! }, +//! signature::{Keypair, Signer}, +//! sysvar, +//! transaction::Transaction, +//! }; +//! +//! /// A struct to hold the values specified in the `SecpSignatureOffsets` struct. +//! struct SecpSignature { +//! signature: [u8; SIGNATURE_SERIALIZED_SIZE], +//! recovery_id: u8, +//! eth_address: [u8; HASHED_PUBKEY_SERIALIZED_SIZE], +//! message: Vec, +//! } +//! +//! /// Create the instruction data for a secp256k1 instruction. +//! /// +//! /// `instruction_index` is the index the secp256k1 instruction will appear +//! /// within the transaction. For simplicity, this function only supports packing +//! /// the signatures into the secp256k1 instruction data, and not into any other +//! /// instructions within the transaction. +//! fn make_secp256k1_instruction_data( +//! signatures: &[SecpSignature], +//! instruction_index: u8, +//! ) -> Result> { +//! assert!(signatures.len() <= u8::max_value().into()); +//! +//! // We're going to pack all the signatures into the secp256k1 instruction data. +//! // Before our signatures though is the signature offset structures +//! // the secp256k1 program parses to find those signatures. +//! // This value represents the byte offset where the signatures begin. +//! let data_start = 1 + signatures.len() * SIGNATURE_OFFSETS_SERIALIZED_SIZE; +//! +//! let mut signature_offsets = vec![]; +//! let mut signature_buffer = vec![]; +//! +//! for signature_bundle in signatures { +//! let data_start = data_start +//! .checked_add(signature_buffer.len()) +//! .expect("overflow"); +//! +//! let signature_offset = data_start; +//! let eth_address_offset = data_start +//! .checked_add(SIGNATURE_SERIALIZED_SIZE + 1) +//! .expect("overflow"); +//! let message_data_offset = eth_address_offset +//! .checked_add(HASHED_PUBKEY_SERIALIZED_SIZE) +//! .expect("overflow"); +//! let message_data_size = signature_bundle.message.len(); +//! +//! let signature_offset = u16::try_from(signature_offset)?; +//! let eth_address_offset = u16::try_from(eth_address_offset)?; +//! let message_data_offset = u16::try_from(message_data_offset)?; +//! let message_data_size = u16::try_from(message_data_size)?; +//! +//! signature_offsets.push(SecpSignatureOffsets { +//! signature_offset, +//! signature_instruction_index: instruction_index, +//! eth_address_offset, +//! eth_address_instruction_index: instruction_index, +//! message_data_offset, +//! message_data_size, +//! message_instruction_index: instruction_index, +//! }); +//! +//! signature_buffer.extend(signature_bundle.signature); +//! signature_buffer.push(signature_bundle.recovery_id); +//! signature_buffer.extend(&signature_bundle.eth_address); +//! signature_buffer.extend(&signature_bundle.message); +//! } +//! +//! let mut instr_data = vec![]; +//! instr_data.push(signatures.len() as u8); +//! +//! for offsets in signature_offsets { +//! let offsets = bincode::serialize(&offsets)?; +//! instr_data.extend(offsets); +//! } +//! +//! instr_data.extend(signature_buffer); +//! +//! Ok(instr_data) +//! } +//! +//! fn demo_secp256k1_custom_many( +//! payer_keypair: &Keypair, +//! client: &RpcClient, +//! program_keypair: &Keypair, +//! ) -> Result<()> { +//! // Sign some messages. +//! let mut signatures = vec![]; +//! for idx in 0..2 { +//! let secret_key = libsecp256k1::SecretKey::random(&mut rand::thread_rng()); +//! let message = format!("hello world {}", idx).into_bytes(); +//! let message_hash = { +//! let mut hasher = keccak::Hasher::default(); +//! hasher.hash(&message); +//! hasher.result() +//! }; +//! let secp_message = libsecp256k1::Message::parse(&message_hash.0); +//! let (signature, recovery_id) = libsecp256k1::sign(&secp_message, &secret_key); +//! let signature = signature.serialize(); +//! let recovery_id = recovery_id.serialize(); +//! +//! let public_key = libsecp256k1::PublicKey::from_secret_key(&secret_key); +//! let eth_address = secp256k1_instruction::construct_eth_pubkey(&public_key); +//! +//! signatures.push(SecpSignature { +//! signature, +//! recovery_id, +//! eth_address, +//! message, +//! }); +//! } +//! +//! let secp256k1_instr_data = make_secp256k1_instruction_data(&signatures, 0)?; +//! let secp256k1_instr = Instruction::new_with_bytes( +//! solana_sdk::secp256k1_program::ID, +//! &secp256k1_instr_data, +//! vec![], +//! ); +//! +//! let program_instr = Instruction::new_with_bytes( +//! program_keypair.pubkey(), +//! &[], +//! vec![ +//! AccountMeta::new_readonly(sysvar::instructions::ID, false) +//! ], +//! ); +//! +//! let blockhash = client.get_latest_blockhash()?; +//! let tx = Transaction::new_signed_with_payer( +//! &[secp256k1_instr, program_instr], +//! Some(&payer_keypair.pubkey()), +//! &[payer_keypair], +//! blockhash, +//! ); +//! +//! client.send_and_confirm_transaction(&tx)?; +//! +//! Ok(()) +//! } +//! ``` + #![cfg(feature = "full")] use { @@ -19,17 +806,45 @@ pub const SIGNATURE_SERIALIZED_SIZE: usize = 64; pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11; pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + 1; +/// Offsets of signature data within a secp256k1 instruction. +/// +/// See the [module documentation][md] for a complete description. +/// +/// [md]: self #[derive(Default, Serialize, Deserialize, Debug)] pub struct SecpSignatureOffsets { - pub signature_offset: u16, // offset to [signature,recovery_id] of 64+1 bytes + /// Offset to 64-byte signature plus 1-byte recovery ID. + pub signature_offset: u16, + /// Within the transaction, the index of the instruction whose instruction data contains the signature. pub signature_instruction_index: u8, - pub eth_address_offset: u16, // offset to eth_address of 20 bytes + /// Offset to 20-byte Ethereum address. + pub eth_address_offset: u16, + /// Within the transaction, the index of the instruction whose instruction data contains the address. pub eth_address_instruction_index: u8, - pub message_data_offset: u16, // offset to start of message data - pub message_data_size: u16, // size of message data + /// Offset to start of message data. + pub message_data_offset: u16, + /// Size of message data in bytes. + pub message_data_size: u16, + /// Within the transaction, the index of the instruction whose instruction data contains the message. pub message_instruction_index: u8, } +/// Sign a message and create a secp256k1 program instruction to verify the signature. +/// +/// This function is suitable for simple uses of the secp256k1 program. +/// More complex uses must encode the secp256k1 instruction data manually. +/// See the [module documentation][md] for examples. +/// +/// [md]: self +/// +/// The instruction generated by this function must be the first instruction +/// included in a transaction or it will not verify. The +/// [`SecpSignatureOffsets`] structure encoded in the instruction data specify +/// the instruction indexes as 0. +/// +/// `message_arr` is hashed with the [`keccak`] hash function prior to signing. +/// +/// [`keccak`]: crate::keccak pub fn new_secp256k1_instruction( priv_key: &libsecp256k1::SecretKey, message_arr: &[u8], @@ -37,7 +852,7 @@ pub fn new_secp256k1_instruction( let secp_pubkey = libsecp256k1::PublicKey::from_secret_key(priv_key); let eth_pubkey = construct_eth_pubkey(&secp_pubkey); let mut hasher = sha3::Keccak256::new(); - hasher.update(&message_arr); + hasher.update(message_arr); let message_hash = hasher.finalize(); let mut message_hash_arr = [0u8; 32]; message_hash_arr.copy_from_slice(message_hash.as_slice()); @@ -92,6 +907,7 @@ pub fn new_secp256k1_instruction( } } +/// Creates an Ethereum address from a secp256k1 public key. pub fn construct_eth_pubkey( pubkey: &libsecp256k1::PublicKey, ) -> [u8; HASHED_PUBKEY_SERIALIZED_SIZE] { @@ -101,6 +917,19 @@ pub fn construct_eth_pubkey( addr } +/// Verifies the signatures specified in the secp256k1 instruction data. +/// +/// This is same the verification routine executed by the runtime's secp256k1 native program, +/// and is primarily of use to the runtime. +/// +/// `data` is the secp256k1 program's instruction data. `instruction_datas` is +/// the full slice of instruction datas for all instructions in the transaction, +/// including the secp256k1 program's instruction data. +/// +/// `feature_set` is the set of active Solana features. It is used to enable or +/// disable a few minor additional checks that were activated on chain +/// subsequent to the addition of the secp256k1 native program. For many +/// purposes passing `Arc::new` is reasonable. pub fn verify( data: &[u8], instruction_datas: &[&[u8]], @@ -224,6 +1053,7 @@ pub mod test { crate::{ feature_set, hash::Hash, + keccak, secp256k1_instruction::{ new_secp256k1_instruction, SecpSignatureOffsets, SIGNATURE_OFFSETS_SERIALIZED_SIZE, }, @@ -453,4 +1283,74 @@ pub mod test { ); assert!(tx.verify_precompiles(&feature_set).is_err()); } + + // Signatures are malleable. + #[test] + fn test_malleability() { + solana_logger::setup(); + + let secret_key = libsecp256k1::SecretKey::random(&mut thread_rng()); + let public_key = libsecp256k1::PublicKey::from_secret_key(&secret_key); + let eth_address = construct_eth_pubkey(&public_key); + + let message = b"hello"; + let message_hash = { + let mut hasher = keccak::Hasher::default(); + hasher.hash(message); + hasher.result() + }; + + let secp_message = libsecp256k1::Message::parse(&message_hash.0); + let (signature, recovery_id) = libsecp256k1::sign(&secp_message, &secret_key); + + // Flip the S value in the signature to make a different but valid signature. + let mut alt_signature = signature; + alt_signature.s = -alt_signature.s; + let alt_recovery_id = libsecp256k1::RecoveryId::parse(recovery_id.serialize() ^ 1).unwrap(); + + let mut data: Vec = vec![]; + let mut both_offsets = vec![]; + + // Verify both signatures of the same message. + let sigs = [(signature, recovery_id), (alt_signature, alt_recovery_id)]; + for (signature, recovery_id) in sigs.iter() { + let signature_offset = data.len(); + data.extend(signature.serialize()); + data.push(recovery_id.serialize()); + let eth_address_offset = data.len(); + data.extend(eth_address); + let message_data_offset = data.len(); + data.extend(message); + + let data_start = 1 + SIGNATURE_OFFSETS_SERIALIZED_SIZE * 2; + + let offsets = SecpSignatureOffsets { + signature_offset: (signature_offset + data_start) as u16, + signature_instruction_index: 0, + eth_address_offset: (eth_address_offset + data_start) as u16, + eth_address_instruction_index: 0, + message_data_offset: (message_data_offset + data_start) as u16, + message_data_size: message.len() as u16, + message_instruction_index: 0, + }; + + both_offsets.push(offsets); + } + + let mut instruction_data: Vec = vec![2]; + + for offsets in both_offsets { + let offsets = bincode::serialize(&offsets).unwrap(); + instruction_data.extend(offsets); + } + + instruction_data.extend(data); + + verify( + &instruction_data, + &[&instruction_data], + &Arc::new(FeatureSet::all_enabled()), + ) + .unwrap(); + } } diff --git a/sdk/src/signature.rs b/sdk/src/signature.rs index ace0bf0981e92d..3f94ccf9353acb 100644 --- a/sdk/src/signature.rs +++ b/sdk/src/signature.rs @@ -179,7 +179,7 @@ mod tests { let off_curve_point = curve25519_dalek::edwards::CompressedEdwardsY(off_curve_bits); assert_eq!(off_curve_point.decompress(), None); - let pubkey = Pubkey::new(&off_curve_bytes); + let pubkey = Pubkey::try_from(off_curve_bytes).unwrap(); let signature = Signature::default(); // Unfortunately, ed25519-dalek doesn't surface the internal error types that we'd ideally // `source()` out of the `SignatureError` returned by `verify_strict()`. So the best we diff --git a/sdk/src/signer/keypair.rs b/sdk/src/signer/keypair.rs index 4ee5c51b07232a..529876248f6c7c 100644 --- a/sdk/src/signer/keypair.rs +++ b/sdk/src/signer/keypair.rs @@ -67,8 +67,9 @@ impl Keypair { } impl Signer for Keypair { + #[inline] fn pubkey(&self) -> Pubkey { - Pubkey::new(self.0.public.as_ref()) + Pubkey::from(self.0.public.to_bytes()) } fn try_pubkey(&self) -> Result { diff --git a/sdk/src/signer/signers.rs b/sdk/src/signer/signers.rs index e4b07c8cd8305c..eaf2fce0e394f6 100644 --- a/sdk/src/signer/signers.rs +++ b/sdk/src/signer/signers.rs @@ -1,7 +1,11 @@ #![cfg(feature = "full")] -use crate::{ - pubkey::Pubkey, - signature::{Signature, Signer, SignerError}, + +use { + crate::{ + pubkey::Pubkey, + signature::{Signature, Signer, SignerError}, + }, + std::sync::Arc, }; /// Convenience trait for working with mixed collections of `Signer`s @@ -59,6 +63,14 @@ impl Signers for Vec> { default_keypairs_impl!(); } +impl Signers for [Arc] { + default_keypairs_impl!(); +} + +impl Signers for Vec> { + default_keypairs_impl!(); +} + impl Signers for Vec<&dyn Signer> { default_keypairs_impl!(); } @@ -158,7 +170,6 @@ mod tests { } #[test] - #[allow(clippy::blacklisted_name)] fn test_dyn_keypairs_by_ref_compile() { let foo = Foo {}; let bar = Bar {}; diff --git a/sdk/src/transaction/error.rs b/sdk/src/transaction/error.rs index 2fe4e0e3756adf..3f25fa5a62541a 100644 --- a/sdk/src/transaction/error.rs +++ b/sdk/src/transaction/error.rs @@ -1,6 +1,8 @@ use { crate::{ - instruction::InstructionError, message::SanitizeMessageError, sanitize::SanitizeError, + instruction::InstructionError, + message::{AddressLoaderError, SanitizeMessageError}, + sanitize::SanitizeError, }, serde::Serialize, thiserror::Error, @@ -156,7 +158,23 @@ impl From for TransactionError { } impl From for TransactionError { - fn from(_err: SanitizeMessageError) -> Self { - Self::SanitizeFailure + fn from(err: SanitizeMessageError) -> Self { + match err { + SanitizeMessageError::AddressLoaderError(err) => Self::from(err), + _ => Self::SanitizeFailure, + } + } +} + +impl From for TransactionError { + fn from(err: AddressLoaderError) -> Self { + match err { + AddressLoaderError::Disabled => Self::UnsupportedVersion, + AddressLoaderError::SlotHashesSysvarNotFound => Self::AccountNotFound, + AddressLoaderError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound, + AddressLoaderError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner, + AddressLoaderError::InvalidAccountData => Self::InvalidAddressLookupTableData, + AddressLoaderError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex, + } } } diff --git a/sdk/src/transaction/mod.rs b/sdk/src/transaction/mod.rs index 0041889a5b7277..caa9c4799e7aff 100644 --- a/sdk/src/transaction/mod.rs +++ b/sdk/src/transaction/mod.rs @@ -1273,12 +1273,12 @@ mod tests { 62, 89, 99, ]) .unwrap(); - let to = Pubkey::new(&[ + let to = Pubkey::from([ 1, 1, 1, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 7, 6, 5, 4, 1, 1, 1, ]); - let program_id = Pubkey::new(&[ + let program_id = Pubkey::from([ 2, 2, 2, 4, 5, 6, 7, 8, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 8, 7, 6, 5, 4, 2, 2, 2, ]); diff --git a/sdk/src/transaction/sanitized.rs b/sdk/src/transaction/sanitized.rs index 35a379f724da0a..c416c00b0de6ec 100644 --- a/sdk/src/transaction/sanitized.rs +++ b/sdk/src/transaction/sanitized.rs @@ -1,12 +1,14 @@ #![cfg(feature = "full")] +pub use crate::message::{AddressLoader, SimpleAddressLoader}; use { super::SanitizedVersionedTransaction, crate::{ hash::Hash, message::{ - v0::{self, LoadedAddresses, MessageAddressTableLookup}, - SanitizedMessage, VersionedMessage, + legacy, + v0::{self, LoadedAddresses}, + LegacyMessage, SanitizedMessage, VersionedMessage, }, precompiles::verify_if_precompile, pubkey::Pubkey, @@ -20,9 +22,9 @@ use { }; /// Maximum number of accounts that a transaction may lock. -/// 64 was chosen because it is roughly twice the previous -/// number of account keys that could fit in a legacy tx. -pub const MAX_TX_ACCOUNT_LOCKS: usize = 64; +/// 128 was chosen because it is the minimum number of accounts +/// needed for the Neon EVM implementation. +pub const MAX_TX_ACCOUNT_LOCKS: usize = 128; /// Sanitized transaction and the hash of its message #[derive(Debug, Clone)] @@ -42,25 +44,6 @@ pub struct TransactionAccountLocks<'a> { pub writable: Vec<&'a Pubkey>, } -pub trait AddressLoader: Clone { - fn load_addresses(self, lookups: &[MessageAddressTableLookup]) -> Result; -} - -#[derive(Clone)] -pub enum SimpleAddressLoader { - Disabled, - Enabled(LoadedAddresses), -} - -impl AddressLoader for SimpleAddressLoader { - fn load_addresses(self, _lookups: &[MessageAddressTableLookup]) -> Result { - match self { - Self::Disabled => Err(TransactionError::AddressLookupTableNotFound), - Self::Enabled(loaded_addresses) => Ok(loaded_addresses), - } - } -} - /// Type that represents whether the transaction message has been precomputed or /// not. pub enum MessageHash { @@ -87,7 +70,9 @@ impl SanitizedTransaction { let signatures = tx.signatures; let SanitizedVersionedMessage { message } = tx.message; let message = match message { - VersionedMessage::Legacy(message) => SanitizedMessage::Legacy(message), + VersionedMessage::Legacy(message) => { + SanitizedMessage::Legacy(LegacyMessage::new(message)) + } VersionedMessage::V0(message) => { let loaded_addresses = address_loader.load_addresses(&message.address_table_lookups)?; @@ -122,7 +107,9 @@ impl SanitizedTransaction { let signatures = tx.signatures; let message = match tx.message { - VersionedMessage::Legacy(message) => SanitizedMessage::Legacy(message), + VersionedMessage::Legacy(message) => { + SanitizedMessage::Legacy(LegacyMessage::new(message)) + } VersionedMessage::V0(message) => { let loaded_addresses = address_loader.load_addresses(&message.address_table_lookups)?; @@ -149,7 +136,7 @@ impl SanitizedTransaction { Ok(Self { message_hash: tx.message.hash(), - message: SanitizedMessage::Legacy(tx.message), + message: SanitizedMessage::Legacy(LegacyMessage::new(tx.message)), is_simple_vote_tx: false, signatures: tx.signatures, }) @@ -200,18 +187,21 @@ impl SanitizedTransaction { signatures, message: VersionedMessage::V0(v0::Message::clone(&sanitized_msg.message)), }, - SanitizedMessage::Legacy(message) => VersionedTransaction { + SanitizedMessage::Legacy(legacy_message) => VersionedTransaction { signatures, - message: VersionedMessage::Legacy(message.clone()), + message: VersionedMessage::Legacy(legacy::Message::clone(&legacy_message.message)), }, } } /// Validate and return the account keys locked by this transaction - pub fn get_account_locks(&self) -> Result { + pub fn get_account_locks( + &self, + tx_account_lock_limit: usize, + ) -> Result { if self.message.has_duplicates() { Err(TransactionError::AccountLoadedTwice) - } else if self.message.account_keys().len() > MAX_TX_ACCOUNT_LOCKS { + } else if self.message.account_keys().len() > tx_account_lock_limit { Err(TransactionError::TooManyAccountLocks) } else { Ok(self.get_account_locks_unchecked()) @@ -257,7 +247,7 @@ impl SanitizedTransaction { /// Return the serialized message data to sign. fn message_data(&self) -> Vec { match &self.message { - SanitizedMessage::Legacy(message) => message.serialize(), + SanitizedMessage::Legacy(legacy_message) => legacy_message.message.serialize(), SanitizedMessage::V0(loaded_msg) => loaded_msg.message.serialize(), } } diff --git a/sdk/src/transaction/versioned/mod.rs b/sdk/src/transaction/versioned/mod.rs index 5c0ce8772289ee..fd749c3892ae5f 100644 --- a/sdk/src/transaction/versioned/mod.rs +++ b/sdk/src/transaction/versioned/mod.rs @@ -20,6 +20,12 @@ use { mod sanitized; pub use sanitized::*; +use { + crate::program_utils::limited_deserialize, + solana_program::{ + nonce::NONCED_TX_MARKER_IX_INDEX, system_instruction::SystemInstruction, system_program, + }, +}; /// Type that serializes to the string "legacy" #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -71,7 +77,7 @@ impl VersionedTransaction { return Err(SignerError::InvalidInput("invalid message".to_string())); } - let signer_keys = keypairs.pubkeys(); + let signer_keys = keypairs.try_pubkeys()?; let expected_signer_keys = &static_account_keys[0..message.header().num_required_signatures as usize]; @@ -81,12 +87,27 @@ impl VersionedTransaction { Ordering::Equal => Ok(()), }?; - if signer_keys != expected_signer_keys { - return Err(SignerError::KeypairPubkeyMismatch); - } - let message_data = message.serialize(); - let signatures = keypairs.try_sign_message(&message_data)?; + let signature_indexes: Vec = expected_signer_keys + .iter() + .map(|signer_key| { + signer_keys + .iter() + .position(|key| key == signer_key) + .ok_or(SignerError::KeypairPubkeyMismatch) + }) + .collect::>()?; + + let unordered_signatures = keypairs.try_sign_message(&message_data)?; + let signatures: Vec = signature_indexes + .into_iter() + .map(|index| { + unordered_signatures + .get(index) + .copied() + .ok_or_else(|| SignerError::InvalidInput("invalid keypairs".to_string())) + }) + .collect::>()?; Ok(Self { signatures, @@ -166,4 +187,194 @@ impl VersionedTransaction { .map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes)) .collect() } + + /// Returns true if transaction begins with a valid advance nonce + /// instruction. Since dynamically loaded addresses can't have write locks + /// demoted without loading addresses, this shouldn't be used in the + /// runtime. + pub fn uses_durable_nonce(&self) -> bool { + let message = &self.message; + message + .instructions() + .get(NONCED_TX_MARKER_IX_INDEX as usize) + .filter(|instruction| { + // Is system program + matches!( + message.static_account_keys().get(instruction.program_id_index as usize), + Some(program_id) if system_program::check_id(program_id) + ) + // Is a nonce advance instruction + && matches!( + limited_deserialize(&instruction.data), + Ok(SystemInstruction::AdvanceNonceAccount) + ) + // Nonce account is writable + && matches!( + instruction.accounts.first(), + Some(index) if message.is_maybe_writable(*index as usize) + ) + }) + .is_some() + } +} + +#[cfg(test)] +mod tests { + use { + super::*, + crate::{ + message::Message as LegacyMessage, + signer::{keypair::Keypair, Signer}, + system_instruction, sysvar, + }, + solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + }, + }; + + #[test] + fn test_try_new() { + let keypair0 = Keypair::new(); + let keypair1 = Keypair::new(); + let keypair2 = Keypair::new(); + + let message = VersionedMessage::Legacy(LegacyMessage::new( + &[Instruction::new_with_bytes( + Pubkey::new_unique(), + &[], + vec![ + AccountMeta::new_readonly(keypair1.pubkey(), true), + AccountMeta::new_readonly(keypair2.pubkey(), false), + ], + )], + Some(&keypair0.pubkey()), + )); + + assert_eq!( + VersionedTransaction::try_new(message.clone(), &[&keypair0]), + Err(SignerError::NotEnoughSigners) + ); + + assert_eq!( + VersionedTransaction::try_new(message.clone(), &[&keypair0, &keypair0]), + Err(SignerError::KeypairPubkeyMismatch) + ); + + assert_eq!( + VersionedTransaction::try_new(message.clone(), &[&keypair1, &keypair2]), + Err(SignerError::KeypairPubkeyMismatch) + ); + + match VersionedTransaction::try_new(message.clone(), &[&keypair0, &keypair1]) { + Ok(tx) => assert_eq!(tx.verify_with_results(), vec![true; 2]), + Err(err) => assert_eq!(Some(err), None), + } + + match VersionedTransaction::try_new(message, &[&keypair1, &keypair0]) { + Ok(tx) => assert_eq!(tx.verify_with_results(), vec![true; 2]), + Err(err) => assert_eq!(Some(err), None), + } + } + + fn nonced_transfer_tx() -> (Pubkey, Pubkey, VersionedTransaction) { + let from_keypair = Keypair::new(); + let from_pubkey = from_keypair.pubkey(); + let nonce_keypair = Keypair::new(); + let nonce_pubkey = nonce_keypair.pubkey(); + let instructions = [ + system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey), + system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42), + ]; + let message = LegacyMessage::new(&instructions, Some(&nonce_pubkey)); + let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default()); + (from_pubkey, nonce_pubkey, tx.into()) + } + + #[test] + fn tx_uses_nonce_ok() { + let (_, _, tx) = nonced_transfer_tx(); + assert!(tx.uses_durable_nonce()); + } + + #[test] + fn tx_uses_nonce_empty_ix_fail() { + assert!(!VersionedTransaction::default().uses_durable_nonce()); + } + + #[test] + fn tx_uses_nonce_bad_prog_id_idx_fail() { + let (_, _, mut tx) = nonced_transfer_tx(); + match &mut tx.message { + VersionedMessage::Legacy(message) => { + message.instructions.get_mut(0).unwrap().program_id_index = 255u8; + } + VersionedMessage::V0(_) => unreachable!(), + }; + assert!(!tx.uses_durable_nonce()); + } + + #[test] + fn tx_uses_nonce_first_prog_id_not_nonce_fail() { + let from_keypair = Keypair::new(); + let from_pubkey = from_keypair.pubkey(); + let nonce_keypair = Keypair::new(); + let nonce_pubkey = nonce_keypair.pubkey(); + let instructions = [ + system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42), + system_instruction::advance_nonce_account(&nonce_pubkey, &nonce_pubkey), + ]; + let message = LegacyMessage::new(&instructions, Some(&from_pubkey)); + let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default()); + let tx = VersionedTransaction::from(tx); + assert!(!tx.uses_durable_nonce()); + } + + #[test] + fn tx_uses_ro_nonce_account() { + let from_keypair = Keypair::new(); + let from_pubkey = from_keypair.pubkey(); + let nonce_keypair = Keypair::new(); + let nonce_pubkey = nonce_keypair.pubkey(); + let account_metas = vec![ + AccountMeta::new_readonly(nonce_pubkey, false), + #[allow(deprecated)] + AccountMeta::new_readonly(sysvar::recent_blockhashes::id(), false), + AccountMeta::new_readonly(nonce_pubkey, true), + ]; + let nonce_instruction = Instruction::new_with_bincode( + system_program::id(), + &system_instruction::SystemInstruction::AdvanceNonceAccount, + account_metas, + ); + let tx = Transaction::new_signed_with_payer( + &[nonce_instruction], + Some(&from_pubkey), + &[&from_keypair, &nonce_keypair], + Hash::default(), + ); + let tx = VersionedTransaction::from(tx); + assert!(!tx.uses_durable_nonce()); + } + + #[test] + fn tx_uses_nonce_wrong_first_nonce_ix_fail() { + let from_keypair = Keypair::new(); + let from_pubkey = from_keypair.pubkey(); + let nonce_keypair = Keypair::new(); + let nonce_pubkey = nonce_keypair.pubkey(); + let instructions = [ + system_instruction::withdraw_nonce_account( + &nonce_pubkey, + &nonce_pubkey, + &from_pubkey, + 42, + ), + system_instruction::transfer(&from_pubkey, &nonce_pubkey, 42), + ]; + let message = LegacyMessage::new(&instructions, Some(&nonce_pubkey)); + let tx = Transaction::new(&[&from_keypair, &nonce_keypair], message, Hash::default()); + let tx = VersionedTransaction::from(tx); + assert!(!tx.uses_durable_nonce()); + } } diff --git a/send-transaction-service/Cargo.toml b/send-transaction-service/Cargo.toml index 46991efe1e0df7..63fbd285b4cf02 100644 --- a/send-transaction-service/Cargo.toml +++ b/send-transaction-service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-send-transaction-service" -version = "1.11.6" +version = "1.14.24" description = "Solana send transaction service" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -12,14 +12,14 @@ edition = "2021" [dependencies] crossbeam-channel = "0.5" log = "0.4.17" -solana-client = { path = "../client", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } [dev-dependencies] -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/send-transaction-service/src/send_transaction_service.rs b/send-transaction-service/src/send_transaction_service.rs index 9195aeb0a7b4b7..5419cd0bb77793 100644 --- a/send-transaction-service/src/send_transaction_service.rs +++ b/send-transaction-service/src/send_transaction_service.rs @@ -332,6 +332,7 @@ impl SendTransactionService { connection_cache: &Arc, retry_rate_ms: u64, leader_forward_count: u64, + exit: Arc, ) -> Self { let config = Config { retry_rate_ms, @@ -345,6 +346,7 @@ impl SendTransactionService { receiver, connection_cache, config, + exit, ) } @@ -355,6 +357,7 @@ impl SendTransactionService { receiver: Receiver, connection_cache: &Arc, config: Config, + exit: Arc, ) -> Self { let stats_report = Arc::new(SendTransactionServiceStatsReport::default()); @@ -362,7 +365,6 @@ impl SendTransactionService { let leader_info_provider = Arc::new(Mutex::new(CurrentLeaderInfo::new(leader_info))); - let exit = Arc::new(AtomicBool::new(false)); let receive_txn_thread = Self::receive_txn_thread( tpu_address, receiver, @@ -410,11 +412,15 @@ impl SendTransactionService { config ); Builder::new() - .name("send-tx-receive".to_string()) + .name("solStxReceive".to_string()) .spawn(move || loop { let recv_timeout_ms = config.batch_send_rate_ms; let stats = &stats_report.stats; - match receiver.recv_timeout(Duration::from_millis(recv_timeout_ms)) { + let recv_result = receiver.recv_timeout(Duration::from_millis(recv_timeout_ms)); + if exit.load(Ordering::Relaxed) { + break; + } + match recv_result { Err(RecvTimeoutError::Disconnected) => { info!("Terminating send-transaction-service."); exit.store(true, Ordering::Relaxed); @@ -510,7 +516,7 @@ impl SendTransactionService { config ); Builder::new() - .name("send-tx-retry".to_string()) + .name("solStxRetry".to_string()) .spawn(move || loop { let retry_interval_ms = config.retry_rate_ms; let stats = &stats_report.stats; @@ -772,7 +778,7 @@ mod test { use { super::*, crate::tpu_info::NullTpuInfo, - crossbeam_channel::unbounded, + crossbeam_channel::{bounded, unbounded}, solana_sdk::{ account::AccountSharedData, genesis_config::create_genesis_config, @@ -800,12 +806,55 @@ mod test { &connection_cache, 1000, 1, + Arc::new(AtomicBool::new(false)), ); drop(sender); send_tranaction_service.join().unwrap(); } + #[test] + fn validator_exit() { + let tpu_address = "127.0.0.1:0".parse().unwrap(); + let bank = Bank::default_for_tests(); + let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); + let (sender, receiver) = bounded(0); + + let dummy_tx_info = || TransactionInfo { + signature: Signature::default(), + wire_transaction: vec![0; 128], + last_valid_block_height: 0, + durable_nonce_info: None, + max_retries: None, + retries: 0, + last_sent_time: None, + }; + + let exit = Arc::new(AtomicBool::new(false)); + let connection_cache = Arc::new(ConnectionCache::default()); + let _send_transaction_service = SendTransactionService::new::( + tpu_address, + &bank_forks, + None, + receiver, + &connection_cache, + 1000, + 1, + exit.clone(), + ); + + sender.send(dummy_tx_info()).unwrap(); + + thread::spawn(move || { + exit.store(true, Ordering::Relaxed); + }); + + let mut option = Ok(()); + while option.is_ok() { + option = sender.send(dummy_tx_info()); + } + } + #[test] fn process_transactions() { solana_logger::setup(); diff --git a/stake-accounts/Cargo.toml b/stake-accounts/Cargo.toml index 8162ca656698f1..1cdd6917f7cec4 100644 --- a/stake-accounts/Cargo.toml +++ b/stake-accounts/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-stake-accounts" description = "Blockchain, Rebuilt for Scale" authors = ["Solana Maintainers "] edition = "2021" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -11,15 +11,15 @@ documentation = "https://docs.rs/solana-stake-accounts" [dependencies] clap = "2.33.1" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-remote-wallet = { path = "../remote-wallet", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-stake-program = { path = "../programs/stake", version = "=1.11.6" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-remote-wallet = { path = "../remote-wallet", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-stake-program = { path = "../programs/stake", version = "=1.14.24" } [dev-dependencies] -solana-runtime = { path = "../runtime", version = "=1.11.6" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/storage-bigtable/Cargo.toml b/storage-bigtable/Cargo.toml index f2a188a43ff0e3..00d4b8ea046a99 100644 --- a/storage-bigtable/Cargo.toml +++ b/storage-bigtable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-storage-bigtable" -version = "1.11.6" +version = "1.14.24" description = "Solana Storage BigTable" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -13,7 +13,7 @@ edition = "2021" backoff = { version = "0.4.0", features = ["tokio"] } bincode = "1.3.3" bytes = "1.0" -bzip2 = "0.4.3" +bzip2 = "0.4.4" enum-iterator = "0.8.1" flate2 = "1.0.24" futures = "0.3.21" @@ -27,12 +27,12 @@ prost-types = "0.11.0" serde = "1.0.138" serde_derive = "1.0.103" smpl_jwt = "0.7.1" -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-storage-proto = { path = "../storage-proto", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-storage-proto = { path = "../storage-proto", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } thiserror = "1.0" -tokio = "~1.14.1" +tokio = "1" tonic = { version = "0.8.0", features = ["tls", "transport"] } zstd = "0.11.2" diff --git a/storage-bigtable/build-proto/Cargo.lock b/storage-bigtable/build-proto/Cargo.lock deleted file mode 100644 index a916ff8462bbc0..00000000000000 --- a/storage-bigtable/build-proto/Cargo.lock +++ /dev/null @@ -1,373 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anyhow" -version = "1.0.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" - -[[package]] -name = "autocfg" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" - -[[package]] -name = "autotools" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8138adefca3e5d2e73bfba83bd6eeaf904b26a7ac1b4a19892cfe16cc7e1701" -dependencies = [ - "cc", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "bytes" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" - -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "either" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" - -[[package]] -name = "fastrand" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" -dependencies = [ - "instant", -] - -[[package]] -name = "fixedbitset" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "indexmap" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "itertools" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" -dependencies = [ - "either", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "multimap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce" - -[[package]] -name = "petgraph" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "prettyplease" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b83ec2d0af5c5c556257ff52c9f98934e243b9fd39604bfb2a9b75ec2e97f18" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "prost" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c3c31cdec40583bb68f0b18403400d01ec4289c383aa047560439952c4dd7" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49d928704208aba2cb1fb022ce1a319bdedcb03caf51ddf82734fa903407762" -dependencies = [ - "bytes", - "heck", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prost", - "prost-types", - "regex", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "prost-types" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d30bc806a29b347314be074ff0608ef8e547286e8ea68b061a2fe55689edc01f" -dependencies = [ - "bytes", - "prost", -] - -[[package]] -name = "proto" -version = "1.11.6" -dependencies = [ - "protobuf-src", - "tonic-build", -] - -[[package]] -name = "protobuf-src" -version = "1.0.5+3.19.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe57f68bf9767f48f8cbcbceb5da21524e2b1330a821c1c2502c447d8043f078" -dependencies = [ - "autotools", -] - -[[package]] -name = "quote" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "syn" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "thiserror" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tonic-build" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fbcd2800e34e743b9ae795867d5f77b535d3a3be69fd731e39145719752df8c" -dependencies = [ - "prettyplease", - "proc-macro2", - "prost-build", - "quote", - "syn", -] - -[[package]] -name = "unicode-xid" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" - -[[package]] -name = "which" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef" -dependencies = [ - "libc", - "thiserror", -] - -[[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-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/storage-bigtable/build-proto/Cargo.toml b/storage-bigtable/build-proto/Cargo.toml index 7da00e8dbb052b..cdf82b5844da23 100644 --- a/storage-bigtable/build-proto/Cargo.toml +++ b/storage-bigtable/build-proto/Cargo.toml @@ -7,10 +7,12 @@ license = "Apache-2.0" name = "proto" publish = false repository = "https://github.com/solana-labs/solana" -version = "1.11.6" - -[workspace] +version = "1.14.24" [dependencies] -protobuf-src = "1.0.5" tonic-build = "0.8.0" + +# windows users should install the protobuf compiler manually and set the PROTOC +# envar to point to the installed binary +[target."cfg(not(windows))".dependencies] +protobuf-src = "1.0.5" diff --git a/storage-bigtable/build-proto/src/main.rs b/storage-bigtable/build-proto/src/main.rs index 4e7b4b171021ad..f2385d48a5ffa5 100644 --- a/storage-bigtable/build-proto/src/main.rs +++ b/storage-bigtable/build-proto/src/main.rs @@ -1,6 +1,7 @@ fn main() -> Result<(), std::io::Error> { const PROTOC_ENVAR: &str = "PROTOC"; if std::env::var(PROTOC_ENVAR).is_err() { + #[cfg(not(windows))] std::env::set_var(PROTOC_ENVAR, protobuf_src::protoc()); } diff --git a/storage-bigtable/src/access_token.rs b/storage-bigtable/src/access_token.rs index 3c2d596cf07081..c6664ba887fa7d 100644 --- a/storage-bigtable/src/access_token.rs +++ b/storage-bigtable/src/access_token.rs @@ -16,6 +16,7 @@ use { }, time::Instant, }, + tokio::time, }; fn load_credentials(filepath: Option) -> Result { @@ -109,15 +110,22 @@ impl AccessToken { } info!("Refreshing token"); - let new_token = Self::get_token(&self.credentials, &self.scope).await; + match time::timeout( + time::Duration::from_secs(5), + Self::get_token(&self.credentials, &self.scope), + ) + .await { - let mut token_w = self.token.write().unwrap(); - match new_token { - Ok(new_token) => *token_w = new_token, - Err(err) => warn!("{}", err), + Ok(new_token) => match (new_token, self.token.write()) { + (Ok(new_token), Ok(mut token_w)) => *token_w = new_token, + (Ok(_new_token), Err(err)) => warn!("{}", err), + (Err(err), _) => warn!("{}", err), + }, + Err(_) => { + warn!("Token refresh timeout") } - self.refresh_active.store(false, Ordering::Relaxed); } + self.refresh_active.store(false, Ordering::Relaxed); } /// Return an access token suitable for use in an HTTP authorization header diff --git a/storage-bigtable/src/bigtable.rs b/storage-bigtable/src/bigtable.rs index 629b1eab387474..2708515925cef7 100644 --- a/storage-bigtable/src/bigtable.rs +++ b/storage-bigtable/src/bigtable.rs @@ -403,7 +403,7 @@ impl) -> InterceptedRequestResult> BigTable { /// /// If `end_at` is provided, the row key listing will end at the key. Otherwise it will /// continue until the `rows_limit` is reached or the end of the table, whichever comes first. - /// If `rows_limit` is zero, the listing will continue until the end of the table. + /// If `rows_limit` is zero, this method will return an empty array. pub async fn get_row_keys( &mut self, table_name: &str, @@ -411,6 +411,9 @@ impl) -> InterceptedRequestResult> BigTable { end_at: Option, rows_limit: i64, ) -> Result> { + if rows_limit == 0 { + return Ok(vec![]); + } self.refresh_access_token().await; let response = self .client @@ -465,7 +468,7 @@ impl) -> InterceptedRequestResult> BigTable { /// /// If `end_at` is provided, the row key listing will end at the key. Otherwise it will /// continue until the `rows_limit` is reached or the end of the table, whichever comes first. - /// If `rows_limit` is zero, the listing will continue until the end of the table. + /// If `rows_limit` is zero, this method will return an empty array. pub async fn get_row_data( &mut self, table_name: &str, @@ -473,8 +476,10 @@ impl) -> InterceptedRequestResult> BigTable { end_at: Option, rows_limit: i64, ) -> Result> { + if rows_limit == 0 { + return Ok(vec![]); + } self.refresh_access_token().await; - let response = self .client .read_rows(ReadRowsRequest { @@ -515,7 +520,7 @@ impl) -> InterceptedRequestResult> BigTable { .read_rows(ReadRowsRequest { table_name: format!("{}{}", self.table_prefix, table_name), app_profile_id: self.app_profile_id.clone(), - rows_limit: 0, // return all keys + rows_limit: 0, // return all existing rows rows: Some(RowSet { row_keys: row_keys .iter() @@ -897,6 +902,7 @@ mod tests { rewards: Some(vec![]), loaded_addresses: LoadedAddresses::default(), return_data: Some(TransactionReturnData::default()), + compute_units_consumed: Some(1234), }, }); let expected_block = ConfirmedBlock { @@ -955,6 +961,7 @@ mod tests { meta.post_token_balances = None; // Legacy bincode implementation does not support token balances meta.rewards = None; // Legacy bincode implementation does not support rewards meta.return_data = None; // Legacy bincode implementation does not support return data + meta.compute_units_consumed = None; // Legacy bincode implementation does not support CU consumed } assert_eq!(block, bincode_block.into()); } else { diff --git a/storage-bigtable/src/lib.rs b/storage-bigtable/src/lib.rs index ba8e8c0e39dc4b..fa96d3a102bc32 100644 --- a/storage-bigtable/src/lib.rs +++ b/storage-bigtable/src/lib.rs @@ -244,6 +244,7 @@ impl From for TransactionStatusMeta { rewards: None, loaded_addresses: LoadedAddresses::default(), return_data: None, + compute_units_consumed: None, } } } @@ -459,8 +460,7 @@ impl LedgerStorage { /// Fetch the next slots after the provided slot that contains a block /// /// start_slot: slot to start the search from (inclusive) - /// limit: stop after this many slots have been found; if limit==0, all records in the table - /// after start_slot will be read + /// limit: stop after this many slots have been found pub async fn get_confirmed_blocks(&self, start_slot: Slot, limit: usize) -> Result> { debug!( "LedgerStorage::get_confirmed_blocks request received: {:?} {:?}", @@ -975,11 +975,7 @@ impl LedgerStorage { .collect(); let tx_deletion_rows = if !expected_tx_infos.is_empty() { - let signatures = expected_tx_infos - .iter() - .map(|(signature, _info)| signature) - .cloned() - .collect::>(); + let signatures = expected_tx_infos.keys().cloned().collect::>(); let fetched_tx_infos: HashMap> = self.connection .get_bincode_cells_with_retry::("tx", &signatures) diff --git a/storage-proto/Cargo.toml b/storage-proto/Cargo.toml index 39174c37dbf99b..7b06b2b2f5e255 100644 --- a/storage-proto/Cargo.toml +++ b/storage-proto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-storage-proto" -version = "1.11.6" +version = "1.14.24" description = "Solana Storage Protobuf Definitions" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,9 +14,9 @@ bincode = "1.3.3" bs58 = "0.4.0" prost = "0.11.0" serde = "1.0.138" -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } [dev-dependencies] enum-iterator = "0.8.1" @@ -29,5 +29,9 @@ name = "solana_storage_proto" targets = ["x86_64-unknown-linux-gnu"] [build-dependencies] -protobuf-src = "1.0.5" tonic-build = "0.8.0" + +# windows users should install the protobuf compiler manually and set the PROTOC +# envar to point to the installed binary +[target."cfg(not(windows))".build-dependencies] +protobuf-src = "1.0.5" diff --git a/storage-proto/build.rs b/storage-proto/build.rs index e5e17400a4feeb..ec975a105411ae 100644 --- a/storage-proto/build.rs +++ b/storage-proto/build.rs @@ -1,6 +1,7 @@ fn main() -> Result<(), std::io::Error> { const PROTOC_ENVAR: &str = "PROTOC"; if std::env::var(PROTOC_ENVAR).is_err() { + #[cfg(not(windows))] std::env::set_var(PROTOC_ENVAR, protobuf_src::protoc()); } diff --git a/storage-proto/proto/confirmed_block.proto b/storage-proto/proto/confirmed_block.proto index 5d69f0e9c2e70b..ef8dc0fb215f54 100644 --- a/storage-proto/proto/confirmed_block.proto +++ b/storage-proto/proto/confirmed_block.proto @@ -59,6 +59,11 @@ message TransactionStatusMeta { repeated bytes loaded_readonly_addresses = 13; ReturnData return_data = 14; bool return_data_none = 15; + + // Sum of compute units consumed by all instructions. + // Available since Solana v1.10.35 / v1.11.6. + // Set to `None` for txs executed on earlier versions. + optional uint64 compute_units_consumed = 16; } message TransactionError { diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 4998d5f9a206a2..0b942a2dd92dee 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -302,7 +302,7 @@ impl From for VersionedMessage { let account_keys = value .account_keys .into_iter() - .map(|key| Pubkey::new(&key)) + .map(|key| Pubkey::try_from(key).unwrap()) .collect(); let recent_blockhash = Hash::new(&value.recent_blockhash); let instructions = value.instructions.into_iter().map(|ix| ix.into()).collect(); @@ -365,6 +365,7 @@ impl From for generated::TransactionStatusMeta { rewards, loaded_addresses, return_data, + compute_units_consumed, } = value; let err = match status { Ok(()) => None, @@ -424,6 +425,7 @@ impl From for generated::TransactionStatusMeta { loaded_readonly_addresses, return_data, return_data_none, + compute_units_consumed, } } } @@ -455,6 +457,7 @@ impl TryFrom for TransactionStatusMeta { loaded_readonly_addresses, return_data, return_data_none, + compute_units_consumed, } = value; let status = match &err { None => Ok(()), @@ -491,12 +494,20 @@ impl TryFrom for TransactionStatusMeta { let loaded_addresses = LoadedAddresses { writable: loaded_writable_addresses .into_iter() - .map(|key| Pubkey::new(&key)) - .collect(), + .map(Pubkey::try_from) + .collect::>() + .map_err(|err| { + let err = format!("Invalid writable address: {err:?}"); + Self::Error::new(bincode::ErrorKind::Custom(err)) + })?, readonly: loaded_readonly_addresses .into_iter() - .map(|key| Pubkey::new(&key)) - .collect(), + .map(Pubkey::try_from) + .collect::>() + .map_err(|err| { + let err = format!("Invalid readonly address: {err:?}"); + Self::Error::new(bincode::ErrorKind::Custom(err)) + })?, }; let return_data = if return_data_none { None @@ -515,6 +526,7 @@ impl TryFrom for TransactionStatusMeta { rewards, loaded_addresses, return_data, + compute_units_consumed, }) } } @@ -596,7 +608,7 @@ impl From for generated::MessageAddressTableLookup { impl From for MessageAddressTableLookup { fn from(value: generated::MessageAddressTableLookup) -> Self { Self { - account_key: Pubkey::new(&value.account_key), + account_key: Pubkey::try_from(value.account_key).unwrap(), writable_indexes: value.writable_indexes, readonly_indexes: value.readonly_indexes, } @@ -615,7 +627,7 @@ impl From for generated::ReturnData { impl From for TransactionReturnData { fn from(value: generated::ReturnData) -> Self { Self { - program_id: Pubkey::new(&value.program_id), + program_id: Pubkey::try_from(value.program_id).unwrap(), data: value.data, } } diff --git a/storage-proto/src/lib.rs b/storage-proto/src/lib.rs index 90cb01903362d3..0832690f1e2b88 100644 --- a/storage-proto/src/lib.rs +++ b/storage-proto/src/lib.rs @@ -176,6 +176,8 @@ pub struct StoredTransactionStatusMeta { pub rewards: Option>, #[serde(deserialize_with = "default_on_eof")] pub return_data: Option, + #[serde(deserialize_with = "default_on_eof")] + pub compute_units_consumed: Option, } impl From for TransactionStatusMeta { @@ -191,6 +193,7 @@ impl From for TransactionStatusMeta { post_token_balances, rewards, return_data, + compute_units_consumed, } = value; Self { status, @@ -207,6 +210,7 @@ impl From for TransactionStatusMeta { .map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()), loaded_addresses: LoadedAddresses::default(), return_data, + compute_units_consumed, } } } @@ -226,6 +230,7 @@ impl TryFrom for StoredTransactionStatusMeta { rewards, loaded_addresses, return_data, + compute_units_consumed, } = value; if !loaded_addresses.is_empty() { @@ -250,6 +255,7 @@ impl TryFrom for StoredTransactionStatusMeta { rewards: rewards .map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()), return_data, + compute_units_consumed, }) } } diff --git a/streamer/Cargo.toml b/streamer/Cargo.toml index bfb38884a08645..bd0edec3c0db59 100644 --- a/streamer/Cargo.toml +++ b/streamer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-streamer" -version = "1.11.6" +version = "1.14.24" description = "Solana Streamer" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -25,15 +25,15 @@ quinn = "0.8.3" rand = "0.7.0" rcgen = "0.9.2" rustls = { version = "0.20.6", features = ["dangerous_configuration"] } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } thiserror = "1.0" -tokio = { version = "~1.14.1", features = ["full"] } +tokio = { version = "1", features = ["full"] } x509-parser = "0.14.0" [dev-dependencies] -solana-logger = { path = "../logger", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } [lib] crate-type = ["lib"] diff --git a/streamer/src/nonblocking/quic.rs b/streamer/src/nonblocking/quic.rs index 5f866d30e029ba..823840eba2c632 100644 --- a/streamer/src/nonblocking/quic.rs +++ b/streamer/src/nonblocking/quic.rs @@ -120,6 +120,8 @@ pub async fn run_server( stats.clone(), )); sleep(Duration::from_micros(WAIT_BETWEEN_NEW_CONNECTIONS_US)).await; + } else { + debug!("accept(): Timed out waiting for connection"); } } } @@ -196,141 +198,148 @@ async fn setup_connection( max_unstaked_connections: usize, stats: Arc, ) { + let from = connecting.remote_address(); if let Ok(connecting_result) = timeout( Duration::from_millis(QUIC_CONNECTION_HANDSHAKE_TIMEOUT_MS), connecting, ) .await { - if let Ok(new_connection) = connecting_result { - stats.total_connections.fetch_add(1, Ordering::Relaxed); - stats.total_new_connections.fetch_add(1, Ordering::Relaxed); - let NewConnection { - connection, - uni_streams, - .. - } = new_connection; - - let remote_addr = connection.remote_address(); - let mut remote_pubkey = None; - - let table_and_stake = { - let (some_pubkey, stake) = get_connection_stake(&connection, staked_nodes.clone()) - .map_or((None, 0), |(pubkey, stake)| (Some(pubkey), stake)); - if stake > 0 { - remote_pubkey = some_pubkey; - let mut connection_table_l = staked_connection_table.lock().unwrap(); - if connection_table_l.total_size >= max_staked_connections { - let num_pruned = connection_table_l.prune_random(stake); - if num_pruned == 0 { - if max_unstaked_connections > 0 { - // If we couldn't prune a connection in the staked connection table, let's - // put this connection in the unstaked connection table. If needed, prune a - // connection from the unstaked connection table. - connection_table_l = unstaked_connection_table.lock().unwrap(); - prune_unstaked_connection_table( - &mut connection_table_l, - max_unstaked_connections, - stats.clone(), - ); - Some((connection_table_l, stake)) + match connecting_result { + Ok(new_connection) => { + stats.total_new_connections.fetch_add(1, Ordering::Relaxed); + let NewConnection { + connection, + uni_streams, + .. + } = new_connection; + + let remote_addr = connection.remote_address(); + let mut remote_pubkey = None; + + let table_and_stake = { + let (some_pubkey, stake) = + get_connection_stake(&connection, staked_nodes.clone()) + .map_or((None, 0), |(pubkey, stake)| (Some(pubkey), stake)); + if stake > 0 { + remote_pubkey = some_pubkey; + let mut connection_table_l = staked_connection_table.lock().unwrap(); + if connection_table_l.total_size >= max_staked_connections { + let num_pruned = connection_table_l.prune_random(stake); + if num_pruned == 0 { + if max_unstaked_connections > 0 { + // If we couldn't prune a connection in the staked connection table, let's + // put this connection in the unstaked connection table. If needed, prune a + // connection from the unstaked connection table. + connection_table_l = unstaked_connection_table.lock().unwrap(); + prune_unstaked_connection_table( + &mut connection_table_l, + max_unstaked_connections, + stats.clone(), + ); + Some((connection_table_l, stake)) + } else { + stats + .connection_add_failed_on_pruning + .fetch_add(1, Ordering::Relaxed); + None + } } else { - stats - .connection_add_failed_on_pruning - .fetch_add(1, Ordering::Relaxed); - None + stats.num_evictions.fetch_add(num_pruned, Ordering::Relaxed); + Some((connection_table_l, stake)) } } else { - stats.num_evictions.fetch_add(num_pruned, Ordering::Relaxed); Some((connection_table_l, stake)) } + } else if max_unstaked_connections > 0 { + let mut connection_table_l = unstaked_connection_table.lock().unwrap(); + prune_unstaked_connection_table( + &mut connection_table_l, + max_unstaked_connections, + stats.clone(), + ); + Some((connection_table_l, 0)) } else { - Some((connection_table_l, stake)) + None } - } else if max_unstaked_connections > 0 { - let mut connection_table_l = unstaked_connection_table.lock().unwrap(); - prune_unstaked_connection_table( - &mut connection_table_l, - max_unstaked_connections, - stats.clone(), - ); - Some((connection_table_l, 0)) - } else { - None - } - }; + }; - if let Some((mut connection_table_l, stake)) = table_and_stake { - let table_type = connection_table_l.peer_type; - let total_stake = staked_nodes.read().map_or(0, |stakes| stakes.total_stake); - drop(staked_nodes); + if let Some((mut connection_table_l, stake)) = table_and_stake { + let table_type = connection_table_l.peer_type; + let total_stake = staked_nodes.read().map_or(0, |stakes| stakes.total_stake); + drop(staked_nodes); - let max_uni_streams = - VarInt::from_u64( - compute_max_allowed_uni_streams(table_type, stake, total_stake) as u64, - ); + let max_uni_streams = VarInt::from_u64(compute_max_allowed_uni_streams( + table_type, + stake, + total_stake, + ) as u64); - debug!( - "Peer type: {:?}, stake {}, total stake {}, max streams {}", - table_type, - stake, - total_stake, - max_uni_streams.unwrap().into_inner() - ); - - if let Ok(max_uni_streams) = max_uni_streams { - connection.set_max_concurrent_uni_streams(max_uni_streams); - if let Some((last_update, stream_exit)) = connection_table_l.try_add_connection( - ConnectionTableKey::new(remote_addr.ip(), remote_pubkey), - remote_addr.port(), - Some(connection), + debug!( + "Peer type: {:?}, stake {}, total stake {}, max streams {}", + table_type, stake, - timing::timestamp(), - max_connections_per_peer, - ) { - drop(connection_table_l); - let stats = stats.clone(); - let connection_table = match table_type { - ConnectionPeerType::Unstaked => { - stats - .connection_added_from_unstaked_peer - .fetch_add(1, Ordering::Relaxed); - unstaked_connection_table.clone() - } - ConnectionPeerType::Staked => { - stats - .connection_added_from_staked_peer - .fetch_add(1, Ordering::Relaxed); - staked_connection_table.clone() - } - }; - tokio::spawn(handle_connection( - uni_streams, - packet_sender, - remote_addr, - remote_pubkey, - last_update, - connection_table, - stream_exit, - stats, - stake, - )); + total_stake, + max_uni_streams.unwrap().into_inner() + ); + + if let Ok(max_uni_streams) = max_uni_streams { + connection.set_max_concurrent_uni_streams(max_uni_streams); + if let Some((last_update, stream_exit)) = connection_table_l + .try_add_connection( + ConnectionTableKey::new(remote_addr.ip(), remote_pubkey), + remote_addr.port(), + Some(connection), + stake, + timing::timestamp(), + max_connections_per_peer, + ) + { + drop(connection_table_l); + let stats = stats.clone(); + let connection_table = match table_type { + ConnectionPeerType::Unstaked => { + stats + .connection_added_from_unstaked_peer + .fetch_add(1, Ordering::Relaxed); + unstaked_connection_table.clone() + } + ConnectionPeerType::Staked => { + stats + .connection_added_from_staked_peer + .fetch_add(1, Ordering::Relaxed); + staked_connection_table.clone() + } + }; + tokio::spawn(handle_connection( + uni_streams, + packet_sender, + remote_addr, + remote_pubkey, + last_update, + connection_table, + stream_exit, + stats, + stake, + )); + } else { + stats.connection_add_failed.fetch_add(1, Ordering::Relaxed); + } } else { - stats.connection_add_failed.fetch_add(1, Ordering::Relaxed); + stats + .connection_add_failed_invalid_stream_count + .fetch_add(1, Ordering::Relaxed); } } else { + connection.close(0u32.into(), &[0u8]); stats - .connection_add_failed_invalid_stream_count + .connection_add_failed_unstaked_node .fetch_add(1, Ordering::Relaxed); } - } else { - connection.close(0u32.into(), &[0u8]); - stats - .connection_add_failed_unstaked_node - .fetch_add(1, Ordering::Relaxed); } - } else { - stats.connection_setup_error.fetch_add(1, Ordering::Relaxed); + Err(e) => { + handle_connection_error(e, &stats, from); + } } } else { stats @@ -339,6 +348,44 @@ async fn setup_connection( } } +fn handle_connection_error(e: quinn::ConnectionError, stats: &StreamStats, from: SocketAddr) { + debug!("error: {:?} from: {:?}", e, from); + stats.connection_setup_error.fetch_add(1, Ordering::Relaxed); + match e { + quinn::ConnectionError::TimedOut => { + stats + .connection_setup_error_timed_out + .fetch_add(1, Ordering::Relaxed); + } + quinn::ConnectionError::ConnectionClosed(_) => { + stats + .connection_setup_error_closed + .fetch_add(1, Ordering::Relaxed); + } + quinn::ConnectionError::TransportError(_) => { + stats + .connection_setup_error_transport + .fetch_add(1, Ordering::Relaxed); + } + quinn::ConnectionError::ApplicationClosed(_) => { + stats + .connection_setup_error_app_closed + .fetch_add(1, Ordering::Relaxed); + } + quinn::ConnectionError::Reset => { + stats + .connection_setup_error_reset + .fetch_add(1, Ordering::Relaxed); + } + quinn::ConnectionError::LocallyClosed => { + stats + .connection_setup_error_locally_closed + .fetch_add(1, Ordering::Relaxed); + } + _ => {} + } +} + async fn handle_connection( mut uni_streams: IncomingUniStreams, packet_sender: Sender, @@ -356,6 +403,7 @@ async fn handle_connection( stats.total_streams.load(Ordering::Relaxed), stats.total_connections.load(Ordering::Relaxed), ); + stats.total_connections.fetch_add(1, Ordering::Relaxed); while !stream_exit.load(Ordering::Relaxed) { if let Ok(stream) = tokio::time::timeout( Duration::from_millis(WAIT_FOR_STREAM_TIMEOUT_MS), diff --git a/streamer/src/packet.rs b/streamer/src/packet.rs index ea8e346f8d88d5..9824b052cec211 100644 --- a/streamer/src/packet.rs +++ b/streamer/src/packet.rs @@ -68,7 +68,7 @@ pub fn send_to( let addr = p.meta.socket_addr(); if socket_addr_space.check(&addr) { if let Some(data) = p.data(..) { - socket.send_to(data, &addr)?; + socket.send_to(data, addr)?; } } } diff --git a/streamer/src/quic.rs b/streamer/src/quic.rs index bfc445444a44b5..f7d4760171f138 100644 --- a/streamer/src/quic.rs +++ b/streamer/src/quic.rs @@ -78,6 +78,7 @@ pub(crate) fn configure_server( server_tls_config.alpn_protocols = vec![ALPN_TPU_PROTOCOL_ID.to_vec()]; let mut server_config = ServerConfig::with_crypto(Arc::new(server_tls_config)); + server_config.use_retry(true); let config = Arc::get_mut(&mut server_config.transport).unwrap(); // QUIC_MAX_CONCURRENT_STREAMS doubled, which was found to improve reliability @@ -137,6 +138,12 @@ pub struct StreamStats { pub(crate) connection_add_failed_on_pruning: AtomicUsize, pub(crate) connection_setup_timeout: AtomicUsize, pub(crate) connection_setup_error: AtomicUsize, + pub(crate) connection_setup_error_closed: AtomicUsize, + pub(crate) connection_setup_error_timed_out: AtomicUsize, + pub(crate) connection_setup_error_transport: AtomicUsize, + pub(crate) connection_setup_error_app_closed: AtomicUsize, + pub(crate) connection_setup_error_reset: AtomicUsize, + pub(crate) connection_setup_error_locally_closed: AtomicUsize, pub(crate) connection_removed: AtomicUsize, pub(crate) connection_remove_failed: AtomicUsize, } @@ -225,6 +232,41 @@ impl StreamStats { self.connection_setup_error.swap(0, Ordering::Relaxed), i64 ), + ( + "connection_setup_error_timed_out", + self.connection_setup_error_timed_out + .swap(0, Ordering::Relaxed), + i64 + ), + ( + "connection_setup_error_closed", + self.connection_setup_error_closed + .swap(0, Ordering::Relaxed), + i64 + ), + ( + "connection_setup_error_transport", + self.connection_setup_error_transport + .swap(0, Ordering::Relaxed), + i64 + ), + ( + "connection_setup_error_app_closed", + self.connection_setup_error_app_closed + .swap(0, Ordering::Relaxed), + i64 + ), + ( + "connection_setup_error_reset", + self.connection_setup_error_reset.swap(0, Ordering::Relaxed), + i64 + ), + ( + "connection_setup_error_locally_closed", + self.connection_setup_error_locally_closed + .swap(0, Ordering::Relaxed), + i64 + ), ( "invalid_chunk", self.total_invalid_chunks.swap(0, Ordering::Relaxed), @@ -303,11 +345,14 @@ pub fn spawn_server( stats, ) }?; - let handle = thread::spawn(move || { - if let Err(e) = runtime.block_on(task) { - warn!("error from runtime.block_on: {:?}", e); - } - }); + let handle = thread::Builder::new() + .name("solQuicServer".into()) + .spawn(move || { + if let Err(e) = runtime.block_on(task) { + warn!("error from runtime.block_on: {:?}", e); + } + }) + .unwrap(); Ok(handle) } diff --git a/streamer/src/recvmmsg.rs b/streamer/src/recvmmsg.rs index bb2691009b1f07..722b4a22cd2fe2 100644 --- a/streamer/src/recvmmsg.rs +++ b/streamer/src/recvmmsg.rs @@ -75,7 +75,8 @@ pub fn recv_mmsg(sock: &UdpSocket, packets: &mut [Packet]) -> io::Result(); let mut hdrs: [mmsghdr; NUM_RCVMMSGS] = unsafe { mem::zeroed() }; - let mut iovs: [iovec; NUM_RCVMMSGS] = unsafe { mem::MaybeUninit::uninit().assume_init() }; + let iovs = mem::MaybeUninit::<[iovec; NUM_RCVMMSGS]>::uninit(); + let mut iovs: [iovec; NUM_RCVMMSGS] = unsafe { iovs.assume_init() }; let mut addrs: [sockaddr_storage; NUM_RCVMMSGS] = unsafe { mem::zeroed() }; let sock_fd = sock.as_raw_fd(); @@ -141,7 +142,7 @@ mod tests { let sent = TEST_NUM_MSGS - 1; for _ in 0..sent { let data = [0; PACKET_DATA_SIZE]; - sender.send_to(&data[..], &addr).unwrap(); + sender.send_to(&data[..], addr).unwrap(); } let mut packets = vec![Packet::default(); TEST_NUM_MSGS]; @@ -167,7 +168,7 @@ mod tests { let sent = TEST_NUM_MSGS + 10; for _ in 0..sent { let data = [0; PACKET_DATA_SIZE]; - sender.send_to(&data[..], &addr).unwrap(); + sender.send_to(&data[..], addr).unwrap(); } let mut packets = vec![Packet::default(); TEST_NUM_MSGS]; @@ -208,7 +209,7 @@ mod tests { let sent = TEST_NUM_MSGS; for _ in 0..sent { let data = [0; PACKET_DATA_SIZE]; - sender.send_to(&data[..], &addr).unwrap(); + sender.send_to(&data[..], addr).unwrap(); } let start = Instant::now(); @@ -243,12 +244,12 @@ mod tests { for _ in 0..sent1 { let data = [0; PACKET_DATA_SIZE]; - sender1.send_to(&data[..], &addr).unwrap(); + sender1.send_to(&data[..], addr).unwrap(); } for _ in 0..sent2 { let data = [0; PACKET_DATA_SIZE]; - sender2.send_to(&data[..], &addr).unwrap(); + sender2.send_to(&data[..], addr).unwrap(); } let mut packets = vec![Packet::default(); TEST_NUM_MSGS]; diff --git a/streamer/src/sendmmsg.rs b/streamer/src/sendmmsg.rs index 8147ac9e1814f9..e035341fedf22c 100644 --- a/streamer/src/sendmmsg.rs +++ b/streamer/src/sendmmsg.rs @@ -134,7 +134,8 @@ where { let size = packets.len(); #[allow(clippy::uninit_assumed_init)] - let mut iovs = vec![unsafe { std::mem::MaybeUninit::uninit().assume_init() }; size]; + let iovec = std::mem::MaybeUninit::::uninit(); + let mut iovs = vec![unsafe { iovec.assume_init() }; size]; let mut addrs = vec![unsafe { std::mem::zeroed() }; size]; let mut hdrs = vec![unsafe { std::mem::zeroed() }; size]; for ((pkt, dest), hdr, iov, addr) in izip!(packets, &mut hdrs, &mut iovs, &mut addrs) { diff --git a/streamer/src/streamer.rs b/streamer/src/streamer.rs index 4d8ee2d1c0a924..7e60f7c8fbaa7b 100644 --- a/streamer/src/streamer.rs +++ b/streamer/src/streamer.rs @@ -166,7 +166,7 @@ pub fn receiver( let res = socket.set_read_timeout(Some(Duration::new(1, 0))); assert!(res.is_ok(), "streamer::receiver set_read_timeout error"); Builder::new() - .name("solana-receiver".to_string()) + .name("solReceiver".to_string()) .spawn(move || { let _ = recv_loop( &socket, @@ -370,7 +370,7 @@ pub fn responder( stats_reporter_sender: Option>>, ) -> JoinHandle<()> { Builder::new() - .name(format!("solana-responder-{}", name)) + .name(format!("solRspndr{}", name)) .spawn(move || { let mut errors = 0; let mut last_error = None; @@ -475,7 +475,7 @@ mod test { let t_responder = { let (s_responder, r_responder) = unbounded(); let t_responder = responder( - "streamer_send_test", + "SendTest", Arc::new(send), r_responder, SocketAddrSpace::Unspecified, diff --git a/streamer/src/tls_certificates.rs b/streamer/src/tls_certificates.rs index 27d3365c885ed4..6f964972a248cc 100644 --- a/streamer/src/tls_certificates.rs +++ b/streamer/src/tls_certificates.rs @@ -57,16 +57,16 @@ pub fn new_self_signed_tls_certificate_chain( } pub fn get_pubkey_from_tls_certificate(certificates: &[rustls::Certificate]) -> Option { - certificates.first().and_then(|der_cert| { - X509Certificate::from_der(der_cert.as_ref()) - .ok() - .and_then(|(_, cert)| { - cert.public_key().parsed().ok().and_then(|key| match key { - PublicKey::Unknown(inner_key) => Some(Pubkey::new(inner_key)), - _ => None, - }) - }) - }) + if certificates.len() == 1 { + let der_cert = &certificates[0]; + let (_, cert) = X509Certificate::from_der(der_cert.as_ref()).ok()?; + match cert.public_key().parsed().ok()? { + PublicKey::Unknown(key) => Pubkey::try_from(key).ok(), + _ => None, + } + } else { + None + } } #[cfg(test)] diff --git a/streamer/tests/recvmmsg.rs b/streamer/tests/recvmmsg.rs index ffda66c6cfc632..7c8f602ad2ca42 100644 --- a/streamer/tests/recvmmsg.rs +++ b/streamer/tests/recvmmsg.rs @@ -22,7 +22,7 @@ pub fn test_recv_mmsg_batch_size() { (0..1000).for_each(|_| { for _ in 0..sent { let data = [0; PACKET_DATA_SIZE]; - sender.send_to(&data[..], &addr).unwrap(); + sender.send_to(&data[..], addr).unwrap(); } let mut packets = vec![Packet::default(); TEST_BATCH_SIZE]; let now = Instant::now(); @@ -38,7 +38,7 @@ pub fn test_recv_mmsg_batch_size() { (0..1000).for_each(|_| { for _ in 0..sent { let data = [0; PACKET_DATA_SIZE]; - sender.send_to(&data[..], &addr).unwrap(); + sender.send_to(&data[..], addr).unwrap(); } let mut packets = vec![Packet::default(); 4]; let mut recv = 0; diff --git a/sys-tuner/Cargo.toml b/sys-tuner/Cargo.toml index af47613a7f9d80..4079d53446a689 100644 --- a/sys-tuner/Cargo.toml +++ b/sys-tuner/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-sys-tuner" description = "The solana cluster system tuner daemon" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -14,8 +14,8 @@ publish = true clap = "2.33.1" libc = "0.2.126" log = "0.4.17" -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [target."cfg(unix)".dependencies] unix_socket2 = "0.5.4" diff --git a/sys-tuner/src/main.rs b/sys-tuner/src/main.rs index 2b8578b9713fd5..7fd5271333e95e 100644 --- a/sys-tuner/src/main.rs +++ b/sys-tuner/src/main.rs @@ -52,7 +52,7 @@ fn tune_poh_service_priority(uid: u32) { info!("PoH thread PID is {}", pid); let pid = format!("{}", pid); let output = Command::new("chrt") - .args(&["-r", "-p", "99", pid.as_str()]) + .args(["-r", "-p", "99", pid.as_str()]) .output() .expect("Expected to set priority of thread"); if output.status.success() { diff --git a/system-test/testnet-automation.sh b/system-test/testnet-automation.sh index 4cf23222c02d73..00426e18d8cdb7 100755 --- a/system-test/testnet-automation.sh +++ b/system-test/testnet-automation.sh @@ -13,7 +13,7 @@ Test failed during step: ${STEP} Failure occured when running the following command: -$(eval echo "$@")" +$*" fi # shellcheck disable=SC2034 diff --git a/test-validator/Cargo.toml b/test-validator/Cargo.toml index 025614811b7e06..b96518cf351c6e 100644 --- a/test-validator/Cargo.toml +++ b/test-validator/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "solana-test-validator" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" homepage = "https://solana.com/" documentation = "https://docs.rs/solana-test-validator" readme = "../README.md" @@ -15,20 +15,20 @@ base64 = "0.13.0" log = "0.4.17" serde_derive = "1.0.103" serde_json = "1.0.81" -solana-cli-output = { path = "../cli-output", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-core = { path = "../core", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-program-runtime = { path = "../program-runtime", version = "=1.11.6" } -solana-program-test = { path = "../program-test", version = "=1.11.6" } -solana-rpc = { path = "../rpc", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -tokio = { version = "~1.14.1", features = ["full"] } +solana-cli-output = { path = "../cli-output", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-core = { path = "../core", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-program-runtime = { path = "../program-runtime", version = "=1.14.24" } +solana-program-test = { path = "../program-test", version = "=1.14.24" } +solana-rpc = { path = "../rpc", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +tokio = { version = "1", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/test-validator/src/lib.rs b/test-validator/src/lib.rs index 73f4f700562a81..5fa6588acf672e 100644 --- a/test-validator/src/lib.rs +++ b/test-validator/src/lib.rs @@ -3,11 +3,15 @@ use { log::*, solana_cli_output::CliAccount, solana_client::{ - connection_cache::{DEFAULT_TPU_CONNECTION_POOL_SIZE, DEFAULT_TPU_USE_QUIC}, + connection_cache::{ + DEFAULT_TPU_CONNECTION_POOL_SIZE, DEFAULT_TPU_ENABLE_UDP, DEFAULT_TPU_USE_QUIC, + }, nonblocking, rpc_client::RpcClient, + rpc_request::MAX_MULTIPLE_ACCOUNTS, }, solana_core::{ + admin_rpc_post_init::AdminRpcRequestMetadataPostInit, tower_storage::TowerStorage, validator::{Validator, ValidatorConfig, ValidatorStartProgress}, }, @@ -122,6 +126,8 @@ pub struct TestValidatorGenesis { deactivate_feature_set: HashSet, compute_unit_limit: Option, pub log_messages_bytes_limit: Option, + pub tpu_enable_udp: bool, + admin_rpc_service_post_init: Arc>>, } impl Default for TestValidatorGenesis { @@ -151,6 +157,9 @@ impl Default for TestValidatorGenesis { deactivate_feature_set: HashSet::::default(), compute_unit_limit: Option::::default(), log_messages_bytes_limit: Option::::default(), + tpu_enable_udp: DEFAULT_TPU_ENABLE_UDP, + admin_rpc_service_post_init: + Arc::>>::default(), } } } @@ -178,6 +187,11 @@ impl TestValidatorGenesis { ledger_path.join("vote-account-keypair.json").exists() } + pub fn tpu_enable_udp(&mut self, tpu_enable_udp: bool) -> &mut Self { + self.tpu_enable_udp = tpu_enable_udp; + self + } + pub fn fee_rate_governor(&mut self, fee_rate_governor: FeeRateGovernor) -> &mut Self { self.fee_rate_governor = fee_rate_governor; self @@ -283,16 +297,24 @@ impl TestValidatorGenesis { where T: IntoIterator, { - for address in addresses { - info!("Fetching {} over RPC...", address); - let res = rpc_client.get_account(&address); - if let Ok(account) = res { - self.add_account(address, AccountSharedData::from(account)); - } else if skip_missing { - warn!("Could not find {}, skipping.", address); - } else { - error!("Failed to fetch {}: {}", address, res.unwrap_err()); - solana_core::validator::abort(); + let addresses: Vec = addresses.into_iter().collect(); + for chunk in addresses.chunks(MAX_MULTIPLE_ACCOUNTS) { + info!("Fetching {:?} over RPC...", chunk); + let responses = rpc_client + .get_multiple_accounts(chunk) + .unwrap_or_else(|err| { + error!("Failed to fetch: {}", err); + solana_core::validator::abort(); + }); + for (address, res) in chunk.iter().zip(responses) { + if let Some(account) = res { + self.add_account(*address, AccountSharedData::from(account)); + } else if skip_missing { + warn!("Could not find {}, skipping.", address); + } else { + error!("Failed to fetch {}", address); + solana_core::validator::abort(); + } } } self @@ -542,6 +564,25 @@ impl TestValidator { .expect("validator start failed") } + /// Create a test validator using udp for TPU. + pub fn with_no_fees_udp( + mint_address: Pubkey, + faucet_addr: Option, + socket_addr_space: SocketAddrSpace, + ) -> Self { + TestValidatorGenesis::default() + .tpu_enable_udp(true) + .fee_rate_governor(FeeRateGovernor::new(0, 0)) + .rent(Rent { + lamports_per_byte_year: 1, + exemption_threshold: 1.0, + ..Rent::default() + }) + .faucet_addr(faucet_addr) + .start_with_mint_address(mint_address, socket_addr_space) + .expect("validator start failed") + } + /// Create and start a `TestValidator` with custom transaction fees and minimal rent. /// Faucet optional. /// @@ -806,6 +847,8 @@ impl TestValidator { socket_addr_space, DEFAULT_TPU_USE_QUIC, DEFAULT_TPU_CONNECTION_POOL_SIZE, + config.tpu_enable_udp, + config.admin_rpc_service_post_init.clone(), )); // Needed to avoid panics in `solana-responder-gossip` in tests that create a number of diff --git a/tokens/Cargo.toml b/tokens/Cargo.toml index bd72624ff4fcd1..707c3ef23a07f9 100644 --- a/tokens/Cargo.toml +++ b/tokens/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-tokens" description = "Blockchain, Rebuilt for Scale" authors = ["Solana Maintainers "] edition = "2021" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -17,23 +17,23 @@ csv = "1.1.6" ctrlc = { version = "3.2.2", features = ["termination"] } indexmap = "1.9.1" indicatif = "0.16.2" -pickledb = "0.4.1" +pickledb = { version = "0.5.1", default-features = false, features = ["yaml"] } serde = { version = "1.0", features = ["derive"] } -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-remote-wallet = { path = "../remote-wallet", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -spl-associated-token-account = { version = "=1.0.5" } -spl-token = { version = "=3.3.1", features = ["no-entrypoint"] } -tempfile = "3.3.0" +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-remote-wallet = { path = "../remote-wallet", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +spl-associated-token-account = { version = "=1.1.3" } +spl-token = { version = "=3.5.0", features = ["no-entrypoint"] } +tempfile = "3.4.0" thiserror = "1.0" [dev-dependencies] bincode = "1.3.3" -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-test-validator = { path = "../test-validator", version = "=1.11.6" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-test-validator = { path = "../test-validator", version = "=1.14.24" } diff --git a/tokens/src/commands.rs b/tokens/src/commands.rs index ec23b0bab9183a..55724c4e74757d 100644 --- a/tokens/src/commands.rs +++ b/tokens/src/commands.rs @@ -913,7 +913,7 @@ pub fn test_process_distribute_tokens_with_client( let allocations_file = NamedTempFile::new().unwrap(); let input_csv = allocations_file.path().to_str().unwrap().to_string(); let mut wtr = csv::WriterBuilder::new().from_writer(allocations_file); - wtr.write_record(&["recipient", "amount"]).unwrap(); + wtr.write_record(["recipient", "amount"]).unwrap(); wtr.write_record(&[ alice_pubkey.to_string(), lamports_to_sol(expected_amount).to_string(), @@ -1013,7 +1013,7 @@ pub fn test_process_create_stake_with_client(client: &RpcClient, sender_keypair: let file = NamedTempFile::new().unwrap(); let input_csv = file.path().to_str().unwrap().to_string(); let mut wtr = csv::WriterBuilder::new().from_writer(file); - wtr.write_record(&["recipient", "amount", "lockup_date"]) + wtr.write_record(["recipient", "amount", "lockup_date"]) .unwrap(); wtr.write_record(&[ alice_pubkey.to_string(), @@ -1135,7 +1135,7 @@ pub fn test_process_distribute_stake_with_client(client: &RpcClient, sender_keyp let file = NamedTempFile::new().unwrap(); let input_csv = file.path().to_str().unwrap().to_string(); let mut wtr = csv::WriterBuilder::new().from_writer(file); - wtr.write_record(&["recipient", "amount", "lockup_date"]) + wtr.write_record(["recipient", "amount", "lockup_date"]) .unwrap(); wtr.write_record(&[ alice_pubkey.to_string(), diff --git a/tokens/src/spl_token.rs b/tokens/src/spl_token.rs index 9ea9d056512014..8d6f4be0a6cc4a 100644 --- a/tokens/src/spl_token.rs +++ b/tokens/src/spl_token.rs @@ -63,6 +63,7 @@ pub fn build_spl_token_instructions( &spl_token_pubkey(&args.fee_payer.pubkey()), &wallet_address, &spl_token_pubkey(&spl_token_args.mint), + &spl_token::id(), ); instructions.push(spl_token_instruction( create_associated_token_account_instruction, diff --git a/transaction-dos/Cargo.toml b/transaction-dos/Cargo.toml index 35eceed0950317..47d7b2c76b4693 100644 --- a/transaction-dos/Cargo.toml +++ b/transaction-dos/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-transaction-dos" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -14,23 +14,23 @@ clap = "2.33.1" log = "0.4.17" rand = "0.7.0" rayon = "1.5.3" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli = { path = "../cli", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-core = { path = "../core", version = "=1.11.6" } -solana-faucet = { path = "../faucet", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-transaction-status = { path = "../transaction-status", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli = { path = "../cli", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-core = { path = "../core", version = "=1.14.24" } +solana-faucet = { path = "../faucet", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-transaction-status = { path = "../transaction-status", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [dev-dependencies] -solana-local-cluster = { path = "../local-cluster", version = "=1.11.6" } +solana-local-cluster = { path = "../local-cluster", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/transaction-status/Cargo.toml b/transaction-status/Cargo.toml index c1115161dc379a..65319093b0758b 100644 --- a/transaction-status/Cargo.toml +++ b/transaction-status/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-transaction-status" -version = "1.11.6" +version = "1.14.24" description = "Solana transaction status types" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -20,16 +20,16 @@ log = "0.4.17" serde = "1.0.138" serde_derive = "1.0.103" serde_json = "1.0.81" -solana-account-decoder = { path = "../account-decoder", version = "=1.11.6" } -solana-measure = { path = "../measure", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } -spl-associated-token-account = { version = "=1.0.5", features = ["no-entrypoint"] } +solana-account-decoder = { path = "../account-decoder", version = "=1.14.24" } +solana-address-lookup-table-program = { path = "../programs/address-lookup-table", version = "=1.14.24" } +solana-measure = { path = "../measure", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } +spl-associated-token-account = { version = "=1.1.3", features = ["no-entrypoint"] } spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] } -spl-token = { version = "=3.3.1", features = ["no-entrypoint"] } -spl-token-2022 = { version = "=0.4.2", features = ["no-entrypoint"] } +spl-token = { version = "=3.5.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "=0.6.1", features = ["no-entrypoint"] } thiserror = "1.0" [package.metadata.docs.rs] diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index 31a1a83741e4fe..69ddd64a01e729 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -1,9 +1,10 @@ #![allow(clippy::integer_arithmetic)] -pub use {crate::extract_memos::extract_and_fmt_memos, solana_runtime::bank::RewardType}; +pub use {crate::extract_memos::extract_and_fmt_memos, solana_sdk::reward_type::RewardType}; use { crate::{ - parse_accounts::{parse_accounts, parse_static_accounts, ParsedAccount}, + option_serializer::OptionSerializer, + parse_accounts::{parse_legacy_message_accounts, parse_v0_message_accounts, ParsedAccount}, parse_instruction::{parse, ParsedInstruction}, }, solana_account_decoder::parse_token::UiTokenAmount, @@ -33,7 +34,9 @@ extern crate lazy_static; extern crate serde_derive; pub mod extract_memos; +pub mod option_serializer; pub mod parse_accounts; +pub mod parse_address_lookup_table; pub mod parse_associated_token; pub mod parse_bpf_loader; pub mod parse_instruction; @@ -72,6 +75,11 @@ pub trait EncodableWithMeta { fn json_encode(&self) -> Self::Encoded; } +trait JsonAccounts { + type Encoded; + fn build_json_accounts(&self) -> Self::Encoded; +} + #[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[serde(rename_all = "camelCase")] pub enum TransactionBinaryEncoding { @@ -113,6 +121,7 @@ pub enum TransactionDetails { Full, Signatures, None, + Accounts, } impl Default for TransactionDetails { @@ -249,10 +258,16 @@ pub struct UiTransactionTokenBalance { pub account_index: u8, pub mint: String, pub ui_token_amount: UiTokenAmount, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub owner: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub program_id: Option, + #[serde( + default = "OptionSerializer::skip", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub owner: OptionSerializer, + #[serde( + default = "OptionSerializer::skip", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub program_id: OptionSerializer, } impl From for UiTransactionTokenBalance { @@ -262,14 +277,14 @@ impl From for UiTransactionTokenBalance { mint: token_balance.mint, ui_token_amount: token_balance.ui_token_amount, owner: if !token_balance.owner.is_empty() { - Some(token_balance.owner) + OptionSerializer::Some(token_balance.owner) } else { - None + OptionSerializer::Skip }, program_id: if !token_balance.program_id.is_empty() { - Some(token_balance.program_id) + OptionSerializer::Some(token_balance.program_id) } else { - None + OptionSerializer::Skip }, } } @@ -288,6 +303,7 @@ pub struct TransactionStatusMeta { pub rewards: Option, pub loaded_addresses: LoadedAddresses, pub return_data: Option, + pub compute_units_consumed: Option, } impl Default for TransactionStatusMeta { @@ -304,6 +320,7 @@ impl Default for TransactionStatusMeta { rewards: None, loaded_addresses: LoadedAddresses::default(), return_data: None, + compute_units_consumed: None, } } } @@ -317,14 +334,46 @@ pub struct UiTransactionStatusMeta { pub fee: u64, pub pre_balances: Vec, pub post_balances: Vec, - pub inner_instructions: Option>, - pub log_messages: Option>, - pub pre_token_balances: Option>, - pub post_token_balances: Option>, - pub rewards: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub loaded_addresses: Option, - pub return_data: Option, + #[serde( + default = "OptionSerializer::none", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub inner_instructions: OptionSerializer>, + #[serde( + default = "OptionSerializer::none", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub log_messages: OptionSerializer>, + #[serde( + default = "OptionSerializer::none", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub pre_token_balances: OptionSerializer>, + #[serde( + default = "OptionSerializer::none", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub post_token_balances: OptionSerializer>, + #[serde( + default = "OptionSerializer::none", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub rewards: OptionSerializer, + #[serde( + default = "OptionSerializer::skip", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub loaded_addresses: OptionSerializer, + #[serde( + default = "OptionSerializer::skip", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub return_data: OptionSerializer, + #[serde( + default = "OptionSerializer::skip", + skip_serializing_if = "OptionSerializer::should_skip" + )] + pub compute_units_consumed: OptionSerializer, } /// A duplicate representation of LoadedAddresses @@ -353,7 +402,7 @@ impl From<&LoadedAddresses> for UiLoadedAddresses { } impl UiTransactionStatusMeta { - fn parse(meta: TransactionStatusMeta, static_keys: &[Pubkey]) -> Self { + fn parse(meta: TransactionStatusMeta, static_keys: &[Pubkey], show_rewards: bool) -> Self { let account_keys = AccountKeys::new(static_keys, Some(&meta.loaded_addresses)); Self { err: meta.status.clone().err(), @@ -361,21 +410,57 @@ impl UiTransactionStatusMeta { fee: meta.fee, pre_balances: meta.pre_balances, post_balances: meta.post_balances, - inner_instructions: meta.inner_instructions.map(|ixs| { - ixs.into_iter() - .map(|ix| UiInnerInstructions::parse(ix, &account_keys)) - .collect() - }), - log_messages: meta.log_messages, + inner_instructions: meta + .inner_instructions + .map(|ixs| { + ixs.into_iter() + .map(|ix| UiInnerInstructions::parse(ix, &account_keys)) + .collect() + }) + .into(), + log_messages: meta.log_messages.into(), + pre_token_balances: meta + .pre_token_balances + .map(|balance| balance.into_iter().map(Into::into).collect()) + .into(), + post_token_balances: meta + .post_token_balances + .map(|balance| balance.into_iter().map(Into::into).collect()) + .into(), + rewards: if show_rewards { meta.rewards } else { None }.into(), + loaded_addresses: OptionSerializer::Skip, + return_data: OptionSerializer::or_skip( + meta.return_data.map(|return_data| return_data.into()), + ), + compute_units_consumed: OptionSerializer::or_skip(meta.compute_units_consumed), + } + } + + fn build_simple(meta: TransactionStatusMeta, show_rewards: bool) -> Self { + Self { + err: meta.status.clone().err(), + status: meta.status, + fee: meta.fee, + pre_balances: meta.pre_balances, + post_balances: meta.post_balances, + inner_instructions: OptionSerializer::Skip, + log_messages: OptionSerializer::Skip, pre_token_balances: meta .pre_token_balances - .map(|balance| balance.into_iter().map(Into::into).collect()), + .map(|balance| balance.into_iter().map(Into::into).collect()) + .into(), post_token_balances: meta .post_token_balances - .map(|balance| balance.into_iter().map(Into::into).collect()), - rewards: meta.rewards, - loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)), - return_data: meta.return_data, + .map(|balance| balance.into_iter().map(Into::into).collect()) + .into(), + rewards: if show_rewards { + meta.rewards.into() + } else { + OptionSerializer::Skip + }, + loaded_addresses: OptionSerializer::Skip, + return_data: OptionSerializer::Skip, + compute_units_consumed: OptionSerializer::Skip, } } } @@ -390,17 +475,23 @@ impl From for UiTransactionStatusMeta { post_balances: meta.post_balances, inner_instructions: meta .inner_instructions - .map(|ixs| ixs.into_iter().map(Into::into).collect()), - log_messages: meta.log_messages, + .map(|ixs| ixs.into_iter().map(Into::into).collect()) + .into(), + log_messages: meta.log_messages.into(), pre_token_balances: meta .pre_token_balances - .map(|balance| balance.into_iter().map(Into::into).collect()), + .map(|balance| balance.into_iter().map(Into::into).collect()) + .into(), post_token_balances: meta .post_token_balances - .map(|balance| balance.into_iter().map(Into::into).collect()), - rewards: meta.rewards, - loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)), - return_data: meta.return_data, + .map(|balance| balance.into_iter().map(Into::into).collect()) + .into(), + rewards: meta.rewards.into(), + loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)).into(), + return_data: OptionSerializer::or_skip( + meta.return_data.map(|return_data| return_data.into()), + ), + compute_units_consumed: OptionSerializer::or_skip(meta.compute_units_consumed), } } } @@ -533,7 +624,11 @@ impl ConfirmedBlock { self.transactions .into_iter() .map(|tx_with_meta| { - tx_with_meta.encode(encoding, options.max_supported_transaction_version) + tx_with_meta.encode( + encoding, + options.max_supported_transaction_version, + options.show_rewards, + ) }) .collect::, _>>()?, ), @@ -549,6 +644,20 @@ impl ConfirmedBlock { ), ), TransactionDetails::None => (None, None), + TransactionDetails::Accounts => ( + Some( + self.transactions + .into_iter() + .map(|tx_with_meta| { + tx_with_meta.build_json_accounts( + options.max_supported_transaction_version, + options.show_rewards, + ) + }) + .collect::, _>>()?, + ), + None, + ), }; Ok(UiConfirmedBlock { previous_blockhash: self.previous_blockhash, @@ -652,6 +761,7 @@ impl TransactionWithStatusMeta { self, encoding: UiTransactionEncoding, max_supported_transaction_version: Option, + show_rewards: bool, ) -> Result { match self { Self::MissingMetadata(ref transaction) => Ok(EncodedTransactionWithStatusMeta { @@ -660,7 +770,7 @@ impl TransactionWithStatusMeta { meta: None, }), Self::Complete(tx_with_meta) => { - tx_with_meta.encode(encoding, max_supported_transaction_version) + tx_with_meta.encode(encoding, max_supported_transaction_version, show_rewards) } } } @@ -671,15 +781,31 @@ impl TransactionWithStatusMeta { Self::Complete(tx_with_meta) => tx_with_meta.account_keys(), } } -} -impl VersionedTransactionWithStatusMeta { - pub fn encode( + fn build_json_accounts( self, - encoding: UiTransactionEncoding, max_supported_transaction_version: Option, + show_rewards: bool, ) -> Result { - let version = match ( + match self { + Self::MissingMetadata(ref transaction) => Ok(EncodedTransactionWithStatusMeta { + version: None, + transaction: transaction.build_json_accounts(), + meta: None, + }), + Self::Complete(tx_with_meta) => { + tx_with_meta.build_json_accounts(max_supported_transaction_version, show_rewards) + } + } + } +} + +impl VersionedTransactionWithStatusMeta { + fn validate_version( + &self, + max_supported_transaction_version: Option, + ) -> Result, EncodeError> { + match ( max_supported_transaction_version, self.transaction.version(), ) { @@ -696,7 +822,16 @@ impl VersionedTransactionWithStatusMeta { Err(EncodeError::UnsupportedTransactionVersion(version)) } } - }?; + } + } + + pub fn encode( + self, + encoding: UiTransactionEncoding, + max_supported_transaction_version: Option, + show_rewards: bool, + ) -> Result { + let version = self.validate_version(max_supported_transaction_version)?; Ok(EncodedTransactionWithStatusMeta { transaction: self.transaction.encode_with_meta(encoding, &self.meta), @@ -704,8 +839,15 @@ impl VersionedTransactionWithStatusMeta { UiTransactionEncoding::JsonParsed => UiTransactionStatusMeta::parse( self.meta, self.transaction.message.static_account_keys(), + show_rewards, ), - _ => UiTransactionStatusMeta::from(self.meta), + _ => { + let mut meta = UiTransactionStatusMeta::from(self.meta); + if !show_rewards { + meta.rewards = OptionSerializer::None; + } + meta + } }), version, }) @@ -717,6 +859,40 @@ impl VersionedTransactionWithStatusMeta { Some(&self.meta.loaded_addresses), ) } + + fn build_json_accounts( + self, + max_supported_transaction_version: Option, + show_rewards: bool, + ) -> Result { + let version = self.validate_version(max_supported_transaction_version)?; + + let account_keys = match &self.transaction.message { + VersionedMessage::Legacy(message) => parse_legacy_message_accounts(message), + VersionedMessage::V0(message) => { + let loaded_message = + LoadedMessage::new_borrowed(message, &self.meta.loaded_addresses); + parse_v0_message_accounts(&loaded_message) + } + }; + + Ok(EncodedTransactionWithStatusMeta { + transaction: EncodedTransaction::Accounts(UiAccountsList { + signatures: self + .transaction + .signatures + .iter() + .map(ToString::to_string) + .collect(), + account_keys, + }), + meta: Some(UiTransactionStatusMeta::build_simple( + self.meta, + show_rewards, + )), + version, + }) + } } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -750,9 +926,11 @@ impl ConfirmedTransactionWithStatusMeta { ) -> Result { Ok(EncodedConfirmedTransactionWithStatusMeta { slot: self.slot, - transaction: self - .tx_with_meta - .encode(encoding, max_supported_transaction_version)?, + transaction: self.tx_with_meta.encode( + encoding, + max_supported_transaction_version, + true, + )?, block_time: self.block_time, }) } @@ -777,6 +955,7 @@ pub enum EncodedTransaction { LegacyBinary(String), // Old way of expressing base-58, retained for RPC backwards compatibility Binary(String, TransactionBinaryEncoding), Json(UiTransaction), + Accounts(UiAccountsList), } impl EncodableWithMeta for VersionedTransaction { @@ -848,10 +1027,20 @@ impl Encodable for Transaction { } } +impl JsonAccounts for Transaction { + type Encoded = EncodedTransaction; + fn build_json_accounts(&self) -> Self::Encoded { + EncodedTransaction::Accounts(UiAccountsList { + signatures: self.signatures.iter().map(ToString::to_string).collect(), + account_keys: parse_legacy_message_accounts(&self.message), + }) + } +} + impl EncodedTransaction { pub fn decode(&self) -> Option { let (blob, encoding) = match self { - Self::Json(_) => return None, + Self::Json(_) | Self::Accounts(_) => return None, Self::LegacyBinary(blob) => (blob, TransactionBinaryEncoding::Base58), Self::Binary(blob, encoding) => (blob, *encoding), }; @@ -897,7 +1086,7 @@ impl Encodable for Message { if encoding == UiTransactionEncoding::JsonParsed { let account_keys = AccountKeys::new(&self.account_keys, None); UiMessage::Parsed(UiParsedMessage { - account_keys: parse_accounts(self), + account_keys: parse_legacy_message_accounts(self), recent_blockhash: self.recent_blockhash.to_string(), instructions: self .instructions @@ -929,7 +1118,7 @@ impl EncodableWithMeta for v0::Message { let account_keys = AccountKeys::new(&self.account_keys, Some(&meta.loaded_addresses)); let loaded_message = LoadedMessage::new_borrowed(self, &meta.loaded_addresses); UiMessage::Parsed(UiParsedMessage { - account_keys: parse_static_accounts(&loaded_message), + account_keys: parse_v0_message_accounts(&loaded_message), recent_blockhash: self.recent_blockhash.to_string(), instructions: self .instructions @@ -969,6 +1158,13 @@ pub struct UiRawMessage { pub address_table_lookups: Option>, } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UiAccountsList { + pub signatures: Vec, + pub account_keys: Vec, +} + /// A duplicate representation of a MessageAddressTableLookup, in raw format, for pretty JSON serialization #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -1009,9 +1205,43 @@ pub struct TransactionByAddrInfo { pub block_time: Option, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct UiTransactionReturnData { + pub program_id: String, + pub data: (String, UiReturnDataEncoding), +} + +impl Default for UiTransactionReturnData { + fn default() -> Self { + Self { + program_id: String::default(), + data: (String::default(), UiReturnDataEncoding::Base64), + } + } +} + +impl From for UiTransactionReturnData { + fn from(return_data: TransactionReturnData) -> Self { + Self { + program_id: return_data.program_id.to_string(), + data: ( + base64::encode(return_data.data), + UiReturnDataEncoding::Base64, + ), + } + } +} + +#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum UiReturnDataEncoding { + Base64, +} + #[cfg(test)] mod test { - use super::*; + use {super::*, serde_json::json}; #[test] fn test_decode_invalid_transaction() { @@ -1105,4 +1335,134 @@ mod test { }; assert!(status.satisfies_commitment(CommitmentConfig::confirmed())); } + + #[test] + fn test_serde_empty_fields() { + fn test_serde<'de, T: serde::Serialize + serde::Deserialize<'de>>( + json_input: &'de str, + expected_json_output: &str, + ) { + let typed_meta: T = serde_json::from_str(json_input).unwrap(); + let reserialized_value = json!(typed_meta); + + let expected_json_output_value: serde_json::Value = + serde_json::from_str(expected_json_output).unwrap(); + assert_eq!(reserialized_value, expected_json_output_value); + } + + let json_input = "{\ + \"err\":null,\ + \"status\":{\"Ok\":null},\ + \"fee\":1234,\ + \"preBalances\":[1,2,3],\ + \"postBalances\":[4,5,6]\ + }"; + let expected_json_output = "{\ + \"err\":null,\ + \"status\":{\"Ok\":null},\ + \"fee\":1234,\ + \"preBalances\":[1,2,3],\ + \"postBalances\":[4,5,6],\ + \"innerInstructions\":null,\ + \"logMessages\":null,\ + \"preTokenBalances\":null,\ + \"postTokenBalances\":null,\ + \"rewards\":null\ + }"; + test_serde::(json_input, expected_json_output); + + let json_input = "{\ + \"accountIndex\":5,\ + \"mint\":\"DXM2yVSouSg1twmQgHLKoSReqXhtUroehWxrTgPmmfWi\",\ + \"uiTokenAmount\": { + \"amount\": \"1\",\ + \"decimals\": 0,\ + \"uiAmount\": 1.0,\ + \"uiAmountString\": \"1\"\ + }\ + }"; + let expected_json_output = "{\ + \"accountIndex\":5,\ + \"mint\":\"DXM2yVSouSg1twmQgHLKoSReqXhtUroehWxrTgPmmfWi\",\ + \"uiTokenAmount\": { + \"amount\": \"1\",\ + \"decimals\": 0,\ + \"uiAmount\": 1.0,\ + \"uiAmountString\": \"1\"\ + }\ + }"; + test_serde::(json_input, expected_json_output); + } + + #[test] + fn test_ui_transaction_status_meta_ctors_serialization() { + let meta = TransactionStatusMeta { + status: Ok(()), + fee: 1234, + pre_balances: vec![1, 2, 3], + post_balances: vec![4, 5, 6], + inner_instructions: None, + log_messages: None, + pre_token_balances: None, + post_token_balances: None, + rewards: None, + loaded_addresses: LoadedAddresses { + writable: vec![], + readonly: vec![], + }, + return_data: None, + compute_units_consumed: None, + }; + let expected_json_output_value: serde_json::Value = serde_json::from_str( + "{\ + \"err\":null,\ + \"status\":{\"Ok\":null},\ + \"fee\":1234,\ + \"preBalances\":[1,2,3],\ + \"postBalances\":[4,5,6],\ + \"innerInstructions\":null,\ + \"logMessages\":null,\ + \"preTokenBalances\":null,\ + \"postTokenBalances\":null,\ + \"rewards\":null,\ + \"loadedAddresses\":{\ + \"readonly\": [],\ + \"writable\": []\ + }\ + }", + ) + .unwrap(); + let ui_meta_from: UiTransactionStatusMeta = meta.clone().into(); + assert_eq!( + serde_json::to_value(&ui_meta_from).unwrap(), + expected_json_output_value + ); + + let expected_json_output_value: serde_json::Value = serde_json::from_str( + "{\ + \"err\":null,\ + \"status\":{\"Ok\":null},\ + \"fee\":1234,\ + \"preBalances\":[1,2,3],\ + \"postBalances\":[4,5,6],\ + \"innerInstructions\":null,\ + \"logMessages\":null,\ + \"preTokenBalances\":null,\ + \"postTokenBalances\":null,\ + \"rewards\":null\ + }", + ) + .unwrap(); + let ui_meta_parse_with_rewards = UiTransactionStatusMeta::parse(meta.clone(), &[], true); + assert_eq!( + serde_json::to_value(&ui_meta_parse_with_rewards).unwrap(), + expected_json_output_value + ); + + let ui_meta_parse_no_rewards = UiTransactionStatusMeta::parse(meta, &[], false); + assert_eq!( + serde_json::to_value(&ui_meta_parse_no_rewards).unwrap(), + expected_json_output_value + ); + } } diff --git a/transaction-status/src/option_serializer.rs b/transaction-status/src/option_serializer.rs new file mode 100644 index 00000000000000..2078d7dff801b6 --- /dev/null +++ b/transaction-status/src/option_serializer.rs @@ -0,0 +1,78 @@ +use serde::{ser::Error, Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum OptionSerializer { + Some(T), + None, + Skip, +} + +impl OptionSerializer { + pub fn none() -> Self { + Self::None + } + + pub fn skip() -> Self { + Self::Skip + } + + pub fn should_skip(&self) -> bool { + matches!(self, Self::Skip) + } + + pub fn or_skip(option: Option) -> Self { + match option { + Option::Some(item) => Self::Some(item), + Option::None => Self::Skip, + } + } + + pub fn as_ref(&self) -> OptionSerializer<&T> { + #[allow(clippy::needless_match)] // The nightly clippy used by CI is incorrect here + match self { + OptionSerializer::Some(item) => OptionSerializer::Some(item), + OptionSerializer::None => OptionSerializer::None, + OptionSerializer::Skip => OptionSerializer::Skip, + } + } +} + +impl From> for OptionSerializer { + fn from(option: Option) -> Self { + match option { + Option::Some(item) => Self::Some(item), + Option::None => Self::None, + } + } +} + +impl From> for Option { + fn from(option: OptionSerializer) -> Self { + match option { + OptionSerializer::Some(item) => Self::Some(item), + _ => Self::None, + } + } +} + +impl Serialize for OptionSerializer { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Some(item) => item.serialize(serializer), + Self::None => serializer.serialize_none(), + Self::Skip => Err(Error::custom("Skip variants should not be serialized")), + } + } +} + +impl<'de, T: Deserialize<'de>> Deserialize<'de> for OptionSerializer { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Option::deserialize(deserializer).map(Into::into) + } +} diff --git a/transaction-status/src/parse_accounts.rs b/transaction-status/src/parse_accounts.rs index a84fd06bf92ca1..6ad0ec82a6fdad 100644 --- a/transaction-status/src/parse_accounts.rs +++ b/transaction-status/src/parse_accounts.rs @@ -6,27 +6,42 @@ pub struct ParsedAccount { pub pubkey: String, pub writable: bool, pub signer: bool, + pub source: Option, } -pub fn parse_accounts(message: &Message) -> Vec { +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub enum ParsedAccountSource { + Transaction, + LookupTable, +} + +pub fn parse_legacy_message_accounts(message: &Message) -> Vec { let mut accounts: Vec = vec![]; for (i, account_key) in message.account_keys.iter().enumerate() { accounts.push(ParsedAccount { pubkey: account_key.to_string(), writable: message.is_writable(i), signer: message.is_signer(i), + source: Some(ParsedAccountSource::Transaction), }); } accounts } -pub fn parse_static_accounts(message: &LoadedMessage) -> Vec { +pub fn parse_v0_message_accounts(message: &LoadedMessage) -> Vec { let mut accounts: Vec = vec![]; - for (i, account_key) in message.static_account_keys().iter().enumerate() { + for (i, account_key) in message.account_keys().iter().enumerate() { + let source = if i < message.static_account_keys().len() { + ParsedAccountSource::Transaction + } else { + ParsedAccountSource::LookupTable + }; accounts.push(ParsedAccount { pubkey: account_key.to_string(), writable: message.is_writable(i), signer: message.is_signer(i), + source: Some(source), }); } accounts @@ -43,7 +58,7 @@ mod test { }; #[test] - fn test_parse_accounts() { + fn test_parse_legacy_message_accounts() { let pubkey0 = Pubkey::new_unique(); let pubkey1 = Pubkey::new_unique(); let pubkey2 = Pubkey::new_unique(); @@ -59,38 +74,44 @@ mod test { }; assert_eq!( - parse_accounts(&message), + parse_legacy_message_accounts(&message), vec![ ParsedAccount { pubkey: pubkey0.to_string(), writable: true, signer: true, + source: Some(ParsedAccountSource::Transaction), }, ParsedAccount { pubkey: pubkey1.to_string(), writable: false, signer: true, + source: Some(ParsedAccountSource::Transaction), }, ParsedAccount { pubkey: pubkey2.to_string(), writable: true, signer: false, + source: Some(ParsedAccountSource::Transaction), }, ParsedAccount { pubkey: pubkey3.to_string(), writable: false, signer: false, + source: Some(ParsedAccountSource::Transaction), }, ] ); } #[test] - fn test_parse_static_accounts() { + fn test_parse_v0_message_accounts() { let pubkey0 = Pubkey::new_unique(); let pubkey1 = Pubkey::new_unique(); let pubkey2 = Pubkey::new_unique(); let pubkey3 = Pubkey::new_unique(); + let pubkey4 = Pubkey::new_unique(); + let pubkey5 = Pubkey::new_unique(); let message = LoadedMessage::new( v0::Message { header: MessageHeader { @@ -102,33 +123,49 @@ mod test { ..v0::Message::default() }, LoadedAddresses { - writable: vec![Pubkey::new_unique()], - readonly: vec![Pubkey::new_unique()], + writable: vec![pubkey4], + readonly: vec![pubkey5], }, ); assert_eq!( - parse_static_accounts(&message), + parse_v0_message_accounts(&message), vec![ ParsedAccount { pubkey: pubkey0.to_string(), writable: true, signer: true, + source: Some(ParsedAccountSource::Transaction), }, ParsedAccount { pubkey: pubkey1.to_string(), writable: false, signer: true, + source: Some(ParsedAccountSource::Transaction), }, ParsedAccount { pubkey: pubkey2.to_string(), writable: true, signer: false, + source: Some(ParsedAccountSource::Transaction), }, ParsedAccount { pubkey: pubkey3.to_string(), writable: false, signer: false, + source: Some(ParsedAccountSource::Transaction), + }, + ParsedAccount { + pubkey: pubkey4.to_string(), + writable: true, + signer: false, + source: Some(ParsedAccountSource::LookupTable), + }, + ParsedAccount { + pubkey: pubkey5.to_string(), + writable: false, + signer: false, + source: Some(ParsedAccountSource::LookupTable), }, ] ); diff --git a/transaction-status/src/parse_address_lookup_table.rs b/transaction-status/src/parse_address_lookup_table.rs new file mode 100644 index 00000000000000..f30b61ad7fe8b5 --- /dev/null +++ b/transaction-status/src/parse_address_lookup_table.rs @@ -0,0 +1,358 @@ +use { + crate::parse_instruction::{ + check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, + }, + bincode::deserialize, + serde_json::json, + solana_address_lookup_table_program::instruction::ProgramInstruction, + solana_sdk::{instruction::CompiledInstruction, message::AccountKeys}, +}; + +pub fn parse_address_lookup_table( + instruction: &CompiledInstruction, + account_keys: &AccountKeys, +) -> Result { + let address_lookup_table_instruction: ProgramInstruction = deserialize(&instruction.data) + .map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::AddressLookupTable) + })?; + match instruction.accounts.iter().max() { + Some(index) if (*index as usize) < account_keys.len() => {} + _ => { + // Runtime should prevent this from ever happening + return Err(ParseInstructionError::InstructionKeyMismatch( + ParsableProgram::AddressLookupTable, + )); + } + } + match address_lookup_table_instruction { + ProgramInstruction::CreateLookupTable { + recent_slot, + bump_seed, + } => { + check_num_address_lookup_table_accounts(&instruction.accounts, 4)?; + Ok(ParsedInstructionEnum { + instruction_type: "createLookupTable".to_string(), + info: json!({ + "lookupTableAccount": account_keys[instruction.accounts[0] as usize].to_string(), + "lookupTableAuthority": account_keys[instruction.accounts[1] as usize].to_string(), + "payerAccount": account_keys[instruction.accounts[2] as usize].to_string(), + "systemProgram": account_keys[instruction.accounts[3] as usize].to_string(), + "recentSlot": recent_slot, + "bumpSeed": bump_seed, + }), + }) + } + ProgramInstruction::FreezeLookupTable => { + check_num_address_lookup_table_accounts(&instruction.accounts, 2)?; + Ok(ParsedInstructionEnum { + instruction_type: "freezeLookupTable".to_string(), + info: json!({ + "lookupTableAccount": account_keys[instruction.accounts[0] as usize].to_string(), + "lookupTableAuthority": account_keys[instruction.accounts[1] as usize].to_string(), + }), + }) + } + ProgramInstruction::ExtendLookupTable { new_addresses } => { + check_num_address_lookup_table_accounts(&instruction.accounts, 2)?; + let new_addresses: Vec = new_addresses + .into_iter() + .map(|address| address.to_string()) + .collect(); + let mut value = json!({ + "lookupTableAccount": account_keys[instruction.accounts[0] as usize].to_string(), + "lookupTableAuthority": account_keys[instruction.accounts[1] as usize].to_string(), + "newAddresses": new_addresses, + }); + let map = value.as_object_mut().unwrap(); + if instruction.accounts.len() >= 4 { + map.insert( + "payerAccount".to_string(), + json!(account_keys[instruction.accounts[2] as usize].to_string()), + ); + map.insert( + "systemProgram".to_string(), + json!(account_keys[instruction.accounts[3] as usize].to_string()), + ); + } + Ok(ParsedInstructionEnum { + instruction_type: "extendLookupTable".to_string(), + info: value, + }) + } + ProgramInstruction::DeactivateLookupTable => { + check_num_address_lookup_table_accounts(&instruction.accounts, 2)?; + Ok(ParsedInstructionEnum { + instruction_type: "deactivateLookupTable".to_string(), + info: json!({ + "lookupTableAccount": account_keys[instruction.accounts[0] as usize].to_string(), + "lookupTableAuthority": account_keys[instruction.accounts[1] as usize].to_string(), + }), + }) + } + ProgramInstruction::CloseLookupTable => { + check_num_address_lookup_table_accounts(&instruction.accounts, 3)?; + Ok(ParsedInstructionEnum { + instruction_type: "closeLookupTable".to_string(), + info: json!({ + "lookupTableAccount": account_keys[instruction.accounts[0] as usize].to_string(), + "lookupTableAuthority": account_keys[instruction.accounts[1] as usize].to_string(), + "recipient": account_keys[instruction.accounts[2] as usize].to_string(), + }), + }) + } + } +} + +fn check_num_address_lookup_table_accounts( + accounts: &[u8], + num: usize, +) -> Result<(), ParseInstructionError> { + check_num_accounts(accounts, num, ParsableProgram::AddressLookupTable) +} + +#[cfg(test)] +mod test { + use { + super::*, + solana_address_lookup_table_program::instruction, + solana_sdk::{message::Message, pubkey::Pubkey, system_program}, + std::str::FromStr, + }; + + #[test] + fn test_parse_create_address_lookup_table_ix() { + let from_pubkey = Pubkey::new_unique(); + // use explicit key to have predicatble bump_seed + let authority = Pubkey::from_str("HkxY6vXdrKzoCQLmdJ3cYo9534FdZQxzBNWTyrJzzqJM").unwrap(); + let slot = 42; + + let (instruction, lookup_table_pubkey) = + instruction::create_lookup_table(authority, from_pubkey, slot); + let mut message = Message::new(&[instruction], None); + assert_eq!( + parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "createLookupTable".to_string(), + info: json!({ + "lookupTableAccount": lookup_table_pubkey.to_string(), + "lookupTableAuthority": authority.to_string(), + "payerAccount": from_pubkey.to_string(), + "systemProgram": system_program::id().to_string(), + "recentSlot": slot, + "bumpSeed": 254, + }), + } + ); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..3], None) + ) + .is_err()); + let keys = message.account_keys.clone(); + message.instructions[0].accounts.pop(); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&keys, None) + ) + .is_err()); + } + + #[test] + fn test_parse_freeze_lookup_table_ix() { + let lookup_table_pubkey = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + + let instruction = instruction::freeze_lookup_table(lookup_table_pubkey, authority); + let mut message = Message::new(&[instruction], None); + assert_eq!( + parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "freezeLookupTable".to_string(), + info: json!({ + "lookupTableAccount": lookup_table_pubkey.to_string(), + "lookupTableAuthority": authority.to_string(), + }), + } + ); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..1], None) + ) + .is_err()); + let keys = message.account_keys.clone(); + message.instructions[0].accounts.pop(); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&keys, None) + ) + .is_err()); + } + + #[test] + fn test_parse_extend_lookup_table_ix() { + let lookup_table_pubkey = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let from_pubkey = Pubkey::new_unique(); + let no_addresses = vec![]; + let address0 = Pubkey::new_unique(); + let address1 = Pubkey::new_unique(); + let some_addresses = vec![address0, address1]; + + // No payer, no addresses + let instruction = + instruction::extend_lookup_table(lookup_table_pubkey, authority, None, no_addresses); + let mut message = Message::new(&[instruction], None); + assert_eq!( + parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "extendLookupTable".to_string(), + info: json!({ + "lookupTableAccount": lookup_table_pubkey.to_string(), + "lookupTableAuthority": authority.to_string(), + "newAddresses": [], + }), + } + ); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..1], None) + ) + .is_err()); + let keys = message.account_keys.clone(); + message.instructions[0].accounts.pop(); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&keys, None) + ) + .is_err()); + + // Some payer, some addresses + let instruction = instruction::extend_lookup_table( + lookup_table_pubkey, + authority, + Some(from_pubkey), + some_addresses, + ); + let mut message = Message::new(&[instruction], None); + assert_eq!( + parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "extendLookupTable".to_string(), + info: json!({ + "lookupTableAccount": lookup_table_pubkey.to_string(), + "lookupTableAuthority": authority.to_string(), + "payerAccount": from_pubkey.to_string(), + "systemProgram": system_program::id().to_string(), + "newAddresses": [ + address0.to_string(), + address1.to_string(), + ], + }), + } + ); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..1], None) + ) + .is_err()); + let keys = message.account_keys.clone(); + message.instructions[0].accounts.pop(); + message.instructions[0].accounts.pop(); + message.instructions[0].accounts.pop(); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&keys, None) + ) + .is_err()); + } + + #[test] + fn test_parse_deactivate_lookup_table_ix() { + let lookup_table_pubkey = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + + let instruction = instruction::deactivate_lookup_table(lookup_table_pubkey, authority); + let mut message = Message::new(&[instruction], None); + assert_eq!( + parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "deactivateLookupTable".to_string(), + info: json!({ + "lookupTableAccount": lookup_table_pubkey.to_string(), + "lookupTableAuthority": authority.to_string(), + }), + } + ); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..1], None) + ) + .is_err()); + let keys = message.account_keys.clone(); + message.instructions[0].accounts.pop(); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&keys, None) + ) + .is_err()); + } + + #[test] + fn test_parse_close_lookup_table_ix() { + let lookup_table_pubkey = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let recipient = Pubkey::new_unique(); + + let instruction = + instruction::close_lookup_table(lookup_table_pubkey, authority, recipient); + let mut message = Message::new(&[instruction], None); + assert_eq!( + parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys, None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "closeLookupTable".to_string(), + info: json!({ + "lookupTableAccount": lookup_table_pubkey.to_string(), + "lookupTableAuthority": authority.to_string(), + "recipient": recipient.to_string(), + }), + } + ); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&message.account_keys[0..2], None) + ) + .is_err()); + let keys = message.account_keys.clone(); + message.instructions[0].accounts.pop(); + assert!(parse_address_lookup_table( + &message.instructions[0], + &AccountKeys::new(&keys, None) + ) + .is_err()); + } +} diff --git a/transaction-status/src/parse_associated_token.rs b/transaction-status/src/parse_associated_token.rs index 955a2bc7a42bbe..428d9b98e99b26 100644 --- a/transaction-status/src/parse_associated_token.rs +++ b/transaction-status/src/parse_associated_token.rs @@ -27,40 +27,56 @@ pub fn parse_associated_token( )); } } - if instruction.data.is_empty() { - check_num_associated_token_accounts(&instruction.accounts, 7)?; - Ok(ParsedInstructionEnum { - instruction_type: "create".to_string(), - info: json!({ - "source": account_keys[instruction.accounts[0] as usize].to_string(), - "account": account_keys[instruction.accounts[1] as usize].to_string(), - "wallet": account_keys[instruction.accounts[2] as usize].to_string(), - "mint": account_keys[instruction.accounts[3] as usize].to_string(), - "systemProgram": account_keys[instruction.accounts[4] as usize].to_string(), - "tokenProgram": account_keys[instruction.accounts[5] as usize].to_string(), - "rentSysvar": account_keys[instruction.accounts[6] as usize].to_string(), - }), - }) + let ata_instruction = if instruction.data.is_empty() { + AssociatedTokenAccountInstruction::Create } else { - let ata_instruction = AssociatedTokenAccountInstruction::try_from_slice(&instruction.data) - .map_err(|_| { - ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) - })?; - match ata_instruction { - AssociatedTokenAccountInstruction::Create => { - check_num_associated_token_accounts(&instruction.accounts, 6)?; - Ok(ParsedInstructionEnum { - instruction_type: "create".to_string(), - info: json!({ - "source": account_keys[instruction.accounts[0] as usize].to_string(), - "account": account_keys[instruction.accounts[1] as usize].to_string(), - "wallet": account_keys[instruction.accounts[2] as usize].to_string(), - "mint": account_keys[instruction.accounts[3] as usize].to_string(), - "systemProgram": account_keys[instruction.accounts[4] as usize].to_string(), - "tokenProgram": account_keys[instruction.accounts[5] as usize].to_string(), - }), - }) - } + AssociatedTokenAccountInstruction::try_from_slice(&instruction.data) + .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))? + }; + + match ata_instruction { + AssociatedTokenAccountInstruction::Create => { + check_num_associated_token_accounts(&instruction.accounts, 6)?; + Ok(ParsedInstructionEnum { + instruction_type: "create".to_string(), + info: json!({ + "source": account_keys[instruction.accounts[0] as usize].to_string(), + "account": account_keys[instruction.accounts[1] as usize].to_string(), + "wallet": account_keys[instruction.accounts[2] as usize].to_string(), + "mint": account_keys[instruction.accounts[3] as usize].to_string(), + "systemProgram": account_keys[instruction.accounts[4] as usize].to_string(), + "tokenProgram": account_keys[instruction.accounts[5] as usize].to_string(), + }), + }) + } + AssociatedTokenAccountInstruction::CreateIdempotent => { + check_num_associated_token_accounts(&instruction.accounts, 6)?; + Ok(ParsedInstructionEnum { + instruction_type: "createIdempotent".to_string(), + info: json!({ + "source": account_keys[instruction.accounts[0] as usize].to_string(), + "account": account_keys[instruction.accounts[1] as usize].to_string(), + "wallet": account_keys[instruction.accounts[2] as usize].to_string(), + "mint": account_keys[instruction.accounts[3] as usize].to_string(), + "systemProgram": account_keys[instruction.accounts[4] as usize].to_string(), + "tokenProgram": account_keys[instruction.accounts[5] as usize].to_string(), + }), + }) + } + AssociatedTokenAccountInstruction::RecoverNested => { + check_num_associated_token_accounts(&instruction.accounts, 7)?; + Ok(ParsedInstructionEnum { + instruction_type: "recoverNested".to_string(), + info: json!({ + "nestedSource": account_keys[instruction.accounts[0] as usize].to_string(), + "nestedMint": account_keys[instruction.accounts[1] as usize].to_string(), + "destination": account_keys[instruction.accounts[2] as usize].to_string(), + "nestedOwner": account_keys[instruction.accounts[3] as usize].to_string(), + "ownerMint": account_keys[instruction.accounts[4] as usize].to_string(), + "wallet": account_keys[instruction.accounts[5] as usize].to_string(), + "tokenProgram": account_keys[instruction.accounts[6] as usize].to_string(), + }), + }) } } } @@ -80,8 +96,11 @@ mod test { super::*, solana_account_decoder::parse_token::pubkey_from_spl_token, spl_associated_token_account::{ - get_associated_token_address, - instruction::create_associated_token_account, + get_associated_token_address, get_associated_token_address_with_program_id, + instruction::{ + create_associated_token_account, create_associated_token_account_idempotent, + recover_nested, + }, solana_program::{ instruction::CompiledInstruction as SplAssociatedTokenCompiledInstruction, message::Message, pubkey::Pubkey as SplAssociatedTokenPubkey, sysvar, @@ -112,7 +131,7 @@ mod test { } #[test] - fn test_parse_associated_token_deprecated() { + fn test_parse_create_deprecated() { let funder = Pubkey::new_unique(); let wallet_address = Pubkey::new_unique(); let mint = Pubkey::new_unique(); @@ -126,6 +145,70 @@ mod test { ); let message = Message::new(&[create_ix], None); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); + let expected_parsed_ix = ParsedInstructionEnum { + instruction_type: "create".to_string(), + info: json!({ + "source": funder.to_string(), + "account": associated_account_address.to_string(), + "wallet": wallet_address.to_string(), + "mint": mint.to_string(), + "systemProgram": solana_sdk::system_program::id().to_string(), + "tokenProgram": spl_token::id().to_string(), + }), + }; + assert_eq!( + parse_associated_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .unwrap(), + expected_parsed_ix, + ); + + // after popping rent account, parsing should still succeed + let rent_account_index = compiled_instruction + .accounts + .iter() + .position(|index| message.account_keys[*index as usize] == sysvar::rent::id()) + .unwrap(); + compiled_instruction.accounts.remove(rent_account_index); + assert_eq!( + parse_associated_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .unwrap(), + expected_parsed_ix, + ); + + // after popping another account, parsing should fail + compiled_instruction.accounts.pop(); + assert!(parse_associated_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .is_err()); + } + + #[test] + fn test_parse_create() { + let funder = Pubkey::new_unique(); + let wallet_address = Pubkey::new_unique(); + let mint = Pubkey::new_unique(); + let token_program_id = Pubkey::new_unique(); + let associated_account_address = get_associated_token_address_with_program_id( + &convert_pubkey(wallet_address), + &convert_pubkey(mint), + &convert_pubkey(token_program_id), + ); + let create_ix = create_associated_token_account( + &convert_pubkey(funder), + &convert_pubkey(wallet_address), + &convert_pubkey(mint), + &convert_pubkey(token_program_id), + ); + let message = Message::new(&[create_ix], None); + let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); assert_eq!( parse_associated_token( &compiled_instruction, @@ -140,8 +223,7 @@ mod test { "wallet": wallet_address.to_string(), "mint": mint.to_string(), "systemProgram": solana_sdk::system_program::id().to_string(), - "tokenProgram": spl_token::id().to_string(), - "rentSysvar": sysvar::rent::id().to_string(), + "tokenProgram": token_program_id.to_string(), }) } ); @@ -154,16 +236,21 @@ mod test { } #[test] - fn test_parse_associated_token() { + fn test_parse_create_idempotent() { let funder = Pubkey::new_unique(); let wallet_address = Pubkey::new_unique(); let mint = Pubkey::new_unique(); - let associated_account_address = - get_associated_token_address(&convert_pubkey(wallet_address), &convert_pubkey(mint)); - let create_ix = create_associated_token_account( + let token_program_id = Pubkey::new_unique(); + let associated_account_address = get_associated_token_address_with_program_id( + &convert_pubkey(wallet_address), + &convert_pubkey(mint), + &convert_pubkey(token_program_id), + ); + let create_ix = create_associated_token_account_idempotent( &convert_pubkey(funder), &convert_pubkey(wallet_address), &convert_pubkey(mint), + &convert_pubkey(token_program_id), ); let message = Message::new(&[create_ix], None); let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); @@ -174,14 +261,70 @@ mod test { ) .unwrap(), ParsedInstructionEnum { - instruction_type: "create".to_string(), + instruction_type: "createIdempotent".to_string(), info: json!({ "source": funder.to_string(), "account": associated_account_address.to_string(), "wallet": wallet_address.to_string(), "mint": mint.to_string(), "systemProgram": solana_sdk::system_program::id().to_string(), - "tokenProgram": spl_token::id().to_string(), + "tokenProgram": token_program_id.to_string(), + }) + } + ); + compiled_instruction.accounts.pop(); + assert!(parse_associated_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .is_err()); + } + + #[test] + fn test_parse_recover_nested() { + let wallet_address = Pubkey::new_unique(); + let owner_mint = Pubkey::new_unique(); + let nested_mint = Pubkey::new_unique(); + let token_program_id = Pubkey::new_unique(); + let owner_associated_account_address = get_associated_token_address_with_program_id( + &convert_pubkey(wallet_address), + &convert_pubkey(owner_mint), + &convert_pubkey(token_program_id), + ); + let nested_associated_account_address = get_associated_token_address_with_program_id( + &owner_associated_account_address, + &convert_pubkey(nested_mint), + &convert_pubkey(token_program_id), + ); + let destination_associated_account_address = get_associated_token_address_with_program_id( + &convert_pubkey(wallet_address), + &convert_pubkey(nested_mint), + &convert_pubkey(token_program_id), + ); + let recover_ix = recover_nested( + &convert_pubkey(wallet_address), + &convert_pubkey(owner_mint), + &convert_pubkey(nested_mint), + &convert_pubkey(token_program_id), + ); + let message = Message::new(&[recover_ix], None); + let mut compiled_instruction = convert_compiled_instruction(&message.instructions[0]); + assert_eq!( + parse_associated_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "recoverNested".to_string(), + info: json!({ + "nestedSource": nested_associated_account_address.to_string(), + "nestedMint": nested_mint.to_string(), + "destination": destination_associated_account_address.to_string(), + "nestedOwner": owner_associated_account_address.to_string(), + "ownerMint": owner_mint.to_string(), + "wallet": wallet_address.to_string(), + "tokenProgram": token_program_id.to_string(), }) } ); diff --git a/transaction-status/src/parse_bpf_loader.rs b/transaction-status/src/parse_bpf_loader.rs index cfb31d36502ea9..1442d81506a047 100644 --- a/transaction-status/src/parse_bpf_loader.rs +++ b/transaction-status/src/parse_bpf_loader.rs @@ -150,19 +150,24 @@ pub fn parse_bpf_upgradeable_loader( }), }) } - UpgradeableLoaderInstruction::ExtendProgramData { additional_bytes } => { + UpgradeableLoaderInstruction::ExtendProgram { additional_bytes } => { check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 2)?; Ok(ParsedInstructionEnum { - instruction_type: "extendProgramData".to_string(), + instruction_type: "extendProgram".to_string(), info: json!({ "additionalBytes": additional_bytes, "programDataAccount": account_keys[instruction.accounts[0] as usize].to_string(), - "systemProgram": account_keys[instruction.accounts[1] as usize].to_string(), - "payerAccount": if instruction.accounts.len() > 2 { + "programAccount": account_keys[instruction.accounts[1] as usize].to_string(), + "systemProgram": if instruction.accounts.len() > 3 { Some(account_keys[instruction.accounts[2] as usize].to_string()) } else { None }, + "payerAccount": if instruction.accounts.len() > 4 { + Some(account_keys[instruction.accounts[3] as usize].to_string()) + } else { + None + }, }), }) } diff --git a/transaction-status/src/parse_instruction.rs b/transaction-status/src/parse_instruction.rs index 29f812c418c875..1b8f2d821e7fca 100644 --- a/transaction-status/src/parse_instruction.rs +++ b/transaction-status/src/parse_instruction.rs @@ -1,6 +1,7 @@ use { crate::{ extract_memos::{spl_memo_id_v1, spl_memo_id_v3}, + parse_address_lookup_table::parse_address_lookup_table, parse_associated_token::{parse_associated_token, spl_associated_token_id}, parse_bpf_loader::{parse_bpf_loader, parse_bpf_upgradeable_loader}, parse_stake::parse_stake, @@ -23,6 +24,7 @@ use { }; lazy_static! { + static ref ADDRESS_LOOKUP_PROGRAM_ID: Pubkey = solana_address_lookup_table_program::id(); static ref ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey = spl_associated_token_id(); static ref BPF_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader::id(); static ref BPF_UPGRADEABLE_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader_upgradeable::id(); @@ -33,6 +35,10 @@ lazy_static! { static ref VOTE_PROGRAM_ID: Pubkey = solana_vote_program::id(); static ref PARSABLE_PROGRAM_IDS: HashMap = { let mut m = HashMap::new(); + m.insert( + *ADDRESS_LOOKUP_PROGRAM_ID, + ParsableProgram::AddressLookupTable, + ); m.insert( *ASSOCIATED_TOKEN_PROGRAM_ID, ParsableProgram::SplAssociatedTokenAccount, @@ -89,6 +95,7 @@ pub struct ParsedInstructionEnum { #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub enum ParsableProgram { + AddressLookupTable, SplAssociatedTokenAccount, SplMemo, SplToken, @@ -108,6 +115,9 @@ pub fn parse( .get(program_id) .ok_or(ParseInstructionError::ProgramNotParsable)?; let parsed_json = match program_name { + ParsableProgram::AddressLookupTable => { + serde_json::to_value(parse_address_lookup_table(instruction, account_keys)?)? + } ParsableProgram::SplAssociatedTokenAccount => { serde_json::to_value(parse_associated_token(instruction, account_keys)?)? } @@ -183,7 +193,7 @@ mod test { } ); - let non_parsable_program_id = Pubkey::new(&[1; 32]); + let non_parsable_program_id = Pubkey::from([1; 32]); assert!(parse(&non_parsable_program_id, &memo_instruction, &no_keys).is_err()); } diff --git a/transaction-status/src/parse_stake.rs b/transaction-status/src/parse_stake.rs index dfa3e9608a34a7..d7da25b17320be 100644 --- a/transaction-status/src/parse_stake.rs +++ b/transaction-status/src/parse_stake.rs @@ -276,7 +276,7 @@ pub fn parse_stake( StakeInstruction::DeactivateDelinquent => { check_num_stake_accounts(&instruction.accounts, 3)?; Ok(ParsedInstructionEnum { - instruction_type: "deactivateDeactive".to_string(), + instruction_type: "deactivateDelinquent".to_string(), info: json!({ "stakeAccount": account_keys[instruction.accounts[0] as usize].to_string(), "voteAccount": account_keys[instruction.accounts[1] as usize].to_string(), diff --git a/transaction-status/src/parse_token.rs b/transaction-status/src/parse_token.rs index 3c43cfbf426a28..1ec72fc0ce6e30 100644 --- a/transaction-status/src/parse_token.rs +++ b/transaction-status/src/parse_token.rs @@ -3,8 +3,8 @@ use { check_num_accounts, ParsableProgram, ParseInstructionError, ParsedInstructionEnum, }, extension::{ - default_account_state::*, interest_bearing_mint::*, memo_transfer::*, - mint_close_authority::*, reallocate::*, transfer_fee::*, + cpi_guard::*, default_account_state::*, interest_bearing_mint::*, memo_transfer::*, + mint_close_authority::*, permanent_delegate::*, reallocate::*, transfer_fee::*, }, serde_json::{json, Map, Value}, solana_account_decoder::parse_token::{ @@ -228,7 +228,9 @@ pub fn parse_token( | AuthorityType::TransferFeeConfig | AuthorityType::WithheldWithdraw | AuthorityType::CloseMint - | AuthorityType::InterestRate => "mint", + | AuthorityType::InterestRate + | AuthorityType::PermanentDelegate + | AuthorityType::ConfidentialTransferMint => "mint", AuthorityType::AccountOwner | AuthorityType::CloseAccount => "account", }; let mut value = json!({ @@ -572,6 +574,21 @@ pub fn parse_token( account_keys, ) } + TokenInstruction::CpiGuardExtension => { + if instruction.data.len() < 2 { + return Err(ParseInstructionError::InstructionNotParsable( + ParsableProgram::SplToken, + )); + } + parse_cpi_guard_instruction(&instruction.data[1..], &instruction.accounts, account_keys) + } + TokenInstruction::InitializePermanentDelegate { delegate } => { + parse_initialize_permanent_delegate_instruction( + delegate, + &instruction.accounts, + account_keys, + ) + } } } @@ -586,6 +603,8 @@ pub enum UiAuthorityType { WithheldWithdraw, CloseMint, InterestRate, + PermanentDelegate, + ConfidentialTransferMint, } impl From for UiAuthorityType { @@ -599,6 +618,8 @@ impl From for UiAuthorityType { AuthorityType::WithheldWithdraw => UiAuthorityType::WithheldWithdraw, AuthorityType::CloseMint => UiAuthorityType::CloseMint, AuthorityType::InterestRate => UiAuthorityType::InterestRate, + AuthorityType::PermanentDelegate => UiAuthorityType::PermanentDelegate, + AuthorityType::ConfidentialTransferMint => UiAuthorityType::ConfidentialTransferMint, } } } @@ -617,6 +638,9 @@ pub enum UiExtensionType { MemoTransfer, NonTransferable, InterestBearingConfig, + CpiGuard, + PermanentDelegate, + NonTransferableAccount, } impl From for UiExtensionType { @@ -635,6 +659,9 @@ impl From for UiExtensionType { ExtensionType::MemoTransfer => UiExtensionType::MemoTransfer, ExtensionType::NonTransferable => UiExtensionType::NonTransferable, ExtensionType::InterestBearingConfig => UiExtensionType::InterestBearingConfig, + ExtensionType::CpiGuard => UiExtensionType::CpiGuard, + ExtensionType::PermanentDelegate => UiExtensionType::PermanentDelegate, + ExtensionType::NonTransferableAccount => UiExtensionType::NonTransferableAccount, } } } diff --git a/transaction-status/src/parse_token/extension/confidential_transfer.rs b/transaction-status/src/parse_token/extension/confidential_transfer.rs new file mode 100644 index 00000000000000..44384f119318f9 --- /dev/null +++ b/transaction-status/src/parse_token/extension/confidential_transfer.rs @@ -0,0 +1,408 @@ +use { + super::*, + solana_account_decoder::parse_token_extension::UiConfidentialTransferMint, + spl_token_2022::{ + extension::confidential_transfer::{instruction::*, ConfidentialTransferMint}, + instruction::{decode_instruction_data, decode_instruction_type}, + }, +}; + +pub(in crate::parse_token) fn parse_confidential_transfer_instruction( + instruction_data: &[u8], + account_indexes: &[u8], + account_keys: &AccountKeys, +) -> Result { + match decode_instruction_type(instruction_data) + .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))? + { + ConfidentialTransferInstruction::InitializeMint => { + check_num_token_accounts(account_indexes, 1)?; + let confidential_transfer_mint: ConfidentialTransferMint = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let confidential_transfer_mint: UiConfidentialTransferMint = + confidential_transfer_mint.into(); + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + }); + let map = value.as_object_mut().unwrap(); + map.append(json!(confidential_transfer_mint).as_object_mut().unwrap()); + Ok(ParsedInstructionEnum { + instruction_type: "initializeConfidentialTransferMint".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::UpdateMint => { + check_num_token_accounts(account_indexes, 3)?; + let confidential_transfer_mint: ConfidentialTransferMint = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let confidential_transfer_mint: UiConfidentialTransferMint = + confidential_transfer_mint.into(); + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "confidentialTransferMintAuthority": account_keys[account_indexes[1] as usize].to_string(), + "newConfidentialTransferMintAuthority": account_keys[account_indexes[2] as usize].to_string(), + }); + let map = value.as_object_mut().unwrap(); + map.append(json!(confidential_transfer_mint).as_object_mut().unwrap()); + Ok(ParsedInstructionEnum { + instruction_type: "updateConfidentialTransferMint".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::ConfigureAccount => { + check_num_token_accounts(account_indexes, 3)?; + let configure_account_data: ConfigureAccountInstructionData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let maximum_pending_balance_credit_counter: u64 = configure_account_data + .maximum_pending_balance_credit_counter + .into(); + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + "mint": account_keys[account_indexes[1] as usize].to_string(), + "decryptableZeroBalance": format!("{}", configure_account_data.decryptable_zero_balance), + "maximumPendingBalanceCreditCounter": maximum_pending_balance_credit_counter, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 2, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "configureConfidentialTransferAccount".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::ApproveAccount => { + check_num_token_accounts(account_indexes, 3)?; + Ok(ParsedInstructionEnum { + instruction_type: "approveConfidentialTransferAccount".to_string(), + info: json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + "mint": account_keys[account_indexes[1] as usize].to_string(), + "confidentialTransferAuditorAuthority": account_keys[account_indexes[2] as usize].to_string(), + }), + }) + } + ConfidentialTransferInstruction::EmptyAccount => { + check_num_token_accounts(account_indexes, 3)?; + let empty_account_data: EmptyAccountInstructionData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let proof_instruction_offset: i8 = empty_account_data.proof_instruction_offset; + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[1] as usize].to_string(), + "proofInstructionOffset": proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 2, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "emptyConfidentialTransferAccount".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::Deposit => { + check_num_token_accounts(account_indexes, 4)?; + let deposit_data: DepositInstructionData = *decode_instruction_data(instruction_data) + .map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let amount: u64 = deposit_data.amount.into(); + let mut value = json!({ + "source": account_keys[account_indexes[0] as usize].to_string(), + "destination": account_keys[account_indexes[1] as usize].to_string(), + "mint": account_keys[account_indexes[2] as usize].to_string(), + "amount": amount, + "decimals": deposit_data.decimals, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 3, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "depositConfidentialTransfer".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::Withdraw => { + check_num_token_accounts(account_indexes, 5)?; + let withdrawal_data: WithdrawInstructionData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let amount: u64 = withdrawal_data.amount.into(); + let proof_instruction_offset: i8 = withdrawal_data.proof_instruction_offset; + let mut value = json!({ + "source": account_keys[account_indexes[0] as usize].to_string(), + "destination": account_keys[account_indexes[1] as usize].to_string(), + "mint": account_keys[account_indexes[2] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[3] as usize].to_string(), + "amount": amount, + "decimals": withdrawal_data.decimals, + "newDecryptableAvailableBalance": format!("{}", withdrawal_data.new_decryptable_available_balance), + "proofInstructionOffset": proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 4, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "withdrawConfidentialTransfer".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::Transfer => { + check_num_token_accounts(account_indexes, 5)?; + let transfer_data: TransferInstructionData = *decode_instruction_data(instruction_data) + .map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let proof_instruction_offset: i8 = transfer_data.proof_instruction_offset; + let mut value = json!({ + "source": account_keys[account_indexes[0] as usize].to_string(), + "destination": account_keys[account_indexes[1] as usize].to_string(), + "mint": account_keys[account_indexes[2] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[3] as usize].to_string(), + "newSourceDecryptableAvailableBalance": format!("{}", transfer_data.new_source_decryptable_available_balance), + "proofInstructionOffset": proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 4, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "confidentialTransfer".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::ApplyPendingBalance => { + check_num_token_accounts(account_indexes, 2)?; + let apply_pending_balance_data: ApplyPendingBalanceData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let expected_pending_balance_credit_counter: u64 = apply_pending_balance_data + .expected_pending_balance_credit_counter + .into(); + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + "newDecryptableAvailableBalance": format!("{}", apply_pending_balance_data.new_decryptable_available_balance), + "expectedPendingBalanceCreditCounter": expected_pending_balance_credit_counter, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "applyPendingConfidentialTransferBalance".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::EnableConfidentialCredits => { + check_num_token_accounts(account_indexes, 2)?; + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "enableConfidentialTransferConfidentialCredits".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::DisableConfidentialCredits => { + check_num_token_accounts(account_indexes, 2)?; + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "disableConfidentialTransferConfidentialCredits".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::EnableNonConfidentialCredits => { + check_num_token_accounts(account_indexes, 2)?; + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "enableConfidentialTransferNonConfidentialCredits".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::DisableNonConfidentialCredits => { + check_num_token_accounts(account_indexes, 2)?; + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: "disableNonConfidentialTransferConfidentialCredits".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::WithdrawWithheldTokensFromMint => { + check_num_token_accounts(account_indexes, 4)?; + let withdraw_withheld_data: WithdrawWithheldTokensFromMintData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let proof_instruction_offset: i8 = withdraw_withheld_data.proof_instruction_offset; + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "feeRecipient": account_keys[account_indexes[1] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[2] as usize].to_string(), + "proofInstructionOffset": proof_instruction_offset, + + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 3, + account_keys, + account_indexes, + "withdrawWithheldAuthority", + "multisigWithdrawWithheldAuthority", + ); + Ok(ParsedInstructionEnum { + instruction_type: "withdrawWithheldConfidentialTransferTokensFromMint".to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::WithdrawWithheldTokensFromAccounts => { + let withdraw_withheld_data: WithdrawWithheldTokensFromAccountsData = + *decode_instruction_data(instruction_data).map_err(|_| { + ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken) + })?; + let num_token_accounts = withdraw_withheld_data.num_token_accounts; + check_num_token_accounts(account_indexes, 4 + num_token_accounts as usize)?; + let proof_instruction_offset: i8 = withdraw_withheld_data.proof_instruction_offset; + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "feeRecipient": account_keys[account_indexes[1] as usize].to_string(), + "instructionsSysvar": account_keys[account_indexes[2] as usize].to_string(), + "proofInstructionOffset": proof_instruction_offset, + }); + let map = value.as_object_mut().unwrap(); + let mut source_accounts: Vec = vec![]; + let first_source_account_index = account_indexes + .len() + .saturating_sub(num_token_accounts as usize); + for i in account_indexes[first_source_account_index..].iter() { + source_accounts.push(account_keys[*i as usize].to_string()); + } + map.insert("sourceAccounts".to_string(), json!(source_accounts)); + parse_signers( + map, + 3, + account_keys, + &account_indexes[..first_source_account_index], + "withdrawWithheldAuthority", + "multisigWithdrawWithheldAuthority", + ); + Ok(ParsedInstructionEnum { + instruction_type: "withdrawWithheldConfidentialTransferTokensFromAccounts" + .to_string(), + info: value, + }) + } + ConfidentialTransferInstruction::HarvestWithheldTokensToMint => { + check_num_token_accounts(account_indexes, 1)?; + let mut value = json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + + }); + let map = value.as_object_mut().unwrap(); + let mut source_accounts: Vec = vec![]; + for i in account_indexes.iter().skip(1) { + source_accounts.push(account_keys[*i as usize].to_string()); + } + map.insert("sourceAccounts".to_string(), json!(source_accounts)); + Ok(ParsedInstructionEnum { + instruction_type: "harvestWithheldConfidentialTransferTokensToMint".to_string(), + info: value, + }) + } + } +} diff --git a/transaction-status/src/parse_token/extension/cpi_guard.rs b/transaction-status/src/parse_token/extension/cpi_guard.rs new file mode 100644 index 00000000000000..8d198249f7477d --- /dev/null +++ b/transaction-status/src/parse_token/extension/cpi_guard.rs @@ -0,0 +1,176 @@ +use { + super::*, + spl_token_2022::{ + extension::cpi_guard::instruction::CpiGuardInstruction, + instruction::decode_instruction_type, + }, +}; + +pub(in crate::parse_token) fn parse_cpi_guard_instruction( + instruction_data: &[u8], + account_indexes: &[u8], + account_keys: &AccountKeys, +) -> Result { + check_num_token_accounts(account_indexes, 2)?; + let instruction_type_str = match decode_instruction_type(instruction_data) + .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::SplToken))? + { + CpiGuardInstruction::Enable => "enable", + CpiGuardInstruction::Disable => "disable", + }; + let mut value = json!({ + "account": account_keys[account_indexes[0] as usize].to_string(), + }); + let map = value.as_object_mut().unwrap(); + parse_signers( + map, + 1, + account_keys, + account_indexes, + "owner", + "multisigOwner", + ); + Ok(ParsedInstructionEnum { + instruction_type: format!("{}CpiGuard", instruction_type_str), + info: value, + }) +} + +#[cfg(test)] +mod test { + use { + super::*, + crate::parse_token::test::*, + solana_sdk::pubkey::Pubkey, + spl_token_2022::{ + extension::cpi_guard::instruction::{disable_cpi_guard, enable_cpi_guard}, + solana_program::message::Message, + }, + }; + + #[test] + fn test_parse_cpi_guard_instruction() { + let account_pubkey = Pubkey::new_unique(); + + // Enable, single owner + let owner_pubkey = Pubkey::new_unique(); + let enable_cpi_guard_ix = enable_cpi_guard( + &spl_token_2022::id(), + &convert_pubkey(account_pubkey), + &convert_pubkey(owner_pubkey), + &[], + ) + .unwrap(); + let message = Message::new(&[enable_cpi_guard_ix], None); + let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); + assert_eq!( + parse_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "enableCpiGuard".to_string(), + info: json!({ + "account": account_pubkey.to_string(), + "owner": owner_pubkey.to_string(), + }) + } + ); + + // Enable, multisig owner + let multisig_pubkey = Pubkey::new_unique(); + let multisig_signer0 = Pubkey::new_unique(); + let multisig_signer1 = Pubkey::new_unique(); + let enable_cpi_guard_ix = enable_cpi_guard( + &spl_token_2022::id(), + &convert_pubkey(account_pubkey), + &convert_pubkey(multisig_pubkey), + &[ + &convert_pubkey(multisig_signer0), + &convert_pubkey(multisig_signer1), + ], + ) + .unwrap(); + let message = Message::new(&[enable_cpi_guard_ix], None); + let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); + assert_eq!( + parse_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "enableCpiGuard".to_string(), + info: json!({ + "account": account_pubkey.to_string(), + "multisigOwner": multisig_pubkey.to_string(), + "signers": vec![ + multisig_signer0.to_string(), + multisig_signer1.to_string(), + ], + }) + } + ); + + // Disable, single owner + let enable_cpi_guard_ix = disable_cpi_guard( + &spl_token_2022::id(), + &convert_pubkey(account_pubkey), + &convert_pubkey(owner_pubkey), + &[], + ) + .unwrap(); + let message = Message::new(&[enable_cpi_guard_ix], None); + let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); + assert_eq!( + parse_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "disableCpiGuard".to_string(), + info: json!({ + "account": account_pubkey.to_string(), + "owner": owner_pubkey.to_string(), + }) + } + ); + + // Enable, multisig owner + let multisig_pubkey = Pubkey::new_unique(); + let multisig_signer0 = Pubkey::new_unique(); + let multisig_signer1 = Pubkey::new_unique(); + let enable_cpi_guard_ix = disable_cpi_guard( + &spl_token_2022::id(), + &convert_pubkey(account_pubkey), + &convert_pubkey(multisig_pubkey), + &[ + &convert_pubkey(multisig_signer0), + &convert_pubkey(multisig_signer1), + ], + ) + .unwrap(); + let message = Message::new(&[enable_cpi_guard_ix], None); + let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); + assert_eq!( + parse_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "disableCpiGuard".to_string(), + info: json!({ + "account": account_pubkey.to_string(), + "multisigOwner": multisig_pubkey.to_string(), + "signers": vec![ + multisig_signer0.to_string(), + multisig_signer1.to_string(), + ], + }) + } + ); + } +} diff --git a/transaction-status/src/parse_token/extension/mod.rs b/transaction-status/src/parse_token/extension/mod.rs index 3c84942651ab79..740bc61ed4efe6 100644 --- a/transaction-status/src/parse_token/extension/mod.rs +++ b/transaction-status/src/parse_token/extension/mod.rs @@ -1,8 +1,10 @@ use super::*; +pub(super) mod cpi_guard; pub(super) mod default_account_state; pub(super) mod interest_bearing_mint; pub(super) mod memo_transfer; pub(super) mod mint_close_authority; +pub(super) mod permanent_delegate; pub(super) mod reallocate; pub(super) mod transfer_fee; diff --git a/transaction-status/src/parse_token/extension/permanent_delegate.rs b/transaction-status/src/parse_token/extension/permanent_delegate.rs new file mode 100644 index 00000000000000..03b528e1af6bac --- /dev/null +++ b/transaction-status/src/parse_token/extension/permanent_delegate.rs @@ -0,0 +1,54 @@ +use {super::*, spl_token_2022::solana_program::pubkey::Pubkey}; + +pub(in crate::parse_token) fn parse_initialize_permanent_delegate_instruction( + delegate: Pubkey, + account_indexes: &[u8], + account_keys: &AccountKeys, +) -> Result { + check_num_token_accounts(account_indexes, 1)?; + Ok(ParsedInstructionEnum { + instruction_type: "initializePermanentDelegate".to_string(), + info: json!({ + "mint": account_keys[account_indexes[0] as usize].to_string(), + "delegate": delegate.to_string(), + }), + }) +} + +#[cfg(test)] +mod test { + use { + super::*, + crate::parse_token::test::*, + solana_sdk::pubkey::Pubkey, + spl_token_2022::{instruction::*, solana_program::message::Message}, + }; + + #[test] + fn test_parse_initialize_permanent_delegate_instruction() { + let mint_pubkey = Pubkey::new_unique(); + let delegate = Pubkey::new_unique(); + let permanent_delegate_ix = initialize_permanent_delegate( + &spl_token_2022::id(), + &convert_pubkey(mint_pubkey), + &convert_pubkey(delegate), + ) + .unwrap(); + let message = Message::new(&[permanent_delegate_ix], None); + let compiled_instruction = convert_compiled_instruction(&message.instructions[0]); + assert_eq!( + parse_token( + &compiled_instruction, + &AccountKeys::new(&convert_account_keys(&message), None) + ) + .unwrap(), + ParsedInstructionEnum { + instruction_type: "initializePermanentDelegate".to_string(), + info: json!({ + "mint": mint_pubkey.to_string(), + "delegate": delegate.to_string(), + }) + } + ); + } +} diff --git a/transaction-status/src/parse_vote.rs b/transaction-status/src/parse_vote.rs index 094b01ec0258d2..ec5079d7e456e8 100644 --- a/transaction-status/src/parse_vote.rs +++ b/transaction-status/src/parse_vote.rs @@ -5,7 +5,7 @@ use { bincode::deserialize, serde_json::json, solana_sdk::{instruction::CompiledInstruction, message::AccountKeys}, - solana_vote_program::{vote_instruction::VoteInstruction, vote_state::VoteStateUpdate}, + solana_vote_program::vote_instruction::VoteInstruction, }; pub fn parse_vote( @@ -135,8 +135,7 @@ pub fn parse_vote( }), }) } - VoteInstruction::CompactUpdateVoteState(compact_vote_state_update) => { - let vote_state_update = VoteStateUpdate::from(compact_vote_state_update); + VoteInstruction::CompactUpdateVoteState(vote_state_update) => { check_num_vote_accounts(&instruction.accounts, 2)?; let vote_state_update = json!({ "lockouts": vote_state_update.lockouts, @@ -153,8 +152,7 @@ pub fn parse_vote( }), }) } - VoteInstruction::CompactUpdateVoteStateSwitch(compact_vote_state_update, hash) => { - let vote_state_update = VoteStateUpdate::from(compact_vote_state_update); + VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, hash) => { check_num_vote_accounts(&instruction.accounts, 2)?; let vote_state_update = json!({ "lockouts": vote_state_update.lockouts, @@ -252,7 +250,7 @@ mod test { solana_sdk::{hash::Hash, message::Message, pubkey::Pubkey, sysvar}, solana_vote_program::{ vote_instruction, - vote_state::{CompactVoteStateUpdate, Vote, VoteAuthorize, VoteInit}, + vote_state::{Vote, VoteAuthorize, VoteInit, VoteStateUpdate}, }, }; @@ -766,7 +764,7 @@ mod test { #[test] fn test_parse_compact_vote_state_update_ix() { let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]); - let compact_vote_state_update = CompactVoteStateUpdate::from(vote_state_update.clone()); + let compact_vote_state_update = vote_state_update.clone(); let vote_pubkey = Pubkey::new_unique(); let authorized_voter_pubkey = Pubkey::new_unique(); @@ -809,7 +807,7 @@ mod test { #[test] fn test_parse_compact_vote_state_update_switch_ix() { let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]); - let compact_vote_state_update = CompactVoteStateUpdate::from(vote_state_update.clone()); + let compact_vote_state_update = vote_state_update.clone(); let vote_pubkey = Pubkey::new_unique(); let authorized_voter_pubkey = Pubkey::new_unique(); diff --git a/transaction-status/src/token_balances.rs b/transaction-status/src/token_balances.rs index b4c047410f3271..85a85a053f910f 100644 --- a/transaction-status/src/token_balances.rs +++ b/transaction-status/src/token_balances.rs @@ -1,19 +1,4 @@ -use { - crate::TransactionTokenBalance, - solana_account_decoder::parse_token::{ - is_known_spl_token_id, pubkey_from_spl_token, spl_token_native_mint, - token_amount_to_ui_amount, UiTokenAmount, - }, - solana_measure::measure::Measure, - solana_metrics::datapoint_debug, - solana_runtime::{bank::Bank, transaction_batch::TransactionBatch}, - solana_sdk::{account::ReadableAccount, pubkey::Pubkey}, - spl_token_2022::{ - extension::StateWithExtensions, - state::{Account as TokenAccount, Mint}, - }, - std::collections::HashMap, -}; +use crate::TransactionTokenBalance; pub type TransactionTokenBalances = Vec>; @@ -34,462 +19,3 @@ impl TransactionTokenBalancesSet { } } } - -fn get_mint_decimals(bank: &Bank, mint: &Pubkey) -> Option { - if mint == &spl_token_native_mint() { - Some(spl_token::native_mint::DECIMALS) - } else { - let mint_account = bank.get_account(mint)?; - - if !is_known_spl_token_id(mint_account.owner()) { - return None; - } - - let decimals = StateWithExtensions::::unpack(mint_account.data()) - .map(|mint| mint.base.decimals) - .ok()?; - - Some(decimals) - } -} - -pub fn collect_token_balances( - bank: &Bank, - batch: &TransactionBatch, - mint_decimals: &mut HashMap, -) -> TransactionTokenBalances { - let mut balances: TransactionTokenBalances = vec![]; - let mut collect_time = Measure::start("collect_token_balances"); - - for transaction in batch.sanitized_transactions() { - let account_keys = transaction.message().account_keys(); - let has_token_program = account_keys.iter().any(is_known_spl_token_id); - - let mut transaction_balances: Vec = vec![]; - if has_token_program { - for (index, account_id) in account_keys.iter().enumerate() { - if transaction.message().is_invoked(index) || is_known_spl_token_id(account_id) { - continue; - } - - if let Some(TokenBalanceData { - mint, - ui_token_amount, - owner, - program_id, - }) = collect_token_balance_from_account(bank, account_id, mint_decimals) - { - transaction_balances.push(TransactionTokenBalance { - account_index: index as u8, - mint, - ui_token_amount, - owner, - program_id, - }); - } - } - } - balances.push(transaction_balances); - } - collect_time.stop(); - datapoint_debug!( - "collect_token_balances", - ("collect_time_us", collect_time.as_us(), i64), - ); - balances -} - -#[derive(Debug, PartialEq)] -struct TokenBalanceData { - mint: String, - owner: String, - ui_token_amount: UiTokenAmount, - program_id: String, -} - -fn collect_token_balance_from_account( - bank: &Bank, - account_id: &Pubkey, - mint_decimals: &mut HashMap, -) -> Option { - let account = bank.get_account(account_id)?; - - if !is_known_spl_token_id(account.owner()) { - return None; - } - - let token_account = StateWithExtensions::::unpack(account.data()).ok()?; - let mint = pubkey_from_spl_token(&token_account.base.mint); - - let decimals = mint_decimals.get(&mint).cloned().or_else(|| { - let decimals = get_mint_decimals(bank, &mint)?; - mint_decimals.insert(mint, decimals); - Some(decimals) - })?; - - Some(TokenBalanceData { - mint: token_account.base.mint.to_string(), - owner: token_account.base.owner.to_string(), - ui_token_amount: token_amount_to_ui_amount(token_account.base.amount, decimals), - program_id: account.owner().to_string(), - }) -} - -#[cfg(test)] -mod test { - use { - super::*, - solana_account_decoder::parse_token::{pubkey_from_spl_token, spl_token_pubkey}, - solana_sdk::{account::Account, genesis_config::create_genesis_config}, - spl_token_2022::{ - extension::{ - immutable_owner::ImmutableOwner, memo_transfer::MemoTransfer, - mint_close_authority::MintCloseAuthority, ExtensionType, StateWithExtensionsMut, - }, - pod::OptionalNonZeroPubkey, - solana_program::{program_option::COption, program_pack::Pack}, - }, - std::collections::BTreeMap, - }; - - #[test] - fn test_collect_token_balance_from_account() { - let (mut genesis_config, _mint_keypair) = create_genesis_config(500); - - // Add a variety of accounts, token and not - let account = Account::new(42, 55, &Pubkey::new_unique()); - - let mint_data = Mint { - mint_authority: COption::None, - supply: 4242, - decimals: 2, - is_initialized: true, - freeze_authority: COption::None, - }; - let mut data = [0; Mint::LEN]; - Mint::pack(mint_data, &mut data).unwrap(); - let mint_pubkey = Pubkey::new_unique(); - let mint = Account { - lamports: 100, - data: data.to_vec(), - owner: pubkey_from_spl_token(&spl_token::id()), - executable: false, - rent_epoch: 0, - }; - let other_mint_pubkey = Pubkey::new_unique(); - let other_mint = Account { - lamports: 100, - data: data.to_vec(), - owner: Pubkey::new_unique(), // !is_known_spl_token_id - executable: false, - rent_epoch: 0, - }; - - let token_owner = Pubkey::new_unique(); - let token_data = TokenAccount { - mint: spl_token_pubkey(&mint_pubkey), - owner: spl_token_pubkey(&token_owner), - amount: 42, - delegate: COption::None, - state: spl_token_2022::state::AccountState::Initialized, - is_native: COption::Some(100), - delegated_amount: 0, - close_authority: COption::None, - }; - let mut data = [0; TokenAccount::LEN]; - TokenAccount::pack(token_data, &mut data).unwrap(); - - let spl_token_account = Account { - lamports: 100, - data: data.to_vec(), - owner: pubkey_from_spl_token(&spl_token::id()), - executable: false, - rent_epoch: 0, - }; - let other_account = Account { - lamports: 100, - data: data.to_vec(), - owner: Pubkey::new_unique(), // !is_known_spl_token_id - executable: false, - rent_epoch: 0, - }; - - let other_mint_data = TokenAccount { - mint: spl_token_pubkey(&other_mint_pubkey), - owner: spl_token_pubkey(&token_owner), - amount: 42, - delegate: COption::None, - state: spl_token_2022::state::AccountState::Initialized, - is_native: COption::Some(100), - delegated_amount: 0, - close_authority: COption::None, - }; - let mut data = [0; TokenAccount::LEN]; - TokenAccount::pack(other_mint_data, &mut data).unwrap(); - - let other_mint_token_account = Account { - lamports: 100, - data: data.to_vec(), - owner: pubkey_from_spl_token(&spl_token::id()), - executable: false, - rent_epoch: 0, - }; - - let mut accounts = BTreeMap::new(); - - let account_pubkey = Pubkey::new_unique(); - accounts.insert(account_pubkey, account); - accounts.insert(mint_pubkey, mint); - accounts.insert(other_mint_pubkey, other_mint); - let spl_token_account_pubkey = Pubkey::new_unique(); - accounts.insert(spl_token_account_pubkey, spl_token_account); - let other_account_pubkey = Pubkey::new_unique(); - accounts.insert(other_account_pubkey, other_account); - let other_mint_account_pubkey = Pubkey::new_unique(); - accounts.insert(other_mint_account_pubkey, other_mint_token_account); - - genesis_config.accounts = accounts; - - let bank = Bank::new_for_tests(&genesis_config); - let mut mint_decimals = HashMap::new(); - - // Account is not owned by spl_token (nor does it have TokenAccount state) - assert_eq!( - collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals), - None - ); - - // Mint does not have TokenAccount state - assert_eq!( - collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals), - None - ); - - // TokenAccount owned by spl_token::id() works - assert_eq!( - collect_token_balance_from_account( - &bank, - &spl_token_account_pubkey, - &mut mint_decimals - ), - Some(TokenBalanceData { - mint: mint_pubkey.to_string(), - owner: token_owner.to_string(), - ui_token_amount: UiTokenAmount { - ui_amount: Some(0.42), - decimals: 2, - amount: "42".to_string(), - ui_amount_string: "0.42".to_string(), - }, - program_id: spl_token::id().to_string(), - }) - ); - - // TokenAccount is not owned by known spl-token program_id - assert_eq!( - collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals), - None - ); - - // TokenAccount's mint is not owned by known spl-token program_id - assert_eq!( - collect_token_balance_from_account( - &bank, - &other_mint_account_pubkey, - &mut mint_decimals - ), - None - ); - } - - #[test] - fn test_collect_token_balance_from_spl_token_2022_account() { - let (mut genesis_config, _mint_keypair) = create_genesis_config(500); - - // Add a variety of accounts, token and not - let account = Account::new(42, 55, &Pubkey::new_unique()); - - let mint_authority = Pubkey::new_unique(); - let mint_size = - ExtensionType::get_account_len::(&[ExtensionType::MintCloseAuthority]); - let mint_base = Mint { - mint_authority: COption::None, - supply: 4242, - decimals: 2, - is_initialized: true, - freeze_authority: COption::None, - }; - let mut mint_data = vec![0; mint_size]; - let mut mint_state = - StateWithExtensionsMut::::unpack_uninitialized(&mut mint_data).unwrap(); - mint_state.base = mint_base; - mint_state.pack_base(); - mint_state.init_account_type().unwrap(); - let mut mint_close_authority = mint_state - .init_extension::(true) - .unwrap(); - mint_close_authority.close_authority = - OptionalNonZeroPubkey::try_from(Some(spl_token_pubkey(&mint_authority))).unwrap(); - - let mint_pubkey = Pubkey::new_unique(); - let mint = Account { - lamports: 100, - data: mint_data.to_vec(), - owner: pubkey_from_spl_token(&spl_token_2022::id()), - executable: false, - rent_epoch: 0, - }; - let other_mint_pubkey = Pubkey::new_unique(); - let other_mint = Account { - lamports: 100, - data: mint_data.to_vec(), - owner: Pubkey::new_unique(), - executable: false, - rent_epoch: 0, - }; - - let token_owner = Pubkey::new_unique(); - let token_base = TokenAccount { - mint: spl_token_pubkey(&mint_pubkey), - owner: spl_token_pubkey(&token_owner), - amount: 42, - delegate: COption::None, - state: spl_token_2022::state::AccountState::Initialized, - is_native: COption::Some(100), - delegated_amount: 0, - close_authority: COption::None, - }; - let account_size = ExtensionType::get_account_len::(&[ - ExtensionType::ImmutableOwner, - ExtensionType::MemoTransfer, - ]); - let mut account_data = vec![0; account_size]; - let mut account_state = - StateWithExtensionsMut::::unpack_uninitialized(&mut account_data) - .unwrap(); - account_state.base = token_base; - account_state.pack_base(); - account_state.init_account_type().unwrap(); - account_state - .init_extension::(true) - .unwrap(); - let mut memo_transfer = account_state.init_extension::(true).unwrap(); - memo_transfer.require_incoming_transfer_memos = true.into(); - - let spl_token_account = Account { - lamports: 100, - data: account_data.to_vec(), - owner: pubkey_from_spl_token(&spl_token_2022::id()), - executable: false, - rent_epoch: 0, - }; - let other_account = Account { - lamports: 100, - data: account_data.to_vec(), - owner: Pubkey::new_unique(), - executable: false, - rent_epoch: 0, - }; - - let other_mint_token_base = TokenAccount { - mint: spl_token_pubkey(&other_mint_pubkey), - owner: spl_token_pubkey(&token_owner), - amount: 42, - delegate: COption::None, - state: spl_token_2022::state::AccountState::Initialized, - is_native: COption::Some(100), - delegated_amount: 0, - close_authority: COption::None, - }; - let account_size = ExtensionType::get_account_len::(&[ - ExtensionType::ImmutableOwner, - ExtensionType::MemoTransfer, - ]); - let mut account_data = vec![0; account_size]; - let mut account_state = - StateWithExtensionsMut::::unpack_uninitialized(&mut account_data) - .unwrap(); - account_state.base = other_mint_token_base; - account_state.pack_base(); - account_state.init_account_type().unwrap(); - account_state - .init_extension::(true) - .unwrap(); - let mut memo_transfer = account_state.init_extension::(true).unwrap(); - memo_transfer.require_incoming_transfer_memos = true.into(); - - let other_mint_token_account = Account { - lamports: 100, - data: account_data.to_vec(), - owner: pubkey_from_spl_token(&spl_token_2022::id()), - executable: false, - rent_epoch: 0, - }; - - let mut accounts = BTreeMap::new(); - - let account_pubkey = Pubkey::new_unique(); - accounts.insert(account_pubkey, account); - accounts.insert(mint_pubkey, mint); - accounts.insert(other_mint_pubkey, other_mint); - let spl_token_account_pubkey = Pubkey::new_unique(); - accounts.insert(spl_token_account_pubkey, spl_token_account); - let other_account_pubkey = Pubkey::new_unique(); - accounts.insert(other_account_pubkey, other_account); - let other_mint_account_pubkey = Pubkey::new_unique(); - accounts.insert(other_mint_account_pubkey, other_mint_token_account); - - genesis_config.accounts = accounts; - - let bank = Bank::new_for_tests(&genesis_config); - let mut mint_decimals = HashMap::new(); - - // Account is not owned by spl_token (nor does it have TokenAccount state) - assert_eq!( - collect_token_balance_from_account(&bank, &account_pubkey, &mut mint_decimals), - None - ); - - // Mint does not have TokenAccount state - assert_eq!( - collect_token_balance_from_account(&bank, &mint_pubkey, &mut mint_decimals), - None - ); - - // TokenAccount owned by spl_token_2022::id() works - assert_eq!( - collect_token_balance_from_account( - &bank, - &spl_token_account_pubkey, - &mut mint_decimals - ), - Some(TokenBalanceData { - mint: mint_pubkey.to_string(), - owner: token_owner.to_string(), - ui_token_amount: UiTokenAmount { - ui_amount: Some(0.42), - decimals: 2, - amount: "42".to_string(), - ui_amount_string: "0.42".to_string(), - }, - program_id: spl_token_2022::id().to_string(), - }) - ); - - // TokenAccount is not owned by known spl-token program_id - assert_eq!( - collect_token_balance_from_account(&bank, &other_account_pubkey, &mut mint_decimals), - None - ); - - // TokenAccount's mint is not owned by known spl-token program_id - assert_eq!( - collect_token_balance_from_account( - &bank, - &other_mint_account_pubkey, - &mut mint_decimals - ), - None - ); - } -} diff --git a/upload-perf/Cargo.toml b/upload-perf/Cargo.toml index 84134971b09499..f39fcba05ffc68 100644 --- a/upload-perf/Cargo.toml +++ b/upload-perf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-upload-perf" -version = "1.11.6" +version = "1.14.24" description = "Metrics Upload Utility" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -11,7 +11,7 @@ publish = false [dependencies] serde_json = "1.0.81" -solana-metrics = { path = "../metrics", version = "=1.11.6" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } [[bin]] name = "solana-upload-perf" diff --git a/upload-perf/src/upload-perf.rs b/upload-perf/src/upload-perf.rs index cb349c94b2619f..16f8a433dbb40d 100644 --- a/upload-perf/src/upload-perf.rs +++ b/upload-perf/src/upload-perf.rs @@ -43,7 +43,7 @@ fn main() { let upload_metrics = args.len() > 2; let git_output = Command::new("git") - .args(&["rev-parse", "HEAD"]) + .args(["rev-parse", "HEAD"]) .output() .expect("failed to execute git rev-parse"); let git_commit_hash = String::from_utf8_lossy(&git_output.stdout); diff --git a/validator/Cargo.toml b/validator/Cargo.toml index ddbe83c0f6d9ce..1f8acf98e9f6b6 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-validator" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -28,30 +28,30 @@ num_cpus = "1.13.1" rand = "0.7.0" serde = "1.0.138" serde_json = "1.0.81" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-core = { path = "../core", version = "=1.11.6" } -solana-download-utils = { path = "../download-utils", version = "=1.11.6" } -solana-entry = { path = "../entry", version = "=1.11.6" } -solana-faucet = { path = "../faucet", version = "=1.11.6" } -solana-genesis-utils = { path = "../genesis-utils", version = "=1.11.6" } -solana-gossip = { path = "../gossip", version = "=1.11.6" } -solana-ledger = { path = "../ledger", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-net-utils = { path = "../net-utils", version = "=1.11.6" } -solana-perf = { path = "../perf", version = "=1.11.6" } -solana-poh = { path = "../poh", version = "=1.11.6" } -solana-rpc = { path = "../rpc", version = "=1.11.6" } -solana-runtime = { path = "../runtime", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.11.6" } -solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.11.6" } -solana-streamer = { path = "../streamer", version = "=1.11.6" } -solana-test-validator = { path = "../test-validator", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } -solana-vote-program = { path = "../programs/vote", version = "=1.11.6" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-core = { path = "../core", version = "=1.14.24" } +solana-download-utils = { path = "../download-utils", version = "=1.14.24" } +solana-entry = { path = "../entry", version = "=1.14.24" } +solana-faucet = { path = "../faucet", version = "=1.14.24" } +solana-genesis-utils = { path = "../genesis-utils", version = "=1.14.24" } +solana-gossip = { path = "../gossip", version = "=1.14.24" } +solana-ledger = { path = "../ledger", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-net-utils = { path = "../net-utils", version = "=1.14.24" } +solana-perf = { path = "../perf", version = "=1.14.24" } +solana-poh = { path = "../poh", version = "=1.14.24" } +solana-rpc = { path = "../rpc", version = "=1.14.24" } +solana-runtime = { path = "../runtime", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.14.24" } +solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.14.24" } +solana-streamer = { path = "../streamer", version = "=1.14.24" } +solana-test-validator = { path = "../test-validator", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } +solana-vote-program = { path = "../programs/vote", version = "=1.14.24" } symlink = "0.1.0" [target.'cfg(not(target_env = "msvc"))'.dependencies] diff --git a/validator/src/admin_rpc_service.rs b/validator/src/admin_rpc_service.rs index 2c32cc8e24a6fd..fc10f6f193206d 100644 --- a/validator/src/admin_rpc_service.rs +++ b/validator/src/admin_rpc_service.rs @@ -7,13 +7,12 @@ use { log::*, serde::{Deserialize, Serialize}, solana_core::{ - consensus::Tower, tower_storage::TowerStorage, validator::ValidatorStartProgress, + admin_rpc_post_init::AdminRpcRequestMetadataPostInit, consensus::Tower, + tower_storage::TowerStorage, validator::ValidatorStartProgress, }, - solana_gossip::{cluster_info::ClusterInfo, contact_info::ContactInfo}, - solana_runtime::bank_forks::BankForks, + solana_gossip::legacy_contact_info::LegacyContactInfo as ContactInfo, solana_sdk::{ exit::Exit, - pubkey::Pubkey, signature::{read_keypair_file, Keypair, Signer}, }, std::{ @@ -26,13 +25,6 @@ use { }, }; -#[derive(Clone)] -pub struct AdminRpcRequestMetadataPostInit { - pub cluster_info: Arc, - pub bank_forks: Arc>, - pub vote_account: Pubkey, -} - #[derive(Clone)] pub struct AdminRpcRequestMetadata { pub rpc_addr: Option, @@ -186,22 +178,25 @@ impl AdminRpc for AdminRpcImpl { fn exit(&self, meta: Self::Metadata) -> Result<()> { debug!("exit admin rpc request received"); - thread::spawn(move || { - // Delay exit signal until this RPC request completes, otherwise the caller of `exit` might - // receive a confusing error as the validator shuts down before a response is sent back. - thread::sleep(Duration::from_millis(100)); - - warn!("validator exit requested"); - meta.validator_exit.write().unwrap().exit(); - - // TODO: Debug why Exit doesn't always cause the validator to fully exit - // (rocksdb background processing or some other stuck thread perhaps?). - // - // If the process is still alive after five seconds, exit harder - thread::sleep(Duration::from_secs(5)); - warn!("validator exit timeout"); - std::process::exit(0); - }); + thread::Builder::new() + .name("solProcessExit".into()) + .spawn(move || { + // Delay exit signal until this RPC request completes, otherwise the caller of `exit` might + // receive a confusing error as the validator shuts down before a response is sent back. + thread::sleep(Duration::from_millis(100)); + + warn!("validator exit requested"); + meta.validator_exit.write().unwrap().exit(); + + // TODO: Debug why Exit doesn't always cause the validator to fully exit + // (rocksdb background processing or some other stuck thread perhaps?). + // + // If the process is still alive after five seconds, exit harder + thread::sleep(Duration::from_secs(5)); + warn!("validator exit timeout"); + std::process::exit(0); + }) + .unwrap(); Ok(()) } @@ -351,14 +346,14 @@ pub fn run(ledger_path: &Path, metadata: AdminRpcRequestMetadata) { let admin_rpc_path = admin_rpc_path(ledger_path); let event_loop = tokio::runtime::Builder::new_multi_thread() - .thread_name("sol-adminrpc-el") + .thread_name("solAdminRpcEl") .worker_threads(3) // Three still seems like a lot, and better than the default of available core count .enable_all() .build() .unwrap(); Builder::new() - .name("solana-adminrpc".to_string()) + .name("solAdminRpc".to_string()) .spawn(move || { let mut io = MetaIoHandler::default(); io.extend_with(AdminRpcImpl.to_delegate()); diff --git a/validator/src/bin/solana-test-validator.rs b/validator/src/bin/solana-test-validator.rs index 84f032e96978fb..fc392b244c810b 100644 --- a/validator/src/bin/solana-test-validator.rs +++ b/validator/src/bin/solana-test-validator.rs @@ -193,20 +193,20 @@ fn main() { .arg( Arg::with_name("bpf_program") .long("bpf-program") - .value_name("ADDRESS_OR_PATH BPF_PROGRAM.SO") + .value_names(&["ADDRESS_OR_KEYPAIR", "BPF_PROGRAM.SO"]) .takes_value(true) .number_of_values(2) .multiple(true) .help( "Add a BPF program to the genesis configuration. \ If the ledger already exists then this parameter is silently ignored. \ - First argument can be a public key or path to file that can be parsed as a keypair", + First argument can be a pubkey string or path to a keypair", ), ) .arg( Arg::with_name("account") .long("account") - .value_name("ADDRESS FILENAME.JSON") + .value_names(&["ADDRESS", "DUMP.JSON"]) .takes_value(true) .number_of_values(2) .allow_hyphen_values(true) @@ -389,11 +389,6 @@ fn main() { .multiple(true) .help("Specify the configuration file for the Geyser plugin."), ) - .arg( - Arg::with_name("no_accounts_db_caching") - .long("no-accounts-db-caching") - .help("Disables accounts caching"), - ) .arg( Arg::with_name("deactivate_feature") .long("deactivate-feature") @@ -685,7 +680,7 @@ fn main() { let mut genesis = TestValidatorGenesis::default(); genesis.max_ledger_shreds = value_of(&matches, "limit_ledger_size"); genesis.max_genesis_archive_unpacked_size = Some(u64::MAX); - genesis.accounts_db_caching_enabled = !matches.is_present("no_accounts_db_caching"); + genesis.accounts_db_caching_enabled = true; genesis.log_messages_bytes_limit = value_t!(matches, "log_messages_bytes_limit", usize).ok(); let tower_storage = Arc::new(FileTowerStorage::new(ledger_path.clone())); @@ -702,7 +697,7 @@ fn main() { start_time: std::time::SystemTime::now(), validator_exit: genesis.validator_exit.clone(), authorized_voter_keypairs: genesis.authorized_voter_keypairs.clone(), - post_init: admin_service_post_init.clone(), + post_init: admin_service_post_init, tower_storage: tower_storage.clone(), }, ); @@ -828,12 +823,6 @@ fn main() { match genesis.start_with_mint_address(mint_address, socket_addr_space) { Ok(test_validator) => { - *admin_service_post_init.write().unwrap() = - Some(admin_rpc_service::AdminRpcRequestMetadataPostInit { - bank_forks: test_validator.bank_forks(), - cluster_info: test_validator.cluster_info(), - vote_account: test_validator.vote_account_address(), - }); if let Some(dashboard) = dashboard { dashboard.run(Duration::from_millis(250)); } @@ -848,7 +837,7 @@ fn main() { } fn remove_directory_contents(ledger_path: &Path) -> Result<(), io::Error> { - for entry in fs::read_dir(&ledger_path)? { + for entry in fs::read_dir(ledger_path)? { let entry = entry?; if entry.metadata()?.is_dir() { fs::remove_dir_all(&entry.path())? diff --git a/validator/src/bootstrap.rs b/validator/src/bootstrap.rs index 9998b986c92360..9868346e520f18 100644 --- a/validator/src/bootstrap.rs +++ b/validator/src/bootstrap.rs @@ -7,9 +7,9 @@ use { solana_genesis_utils::download_then_check_genesis_hash, solana_gossip::{ cluster_info::{ClusterInfo, Node}, - contact_info::ContactInfo, crds_value, gossip_service::GossipService, + legacy_contact_info::LegacyContactInfo as ContactInfo, }, solana_runtime::{ snapshot_archive_info::SnapshotArchiveInfoGetter, @@ -41,6 +41,10 @@ use { }, }; +/// When downloading snapshots, wait at most this long for snapshot hashes from _all_ known +/// validators. Afterwards, wait for snapshot hashes from _any_ know validator. +const WAIT_FOR_ALL_KNOWN_VALIDATORS: Duration = Duration::from_secs(60); + #[derive(Debug)] pub struct RpcBootstrapConfig { pub no_genesis_fetch: bool, @@ -570,10 +574,20 @@ fn get_rpc_node( } } + let known_validators_to_wait_for = if newer_cluster_snapshot_timeout + .as_ref() + .map(|timer: &Instant| timer.elapsed() < WAIT_FOR_ALL_KNOWN_VALIDATORS) + .unwrap_or(true) + { + KnownValidatorsToWaitFor::All + } else { + KnownValidatorsToWaitFor::Any + }; let peer_snapshot_hashes = get_peer_snapshot_hashes( cluster_info, &rpc_peers, validator_config.known_validators.as_ref(), + known_validators_to_wait_for, bootstrap_config.incremental_snapshot_fetch, ); @@ -655,14 +669,16 @@ fn get_peer_snapshot_hashes( cluster_info: &ClusterInfo, rpc_peers: &[ContactInfo], known_validators: Option<&HashSet>, + known_validators_to_wait_for: KnownValidatorsToWaitFor, incremental_snapshot_fetch: bool, ) -> Vec { let mut peer_snapshot_hashes = get_eligible_peer_snapshot_hashes(cluster_info, rpc_peers, incremental_snapshot_fetch); - if known_validators.is_some() { + if let Some(known_validators) = known_validators { let known_snapshot_hashes = get_snapshot_hashes_from_known_validators( cluster_info, known_validators, + known_validators_to_wait_for, incremental_snapshot_fetch, ); retain_peer_snapshot_hashes_that_match_known_snapshot_hashes( @@ -692,7 +708,8 @@ type KnownSnapshotHashes = HashMap<(Slot, Hash), HashSet<(Slot, Hash)>>; /// This applies to both full and incremental snapshot hashes. fn get_snapshot_hashes_from_known_validators( cluster_info: &ClusterInfo, - known_validators: Option<&HashSet>, + known_validators: &HashSet, + known_validators_to_wait_for: KnownValidatorsToWaitFor, incremental_snapshot_fetch: bool, ) -> KnownSnapshotHashes { // Get the full snapshot hashes for a node from CRDS @@ -711,19 +728,82 @@ fn get_snapshot_hashes_from_known_validators( .map(|hashes| (hashes.base, hashes.hashes)) }; - known_validators - .map(|known_validators| { - build_known_snapshot_hashes( - known_validators, - get_full_snapshot_hashes_for_node, - get_incremental_snapshot_hashes_for_node, - incremental_snapshot_fetch, - ) - }) - .unwrap_or_else(|| { - trace!("No known validators, so no known snapshot hashes"); - KnownSnapshotHashes::new() - }) + if !do_known_validators_have_all_snapshot_hashes( + known_validators, + known_validators_to_wait_for, + get_full_snapshot_hashes_for_node, + get_incremental_snapshot_hashes_for_node, + incremental_snapshot_fetch, + ) { + debug!( + "Snapshot hashes have not been discovered from known validators. \ + This likely means the gossip tables are not fully populated. \ + We will sleep and retry..." + ); + return KnownSnapshotHashes::default(); + } + + build_known_snapshot_hashes( + known_validators, + get_full_snapshot_hashes_for_node, + get_incremental_snapshot_hashes_for_node, + incremental_snapshot_fetch, + ) +} + +/// Check if we can discover snapshot hashes for the known validators. +/// +/// This is a work-around to ensure the gossip tables are populated enough so that the bootstrap +/// process will download both full and incremental snapshots. If the incremental snapshot hashes +/// are not yet populated from gossip, then it is possible (and has been seen often) to only +/// discover full snapshots—and ones that are very old (up to 25,000 slots)—but *not* discover any +/// of their associated incremental snapshots. +/// +/// This function will return false if we do not yet have snapshot hashes from known validators; +/// and true otherwise. Either require snapshot hashes from *all* or *any* of the known validators +/// based on the `KnownValidatorsToWaitFor` parameter. +fn do_known_validators_have_all_snapshot_hashes<'a, F1, F2>( + known_validators: impl IntoIterator, + known_validators_to_wait_for: KnownValidatorsToWaitFor, + get_full_snapshot_hashes_for_node: F1, + get_incremental_snapshot_hashes_for_node: F2, + incremental_snapshot_fetch: bool, +) -> bool +where + F1: Fn(&'a Pubkey) -> Vec<(Slot, Hash)>, + F2: Fn(&'a Pubkey) -> Option<((Slot, Hash), Vec<(Slot, Hash)>)>, +{ + let node_has_full_snapshot_hashes = |node| !get_full_snapshot_hashes_for_node(node).is_empty(); + let node_has_incremental_snapshot_hashes = |node| { + get_incremental_snapshot_hashes_for_node(node) + .map(|(_, hashes)| !hashes.is_empty()) + .unwrap_or(false) + }; + + // Does this node have all the snapshot hashes? + // If incremental snapshots are disabled, only check for full snapshot hashes; otherwise check + // for both full and incremental snapshot hashes. + let node_has_all_snapshot_hashes = |node| { + node_has_full_snapshot_hashes(node) + && (!incremental_snapshot_fetch || node_has_incremental_snapshot_hashes(node)) + }; + + match known_validators_to_wait_for { + KnownValidatorsToWaitFor::All => known_validators + .into_iter() + .all(node_has_all_snapshot_hashes), + KnownValidatorsToWaitFor::Any => known_validators + .into_iter() + .any(node_has_all_snapshot_hashes), + } +} + +/// When waiting for snapshot hashes from the known validators, should we wait for *all* or *any* +/// of them? +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum KnownValidatorsToWaitFor { + All, + Any, } /// Build the known snapshot hashes from a set of nodes. diff --git a/validator/src/lib.rs b/validator/src/lib.rs index ba1efd3f44ca14..a093e48602ab31 100644 --- a/validator/src/lib.rs +++ b/validator/src/lib.rs @@ -57,7 +57,7 @@ pub fn redirect_stderr_to_file(logfile: Option) -> Option { use log::info; let mut signals = - signal_hook::iterator::Signals::new(&[signal_hook::consts::SIGUSR1]) + signal_hook::iterator::Signals::new([signal_hook::consts::SIGUSR1]) .unwrap_or_else(|err| { eprintln!("Unable to register SIGUSR1 handler: {:?}", err); exit(1); @@ -65,15 +65,20 @@ pub fn redirect_stderr_to_file(logfile: Option) -> Option solana_logger::setup_with_default(filter); redirect_stderr(&logfile); - Some(std::thread::spawn(move || { - for signal in signals.forever() { - info!( - "received SIGUSR1 ({}), reopening log file: {:?}", - signal, logfile - ); - redirect_stderr(&logfile); - } - })) + Some( + std::thread::Builder::new() + .name("solSigUsr1".into()) + .spawn(move || { + for signal in signals.forever() { + info!( + "received SIGUSR1 ({}), reopening log file: {:?}", + signal, logfile + ); + redirect_stderr(&logfile); + } + }) + .unwrap(), + ) } #[cfg(not(unix))] { diff --git a/validator/src/main.rs b/validator/src/main.rs index ce8c12c06fe3e7..937c69de2121df 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -19,8 +19,10 @@ use { keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, }, solana_client::{ - connection_cache::DEFAULT_TPU_CONNECTION_POOL_SIZE, rpc_client::RpcClient, - rpc_config::RpcLeaderScheduleConfig, rpc_request::MAX_MULTIPLE_ACCOUNTS, + connection_cache::{DEFAULT_TPU_CONNECTION_POOL_SIZE, DEFAULT_TPU_ENABLE_UDP}, + rpc_client::RpcClient, + rpc_config::RpcLeaderScheduleConfig, + rpc_request::MAX_MULTIPLE_ACCOUNTS, }, solana_core::{ ledger_cleanup_service::{DEFAULT_MAX_LEDGER_SHREDS, DEFAULT_MIN_MAX_LEDGER_SHREDS}, @@ -29,7 +31,7 @@ use { tpu::DEFAULT_TPU_COALESCE_MS, validator::{is_snapshot_config_valid, Validator, ValidatorConfig, ValidatorStartProgress}, }, - solana_gossip::{cluster_info::Node, contact_info::ContactInfo}, + solana_gossip::{cluster_info::Node, legacy_contact_info::LegacyContactInfo as ContactInfo}, solana_ledger::blockstore_options::{ BlockstoreCompressionType, BlockstoreRecoveryMode, LedgerColumnOptions, ShredStorageType, DEFAULT_ROCKS_FIFO_SHRED_STORAGE_SIZE_BYTES, @@ -164,8 +166,16 @@ fn wait_for_restart_window( let progress_bar = new_spinner_progress_bar(); let monitor_start_time = SystemTime::now(); + + let mut seen_incremential_snapshot = false; loop { let snapshot_slot_info = rpc_client.get_highest_snapshot_slot().ok(); + let snapshot_slot_info_has_incremential = snapshot_slot_info + .as_ref() + .map(|snapshot_slot_info| snapshot_slot_info.incremental.is_some()) + .unwrap_or_default(); + seen_incremential_snapshot |= snapshot_slot_info_has_incremential; + let epoch_info = rpc_client.get_epoch_info_with_commitment(CommitmentConfig::processed())?; let healthy = rpc_client.get_health().ok().is_some(); let delinquent_stake_percentage = { @@ -294,16 +304,16 @@ fn wait_for_restart_window( } }; - let snapshot_slot = snapshot_slot_info.map(|snapshot_slot_info| { - snapshot_slot_info - .incremental - .unwrap_or(snapshot_slot_info.full) - }); match in_leader_schedule_hole { Ok(_) => { if skip_new_snapshot_check { break; // Restart! } + let snapshot_slot = snapshot_slot_info.map(|snapshot_slot_info| { + snapshot_slot_info + .incremental + .unwrap_or(snapshot_slot_info.full) + }); if restart_snapshot == None { restart_snapshot = snapshot_slot; } @@ -313,6 +323,16 @@ fn wait_for_restart_window( >= (max_delinquency_percentage as f64 / 100.) { style("Delinquency too high").red().to_string() + } else if seen_incremential_snapshot && !snapshot_slot_info_has_incremential + { + // Restarts using just a full snapshot will put the node significantly + // further behind than if an incremental snapshot is also used, as full + // snapshots are larger and take much longer to create. + // + // Therefore if the node just created a new full snapshot, wait a + // little longer until it creates the first incremental snapshot for + // the full snapshot. + "Waiting for incremental snapshot".to_string() } else { break; // Restart! } @@ -606,34 +626,35 @@ pub fn main() { ) .arg( Arg::with_name("minimal_rpc_api") - .long("--minimal-rpc-api") + .long("minimal-rpc-api") .takes_value(false) .hidden(true) .help("Only expose the RPC methods required to serve snapshots to other nodes"), ) .arg( Arg::with_name("full_rpc_api") - .long("--full-rpc-api") + .long("full-rpc-api") .conflicts_with("minimal_rpc_api") .takes_value(false) .help("Expose RPC methods for querying chain state and transaction history"), ) .arg( Arg::with_name("obsolete_v1_7_rpc_api") - .long("--enable-rpc-obsolete_v1_7") + .long("enable-rpc-obsolete_v1_7") .takes_value(false) .help("Enable the obsolete RPC methods removed in v1.7"), ) .arg( Arg::with_name("private_rpc") - .long("--private-rpc") + .long("private-rpc") .takes_value(false) .help("Do not publish the RPC port for use by others") ) .arg( Arg::with_name("no_port_check") - .long("--no-port-check") + .long("no-port-check") .takes_value(false) + .hidden(true) .help("Do not perform TCP/UDP reachable port checks at start-up") ) .arg( @@ -948,32 +969,37 @@ pub fn main() { .arg( Arg::with_name("no_poh_speed_test") .long("no-poh-speed-test") + .hidden(true) .help("Skip the check for PoH speed."), ) .arg( Arg::with_name("no_os_network_limits_test") - .hidden(true) .long("no-os-network-limits-test") + .hidden(true) .help("Skip checks for OS network limits.") ) .arg( Arg::with_name("no_os_memory_stats_reporting") .long("no-os-memory-stats-reporting") + .hidden(true) .help("Disable reporting of OS memory statistics.") ) .arg( Arg::with_name("no_os_network_stats_reporting") .long("no-os-network-stats-reporting") + .hidden(true) .help("Disable reporting of OS network statistics.") ) .arg( Arg::with_name("no_os_cpu_stats_reporting") .long("no-os-cpu-stats-reporting") + .hidden(true) .help("Disable reporting of OS CPU statistics.") ) .arg( Arg::with_name("no_os_disk_stats_reporting") .long("no-os-disk-stats-reporting") + .hidden(true) .help("Disable reporting of OS disk statistics.") ) .arg( @@ -1213,13 +1239,27 @@ pub fn main() { Arg::with_name("tpu_use_quic") .long("tpu-use-quic") .takes_value(false) + .hidden(true) + .conflicts_with("tpu_disable_quic") .help("Use QUIC to send transactions."), ) + .arg( + Arg::with_name("tpu_disable_quic") + .long("tpu-disable-quic") + .takes_value(false) + .help("Do not use QUIC to send transactions."), + ) + .arg( + Arg::with_name("tpu_enable_udp") + .long("tpu-enable-udp") + .takes_value(false) + .help("Enable UDP for receiving/sending transactions."), + ) .arg( Arg::with_name("disable_quic_servers") .long("disable-quic-servers") .takes_value(false) - .help("Disable QUIC TPU servers"), + .hidden(true) ) .arg( Arg::with_name("enable_quic_servers") @@ -1591,6 +1631,12 @@ pub fn main() { .value_name("NUM") .help("Specify hashes per batch in PoH service"), ) + .arg( + Arg::with_name("process_ledger_before_services") + .long("process-ledger-before-services") + .hidden(true) + .help("Process the local ledger fully before starting networking services") + ) .arg( Arg::with_name("account_indexes") .long("account-index") @@ -1619,16 +1665,11 @@ pub fn main() { .value_name("KEY") .help("When account indexes are enabled, only include specific keys in the index. This overrides --account-index-exclude-key."), ) - .arg( - Arg::with_name("no_accounts_db_caching") - .long("no-accounts-db-caching") - .help("Disables accounts caching"), - ) .arg( Arg::with_name("accounts_db_skip_shrink") .long("accounts-db-skip-shrink") - .help("Enables faster starting of validators by skipping shrink. \ - This option is for use during testing."), + .help("This is obsolete since it is now enabled by default. Enables faster starting of validators by skipping startup clean and shrink.") + .hidden(true), ) .arg( Arg::with_name("accounts_db_skip_rewrites") @@ -1665,13 +1706,20 @@ pub fn main() { .value_name("MEGABYTES") .validator(is_parsable::) .takes_value(true) + .requires("enable_accounts_disk_index") .help("How much memory the accounts index can consume. If this is exceeded, some account index entries will be stored on disk."), ) .arg( Arg::with_name("disable_accounts_disk_index") .long("disable-accounts-disk-index") .help("Disable the disk-based accounts index if it is enabled by default.") - .conflicts_with("accounts_index_memory_limit_mb") + .conflicts_with("enable_accounts_disk_index") + ) + .arg( + Arg::with_name("enable_accounts_disk_index") + .long("enable-accounts-disk-index") + .conflicts_with("disable_accounts_disk_index") + .help("Enable the disk-based accounts index if it is disabled by default.") ) .arg( Arg::with_name("accounts_index_bins") @@ -1739,7 +1787,6 @@ pub fn main() { // legacy nop argument Arg::with_name("accounts_db_caching_enabled") .long("accounts-db-caching-enabled") - .conflicts_with("no_accounts_db_caching") .hidden(true) ) .arg( @@ -1765,13 +1812,6 @@ pub fn main() { it becomes a candidate for shrinking. The value must between 0. and 1.0 \ inclusive."), ) - .arg( - Arg::with_name("no_duplicate_instance_check") - .long("no-duplicate-instance-check") - .takes_value(false) - .help("Disables duplicate instance check") - .hidden(true), - ) .arg( Arg::with_name("allow_private_addr") .long("allow-private-addr") @@ -2314,8 +2354,13 @@ pub fn main() { let restricted_repair_only_mode = matches.is_present("restricted_repair_only_mode"); let accounts_shrink_optimize_total_space = value_t_or_exit!(matches, "accounts_shrink_optimize_total_space", bool); - let tpu_use_quic = matches.is_present("tpu_use_quic"); - let enable_quic_servers = !matches.is_present("disable_quic_servers"); + let tpu_use_quic = !matches.is_present("tpu_disable_quic"); + let tpu_enable_udp = if matches.is_present("tpu_enable_udp") { + true + } else { + DEFAULT_TPU_ENABLE_UDP + }; + let tpu_connection_pool_size = value_t_or_exit!(matches, "tpu_connection_pool_size", usize); let shrink_ratio = value_t_or_exit!(matches, "accounts_shrink_ratio", f64); @@ -2410,10 +2455,13 @@ pub fn main() { accounts_index_config.index_limit_mb = if let Some(limit) = value_t!(matches, "accounts_index_memory_limit_mb", usize).ok() { IndexLimitMb::Limit(limit) - } else if matches.is_present("disable_accounts_disk_index") { - IndexLimitMb::InMemOnly - } else { + } else if matches.is_present("enable_accounts_disk_index") { IndexLimitMb::Unspecified + } else { + if matches.is_present("disable_accounts_disk_index") { + warn!("ignoring `--disable-accounts-disk-index` as it specifies default behavior"); + } + IndexLimitMb::InMemOnly }; { @@ -2485,6 +2533,9 @@ pub fn main() { if matches.is_present("enable_quic_servers") { warn!("--enable-quic-servers is now the default behavior. This flag is deprecated and can be removed from the launch args"); } + if matches.is_present("disable_quic_servers") { + warn!("--disable-quic-servers is deprecated. The quic server cannot be disabled."); + } let rpc_bigtable_config = if matches.is_present("enable_rpc_bigtable_ledger_storage") || matches.is_present("enable_bigtable_ledger_upload") @@ -2537,6 +2588,10 @@ pub fn main() { } let full_api = matches.is_present("full_rpc_api"); + if matches.is_present("accounts_db_skip_shrink") { + warn!("`--accounts-db-skip-shrink` is deprecated. please consider removing it from the validator command line argument list"); + } + let mut validator_config = ValidatorConfig { require_tower: matches.is_present("require_tower"), tower_storage, @@ -2651,11 +2706,12 @@ pub fn main() { .unwrap_or(poh_service::DEFAULT_PINNED_CPU_CORE), poh_hashes_per_batch: value_of(&matches, "poh_hashes_per_batch") .unwrap_or(poh_service::DEFAULT_HASHES_PER_BATCH), + process_ledger_before_services: matches.is_present("process_ledger_before_services"), account_indexes, - accounts_db_caching_enabled: !matches.is_present("no_accounts_db_caching"), + accounts_db_caching_enabled: true, accounts_db_test_hash_calculation: matches.is_present("accounts_db_test_hash_calculation"), accounts_db_config, - accounts_db_skip_shrink: matches.is_present("accounts_db_skip_shrink"), + accounts_db_skip_shrink: true, tpu_coalesce_ms, no_wait_for_vote_to_start_leader: matches.is_present("no_wait_for_vote_to_start_leader"), accounts_shrink_ratio, @@ -2664,7 +2720,6 @@ pub fn main() { log_messages_bytes_limit: value_of(&matches, "log_messages_bytes_limit"), ..RuntimeConfig::default() }, - enable_quic_servers, ..ValidatorConfig::default() }; @@ -2915,7 +2970,7 @@ pub fn main() { if SystemMonitorService::check_os_network_limits() { info!("OS network limits test passed."); } else { - eprintln!("OS network limit test failed. solana-sys-tuner may be used to configure OS network limits. Bypass check with --no-os-network-limits-test."); + eprintln!("OS network limit test failed. See: https://docs.solana.com/running-validator/validator-start#system-tuning"); exit(1); } } @@ -3046,7 +3101,7 @@ pub fn main() { let identity_keypair = Arc::new(identity_keypair); - let should_check_duplicate_instance = !matches.is_present("no_duplicate_instance_check"); + let should_check_duplicate_instance = true; if !cluster_entrypoints.is_empty() { bootstrap::rpc_bootstrap( &node, @@ -3089,13 +3144,9 @@ pub fn main() { socket_addr_space, tpu_use_quic, tpu_connection_pool_size, + tpu_enable_udp, + admin_service_post_init, ); - *admin_service_post_init.write().unwrap() = - Some(admin_rpc_service::AdminRpcRequestMetadataPostInit { - bank_forks: validator.bank_forks.clone(), - cluster_info: validator.cluster_info.clone(), - vote_account, - }); if let Some(filename) = init_complete_file { File::create(filename).unwrap_or_else(|_| { diff --git a/version/Cargo.toml b/version/Cargo.toml index e7667f4df1815e..5877d413589931 100644 --- a/version/Cargo.toml +++ b/version/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "solana-version" -version = "1.11.6" +version = "1.14.24" description = "Solana Version" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" @@ -14,9 +14,9 @@ log = "0.4.17" semver = "1.0.10" serde = "1.0.138" serde_derive = "1.0.103" -solana-frozen-abi = { path = "../frozen-abi", version = "=1.11.6" } -solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-frozen-abi = { path = "../frozen-abi", version = "=1.14.24" } +solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } [lib] name = "solana_version" diff --git a/watchtower/Cargo.toml b/watchtower/Cargo.toml index fd783a30c0a9d6..9a3add66991db8 100644 --- a/watchtower/Cargo.toml +++ b/watchtower/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Solana Maintainers "] edition = "2021" name = "solana-watchtower" description = "Blockchain, Rebuilt for Scale" -version = "1.11.6" +version = "1.14.24" repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" @@ -13,15 +13,15 @@ documentation = "https://docs.rs/solana-watchtower" clap = "2.33.1" humantime = "2.0.1" log = "0.4.17" -solana-clap-utils = { path = "../clap-utils", version = "=1.11.6" } -solana-cli-config = { path = "../cli-config", version = "=1.11.6" } -solana-cli-output = { path = "../cli-output", version = "=1.11.6" } -solana-client = { path = "../client", version = "=1.11.6" } -solana-logger = { path = "../logger", version = "=1.11.6" } -solana-metrics = { path = "../metrics", version = "=1.11.6" } -solana-notifier = { path = "../notifier", version = "=1.11.6" } -solana-sdk = { path = "../sdk", version = "=1.11.6" } -solana-version = { path = "../version", version = "=1.11.6" } +solana-clap-utils = { path = "../clap-utils", version = "=1.14.24" } +solana-cli-config = { path = "../cli-config", version = "=1.14.24" } +solana-cli-output = { path = "../cli-output", version = "=1.14.24" } +solana-client = { path = "../client", version = "=1.14.24" } +solana-logger = { path = "../logger", version = "=1.14.24" } +solana-metrics = { path = "../metrics", version = "=1.14.24" } +solana-notifier = { path = "../notifier", version = "=1.14.24" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } +solana-version = { path = "../version", version = "=1.14.24" } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/watchtower/src/main.rs b/watchtower/src/main.rs index 15504001e57486..bf3d0a807bed79 100644 --- a/watchtower/src/main.rs +++ b/watchtower/src/main.rs @@ -30,10 +30,12 @@ struct Config { ignore_http_bad_gateway: bool, interval: Duration, json_rpc_url: String, + rpc_timeout: Duration, minimum_validator_identity_balance: u64, monitor_active_stake: bool, unhealthy_threshold: usize, validator_identity_pubkeys: Vec, + name_suffix: String, } fn get_config() -> Config { @@ -79,6 +81,14 @@ fn get_config() -> Config { .validator(is_url) .help("JSON RPC URL for the cluster"), ) + .arg( + Arg::with_name("rpc_timeout") + .long("rpc-timeout") + .value_name("SECONDS") + .takes_value(true) + .default_value("30") + .help("Timeout value for RPC requests"), + ) .arg( Arg::with_name("interval") .long("interval") @@ -134,6 +144,14 @@ fn get_config() -> Config { no alerting should a Bad Gateway error be a side effect of \ the real problem") ) + .arg( + Arg::with_name("name_suffix") + .long("name-suffix") + .value_name("SUFFIX") + .takes_value(true) + .default_value("") + .help("Add this string into all notification messages after \"solana-watchtower\"") + ) .get_matches(); let config = if let Some(config_file) = matches.value_of("config_file") { @@ -151,6 +169,8 @@ fn get_config() -> Config { )); let json_rpc_url = value_t!(matches, "json_rpc_url", String).unwrap_or_else(|_| config.json_rpc_url.clone()); + let rpc_timeout = value_t_or_exit!(matches, "rpc_timeout", u64); + let rpc_timeout = Duration::from_secs(rpc_timeout); let validator_identity_pubkeys: Vec<_> = pubkeys_of(&matches, "validator_identities") .unwrap_or_default() .into_iter() @@ -159,15 +179,19 @@ fn get_config() -> Config { let monitor_active_stake = matches.is_present("monitor_active_stake"); let ignore_http_bad_gateway = matches.is_present("ignore_http_bad_gateway"); + let name_suffix = value_t_or_exit!(matches, "name_suffix", String); + let config = Config { address_labels: config.address_labels, ignore_http_bad_gateway, interval, json_rpc_url, + rpc_timeout, minimum_validator_identity_balance, monitor_active_stake, unhealthy_threshold, validator_identity_pubkeys, + name_suffix, }; info!("RPC URL: {}", config.json_rpc_url); @@ -208,7 +232,7 @@ fn main() -> Result<(), Box> { let config = get_config(); - let rpc_client = RpcClient::new(config.json_rpc_url.clone()); + let rpc_client = RpcClient::new_with_timeout(config.json_rpc_url.clone(), config.rpc_timeout); let notifier = Notifier::default(); let mut last_transaction_count = 0; let mut last_recent_blockhash = Hash::default(); @@ -337,8 +361,8 @@ fn main() -> Result<(), Box> { if let Some((failure_test_name, failure_error_message)) = &failure { let notification_msg = format!( - "solana-watchtower: Error: {}: {}", - failure_test_name, failure_error_message + "solana-watchtower{}: Error: {}: {}", + config.name_suffix, failure_test_name, failure_error_message ); num_consecutive_failures += 1; if num_consecutive_failures > config.unhealthy_threshold { @@ -370,7 +394,10 @@ fn main() -> Result<(), Box> { humantime::format_duration(alarm_duration) ); info!("{}", all_clear_msg); - notifier.send(&format!("solana-watchtower: {}", all_clear_msg)); + notifier.send(&format!( + "solana-watchtower{}: {}", + config.name_suffix, all_clear_msg + )); } last_notification_msg = "".into(); last_success = Instant::now(); diff --git a/zk-token-sdk/Cargo.toml b/zk-token-sdk/Cargo.toml index f61e50b2f2dab3..7345f6d7d889f2 100644 --- a/zk-token-sdk/Cargo.toml +++ b/zk-token-sdk/Cargo.toml @@ -3,7 +3,7 @@ name = "solana-zk-token-sdk" description = "Solana Zk Token SDK" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" -version = "1.11.6" +version = "1.14.24" license = "Apache-2.0" edition = "2021" @@ -12,7 +12,7 @@ base64 = "0.13" bytemuck = { version = "1.11.0", features = ["derive"] } num-derive = "0.3" num-traits = "0.2" -solana-program = { path = "../sdk/program", version = "=1.11.6" } +solana-program = { path = "../sdk/program", version = "=1.14.24" } [target.'cfg(not(target_os = "solana"))'.dependencies] aes-gcm-siv = "0.10.3" @@ -22,13 +22,14 @@ byteorder = "1" cipher = "0.4" curve25519-dalek = { version = "3.2.1", features = ["serde"] } getrandom = { version = "0.1", features = ["dummy"] } +itertools = "0.10.3" lazy_static = "1.4.0" merlin = "3" rand = "0.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha3 = "0.9" -solana-sdk = { path = "../sdk", version = "=1.11.6" } +solana-sdk = { path = "../sdk", version = "=1.14.24" } subtle = "2" thiserror = "1.0" zeroize = { version = "1.3", default-features = false, features = ["zeroize_derive"] } diff --git a/zk-token-sdk/src/curve25519/edwards.rs b/zk-token-sdk/src/curve25519/edwards.rs index 8cd7a4abd4c9e7..0dd019b1910d0a 100644 --- a/zk-token-sdk/src/curve25519/edwards.rs +++ b/zk-token-sdk/src/curve25519/edwards.rs @@ -101,7 +101,7 @@ mod target_arch { #[cfg(not(target_os = "solana"))] fn multiply(scalar: &PodScalar, point: &Self) -> Option { - let scalar: Scalar = scalar.into(); + let scalar: Scalar = scalar.try_into().ok()?; let point: EdwardsPoint = point.try_into().ok()?; let result = &scalar * &point; @@ -114,8 +114,13 @@ mod target_arch { type Point = Self; fn multiscalar_multiply(scalars: &[PodScalar], points: &[Self]) -> Option { + let scalars = scalars + .iter() + .map(|scalar| Scalar::try_from(scalar).ok()) + .collect::>>()?; + EdwardsPoint::optional_multiscalar_mul( - scalars.iter().map(Scalar::from), + scalars, points .iter() .map(|point| EdwardsPoint::try_from(point).ok()), @@ -212,6 +217,28 @@ mod target_arch { None } } + + pub fn multiscalar_multiply_edwards( + scalars: &[PodScalar], + points: &[PodEdwardsPoint], + ) -> Option { + let mut result_point = PodEdwardsPoint::zeroed(); + let result = unsafe { + solana_program::syscalls::sol_curve_multiscalar_mul( + CURVE25519_EDWARDS, + scalars.as_ptr() as *const u8, + points.as_ptr() as *const u8, + points.len() as u64, + &mut result_point.0 as *mut u8, + ) + }; + + if result == 0 { + Some(result_point) + } else { + None + } + } } #[cfg(test)] diff --git a/zk-token-sdk/src/curve25519/ristretto.rs b/zk-token-sdk/src/curve25519/ristretto.rs index 612f9309bfbe6c..772441a32aa65f 100644 --- a/zk-token-sdk/src/curve25519/ristretto.rs +++ b/zk-token-sdk/src/curve25519/ristretto.rs @@ -101,7 +101,7 @@ mod target_arch { #[cfg(not(target_os = "solana"))] fn multiply(scalar: &PodScalar, point: &Self) -> Option { - let scalar: Scalar = scalar.into(); + let scalar: Scalar = scalar.try_into().ok()?; let point: RistrettoPoint = point.try_into().ok()?; let result = &scalar * &point; @@ -114,8 +114,13 @@ mod target_arch { type Point = Self; fn multiscalar_multiply(scalars: &[PodScalar], points: &[Self]) -> Option { + let scalars = scalars + .iter() + .map(|scalar| Scalar::try_from(scalar).ok()) + .collect::>>()?; + RistrettoPoint::optional_multiscalar_mul( - scalars.iter().map(Scalar::from), + scalars, points .iter() .map(|point| RistrettoPoint::try_from(point).ok()), @@ -214,6 +219,28 @@ mod target_arch { None } } + + pub fn multiscalar_multiply_ristretto( + scalars: &[PodScalar], + points: &[PodRistrettoPoint], + ) -> Option { + let mut result_point = PodRistrettoPoint::zeroed(); + let result = unsafe { + solana_program::syscalls::sol_curve_multiscalar_mul( + CURVE25519_RISTRETTO, + scalars.as_ptr() as *const u8, + points.as_ptr() as *const u8, + points.len() as u64, + &mut result_point.0 as *mut u8, + ) + }; + + if result == 0 { + Some(result_point) + } else { + None + } + } } #[cfg(test)] diff --git a/zk-token-sdk/src/curve25519/scalar.rs b/zk-token-sdk/src/curve25519/scalar.rs index 103419863a9c2f..e154851902a043 100644 --- a/zk-token-sdk/src/curve25519/scalar.rs +++ b/zk-token-sdk/src/curve25519/scalar.rs @@ -6,7 +6,7 @@ pub struct PodScalar(pub [u8; 32]); #[cfg(not(target_os = "solana"))] mod target_arch { - use {super::*, curve25519_dalek::scalar::Scalar}; + use {super::*, crate::curve25519::errors::Curve25519Error, curve25519_dalek::scalar::Scalar}; impl From<&Scalar> for PodScalar { fn from(scalar: &Scalar) -> Self { @@ -14,9 +14,11 @@ mod target_arch { } } - impl From<&PodScalar> for Scalar { - fn from(pod: &PodScalar) -> Self { - Scalar::from_bits(pod.0) + impl TryFrom<&PodScalar> for Scalar { + type Error = Curve25519Error; + + fn try_from(pod: &PodScalar) -> Result { + Scalar::from_canonical_bytes(pod.0).ok_or(Curve25519Error::PodConversion) } } } diff --git a/zk-token-sdk/src/encryption/auth_encryption.rs b/zk-token-sdk/src/encryption/auth_encryption.rs index 02c1478a4c3f8c..50a72230453c64 100644 --- a/zk-token-sdk/src/encryption/auth_encryption.rs +++ b/zk-token-sdk/src/encryption/auth_encryption.rs @@ -1,3 +1,6 @@ +//! Authenticated encryption implementation. +//! +//! This module is a simple wrapper of the `Aes128GcmSiv` implementation. #[cfg(not(target_os = "solana"))] use { aes_gcm_siv::{ @@ -16,6 +19,7 @@ use { signer::{Signer, SignerError}, }, std::{convert::TryInto, fmt}, + subtle::ConstantTimeEq, zeroize::Zeroize, }; @@ -71,7 +75,7 @@ impl AeKey { // Some `Signer` implementations return the default signature, which is not suitable for // use as key material - if signature == Signature::default() { + if bool::from(signature.as_ref().ct_eq(Signature::default().as_ref())) { Err(SignerError::Custom("Rejecting default signature".into())) } else { Ok(AeKey(signature.as_ref()[..16].try_into().unwrap())) @@ -91,8 +95,8 @@ impl AeKey { } } -/// For the purpose of encrypting balances for ZK-Token accounts, the nonce and ciphertext sizes -/// should always be fixed. +/// For the purpose of encrypting balances for the spl token accounts, the nonce and ciphertext +/// sizes should always be fixed. pub type Nonce = [u8; 12]; pub type Ciphertext = [u8; 24]; diff --git a/zk-token-sdk/src/encryption/decode_u32_precomputation_for_G.bincode b/zk-token-sdk/src/encryption/decode_u32_precomputation_for_G.bincode index 7be1353b3e5ff6..f8aaaa937e8190 100644 Binary files a/zk-token-sdk/src/encryption/decode_u32_precomputation_for_G.bincode and b/zk-token-sdk/src/encryption/decode_u32_precomputation_for_G.bincode differ diff --git a/zk-token-sdk/src/encryption/discrete_log.rs b/zk-token-sdk/src/encryption/discrete_log.rs index 03beda9efdb202..daf2edc379327f 100644 --- a/zk-token-sdk/src/encryption/discrete_log.rs +++ b/zk-token-sdk/src/encryption/discrete_log.rs @@ -1,16 +1,36 @@ +//! The discrete log implementation for the twisted ElGamal decryption. +//! +//! The implementation uses the baby-step giant-step method, which consists of a precomputation +//! step and an online step. The precomputation step involves computing a hash table of a number +//! of Ristretto points that is independent of a discrete log instance. The online phase computes +//! the final discrete log solution using the discrete log instance and the pre-computed hash +//! table. More details on the baby-step giant-step algorithm and the implementation can be found +//! in the [spl documentation](https://spl.solana.com). +//! +//! The implementation is NOT intended to run in constant-time. There are some measures to prevent +//! straightforward timing attacks. For instance, it does not short-circuit the search when a +//! solution is found. However, the use of hashtables, batching, and threads make the +//! implementation inherently not constant-time. This may theoretically allow an adversary to gain +//! information on a discrete log solution depending on the execution time of the implementation. +//! + #![cfg(not(target_os = "solana"))] use { - crate::errors::ProofError, + crate::encryption::errors::DiscreteLogError, curve25519_dalek::{ - constants::RISTRETTO_BASEPOINT_POINT as G, ristretto::RistrettoPoint, scalar::Scalar, - traits::Identity, + constants::RISTRETTO_BASEPOINT_POINT as G, + ristretto::RistrettoPoint, + scalar::Scalar, + traits::{Identity, IsIdentity}, }, + itertools::Itertools, serde::{Deserialize, Serialize}, std::{collections::HashMap, thread}, }; const TWO16: u64 = 65536; // 2^16 +const TWO17: u64 = 131072; // 2^17 /// Type that captures a discrete log challenge. /// @@ -28,6 +48,8 @@ pub struct DiscreteLog { range_bound: usize, /// Ristretto point representing each step of the discrete log search step_point: RistrettoPoint, + /// Ristretto point compression batch size + compression_batch_size: usize, } #[derive(Serialize, Deserialize, Default)] @@ -38,11 +60,11 @@ pub struct DecodePrecomputation(HashMap<[u8; 32], u16>); fn decode_u32_precomputation(generator: RistrettoPoint) -> DecodePrecomputation { let mut hashmap = HashMap::new(); - let two16_scalar = Scalar::from(TWO16); + let two17_scalar = Scalar::from(TWO17); let identity = RistrettoPoint::identity(); // 0 * G - let generator = two16_scalar * generator; // 2^16 * G + let generator = two17_scalar * generator; // 2^17 * G - // iterator for 2^12*0G , 2^12*1G, 2^12*2G, ... + // iterator for 2^17*0G , 2^17*1G, 2^17*2G, ... let ristretto_iter = RistrettoIterator::new((identity, 0), (generator, 1)); for (point, x_hi) in ristretto_iter.take(TWO16 as usize) { let key = point.compress().to_bytes(); @@ -73,14 +95,15 @@ impl DiscreteLog { num_threads: 1, range_bound: TWO16 as usize, step_point: G, + compression_batch_size: 32, } } /// Adjusts number of threads in a discrete log instance. - pub fn num_threads(&mut self, num_threads: usize) -> Result<(), ProofError> { + pub fn num_threads(&mut self, num_threads: usize) -> Result<(), DiscreteLogError> { // number of threads must be a positive power-of-two integer - if num_threads == 0 || (num_threads & (num_threads - 1)) != 0 { - return Err(ProofError::DiscreteLogThreads); + if num_threads == 0 || (num_threads & (num_threads - 1)) != 0 || num_threads > 65536 { + return Err(DiscreteLogError::DiscreteLogThreads); } self.num_threads = num_threads; @@ -90,8 +113,21 @@ impl DiscreteLog { Ok(()) } + /// Adjusts inversion batch size in a discrete log instance. + pub fn set_compression_batch_size( + &mut self, + compression_batch_size: usize, + ) -> Result<(), DiscreteLogError> { + if compression_batch_size >= TWO16 as usize { + return Err(DiscreteLogError::DiscreteLogBatchSize); + } + self.compression_batch_size = compression_batch_size; + + Ok(()) + } + /// Solves the discrete log problem under the assumption that the solution - /// is a 32-bit number. + /// is a positive 32-bit number. pub fn decode_u32(self) -> Option { let mut starting_point = self.target; let handles = (0..self.num_threads) @@ -102,8 +138,13 @@ impl DiscreteLog { (-(&self.step_point), self.num_threads as u64), ); - let handle = - thread::spawn(move || Self::decode_range(ristretto_iterator, self.range_bound)); + let handle = thread::spawn(move || { + Self::decode_range( + ristretto_iterator, + self.range_bound, + self.compression_batch_size, + ) + }); starting_point -= G; handle @@ -120,21 +161,45 @@ impl DiscreteLog { solution } - fn decode_range(ristretto_iterator: RistrettoIterator, range_bound: usize) -> Option { + fn decode_range( + ristretto_iterator: RistrettoIterator, + range_bound: usize, + compression_batch_size: usize, + ) -> Option { let hashmap = &DECODE_PRECOMPUTATION_FOR_G; let mut decoded = None; - for (point, x_lo) in ristretto_iterator.take(range_bound) { - let key = point.compress().to_bytes(); - if hashmap.0.contains_key(&key) { - let x_hi = hashmap.0[&key]; - decoded = Some(x_lo + TWO16 * x_hi as u64); + + for batch in &ristretto_iterator + .take(range_bound) + .chunks(compression_batch_size) + { + // batch compression currently errors if any point in the batch is the identity point + let (batch_points, batch_indices): (Vec<_>, Vec<_>) = batch + .filter(|(point, index)| { + if point.is_identity() { + decoded = Some(*index); + return false; + } + true + }) + .unzip(); + + let batch_compressed = RistrettoPoint::double_and_compress_batch(&batch_points); + + for (point, x_lo) in batch_compressed.iter().zip(batch_indices.iter()) { + let key = point.to_bytes(); + if hashmap.0.contains_key(&key) { + let x_hi = hashmap.0[&key]; + decoded = Some(x_lo + TWO16 * x_hi as u64); + } } } + decoded } } -/// HashableRistretto iterator. +/// Hashable Ristretto iterator. /// /// Given an initial point X and a stepping point P, the iterator iterates through /// X + 0*P, X + 1*P, X + 2*P, X + 3*P, ... @@ -167,6 +232,7 @@ mod tests { #[allow(non_snake_case)] fn test_serialize_decode_u32_precomputation_for_G() { let decode_u32_precomputation_for_G = decode_u32_precomputation(G); + // let decode_u32_precomputation_for_G = decode_u32_precomputation(G); if decode_u32_precomputation_for_G.0 != DECODE_PRECOMPUTATION_FOR_G.0 { use std::{fs::File, io::Write, path::PathBuf}; @@ -183,7 +249,7 @@ mod tests { #[test] fn test_decode_correctness() { // general case - let amount: u64 = 55; + let amount: u64 = 4294967295; let instance = DiscreteLog::new(G, Scalar::from(amount) * G); diff --git a/zk-token-sdk/src/encryption/elgamal.rs b/zk-token-sdk/src/encryption/elgamal.rs index e49454c2a947e8..8b84e24a46ae94 100644 --- a/zk-token-sdk/src/encryption/elgamal.rs +++ b/zk-token-sdk/src/encryption/elgamal.rs @@ -10,15 +10,14 @@ //! directly as a Pedersen commitment. Therefore, proof systems that are designed specifically for //! Pedersen commitments can be used on the twisted ElGamal ciphertexts. //! -//! As the messages are encrypted as scalar elements (a.k.a. in the "exponent"), the encryption -//! scheme requires solving discrete log to recover the original plaintext. +//! As the messages are encrypted as scalar elements (a.k.a. in the "exponent"), one must solve the +//! discrete log to recover the originally encrypted value. use { crate::encryption::{ discrete_log::DiscreteLog, pedersen::{Pedersen, PedersenCommitment, PedersenOpening, G, H}, }, - arrayref::{array_ref, array_refs}, core::ops::{Add, Mul, Sub}, curve25519_dalek::{ ristretto::{CompressedRistretto, RistrettoPoint}, @@ -58,7 +57,7 @@ impl ElGamal { #[cfg(not(target_os = "solana"))] #[allow(non_snake_case)] fn keygen() -> ElGamalKeypair { - // secret scalar should be zero with negligible probability + // secret scalar should be non-zero except with negligible probability let mut s = Scalar::random(&mut OsRng); let keypair = Self::keygen_with_scalar(&s); @@ -67,20 +66,18 @@ impl ElGamal { } /// Generates an ElGamal keypair from a scalar input that determines the ElGamal private key. + /// + /// This function panics if the input scalar is zero, which is not a valid key. #[cfg(not(target_os = "solana"))] #[allow(non_snake_case)] fn keygen_with_scalar(s: &Scalar) -> ElGamalKeypair { - assert!(s != &Scalar::zero()); - - let P = s.invert() * &(*H); + let secret = ElGamalSecretKey(*s); + let public = ElGamalPubkey::new(&secret); - ElGamalKeypair { - public: ElGamalPubkey(P), - secret: ElGamalSecretKey(*s), - } + ElGamalKeypair { public, secret } } - /// On input an ElGamal public key and a mesage to be encrypted, the function returns a + /// On input an ElGamal public key and an amount to be encrypted, the function returns a /// corresponding ElGamal ciphertext. /// /// This function is randomized. It internally samples a scalar element using `OsRng`. @@ -92,8 +89,8 @@ impl ElGamal { ElGamalCiphertext { commitment, handle } } - /// On input a public key, message, and Pedersen opening, the function - /// returns the corresponding ElGamal ciphertext. + /// On input a public key, amount, and Pedersen opening, the function returns the corresponding + /// ElGamal ciphertext. #[cfg(not(target_os = "solana"))] fn encrypt_with>( amount: T, @@ -106,7 +103,7 @@ impl ElGamal { ElGamalCiphertext { commitment, handle } } - /// On input a message, the function returns a twisted ElGamal ciphertext where the associated + /// On input an amount, the function returns a twisted ElGamal ciphertext where the associated /// Pedersen opening is always zero. Since the opening is zero, any twisted ElGamal ciphertext /// of this form is a valid ciphertext under any ElGamal public key. #[cfg(not(target_os = "solana"))] @@ -117,10 +114,11 @@ impl ElGamal { ElGamalCiphertext { commitment, handle } } - /// On input a secret key and a ciphertext, the function returns the decrypted message. + /// On input a secret key and a ciphertext, the function returns the discrete log encoding of + /// original amount. /// /// The output of this function is of type `DiscreteLog`. To recover, the originally encrypted - /// message, use `DiscreteLog::decode`. + /// amount, use `DiscreteLog::decode`. #[cfg(not(target_os = "solana"))] fn decrypt(secret: &ElGamalSecretKey, ciphertext: &ElGamalCiphertext) -> DiscreteLog { DiscreteLog::new( @@ -129,8 +127,11 @@ impl ElGamal { ) } - /// On input a secret key and a ciphertext, the function returns the decrypted message - /// interpretted as type `u32`. + /// On input a secret key and a ciphertext, the function returns the decrypted amount + /// interpretted as a positive 32-bit number (but still of type `u64`). + /// + /// If the originally encrypted amount is not a positive 32-bit number, then the function + /// returns `None`. #[cfg(not(target_os = "solana"))] fn decrypt_u32(secret: &ElGamalSecretKey, ciphertext: &ElGamalCiphertext) -> Option { let discrete_log_instance = Self::decrypt(secret, ciphertext); @@ -150,32 +151,26 @@ pub struct ElGamalKeypair { } impl ElGamalKeypair { - /// Deterministically derives an ElGamal keypair from an Ed25519 signing key and a Solana address. + /// Deterministically derives an ElGamal keypair from an Ed25519 signing key and a Solana + /// address. + /// + /// This function exists for applications where a user may not wish to maintin a Solana + /// (Ed25519) keypair and an ElGamal keypair separately. A user may wish to solely maintain the + /// Solana keypair and then derive the ElGamal keypair on-the-fly whenever + /// encryption/decryption is needed. + /// + /// For the spl token-2022 confidential extension application, the ElGamal encryption public + /// key is specified in a token account address. A natural way to derive an ElGamal keypair is + /// then to define it from the hash of a Solana keypair and a Solana address. However, for + /// general hardware wallets, the signing key is not exposed in the API. Therefore, this + /// function uses a signer to sign a pre-specified message with respect to a Solana address. + /// The resulting signature is then hashed to derive an ElGamal keypair. #[cfg(not(target_os = "solana"))] #[allow(non_snake_case)] pub fn new(signer: &dyn Signer, address: &Pubkey) -> Result { - let message = Message::new( - &[Instruction::new_with_bytes( - *address, - b"ElGamalSecretKey", - vec![], - )], - Some(&signer.try_pubkey()?), - ); - let signature = signer.try_sign_message(&message.serialize())?; - - // Some `Signer` implementations return the default signature, which is not suitable for - // use as key material - if signature == Signature::default() { - return Err(SignerError::Custom("Rejecting default signature".into())); - } - - let mut scalar = Scalar::hash_from_bytes::(signature.as_ref()); - let keypair = ElGamal::keygen_with_scalar(&scalar); - - // TODO: zeroize signature? - scalar.zeroize(); - Ok(keypair) + let secret = ElGamalSecretKey::new(signer, address)?; + let public = ElGamalPubkey::new(&secret); + Ok(ElGamalKeypair { public, secret }) } /// Generates the public and secret keys for ElGamal encryption. @@ -195,8 +190,12 @@ impl ElGamalKeypair { } pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 64 { + return None; + } + Some(Self { - public: ElGamalPubkey::from_bytes(bytes[..32].try_into().ok()?)?, + public: ElGamalPubkey::from_bytes(&bytes[..32])?, secret: ElGamalSecretKey::from_bytes(bytes[32..].try_into().ok()?)?, }) } @@ -264,7 +263,10 @@ impl ElGamalPubkey { /// Derives the `ElGamalPubkey` that uniquely corresponds to an `ElGamalSecretKey`. #[allow(non_snake_case)] pub fn new(secret: &ElGamalSecretKey) -> Self { - ElGamalPubkey(&secret.0 * &(*H)) + let s = &secret.0; + assert!(s != &Scalar::zero()); + + ElGamalPubkey(s.invert() * &(*H)) } pub fn get_point(&self) -> &RistrettoPoint { @@ -276,7 +278,11 @@ impl ElGamalPubkey { self.0.compress().to_bytes() } - pub fn from_bytes(bytes: &[u8; 32]) -> Option { + pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 32 { + return None; + } + Some(ElGamalPubkey( CompressedRistretto::from_slice(bytes).decompress()?, )) @@ -321,6 +327,8 @@ pub struct ElGamalSecretKey(Scalar); impl ElGamalSecretKey { /// Deterministically derives an ElGamal keypair from an Ed25519 signing key and a Solana /// address. + /// + /// See `ElGamalKeypair::new` for more context on the key derivation. pub fn new(signer: &dyn Signer, address: &Pubkey) -> Result { let message = Message::new( &[Instruction::new_with_bytes( @@ -334,13 +342,13 @@ impl ElGamalSecretKey { // Some `Signer` implementations return the default signature, which is not suitable for // use as key material - if signature == Signature::default() { - Err(SignerError::Custom("Rejecting default signature".into())) - } else { - Ok(ElGamalSecretKey(Scalar::hash_from_bytes::( - signature.as_ref(), - ))) + if bool::from(signature.as_ref().ct_eq(Signature::default().as_ref())) { + return Err(SignerError::Custom("Rejecting default signatures".into())); } + + Ok(ElGamalSecretKey(Scalar::hash_from_bytes::( + signature.as_ref(), + ))) } /// Randomly samples an ElGamal secret key. @@ -375,8 +383,11 @@ impl ElGamalSecretKey { self.0.to_bytes() } - pub fn from_bytes(bytes: [u8; 32]) -> Option { - Scalar::from_canonical_bytes(bytes).map(ElGamalSecretKey) + pub fn from_bytes(bytes: &[u8]) -> Option { + match bytes.try_into() { + Ok(bytes) => Scalar::from_canonical_bytes(bytes).map(ElGamalSecretKey), + _ => None, + } } } @@ -431,27 +442,29 @@ impl ElGamalCiphertext { } pub fn from_bytes(bytes: &[u8]) -> Option { - let bytes = array_ref![bytes, 0, 64]; - let (commitment, handle) = array_refs![bytes, 32, 32]; - - let commitment = CompressedRistretto::from_slice(commitment).decompress()?; - let handle = CompressedRistretto::from_slice(handle).decompress()?; + if bytes.len() != 64 { + return None; + } Some(ElGamalCiphertext { - commitment: PedersenCommitment(commitment), - handle: DecryptHandle(handle), + commitment: PedersenCommitment::from_bytes(&bytes[..32])?, + handle: DecryptHandle::from_bytes(&bytes[32..])?, }) } /// Decrypts the ciphertext using an ElGamal secret key. /// /// The output of this function is of type `DiscreteLog`. To recover, the originally encrypted - /// message, use `DiscreteLog::decode`. + /// amount, use `DiscreteLog::decode`. pub fn decrypt(&self, secret: &ElGamalSecretKey) -> DiscreteLog { ElGamal::decrypt(secret, self) } - /// Decrypts the ciphertext using an ElGamal secret key interpretting the message as type `u32`. + /// Decrypts the ciphertext using an ElGamal secret key assuming that the message is a positive + /// 32-bit number. + /// + /// If the originally encrypted amount is not a positive 32-bit number, then the function + /// returns `None`. pub fn decrypt_u32(&self, secret: &ElGamalSecretKey) -> Option { ElGamal::decrypt_u32(secret, self) } @@ -549,6 +562,10 @@ impl DecryptHandle { } pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 32 { + return None; + } + Some(DecryptHandle( CompressedRistretto::from_slice(bytes).decompress()?, )) diff --git a/zk-token-sdk/src/encryption/errors.rs b/zk-token-sdk/src/encryption/errors.rs new file mode 100644 index 00000000000000..d644787f5bd9ed --- /dev/null +++ b/zk-token-sdk/src/encryption/errors.rs @@ -0,0 +1,10 @@ +//! Errors related to the twisted ElGamal encryption scheme. +use thiserror::Error; + +#[derive(Error, Clone, Debug, Eq, PartialEq)] +pub enum DiscreteLogError { + #[error("discrete log number of threads not power-of-two")] + DiscreteLogThreads, + #[error("discrete log batch size too large")] + DiscreteLogBatchSize, +} diff --git a/zk-token-sdk/src/encryption/mod.rs b/zk-token-sdk/src/encryption/mod.rs index a90b88a5faaabf..0025ec735d301b 100644 --- a/zk-token-sdk/src/encryption/mod.rs +++ b/zk-token-sdk/src/encryption/mod.rs @@ -13,4 +13,5 @@ pub mod auth_encryption; pub mod discrete_log; pub mod elgamal; +pub mod errors; pub mod pedersen; diff --git a/zk-token-sdk/src/encryption/pedersen.rs b/zk-token-sdk/src/encryption/pedersen.rs index 345809317dcbe9..6de1d4016a9933 100644 --- a/zk-token-sdk/src/encryption/pedersen.rs +++ b/zk-token-sdk/src/encryption/pedersen.rs @@ -28,21 +28,21 @@ lazy_static::lazy_static! { /// Algorithm handle for the Pedersen commitment scheme. pub struct Pedersen; impl Pedersen { - /// On input a message, the function returns a Pedersen commitment of the message and the - /// corresponding opening. + /// On input a message (numeric amount), the function returns a Pedersen commitment of the + /// message and the corresponding opening. /// /// This function is randomized. It internally samples a Pedersen opening using `OsRng`. #[cfg(not(target_os = "solana"))] #[allow(clippy::new_ret_no_self)] - pub fn new>(message: T) -> (PedersenCommitment, PedersenOpening) { + pub fn new>(amount: T) -> (PedersenCommitment, PedersenOpening) { let opening = PedersenOpening::new_rand(); - let commitment = Pedersen::with(message, &opening); + let commitment = Pedersen::with(amount, &opening); (commitment, opening) } - /// On input a message and a Pedersen opening, the function returns the corresponding Pedersen - /// commitment. + /// On input a message (numeric amount) and a Pedersen opening, the function returns the + /// corresponding Pedersen commitment. /// /// This function is deterministic. #[allow(non_snake_case)] @@ -53,7 +53,8 @@ impl Pedersen { PedersenCommitment(RistrettoPoint::multiscalar_mul(&[x, *r], &[*G, *H])) } - /// On input a message, the function returns a Pedersen commitment with zero as the opening. + /// On input a message (numeric amount), the function returns a Pedersen commitment with zero + /// as the opening. /// /// This function is deterministic. pub fn encode>(amount: T) -> PedersenCommitment { @@ -176,6 +177,10 @@ impl PedersenCommitment { } pub fn from_bytes(bytes: &[u8]) -> Option { + if bytes.len() != 32 { + return None; + } + Some(PedersenCommitment( CompressedRistretto::from_slice(bytes).decompress()?, )) @@ -296,6 +301,9 @@ mod tests { let decoded = PedersenCommitment::from_bytes(&encoded).unwrap(); assert_eq!(comm, decoded); + + // incorrect length encoding + assert_eq!(PedersenCommitment::from_bytes(&[0; 33]), None); } #[test] @@ -306,6 +314,9 @@ mod tests { let decoded = PedersenOpening::from_bytes(&encoded).unwrap(); assert_eq!(open, decoded); + + // incorrect length encoding + assert_eq!(PedersenOpening::from_bytes(&[0; 33]), None); } #[test] diff --git a/zk-token-sdk/src/errors.rs b/zk-token-sdk/src/errors.rs index 61f958a2d686d3..e9e36e43f73c18 100644 --- a/zk-token-sdk/src/errors.rs +++ b/zk-token-sdk/src/errors.rs @@ -10,26 +10,46 @@ pub enum ProofError { TransferAmount, #[error("proof generation failed")] Generation, - #[error("proof failed to verify")] - Verification, - #[error("range proof failed to verify")] - RangeProof, - #[error("equality proof failed to verify")] + #[error("proof verification failed")] + VerificationError(ProofType, ProofVerificationError), + #[error("failed to decrypt ciphertext")] + Decryption, + #[error("invalid ciphertext data")] + CiphertextDeserialization, + #[error("invalid pubkey data")] + PubkeyDeserialization, + #[error("ciphertext does not exist in instruction data")] + MissingCiphertext, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum ProofType { EqualityProof, - #[error("fee proof failed to verify")] - FeeProof, - #[error("zero-balance proof failed to verify")] - ZeroBalanceProof, - #[error("validity proof failed to verify")] ValidityProof, + ZeroBalanceProof, + FeeSigmaProof, + PubkeyValidityProof, + RangeProof, +} + +#[derive(Error, Clone, Debug, Eq, PartialEq)] +pub enum ProofVerificationError { + #[error("required algebraic relation does not hold")] + AlgebraicRelation, + #[error("malformed proof")] + Deserialization, + #[error("multiscalar multiplication failed")] + MultiscalarMul, + #[error("transcript failed to produce a challenge")] + Transcript(#[from] TranscriptError), #[error( - "`zk_token_elgamal::pod::ElGamalCiphertext` contains invalid ElGamalCiphertext ciphertext" + "attempted to verify range proof with a non-power-of-two bit size or bit size is too big" )] - InconsistentCTData, - #[error("failed to decrypt ciphertext from transfer data")] - Decryption, - #[error("discrete log number of threads not power-of-two")] - DiscreteLogThreads, + InvalidBitSize, + #[error("insufficient generators for the proof")] + InvalidGeneratorsLength, + #[error("number of blinding factors do not match the number of values")] + WrongNumBlindingFactors, } #[derive(Error, Clone, Debug, Eq, PartialEq)] @@ -39,30 +59,36 @@ pub enum TranscriptError { } impl From for ProofError { - fn from(_err: RangeProofError) -> Self { - Self::RangeProof + fn from(err: RangeProofError) -> Self { + Self::VerificationError(ProofType::RangeProof, err.0) } } impl From for ProofError { - fn from(_err: EqualityProofError) -> Self { - Self::EqualityProof + fn from(err: EqualityProofError) -> Self { + Self::VerificationError(ProofType::EqualityProof, err.0) } } impl From for ProofError { - fn from(_err: FeeSigmaProofError) -> Self { - Self::FeeProof + fn from(err: FeeSigmaProofError) -> Self { + Self::VerificationError(ProofType::FeeSigmaProof, err.0) } } impl From for ProofError { - fn from(_err: ZeroBalanceProofError) -> Self { - Self::ZeroBalanceProof + fn from(err: ZeroBalanceProofError) -> Self { + Self::VerificationError(ProofType::ZeroBalanceProof, err.0) } } impl From for ProofError { - fn from(_err: ValidityProofError) -> Self { - Self::ValidityProof + fn from(err: ValidityProofError) -> Self { + Self::VerificationError(ProofType::ValidityProof, err.0) + } +} + +impl From for ProofError { + fn from(err: PubkeyValidityProofError) -> Self { + Self::VerificationError(ProofType::PubkeyValidityProof, err.0) } } diff --git a/zk-token-sdk/src/instruction/close_account.rs b/zk-token-sdk/src/instruction/close_account.rs index 4525f87901cd71..c745e400e3d9c8 100644 --- a/zk-token-sdk/src/instruction/close_account.rs +++ b/zk-token-sdk/src/instruction/close_account.rs @@ -1,19 +1,21 @@ -use { - crate::zk_token_elgamal::pod, - bytemuck::{Pod, Zeroable}, -}; #[cfg(not(target_os = "solana"))] use { crate::{ encryption::elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, errors::ProofError, - instruction::Verifiable, sigma_proofs::zero_balance_proof::ZeroBalanceProof, transcript::TranscriptProtocol, }, merlin::Transcript, std::convert::TryInto, }; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; /// This struct includes the cryptographic proof *and* the account data information needed to verify /// the proof @@ -25,14 +27,21 @@ use { #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct CloseAccountData { + /// The context data for the close account proof + pub context: CloseAccountProofContext, + + /// Proof that the source account available balance is zero + pub proof: CloseAccountProof, // 96 bytes +} + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct CloseAccountProofContext { /// The source account ElGamal pubkey pub pubkey: pod::ElGamalPubkey, // 32 bytes /// The source account available balance in encrypted form pub ciphertext: pod::ElGamalCiphertext, // 64 bytes - - /// Proof that the source account available balance is zero - pub proof: CloseAccountProof, // 64 bytes } #[cfg(not(target_os = "solana"))] @@ -44,25 +53,32 @@ impl CloseAccountData { let pod_pubkey = pod::ElGamalPubkey((&keypair.public).to_bytes()); let pod_ciphertext = pod::ElGamalCiphertext(ciphertext.to_bytes()); - let mut transcript = CloseAccountProof::transcript_new(&pod_pubkey, &pod_ciphertext); + let context = CloseAccountProofContext { + pubkey: pod_pubkey, + ciphertext: pod_ciphertext, + }; + let mut transcript = CloseAccountProof::transcript_new(&pod_pubkey, &pod_ciphertext); let proof = CloseAccountProof::new(keypair, ciphertext, &mut transcript); - Ok(CloseAccountData { - pubkey: pod_pubkey, - ciphertext: pod_ciphertext, - proof, - }) + Ok(CloseAccountData { context, proof }) } } -#[cfg(not(target_os = "solana"))] -impl Verifiable for CloseAccountData { - fn verify(&self) -> Result<(), ProofError> { - let mut transcript = CloseAccountProof::transcript_new(&self.pubkey, &self.ciphertext); +impl ZkProofData for CloseAccountData { + const PROOF_TYPE: ProofType = ProofType::CloseAccount; + + fn context_data(&self) -> &CloseAccountProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { + let mut transcript = + CloseAccountProof::transcript_new(&self.context.pubkey, &self.context.ciphertext); - let pubkey = self.pubkey.try_into()?; - let ciphertext = self.ciphertext.try_into()?; + let pubkey = self.context.pubkey.try_into()?; + let ciphertext = self.context.ciphertext.try_into()?; self.proof.verify(&pubkey, &ciphertext, &mut transcript) } } @@ -127,11 +143,11 @@ mod test { // general case: encryption of 0 let ciphertext = keypair.public.encrypt(0_u64); let close_account_data = CloseAccountData::new(&keypair, &ciphertext).unwrap(); - assert!(close_account_data.verify().is_ok()); + assert!(close_account_data.verify_proof().is_ok()); // general case: encryption of > 0 let ciphertext = keypair.public.encrypt(1_u64); let close_account_data = CloseAccountData::new(&keypair, &ciphertext).unwrap(); - assert!(close_account_data.verify().is_err()); + assert!(close_account_data.verify_proof().is_err()); } } diff --git a/zk-token-sdk/src/instruction/ctxt_ctxt_equality.rs b/zk-token-sdk/src/instruction/ctxt_ctxt_equality.rs new file mode 100644 index 00000000000000..0451c05c68cea3 --- /dev/null +++ b/zk-token-sdk/src/instruction/ctxt_ctxt_equality.rs @@ -0,0 +1,228 @@ +//! The ciphertext-ciphertext equality proof instruction. +//! +//! A ciphertext-ciphertext equality proof is defined with respect to two twisted ElGamal +//! ciphertexts. The proof certifies that the two ciphertexts encrypt the same message. To generate +//! the proof, a prover must provide the decryption key for the first ciphertext and the randomness +//! used to generate the second ciphertext. +//! +//! The first ciphertext associated with the proof is referred to as the "source" ciphertext. The +//! second ciphertext associated with the proof is referred to as the "destination" ciphertext. + +#[cfg(not(target_os = "solana"))] +use { + crate::{ + encryption::{ + elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, + pedersen::PedersenOpening, + }, + errors::ProofError, + sigma_proofs::ctxt_ctxt_equality_proof::CiphertextCiphertextEqualityProof, + transcript::TranscriptProtocol, + }, + merlin::Transcript, + std::convert::TryInto, +}; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::VerifyCiphertextCiphertextEquality` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct CiphertextCiphertextEqualityProofData { + pub context: CiphertextCiphertextEqualityProofContext, + + pub proof: pod::CiphertextCiphertextEqualityProof, +} + +/// The context data needed to verify a ciphertext-ciphertext equality proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct CiphertextCiphertextEqualityProofContext { + pub source_pubkey: pod::ElGamalPubkey, // 32 bytes + + pub destination_pubkey: pod::ElGamalPubkey, // 32 bytes + + pub source_ciphertext: pod::ElGamalCiphertext, // 64 bytes + + pub destination_ciphertext: pod::ElGamalCiphertext, // 64 bytes +} + +#[cfg(not(target_os = "solana"))] +impl CiphertextCiphertextEqualityProofData { + pub fn new( + source_keypair: &ElGamalKeypair, + destination_pubkey: &ElGamalPubkey, + source_ciphertext: &ElGamalCiphertext, + destination_ciphertext: &ElGamalCiphertext, + destination_opening: &PedersenOpening, + amount: u64, + ) -> Result { + let pod_source_pubkey = pod::ElGamalPubkey(source_keypair.public.to_bytes()); + let pod_destination_pubkey = pod::ElGamalPubkey(destination_pubkey.to_bytes()); + let pod_source_ciphertext = pod::ElGamalCiphertext(source_ciphertext.to_bytes()); + let pod_destination_ciphertext = pod::ElGamalCiphertext(destination_ciphertext.to_bytes()); + + let context = CiphertextCiphertextEqualityProofContext { + source_pubkey: pod_source_pubkey, + destination_pubkey: pod_destination_pubkey, + source_ciphertext: pod_source_ciphertext, + destination_ciphertext: pod_destination_ciphertext, + }; + + let mut transcript = CiphertextCiphertextEqualityProof::transcript_new( + &pod_source_pubkey, + &pod_destination_pubkey, + &pod_source_ciphertext, + &pod_destination_ciphertext, + ); + + let proof = CiphertextCiphertextEqualityProof::new( + source_keypair, + destination_pubkey, + source_ciphertext, + destination_opening, + amount, + &mut transcript, + ) + .into(); + + Ok(Self { context, proof }) + } +} + +impl ZkProofData + for CiphertextCiphertextEqualityProofData +{ + const PROOF_TYPE: ProofType = ProofType::CiphertextCiphertextEquality; + + fn context_data(&self) -> &CiphertextCiphertextEqualityProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { + let mut transcript = CiphertextCiphertextEqualityProof::transcript_new( + &self.context.source_pubkey, + &self.context.destination_pubkey, + &self.context.source_ciphertext, + &self.context.destination_ciphertext, + ); + + let source_pubkey = self.context.source_pubkey.try_into()?; + let destination_pubkey = self.context.destination_pubkey.try_into()?; + let source_ciphertext = self.context.source_ciphertext.try_into()?; + let destination_ciphertext = self.context.destination_ciphertext.try_into()?; + let proof: CiphertextCiphertextEqualityProof = self.proof.try_into()?; + + proof + .verify( + &source_pubkey, + &destination_pubkey, + &source_ciphertext, + &destination_ciphertext, + &mut transcript, + ) + .map_err(|e| e.into()) + } +} + +#[allow(non_snake_case)] +#[cfg(not(target_os = "solana"))] +impl CiphertextCiphertextEqualityProof { + fn transcript_new( + source_pubkey: &pod::ElGamalPubkey, + destination_pubkey: &pod::ElGamalPubkey, + source_ciphertext: &pod::ElGamalCiphertext, + destination_ciphertext: &pod::ElGamalCiphertext, + ) -> Transcript { + let mut transcript = Transcript::new(b"CiphertextCiphertextEqualityProof"); + + transcript.append_pubkey(b"pubkey-source", source_pubkey); + transcript.append_pubkey(b"pubkey-dest", destination_pubkey); + + transcript.append_ciphertext(b"ciphertext-source", source_ciphertext); + transcript.append_ciphertext(b"ciphertext-dest", destination_ciphertext); + + transcript + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_ciphertext_ciphertext_instruction_correctness() { + let source_keypair = ElGamalKeypair::new_rand(); + let destination_keypair = ElGamalKeypair::new_rand(); + + let amount: u64 = 0; + let source_ciphertext = source_keypair.public.encrypt(amount); + + let destination_opening = PedersenOpening::new_rand(); + let destination_ciphertext = destination_keypair + .public + .encrypt_with(amount, &destination_opening); + + let proof_data = CiphertextCiphertextEqualityProofData::new( + &source_keypair, + &destination_keypair.public, + &source_ciphertext, + &destination_ciphertext, + &destination_opening, + amount, + ) + .unwrap(); + + assert!(proof_data.verify_proof().is_ok()); + + let amount: u64 = 55; + let source_ciphertext = source_keypair.public.encrypt(amount); + + let destination_opening = PedersenOpening::new_rand(); + let destination_ciphertext = destination_keypair + .public + .encrypt_with(amount, &destination_opening); + + let proof_data = CiphertextCiphertextEqualityProofData::new( + &source_keypair, + &destination_keypair.public, + &source_ciphertext, + &destination_ciphertext, + &destination_opening, + amount, + ) + .unwrap(); + + assert!(proof_data.verify_proof().is_ok()); + + let amount = u64::max_value(); + let source_ciphertext = source_keypair.public.encrypt(amount); + + let destination_opening = PedersenOpening::new_rand(); + let destination_ciphertext = destination_keypair + .public + .encrypt_with(amount, &destination_opening); + + let proof_data = CiphertextCiphertextEqualityProofData::new( + &source_keypair, + &destination_keypair.public, + &source_ciphertext, + &destination_ciphertext, + &destination_opening, + amount, + ) + .unwrap(); + + assert!(proof_data.verify_proof().is_ok()); + } +} diff --git a/zk-token-sdk/src/instruction/mod.rs b/zk-token-sdk/src/instruction/mod.rs index 3b2cf53925a8f7..f480551f000136 100644 --- a/zk-token-sdk/src/instruction/mod.rs +++ b/zk-token-sdk/src/instruction/mod.rs @@ -1,9 +1,11 @@ -pub mod close_account; +pub mod ctxt_ctxt_equality; +pub mod pubkey_validity; pub mod transfer; pub mod transfer_with_fee; pub mod withdraw; -pub mod withdraw_withheld; +pub mod zero_balance; +use num_derive::{FromPrimitive, ToPrimitive}; #[cfg(not(target_os = "solana"))] use { crate::{ @@ -14,47 +16,71 @@ use { errors::ProofError, }, curve25519_dalek::scalar::Scalar, - subtle::ConstantTimeEq, }; pub use { - close_account::CloseAccountData, transfer::TransferData, - transfer_with_fee::TransferWithFeeData, withdraw::WithdrawData, - withdraw_withheld::WithdrawWithheldTokensData, + bytemuck::Pod, + ctxt_ctxt_equality::{ + CiphertextCiphertextEqualityProofContext, CiphertextCiphertextEqualityProofData, + }, + pubkey_validity::{PubkeyValidityData, PubkeyValidityProofContext}, + transfer::{TransferData, TransferProofContext}, + transfer_with_fee::{FeeParameters, TransferWithFeeData, TransferWithFeeProofContext}, + withdraw::{WithdrawData, WithdrawProofContext}, + zero_balance::{ZeroBalanceProofContext, ZeroBalanceProofData}, }; -#[cfg(not(target_os = "solana"))] -pub trait Verifiable { - fn verify(&self) -> Result<(), ProofError>; +#[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive, PartialEq, Eq)] +#[repr(u8)] +pub enum ProofType { + /// Empty proof type used to distinguish if a proof context account is initialized + Uninitialized, + ZeroBalance, + Withdraw, + CiphertextCiphertextEquality, + Transfer, + TransferWithFee, + PubkeyValidity, +} + +pub trait ZkProofData { + const PROOF_TYPE: ProofType; + + fn context_data(&self) -> &T; + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError>; } #[cfg(not(target_os = "solana"))] #[derive(Debug, Copy, Clone)] pub enum Role { Source, - Dest, + Destination, Auditor, + WithdrawWithheldAuthority, } /// Takes in a 64-bit number `amount` and a bit length `bit_length`. It returns: /// - the `bit_length` low bits of `amount` interpretted as u64 /// - the (64 - `bit_length`) high bits of `amount` interpretted as u64 #[cfg(not(target_os = "solana"))] -pub fn split_u64( - amount: u64, - lo_bit_length: usize, - hi_bit_length: usize, -) -> Result<(u64, u64), ProofError> { - assert!(lo_bit_length <= 64); - assert!(hi_bit_length <= 64); - - if !bool::from((amount >> (lo_bit_length + hi_bit_length)).ct_eq(&0u64)) { - return Err(ProofError::TransferAmount); +pub fn split_u64(amount: u64, bit_length: usize) -> (u64, u64) { + if bit_length == 64 { + (amount, 0) + } else { + let lo = amount << (64 - bit_length) >> (64 - bit_length); + let hi = amount >> bit_length; + (lo, hi) } +} - let lo = amount << (64 - lo_bit_length) >> (64 - lo_bit_length); - let hi = amount >> lo_bit_length; - - Ok((lo, hi)) +#[cfg(not(target_os = "solana"))] +pub fn combine_lo_hi_u64(amount_lo: u64, amount_hi: u64, bit_length: usize) -> u64 { + if bit_length == 64 { + amount_lo + } else { + amount_lo + (amount_hi << bit_length) + } } #[cfg(not(target_os = "solana"))] diff --git a/zk-token-sdk/src/instruction/pubkey_validity.rs b/zk-token-sdk/src/instruction/pubkey_validity.rs new file mode 100644 index 00000000000000..adaca3e7ccc794 --- /dev/null +++ b/zk-token-sdk/src/instruction/pubkey_validity.rs @@ -0,0 +1,99 @@ +//! The public-key validity proof instruction. +//! +//! A public-key validity proof system is defined with respect to an ElGamal public key. The proof +//! certifies that a given public key is a valid ElGamal public key (i.e. the prover knows a +//! corresponding secret key). To generate the proof, a prover must provide the secret key for the +//! public key. + +#[cfg(not(target_os = "solana"))] +use { + crate::{ + encryption::elgamal::ElGamalKeypair, errors::ProofError, + sigma_proofs::pubkey_proof::PubkeyValidityProof, transcript::TranscriptProtocol, + }, + merlin::Transcript, + std::convert::TryInto, +}; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the `ProofInstruction::VerifyPubkeyValidity` +/// instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct PubkeyValidityData { + /// The context data for the public key validity proof + pub context: PubkeyValidityProofContext, // 32 bytes + + /// Proof that the public key is well-formed + pub proof: pod::PubkeyValidityProof, // 64 bytes +} + +/// The context data needed to verify a pubkey validity proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct PubkeyValidityProofContext { + /// The public key to be proved + pub pubkey: pod::ElGamalPubkey, // 32 bytes +} + +#[cfg(not(target_os = "solana"))] +impl PubkeyValidityData { + pub fn new(keypair: &ElGamalKeypair) -> Result { + let pod_pubkey = pod::ElGamalPubkey(keypair.public.to_bytes()); + + let context = PubkeyValidityProofContext { pubkey: pod_pubkey }; + + let mut transcript = PubkeyValidityProof::transcript_new(&pod_pubkey); + let proof = PubkeyValidityProof::new(keypair, &mut transcript).into(); + + Ok(PubkeyValidityData { context, proof }) + } +} + +impl ZkProofData for PubkeyValidityData { + const PROOF_TYPE: ProofType = ProofType::PubkeyValidity; + + fn context_data(&self) -> &PubkeyValidityProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { + let mut transcript = PubkeyValidityProof::transcript_new(&self.context.pubkey); + let pubkey = self.context.pubkey.try_into()?; + let proof: PubkeyValidityProof = self.proof.try_into()?; + proof.verify(&pubkey, &mut transcript).map_err(|e| e.into()) + } +} + +#[allow(non_snake_case)] +#[cfg(not(target_os = "solana"))] +impl PubkeyValidityProof { + fn transcript_new(pubkey: &pod::ElGamalPubkey) -> Transcript { + let mut transcript = Transcript::new(b"PubkeyProof"); + transcript.append_pubkey(b"pubkey", pubkey); + transcript + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_pubkey_validity_instruction_correctness() { + let keypair = ElGamalKeypair::new_rand(); + + let pubkey_validity_data = PubkeyValidityData::new(&keypair).unwrap(); + assert!(pubkey_validity_data.verify_proof().is_ok()); + } +} diff --git a/zk-token-sdk/src/instruction/transfer.rs b/zk-token-sdk/src/instruction/transfer.rs index d56cad1280d5d5..e897b376ac9300 100644 --- a/zk-token-sdk/src/instruction/transfer.rs +++ b/zk-token-sdk/src/instruction/transfer.rs @@ -1,7 +1,3 @@ -use { - crate::zk_token_elgamal::pod, - bytemuck::{Pod, Zeroable}, -}; #[cfg(not(target_os = "solana"))] use { crate::{ @@ -12,10 +8,11 @@ use { pedersen::{Pedersen, PedersenCommitment, PedersenOpening}, }, errors::ProofError, - instruction::{combine_lo_hi_ciphertexts, split_u64, Role, Verifiable}, + instruction::{combine_lo_hi_ciphertexts, split_u64, Role}, range_proof::RangeProof, sigma_proofs::{ - equality_proof::CtxtCommEqualityProof, validity_proof::AggregatedValidityProof, + ctxt_comm_equality_proof::CiphertextCommitmentEqualityProof, + validity_proof::AggregatedValidityProof, }, transcript::TranscriptProtocol, }, @@ -23,6 +20,13 @@ use { merlin::Transcript, std::convert::TryInto, }; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; #[cfg(not(target_os = "solana"))] const TRANSFER_SOURCE_AMOUNT_BITS: usize = 64; @@ -39,23 +43,35 @@ lazy_static::lazy_static! { TRANSFER_AMOUNT_LO_NEGATED_BITS) - 1); } +/// The instruction data that is needed for the `ProofInstruction::VerifyTransfer` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct TransferData { - /// Group encryption of the low 32 bits of the transfer amount - pub ciphertext_lo: pod::TransferAmountEncryption, + /// The context data for the transfer proof + pub context: TransferProofContext, - /// Group encryption of the high 32 bits of the transfer amount - pub ciphertext_hi: pod::TransferAmountEncryption, + /// Zero-knowledge proofs for Transfer + pub proof: TransferProof, +} + +/// The context data needed to verify a transfer proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct TransferProofContext { + /// Group encryption of the low 16 bits of the transfer amount + pub ciphertext_lo: pod::TransferAmountEncryption, // 128 bytes + + /// Group encryption of the high 48 bits of the transfer amount + pub ciphertext_hi: pod::TransferAmountEncryption, // 128 bytes /// The public encryption keys associated with the transfer: source, dest, and auditor - pub transfer_pubkeys: pod::TransferPubkeys, + pub transfer_pubkeys: pod::TransferPubkeys, // 96 bytes /// The final spendable ciphertext after the transfer - pub new_source_ciphertext: pod::ElGamalCiphertext, - - /// Zero-knowledge proofs for Transfer - pub proof: TransferProof, + pub new_source_ciphertext: pod::ElGamalCiphertext, // 64 bytes } #[cfg(not(target_os = "solana"))] @@ -68,11 +84,7 @@ impl TransferData { (destination_pubkey, auditor_pubkey): (&ElGamalPubkey, &ElGamalPubkey), ) -> Result { // split and encrypt transfer amount - let (amount_lo, amount_hi) = split_u64( - transfer_amount, - TRANSFER_AMOUNT_LO_BITS, - TRANSFER_AMOUNT_HI_BITS, - )?; + let (amount_lo, amount_hi) = split_u64(transfer_amount, TRANSFER_AMOUNT_LO_BITS); let (ciphertext_lo, opening_lo) = TransferAmountEncryption::new( amount_lo, @@ -120,6 +132,13 @@ impl TransferData { let pod_ciphertext_hi: pod::TransferAmountEncryption = ciphertext_hi.into(); let pod_new_source_ciphertext: pod::ElGamalCiphertext = new_source_ciphertext.into(); + let context = TransferProofContext { + ciphertext_lo: pod_ciphertext_lo, + ciphertext_hi: pod_ciphertext_hi, + transfer_pubkeys: pod_transfer_pubkeys, + new_source_ciphertext: pod_new_source_ciphertext, + }; + let mut transcript = TransferProof::transcript_new( &pod_transfer_pubkeys, &pod_ciphertext_lo, @@ -137,45 +156,49 @@ impl TransferData { &mut transcript, ); - Ok(Self { - ciphertext_lo: pod_ciphertext_lo, - ciphertext_hi: pod_ciphertext_hi, - transfer_pubkeys: pod_transfer_pubkeys, - new_source_ciphertext: pod_new_source_ciphertext, - proof, - }) + Ok(Self { context, proof }) } /// Extracts the lo ciphertexts associated with a transfer data fn ciphertext_lo(&self, role: Role) -> Result { - let ciphertext_lo: TransferAmountEncryption = self.ciphertext_lo.try_into()?; + let ciphertext_lo: TransferAmountEncryption = self.context.ciphertext_lo.try_into()?; let handle_lo = match role { - Role::Source => ciphertext_lo.source_handle, - Role::Dest => ciphertext_lo.destination_handle, - Role::Auditor => ciphertext_lo.auditor_handle, + Role::Source => Some(ciphertext_lo.source_handle), + Role::Destination => Some(ciphertext_lo.destination_handle), + Role::Auditor => Some(ciphertext_lo.auditor_handle), + Role::WithdrawWithheldAuthority => None, }; - Ok(ElGamalCiphertext { - commitment: ciphertext_lo.commitment, - handle: handle_lo, - }) + if let Some(handle) = handle_lo { + Ok(ElGamalCiphertext { + commitment: ciphertext_lo.commitment, + handle, + }) + } else { + Err(ProofError::MissingCiphertext) + } } /// Extracts the lo ciphertexts associated with a transfer data fn ciphertext_hi(&self, role: Role) -> Result { - let ciphertext_hi: TransferAmountEncryption = self.ciphertext_hi.try_into()?; + let ciphertext_hi: TransferAmountEncryption = self.context.ciphertext_hi.try_into()?; let handle_hi = match role { - Role::Source => ciphertext_hi.source_handle, - Role::Dest => ciphertext_hi.destination_handle, - Role::Auditor => ciphertext_hi.auditor_handle, + Role::Source => Some(ciphertext_hi.source_handle), + Role::Destination => Some(ciphertext_hi.destination_handle), + Role::Auditor => Some(ciphertext_hi.auditor_handle), + Role::WithdrawWithheldAuthority => None, }; - Ok(ElGamalCiphertext { - commitment: ciphertext_hi.commitment, - handle: handle_hi, - }) + if let Some(handle) = handle_hi { + Ok(ElGamalCiphertext { + commitment: ciphertext_hi.commitment, + handle, + }) + } else { + Err(ProofError::MissingCiphertext) + } } /// Decrypts transfer amount from transfer data @@ -195,21 +218,27 @@ impl TransferData { } } -#[cfg(not(target_os = "solana"))] -impl Verifiable for TransferData { - fn verify(&self) -> Result<(), ProofError> { +impl ZkProofData for TransferData { + const PROOF_TYPE: ProofType = ProofType::Transfer; + + fn context_data(&self) -> &TransferProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { // generate transcript and append all public inputs let mut transcript = TransferProof::transcript_new( - &self.transfer_pubkeys, - &self.ciphertext_lo, - &self.ciphertext_hi, - &self.new_source_ciphertext, + &self.context.transfer_pubkeys, + &self.context.ciphertext_lo, + &self.context.ciphertext_hi, + &self.context.new_source_ciphertext, ); - let ciphertext_lo = self.ciphertext_lo.try_into()?; - let ciphertext_hi = self.ciphertext_hi.try_into()?; - let transfer_pubkeys = self.transfer_pubkeys.try_into()?; - let new_spendable_ciphertext = self.new_source_ciphertext.try_into()?; + let ciphertext_lo = self.context.ciphertext_lo.try_into()?; + let ciphertext_hi = self.context.ciphertext_hi.try_into()?; + let transfer_pubkeys = self.context.transfer_pubkeys.try_into()?; + let new_spendable_ciphertext = self.context.new_source_ciphertext.try_into()?; self.proof.verify( &ciphertext_lo, @@ -229,7 +258,7 @@ pub struct TransferProof { pub new_source_commitment: pod::PedersenCommitment, /// Associated equality proof - pub equality_proof: pod::CtxtCommEqualityProof, + pub equality_proof: pod::CiphertextCommitmentEqualityProof, /// Associated ciphertext validity proof pub validity_proof: pod::AggregatedValidityProof, @@ -284,11 +313,11 @@ impl TransferProof { transcript.append_commitment(b"commitment-new-source", &pod_new_source_commitment); // generate equality_proof - let equality_proof = CtxtCommEqualityProof::new( + let equality_proof = CiphertextCommitmentEqualityProof::new( source_keypair, new_source_ciphertext, - source_new_balance, &source_opening, + source_new_balance, transcript, ); @@ -358,13 +387,11 @@ impl TransferProof { transcript.append_commitment(b"commitment-new-source", &self.new_source_commitment); let commitment: PedersenCommitment = self.new_source_commitment.try_into()?; - let equality_proof: CtxtCommEqualityProof = self.equality_proof.try_into()?; + let equality_proof: CiphertextCommitmentEqualityProof = self.equality_proof.try_into()?; let aggregated_validity_proof: AggregatedValidityProof = self.validity_proof.try_into()?; let range_proof: RangeProof = self.range_proof.try_into()?; // verify equality proof - // - // TODO: we can also consider verifying equality and range proof in a batch equality_proof.verify( &transfer_pubkeys.source_pubkey, ciphertext_new_spendable, @@ -453,11 +480,11 @@ impl TransferPubkeys { let (source_pubkey, destination_pubkey, auditor_pubkey) = array_refs![bytes, 32, 32, 32]; let source_pubkey = - ElGamalPubkey::from_bytes(source_pubkey).ok_or(ProofError::Verification)?; - let destination_pubkey = - ElGamalPubkey::from_bytes(destination_pubkey).ok_or(ProofError::Verification)?; + ElGamalPubkey::from_bytes(source_pubkey).ok_or(ProofError::PubkeyDeserialization)?; + let destination_pubkey = ElGamalPubkey::from_bytes(destination_pubkey) + .ok_or(ProofError::PubkeyDeserialization)?; let auditor_pubkey = - ElGamalPubkey::from_bytes(auditor_pubkey).ok_or(ProofError::Verification)?; + ElGamalPubkey::from_bytes(auditor_pubkey).ok_or(ProofError::PubkeyDeserialization)?; Ok(Self { source_pubkey, @@ -535,7 +562,7 @@ mod test { ) .unwrap(); - assert!(transfer_data.verify().is_ok()); + assert!(transfer_data.verify_proof().is_ok()); // Case 2: transfer max amount @@ -556,7 +583,7 @@ mod test { ) .unwrap(); - assert!(transfer_data.verify().is_ok()); + assert!(transfer_data.verify_proof().is_ok()); // Case 3: general success case @@ -576,28 +603,9 @@ mod test { ) .unwrap(); - assert!(transfer_data.verify().is_ok()); - - // Case 4: transfer amount too big - - // create source account spendable ciphertext - let spendable_balance: u64 = u64::max_value(); - let spendable_ciphertext = source_keypair.public.encrypt(spendable_balance); - - // transfer amount - let transfer_amount: u64 = 1u64 << (TRANSFER_AMOUNT_LO_BITS + TRANSFER_AMOUNT_HI_BITS); - - // create transfer data - let transfer_data = TransferData::new( - transfer_amount, - (spendable_balance, &spendable_ciphertext), - &source_keypair, - (&dest_pk, &auditor_pk), - ); - - assert!(transfer_data.is_err()); + assert!(transfer_data.verify_proof().is_ok()); - // Case 5: invalid destination or auditor pubkey + // Case 4: invalid destination or auditor pubkey let spendable_balance: u64 = 0; let spendable_ciphertext = source_keypair.public.encrypt(spendable_balance); @@ -615,7 +623,7 @@ mod test { ) .unwrap(); - assert!(transfer_data.verify().is_err()); + assert!(transfer_data.verify_proof().is_err()); // auditor pubkey invalid let dest_pk = ElGamalKeypair::new_rand().public; @@ -629,7 +637,7 @@ mod test { ) .unwrap(); - assert!(transfer_data.verify().is_err()); + assert!(transfer_data.verify_proof().is_err()); } #[test] @@ -671,7 +679,9 @@ mod test { ); assert_eq!( - transfer_data.decrypt_amount(Role::Dest, &dest_sk).unwrap(), + transfer_data + .decrypt_amount(Role::Destination, &dest_sk) + .unwrap(), 550000_u64, ); diff --git a/zk-token-sdk/src/instruction/transfer_with_fee.rs b/zk-token-sdk/src/instruction/transfer_with_fee.rs index bdab2e2d8a7337..45a9496d639aa8 100644 --- a/zk-token-sdk/src/instruction/transfer_with_fee.rs +++ b/zk-token-sdk/src/instruction/transfer_with_fee.rs @@ -1,7 +1,3 @@ -use { - crate::zk_token_elgamal::pod, - bytemuck::{Pod, Zeroable}, -}; #[cfg(not(target_os = "solana"))] use { crate::{ @@ -14,13 +10,12 @@ use { errors::ProofError, instruction::{ combine_lo_hi_ciphertexts, combine_lo_hi_commitments, combine_lo_hi_openings, - split_u64, transfer::TransferAmountEncryption, Role, Verifiable, + combine_lo_hi_u64, split_u64, transfer::TransferAmountEncryption, Role, }, range_proof::RangeProof, sigma_proofs::{ - equality_proof::CtxtCommEqualityProof, - fee_proof::FeeSigmaProof, - validity_proof::{AggregatedValidityProof, ValidityProof}, + ctxt_comm_equality_proof::CiphertextCommitmentEqualityProof, fee_proof::FeeSigmaProof, + validity_proof::AggregatedValidityProof, }, transcript::TranscriptProtocol, }, @@ -30,6 +25,13 @@ use { std::convert::TryInto, subtle::{ConditionallySelectable, ConstantTimeGreater}, }; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; #[cfg(not(target_os = "solana"))] const MAX_FEE_BASIS_POINTS: u64 = 10_000; @@ -45,7 +47,11 @@ const TRANSFER_AMOUNT_LO_NEGATED_BITS: usize = 16; #[cfg(not(target_os = "solana"))] const TRANSFER_AMOUNT_HI_BITS: usize = 32; #[cfg(not(target_os = "solana"))] -const TRANSFER_DELTA_BITS: usize = 64; +const TRANSFER_DELTA_BITS: usize = 48; +#[cfg(not(target_os = "solana"))] +const FEE_AMOUNT_LO_BITS: usize = 16; +#[cfg(not(target_os = "solana"))] +const FEE_AMOUNT_HI_BITS: usize = 32; #[cfg(not(target_os = "solana"))] lazy_static::lazy_static! { @@ -54,30 +60,44 @@ lazy_static::lazy_static! { pub static ref COMMITMENT_MAX_FEE_BASIS_POINTS: PedersenCommitment = Pedersen::encode(MAX_FEE_BASIS_POINTS); } -// #[derive(Clone, Copy, Pod, Zeroable)] +/// The instruction data that is needed for the `ProofInstruction::TransferWithFee` instruction. +/// +/// It includes the cryptographic proof as well as the cotnext data information needed to verify +/// the proof. #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct TransferWithFeeData { - /// Group encryption of the low 32 bites of the transfer amount - pub ciphertext_lo: pod::TransferAmountEncryption, + /// The context data for the transfer with fee proof + pub context: TransferWithFeeProofContext, + + // transfer fee proof + pub proof: TransferWithFeeProof, +} + +/// The context data needed to verify a transfer-with-fee proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct TransferWithFeeProofContext { + /// Group encryption of the low 16 bites of the transfer amount + pub ciphertext_lo: pod::TransferAmountEncryption, // 128 bytes - /// Group encryption of the high 32 bits of the transfer amount - pub ciphertext_hi: pod::TransferAmountEncryption, + /// Group encryption of the high 48 bits of the transfer amount + pub ciphertext_hi: pod::TransferAmountEncryption, // 128 bytes /// The public encryption keys associated with the transfer: source, dest, and auditor - pub transfer_with_fee_pubkeys: pod::TransferWithFeePubkeys, + pub transfer_with_fee_pubkeys: pod::TransferWithFeePubkeys, // 128 bytes /// The final spendable ciphertext after the transfer, - pub new_source_ciphertext: pod::ElGamalCiphertext, + pub new_source_ciphertext: pod::ElGamalCiphertext, // 64 bytes - // transfer fee encryption - pub fee_ciphertext: pod::FeeEncryption, + // transfer fee encryption of the low 16 bits of the transfer fee amount + pub fee_ciphertext_lo: pod::FeeEncryption, // 96 bytes - // fee parameters - pub fee_parameters: pod::FeeParameters, + // transfer fee encryption of the hi 32 bits of the transfer fee amount + pub fee_ciphertext_hi: pod::FeeEncryption, // 96 bytes - // transfer fee proof - pub proof: TransferWithFeeProof, + // fee parameters + pub fee_parameters: pod::FeeParameters, // 10 bytes } #[cfg(not(target_os = "solana"))] @@ -91,11 +111,7 @@ impl TransferWithFeeData { withdraw_withheld_authority_pubkey: &ElGamalPubkey, ) -> Result { // split and encrypt transfer amount - let (amount_lo, amount_hi) = split_u64( - transfer_amount, - TRANSFER_AMOUNT_LO_BITS, - TRANSFER_AMOUNT_HI_BITS, - )?; + let (amount_lo, amount_hi) = split_u64(transfer_amount, TRANSFER_AMOUNT_LO_BITS); let (ciphertext_lo, opening_lo) = TransferAmountEncryption::new( amount_lo, @@ -132,7 +148,9 @@ impl TransferWithFeeData { TRANSFER_AMOUNT_LO_BITS, ); - // calculate and encrypt fee + // calculate fee + // + // TODO: add comment on delta fee let (fee_amount, delta_fee) = calculate_fee(transfer_amount, fee_parameters.fee_rate_basis_points) .ok_or(ProofError::Generation)?; @@ -141,8 +159,17 @@ impl TransferWithFeeData { let fee_to_encrypt = u64::conditional_select(&fee_parameters.maximum_fee, &fee_amount, below_max); - let (fee_ciphertext, opening_fee) = FeeEncryption::new( - fee_to_encrypt, + // split and encrypt fee + let (fee_to_encrypt_lo, fee_to_encrypt_hi) = split_u64(fee_to_encrypt, FEE_AMOUNT_LO_BITS); + + let (fee_ciphertext_lo, opening_fee_lo) = FeeEncryption::new( + fee_to_encrypt_lo, + destination_pubkey, + withdraw_withheld_authority_pubkey, + ); + + let (fee_ciphertext_hi, opening_fee_hi) = FeeEncryption::new( + fee_to_encrypt_hi, destination_pubkey, withdraw_withheld_authority_pubkey, ); @@ -157,14 +184,26 @@ impl TransferWithFeeData { let pod_ciphertext_lo: pod::TransferAmountEncryption = ciphertext_lo.to_pod(); let pod_ciphertext_hi: pod::TransferAmountEncryption = ciphertext_hi.to_pod(); let pod_new_source_ciphertext: pod::ElGamalCiphertext = new_source_ciphertext.into(); - let pod_fee_ciphertext: pod::FeeEncryption = fee_ciphertext.to_pod(); + let pod_fee_ciphertext_lo: pod::FeeEncryption = fee_ciphertext_lo.to_pod(); + let pod_fee_ciphertext_hi: pod::FeeEncryption = fee_ciphertext_hi.to_pod(); + + let context = TransferWithFeeProofContext { + ciphertext_lo: pod_ciphertext_lo, + ciphertext_hi: pod_ciphertext_hi, + transfer_with_fee_pubkeys: pod_transfer_with_fee_pubkeys, + new_source_ciphertext: pod_new_source_ciphertext, + fee_ciphertext_lo: pod_fee_ciphertext_lo, + fee_ciphertext_hi: pod_fee_ciphertext_hi, + fee_parameters: fee_parameters.into(), + }; let mut transcript = TransferWithFeeProof::transcript_new( &pod_transfer_with_fee_pubkeys, &pod_ciphertext_lo, &pod_ciphertext_hi, &pod_new_source_ciphertext, - &pod_fee_ciphertext, + &pod_fee_ciphertext_lo, + &pod_fee_ciphertext_hi, ); let proof = TransferWithFeeProof::new( @@ -173,54 +212,103 @@ impl TransferWithFeeData { source_keypair, (destination_pubkey, auditor_pubkey), (new_spendable_balance, &new_source_ciphertext), - (fee_to_encrypt, &fee_ciphertext, &opening_fee), + (fee_to_encrypt_lo, &fee_ciphertext_lo, &opening_fee_lo), + (fee_to_encrypt_hi, &fee_ciphertext_hi, &opening_fee_hi), delta_fee, withdraw_withheld_authority_pubkey, fee_parameters, &mut transcript, ); - Ok(Self { - ciphertext_lo: pod_ciphertext_lo, - ciphertext_hi: pod_ciphertext_hi, - transfer_with_fee_pubkeys: pod_transfer_with_fee_pubkeys, - new_source_ciphertext: pod_new_source_ciphertext, - fee_ciphertext: pod_fee_ciphertext, - fee_parameters: fee_parameters.into(), - proof, - }) + Ok(Self { context, proof }) } /// Extracts the lo ciphertexts associated with a transfer-with-fee data fn ciphertext_lo(&self, role: Role) -> Result { - let ciphertext_lo: TransferAmountEncryption = self.ciphertext_lo.try_into()?; + let ciphertext_lo: TransferAmountEncryption = self.context.ciphertext_lo.try_into()?; let handle_lo = match role { - Role::Source => ciphertext_lo.source_handle, - Role::Dest => ciphertext_lo.destination_handle, - Role::Auditor => ciphertext_lo.auditor_handle, + Role::Source => Some(ciphertext_lo.source_handle), + Role::Destination => Some(ciphertext_lo.destination_handle), + Role::Auditor => Some(ciphertext_lo.auditor_handle), + Role::WithdrawWithheldAuthority => None, }; - Ok(ElGamalCiphertext { - commitment: ciphertext_lo.commitment, - handle: handle_lo, - }) + if let Some(handle) = handle_lo { + Ok(ElGamalCiphertext { + commitment: ciphertext_lo.commitment, + handle, + }) + } else { + Err(ProofError::MissingCiphertext) + } } /// Extracts the lo ciphertexts associated with a transfer-with-fee data fn ciphertext_hi(&self, role: Role) -> Result { - let ciphertext_hi: TransferAmountEncryption = self.ciphertext_hi.try_into()?; + let ciphertext_hi: TransferAmountEncryption = self.context.ciphertext_hi.try_into()?; let handle_hi = match role { - Role::Source => ciphertext_hi.source_handle, - Role::Dest => ciphertext_hi.destination_handle, - Role::Auditor => ciphertext_hi.auditor_handle, + Role::Source => Some(ciphertext_hi.source_handle), + Role::Destination => Some(ciphertext_hi.destination_handle), + Role::Auditor => Some(ciphertext_hi.auditor_handle), + Role::WithdrawWithheldAuthority => None, }; - Ok(ElGamalCiphertext { - commitment: ciphertext_hi.commitment, - handle: handle_hi, - }) + if let Some(handle) = handle_hi { + Ok(ElGamalCiphertext { + commitment: ciphertext_hi.commitment, + handle, + }) + } else { + Err(ProofError::MissingCiphertext) + } + } + + /// Extracts the lo fee ciphertexts associated with a transfer_with_fee data + fn fee_ciphertext_lo(&self, role: Role) -> Result { + let fee_ciphertext_lo: FeeEncryption = self.context.fee_ciphertext_lo.try_into()?; + + let fee_handle_lo = match role { + Role::Source => None, + Role::Destination => Some(fee_ciphertext_lo.destination_handle), + Role::Auditor => None, + Role::WithdrawWithheldAuthority => { + Some(fee_ciphertext_lo.withdraw_withheld_authority_handle) + } + }; + + if let Some(handle) = fee_handle_lo { + Ok(ElGamalCiphertext { + commitment: fee_ciphertext_lo.commitment, + handle, + }) + } else { + Err(ProofError::MissingCiphertext) + } + } + + /// Extracts the hi fee ciphertexts associated with a transfer_with_fee data + fn fee_ciphertext_hi(&self, role: Role) -> Result { + let fee_ciphertext_hi: FeeEncryption = self.context.fee_ciphertext_hi.try_into()?; + + let fee_handle_hi = match role { + Role::Source => None, + Role::Destination => Some(fee_ciphertext_hi.destination_handle), + Role::Auditor => None, + Role::WithdrawWithheldAuthority => { + Some(fee_ciphertext_hi.withdraw_withheld_authority_handle) + } + }; + + if let Some(handle) = fee_handle_hi { + Ok(ElGamalCiphertext { + commitment: fee_ciphertext_hi.commitment, + handle, + }) + } else { + Err(ProofError::MissingCiphertext) + } } /// Decrypts transfer amount from transfer-with-fee data @@ -232,39 +320,64 @@ impl TransferWithFeeData { let amount_hi = ciphertext_hi.decrypt_u32(sk); if let (Some(amount_lo), Some(amount_hi)) = (amount_lo, amount_hi) { - let two_power = 1 << TRANSFER_AMOUNT_LO_BITS; - Ok(amount_lo + two_power * amount_hi) + let shifted_amount_hi = amount_hi << TRANSFER_AMOUNT_LO_BITS; + Ok(amount_lo + shifted_amount_hi) } else { - Err(ProofError::Verification) + Err(ProofError::Decryption) + } + } + + /// Decrypts transfer amount from transfer-with-fee data + pub fn decrypt_fee_amount(&self, role: Role, sk: &ElGamalSecretKey) -> Result { + let ciphertext_lo = self.fee_ciphertext_lo(role)?; + let ciphertext_hi = self.fee_ciphertext_hi(role)?; + + let fee_amount_lo = ciphertext_lo.decrypt_u32(sk); + let fee_amount_hi = ciphertext_hi.decrypt_u32(sk); + + if let (Some(fee_amount_lo), Some(fee_amount_hi)) = (fee_amount_lo, fee_amount_hi) { + let shifted_fee_amount_hi = fee_amount_hi << FEE_AMOUNT_LO_BITS; + Ok(fee_amount_lo + shifted_fee_amount_hi) + } else { + Err(ProofError::Decryption) } } } -#[cfg(not(target_os = "solana"))] -impl Verifiable for TransferWithFeeData { - fn verify(&self) -> Result<(), ProofError> { +impl ZkProofData for TransferWithFeeData { + const PROOF_TYPE: ProofType = ProofType::TransferWithFee; + + fn context_data(&self) -> &TransferWithFeeProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { let mut transcript = TransferWithFeeProof::transcript_new( - &self.transfer_with_fee_pubkeys, - &self.ciphertext_lo, - &self.ciphertext_hi, - &self.new_source_ciphertext, - &self.fee_ciphertext, + &self.context.transfer_with_fee_pubkeys, + &self.context.ciphertext_lo, + &self.context.ciphertext_hi, + &self.context.new_source_ciphertext, + &self.context.fee_ciphertext_lo, + &self.context.fee_ciphertext_hi, ); - let ciphertext_lo = self.ciphertext_lo.try_into()?; - let ciphertext_hi = self.ciphertext_hi.try_into()?; - let pubkeys_transfer_with_fee = self.transfer_with_fee_pubkeys.try_into()?; - let new_source_ciphertext = self.new_source_ciphertext.try_into()?; + let ciphertext_lo = self.context.ciphertext_lo.try_into()?; + let ciphertext_hi = self.context.ciphertext_hi.try_into()?; + let pubkeys_transfer_with_fee = self.context.transfer_with_fee_pubkeys.try_into()?; + let new_source_ciphertext = self.context.new_source_ciphertext.try_into()?; - let fee_ciphertext = self.fee_ciphertext.try_into()?; - let fee_parameters = self.fee_parameters.into(); + let fee_ciphertext_lo = self.context.fee_ciphertext_lo.try_into()?; + let fee_ciphertext_hi = self.context.fee_ciphertext_hi.try_into()?; + let fee_parameters = self.context.fee_parameters.into(); self.proof.verify( &ciphertext_lo, &ciphertext_hi, &pubkeys_transfer_with_fee, &new_source_ciphertext, - &fee_ciphertext, + &fee_ciphertext_lo, + &fee_ciphertext_hi, fee_parameters, &mut transcript, ) @@ -277,10 +390,10 @@ impl Verifiable for TransferWithFeeData { pub struct TransferWithFeeProof { pub new_source_commitment: pod::PedersenCommitment, pub claimed_commitment: pod::PedersenCommitment, - pub equality_proof: pod::CtxtCommEqualityProof, + pub equality_proof: pod::CiphertextCommitmentEqualityProof, pub ciphertext_amount_validity_proof: pod::AggregatedValidityProof, pub fee_sigma_proof: pod::FeeSigmaProof, - pub fee_ciphertext_validity_proof: pod::ValidityProof, + pub fee_ciphertext_validity_proof: pod::AggregatedValidityProof, pub range_proof: pod::RangeProof256, } @@ -292,7 +405,8 @@ impl TransferWithFeeProof { ciphertext_lo: &pod::TransferAmountEncryption, ciphertext_hi: &pod::TransferAmountEncryption, new_source_ciphertext: &pod::ElGamalCiphertext, - fee_ciphertext: &pod::FeeEncryption, + fee_ciphertext_lo: &pod::FeeEncryption, + fee_ciphertext_hi: &pod::FeeEncryption, ) -> Transcript { let mut transcript = Transcript::new(b"FeeProof"); @@ -319,11 +433,18 @@ impl TransferWithFeeProof { transcript.append_ciphertext(b"ctxt-new-source", new_source_ciphertext); - transcript.append_commitment(b"comm-fee", &fee_ciphertext.commitment); - transcript.append_handle(b"fee-dest-handle", &fee_ciphertext.destination_handle); + transcript.append_commitment(b"comm-fee-lo", &fee_ciphertext_lo.commitment); + transcript.append_handle(b"handle-fee-lo-dest", &fee_ciphertext_lo.destination_handle); transcript.append_handle( - b"handle-fee-auditor", - &fee_ciphertext.withdraw_withheld_authority_handle, + b"handle-fee-lo-auditor", + &fee_ciphertext_lo.withdraw_withheld_authority_handle, + ); + + transcript.append_commitment(b"comm-fee-hi", &fee_ciphertext_hi.commitment); + transcript.append_handle(b"handle-fee-hi-dest", &fee_ciphertext_hi.destination_handle); + transcript.append_handle( + b"handle-fee-hi-auditor", + &fee_ciphertext_hi.withdraw_withheld_authority_handle, ); transcript @@ -337,8 +458,9 @@ impl TransferWithFeeProof { source_keypair: &ElGamalKeypair, (destination_pubkey, auditor_pubkey): (&ElGamalPubkey, &ElGamalPubkey), (source_new_balance, new_source_ciphertext): (u64, &ElGamalCiphertext), - - (fee_amount, fee_ciphertext, opening_fee): (u64, &FeeEncryption, &PedersenOpening), + // fee parameters + (fee_amount_lo, fee_ciphertext_lo, opening_fee_lo): (u64, &FeeEncryption, &PedersenOpening), + (fee_amount_hi, fee_ciphertext_hi, opening_fee_hi): (u64, &FeeEncryption, &PedersenOpening), delta_fee: u64, withdraw_withheld_authority_pubkey: &ElGamalPubkey, fee_parameters: FeeParameters, @@ -349,20 +471,16 @@ impl TransferWithFeeProof { // generate a Pedersen commitment for the remaining balance in source let (new_source_commitment, opening_source) = Pedersen::new(source_new_balance); - let (claimed_commitment, opening_claimed) = Pedersen::new(delta_fee); - let pod_new_source_commitment: pod::PedersenCommitment = new_source_commitment.into(); - let pod_claimed_commitment: pod::PedersenCommitment = claimed_commitment.into(); transcript.append_commitment(b"commitment-new-source", &pod_new_source_commitment); - transcript.append_commitment(b"commitment-claimed", &pod_claimed_commitment); // generate equality_proof - let equality_proof = CtxtCommEqualityProof::new( + let equality_proof = CiphertextCommitmentEqualityProof::new( source_keypair, new_source_ciphertext, - source_new_balance, &opening_source, + source_new_balance, transcript, ); @@ -374,88 +492,91 @@ impl TransferWithFeeProof { transcript, ); + // compute claimed delta commitment + let (claimed_commitment, opening_claimed) = Pedersen::new(delta_fee); + let pod_claimed_commitment: pod::PedersenCommitment = claimed_commitment.into(); + transcript.append_commitment(b"commitment-claimed", &pod_claimed_commitment); + + let combined_commitment = combine_lo_hi_commitments( + &ciphertext_lo.commitment, + &ciphertext_hi.commitment, + TRANSFER_AMOUNT_LO_BITS, + ); + let combined_opening = + combine_lo_hi_openings(opening_lo, opening_hi, TRANSFER_AMOUNT_LO_BITS); + + let combined_fee_amount = + combine_lo_hi_u64(fee_amount_lo, fee_amount_hi, TRANSFER_AMOUNT_LO_BITS); + let combined_fee_commitment = combine_lo_hi_commitments( + &fee_ciphertext_lo.commitment, + &fee_ciphertext_hi.commitment, + TRANSFER_AMOUNT_LO_BITS, + ); + let combined_fee_opening = + combine_lo_hi_openings(opening_fee_lo, opening_fee_hi, TRANSFER_AMOUNT_LO_BITS); + + // compute real delta commitment let (delta_commitment, opening_delta) = compute_delta_commitment_and_opening( - (&ciphertext_lo.commitment, opening_lo), - (&ciphertext_hi.commitment, opening_hi), - (&fee_ciphertext.commitment, opening_fee), + (&combined_commitment, &combined_opening), + (&combined_fee_commitment, &combined_fee_opening), fee_parameters.fee_rate_basis_points, ); + let pod_delta_commitment: pod::PedersenCommitment = delta_commitment.into(); + transcript.append_commitment(b"commitment-delta", &pod_delta_commitment); + // generate fee sigma proof let fee_sigma_proof = FeeSigmaProof::new( - (fee_amount, &fee_ciphertext.commitment, opening_fee), + ( + combined_fee_amount, + &combined_fee_commitment, + &combined_fee_opening, + ), (delta_fee, &delta_commitment, &opening_delta), (&claimed_commitment, &opening_claimed), fee_parameters.maximum_fee, transcript, ); - let fee_ciphertext_validity_proof = ValidityProof::new( + // generate ciphertext validity proof for fee ciphertexts + let fee_ciphertext_validity_proof = AggregatedValidityProof::new( (destination_pubkey, withdraw_withheld_authority_pubkey), - fee_amount, - opening_fee, + (fee_amount_lo, fee_amount_hi), + (opening_fee_lo, opening_fee_hi), transcript, ); // generate the range proof let opening_claimed_negated = &PedersenOpening::default() - &opening_claimed; - let range_proof = if TRANSFER_AMOUNT_LO_BITS == 32 { - RangeProof::new( - vec![ - source_new_balance, - transfer_amount_lo as u64, - transfer_amount_hi as u64, - delta_fee, - MAX_FEE_BASIS_POINTS - delta_fee, - ], - vec![ - TRANSFER_SOURCE_AMOUNT_BITS, - TRANSFER_AMOUNT_LO_BITS, - TRANSFER_AMOUNT_HI_BITS, - TRANSFER_DELTA_BITS, - TRANSFER_DELTA_BITS, - ], - vec![ - &opening_source, - opening_lo, - opening_hi, - &opening_claimed, - &opening_claimed_negated, - ], - transcript, - ) - } else { - let transfer_amount_lo_negated = - ((1 << TRANSFER_AMOUNT_LO_NEGATED_BITS) - 1) - transfer_amount_lo as u64; - let opening_lo_negated = &PedersenOpening::default() - opening_lo; - - RangeProof::new( - vec![ - source_new_balance, - transfer_amount_lo as u64, - transfer_amount_lo_negated, - transfer_amount_hi as u64, - delta_fee, - MAX_FEE_BASIS_POINTS - delta_fee, - ], - vec![ - TRANSFER_SOURCE_AMOUNT_BITS, - TRANSFER_AMOUNT_LO_BITS, - TRANSFER_AMOUNT_LO_NEGATED_BITS, - TRANSFER_AMOUNT_HI_BITS, - TRANSFER_DELTA_BITS, - TRANSFER_DELTA_BITS, - ], - vec![ - &opening_source, - opening_lo, - &opening_lo_negated, - opening_hi, - &opening_claimed, - &opening_claimed_negated, - ], - transcript, - ) - }; + let range_proof = RangeProof::new( + vec![ + source_new_balance, + transfer_amount_lo, + transfer_amount_hi, + delta_fee, + MAX_FEE_BASIS_POINTS - delta_fee, + fee_amount_lo, + fee_amount_hi, + ], + vec![ + TRANSFER_SOURCE_AMOUNT_BITS, // 64 + TRANSFER_AMOUNT_LO_BITS, // 16 + TRANSFER_AMOUNT_HI_BITS, // 32 + TRANSFER_DELTA_BITS, // 48 + TRANSFER_DELTA_BITS, // 48 + FEE_AMOUNT_LO_BITS, // 16 + FEE_AMOUNT_HI_BITS, // 32 + ], + vec![ + &opening_source, + opening_lo, + opening_hi, + &opening_claimed, + &opening_claimed_negated, + opening_fee_lo, + opening_fee_hi, + ], + transcript, + ); Self { new_source_commitment: pod_new_source_commitment, @@ -474,22 +595,22 @@ impl TransferWithFeeProof { ciphertext_hi: &TransferAmountEncryption, transfer_with_fee_pubkeys: &TransferWithFeePubkeys, new_spendable_ciphertext: &ElGamalCiphertext, - - fee_ciphertext: &FeeEncryption, + // fee parameters + fee_ciphertext_lo: &FeeEncryption, + fee_ciphertext_hi: &FeeEncryption, fee_parameters: FeeParameters, transcript: &mut Transcript, ) -> Result<(), ProofError> { transcript.append_commitment(b"commitment-new-source", &self.new_source_commitment); - transcript.append_commitment(b"commitment-claimed", &self.claimed_commitment); let new_source_commitment: PedersenCommitment = self.new_source_commitment.try_into()?; let claimed_commitment: PedersenCommitment = self.claimed_commitment.try_into()?; - let equality_proof: CtxtCommEqualityProof = self.equality_proof.try_into()?; + let equality_proof: CiphertextCommitmentEqualityProof = self.equality_proof.try_into()?; let ciphertext_amount_validity_proof: AggregatedValidityProof = self.ciphertext_amount_validity_proof.try_into()?; let fee_sigma_proof: FeeSigmaProof = self.fee_sigma_proof.try_into()?; - let fee_ciphertext_validity_proof: ValidityProof = + let fee_ciphertext_validity_proof: AggregatedValidityProof = self.fee_ciphertext_validity_proof.try_into()?; let range_proof: RangeProof = self.range_proof.try_into()?; @@ -517,30 +638,51 @@ impl TransferWithFeeProof { )?; // verify fee sigma proof - let delta_commitment = compute_delta_commitment( + transcript.append_commitment(b"commitment-claimed", &self.claimed_commitment); + + let combined_commitment = combine_lo_hi_commitments( &ciphertext_lo.commitment, &ciphertext_hi.commitment, - &fee_ciphertext.commitment, + TRANSFER_AMOUNT_LO_BITS, + ); + let combined_fee_commitment = combine_lo_hi_commitments( + &fee_ciphertext_lo.commitment, + &fee_ciphertext_hi.commitment, + TRANSFER_AMOUNT_LO_BITS, + ); + + let delta_commitment = compute_delta_commitment( + &combined_commitment, + &combined_fee_commitment, fee_parameters.fee_rate_basis_points, ); + let pod_delta_commitment: pod::PedersenCommitment = delta_commitment.into(); + transcript.append_commitment(b"commitment-delta", &pod_delta_commitment); + + // verify fee sigma proof fee_sigma_proof.verify( - &fee_ciphertext.commitment, + &combined_fee_commitment, &delta_commitment, &claimed_commitment, fee_parameters.maximum_fee, transcript, )?; + // verify ciphertext validity proof for fee ciphertexts fee_ciphertext_validity_proof.verify( - &fee_ciphertext.commitment, ( &transfer_with_fee_pubkeys.destination_pubkey, &transfer_with_fee_pubkeys.withdraw_withheld_authority_pubkey, ), + (&fee_ciphertext_lo.commitment, &fee_ciphertext_hi.commitment), + ( + &fee_ciphertext_lo.destination_handle, + &fee_ciphertext_hi.destination_handle, + ), ( - &fee_ciphertext.destination_handle, - &fee_ciphertext.withdraw_withheld_authority_handle, + &fee_ciphertext_lo.withdraw_withheld_authority_handle, + &fee_ciphertext_hi.withdraw_withheld_authority_handle, ), transcript, )?; @@ -549,34 +691,27 @@ impl TransferWithFeeProof { let new_source_commitment = self.new_source_commitment.try_into()?; let claimed_commitment_negated = &(*COMMITMENT_MAX_FEE_BASIS_POINTS) - &claimed_commitment; - if TRANSFER_AMOUNT_LO_BITS == 32 { - range_proof.verify( - vec![ - &new_source_commitment, - &ciphertext_lo.commitment, - &ciphertext_hi.commitment, - &claimed_commitment, - &claimed_commitment_negated, - ], - vec![64, 32, 32, 64, 64], - transcript, - )?; - } else { - let commitment_lo_negated = &(*COMMITMENT_MAX) - &ciphertext_lo.commitment; - - range_proof.verify( - vec![ - &new_source_commitment, - &ciphertext_lo.commitment, - &commitment_lo_negated, - &ciphertext_hi.commitment, - &claimed_commitment, - &claimed_commitment_negated, - ], - vec![64, 16, 16, 32, 64, 64], - transcript, - )?; - } + range_proof.verify( + vec![ + &new_source_commitment, + &ciphertext_lo.commitment, + &ciphertext_hi.commitment, + &claimed_commitment, + &claimed_commitment_negated, + &fee_ciphertext_lo.commitment, + &fee_ciphertext_hi.commitment, + ], + vec![ + TRANSFER_SOURCE_AMOUNT_BITS, // 64 + TRANSFER_AMOUNT_LO_BITS, // 16 + TRANSFER_AMOUNT_HI_BITS, // 32 + TRANSFER_DELTA_BITS, // 48 + TRANSFER_DELTA_BITS, // 48 + FEE_AMOUNT_LO_BITS, // 16 + FEE_AMOUNT_HI_BITS, // 32 + ], + transcript, + )?; Ok(()) } @@ -610,14 +745,14 @@ impl TransferWithFeePubkeys { array_refs![bytes, 32, 32, 32, 32]; let source_pubkey = - ElGamalPubkey::from_bytes(source_pubkey).ok_or(ProofError::Verification)?; - let destination_pubkey = - ElGamalPubkey::from_bytes(destination_pubkey).ok_or(ProofError::Verification)?; + ElGamalPubkey::from_bytes(source_pubkey).ok_or(ProofError::PubkeyDeserialization)?; + let destination_pubkey = ElGamalPubkey::from_bytes(destination_pubkey) + .ok_or(ProofError::PubkeyDeserialization)?; let auditor_pubkey = - ElGamalPubkey::from_bytes(auditor_pubkey).ok_or(ProofError::Verification)?; + ElGamalPubkey::from_bytes(auditor_pubkey).ok_or(ProofError::PubkeyDeserialization)?; let withdraw_withheld_authority_pubkey = ElGamalPubkey::from_bytes(withdraw_withheld_authority_pubkey) - .ok_or(ProofError::Verification)?; + .ok_or(ProofError::PubkeyDeserialization)?; Ok(Self { source_pubkey, @@ -697,53 +832,47 @@ impl FeeParameters { #[cfg(not(target_os = "solana"))] fn calculate_fee(transfer_amount: u64, fee_rate_basis_points: u16) -> Option<(u64, u64)> { let numerator = (transfer_amount as u128).checked_mul(fee_rate_basis_points as u128)?; - let mut fee = numerator.checked_div(ONE_IN_BASIS_POINTS)?; - let mut delta_fee = 0_u128; - let remainder = numerator.checked_rem(ONE_IN_BASIS_POINTS)?; - if remainder > 0 { - fee = fee.checked_add(1)?; + // Warning: Division may involve CPU opcodes that have variable execution times. This + // non-constant-time execution of the fee calculation can theoretically reveal information + // about the transfer amount. For transfers that invole extremely sensitive data, additional + // care should be put into how the fees are calculated. + let fee = numerator + .checked_add(ONE_IN_BASIS_POINTS)? + .checked_sub(1)? + .checked_div(ONE_IN_BASIS_POINTS)?; - let scaled_fee = fee.checked_mul(ONE_IN_BASIS_POINTS)?; - delta_fee = scaled_fee.checked_sub(numerator)?; - } + let delta_fee = fee + .checked_mul(ONE_IN_BASIS_POINTS)? + .checked_sub(numerator)?; - let fee = u64::try_from(fee).ok()?; Some((fee as u64, delta_fee as u64)) } #[cfg(not(target_os = "solana"))] fn compute_delta_commitment_and_opening( - (commitment_lo, opening_lo): (&PedersenCommitment, &PedersenOpening), - (commitment_hi, opening_hi): (&PedersenCommitment, &PedersenOpening), - (fee_commitment, opening_fee): (&PedersenCommitment, &PedersenOpening), + (combined_commitment, combined_opening): (&PedersenCommitment, &PedersenOpening), + (combined_fee_commitment, combined_fee_opening): (&PedersenCommitment, &PedersenOpening), fee_rate_basis_points: u16, ) -> (PedersenCommitment, PedersenOpening) { let fee_rate_scalar = Scalar::from(fee_rate_basis_points); + let delta_commitment = combined_fee_commitment * Scalar::from(MAX_FEE_BASIS_POINTS) + - combined_commitment * &fee_rate_scalar; + let delta_opening = combined_fee_opening * Scalar::from(MAX_FEE_BASIS_POINTS) + - combined_opening * &fee_rate_scalar; - let delta_commitment = fee_commitment * Scalar::from(MAX_FEE_BASIS_POINTS) - - &(&combine_lo_hi_commitments(commitment_lo, commitment_hi, TRANSFER_AMOUNT_LO_BITS) - * &fee_rate_scalar); - - let opening_delta = opening_fee * Scalar::from(MAX_FEE_BASIS_POINTS) - - &(&combine_lo_hi_openings(opening_lo, opening_hi, TRANSFER_AMOUNT_LO_BITS) - * &fee_rate_scalar); - - (delta_commitment, opening_delta) + (delta_commitment, delta_opening) } #[cfg(not(target_os = "solana"))] fn compute_delta_commitment( - commitment_lo: &PedersenCommitment, - commitment_hi: &PedersenCommitment, - fee_commitment: &PedersenCommitment, + combined_commitment: &PedersenCommitment, + combined_fee_commitment: &PedersenCommitment, fee_rate_basis_points: u16, ) -> PedersenCommitment { let fee_rate_scalar = Scalar::from(fee_rate_basis_points); - - fee_commitment * Scalar::from(MAX_FEE_BASIS_POINTS) - - &(&combine_lo_hi_commitments(commitment_lo, commitment_hi, TRANSFER_AMOUNT_LO_BITS) - * &fee_rate_scalar) + combined_fee_commitment * Scalar::from(MAX_FEE_BASIS_POINTS) + - combined_commitment * &fee_rate_scalar } #[cfg(test)] @@ -778,7 +907,7 @@ mod test { ) .unwrap(); - assert!(fee_data.verify().is_ok()); + assert!(fee_data.verify_proof().is_ok()); // Case 2: transfer max amount let spendable_balance: u64 = u64::max_value(); @@ -802,7 +931,7 @@ mod test { ) .unwrap(); - assert!(fee_data.verify().is_ok()); + assert!(fee_data.verify_proof().is_ok()); // Case 3: general success case let spendable_balance: u64 = 120; @@ -825,31 +954,9 @@ mod test { ) .unwrap(); - assert!(fee_data.verify().is_ok()); - - // Case 4: transfer amount too big - let spendable_balance: u64 = u64::max_value(); - let spendable_ciphertext = source_keypair.public.encrypt(spendable_balance); - - let transfer_amount: u64 = 1u64 << (TRANSFER_AMOUNT_LO_BITS + TRANSFER_AMOUNT_HI_BITS); - - let fee_parameters = FeeParameters { - fee_rate_basis_points: 400, - maximum_fee: 3, - }; - - let fee_data = TransferWithFeeData::new( - transfer_amount, - (spendable_balance, &spendable_ciphertext), - &source_keypair, - (&destination_pubkey, &auditor_pubkey), - fee_parameters, - &withdraw_withheld_authority_pubkey, - ); - - assert!(fee_data.is_err()); + assert!(fee_data.verify_proof().is_ok()); - // Case 5: invalid destination, auditor, or withdraw authority pubkeys + // Case 4: invalid destination, auditor, or withdraw authority pubkeys let spendable_balance: u64 = 120; let spendable_ciphertext = source_keypair.public.encrypt(spendable_balance); @@ -875,7 +982,7 @@ mod test { ) .unwrap(); - assert!(fee_data.verify().is_err()); + assert!(fee_data.verify_proof().is_err()); // auditor pubkey invalid let destination_pubkey: ElGamalPubkey = ElGamalKeypair::new_rand().public; @@ -892,7 +999,7 @@ mod test { ) .unwrap(); - assert!(fee_data.verify().is_err()); + assert!(fee_data.verify_proof().is_err()); // withdraw authority invalid let destination_pubkey: ElGamalPubkey = ElGamalKeypair::new_rand().public; @@ -909,6 +1016,6 @@ mod test { ) .unwrap(); - assert!(fee_data.verify().is_err()); + assert!(fee_data.verify_proof().is_err()); } } diff --git a/zk-token-sdk/src/instruction/withdraw.rs b/zk-token-sdk/src/instruction/withdraw.rs index 9aa606e8ca4203..5b940d99bc4f1b 100644 --- a/zk-token-sdk/src/instruction/withdraw.rs +++ b/zk-token-sdk/src/instruction/withdraw.rs @@ -1,7 +1,3 @@ -use { - crate::zk_token_elgamal::pod, - bytemuck::{Pod, Zeroable}, -}; #[cfg(not(target_os = "solana"))] use { crate::{ @@ -10,41 +6,52 @@ use { pedersen::{Pedersen, PedersenCommitment}, }, errors::ProofError, - instruction::Verifiable, range_proof::RangeProof, - sigma_proofs::equality_proof::CtxtCommEqualityProof, + sigma_proofs::ctxt_comm_equality_proof::CiphertextCommitmentEqualityProof, transcript::TranscriptProtocol, }, merlin::Transcript, std::convert::TryInto, }; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; #[cfg(not(target_os = "solana"))] const WITHDRAW_AMOUNT_BIT_LENGTH: usize = 64; -/// This struct includes the cryptographic proof *and* the account data information needed to verify -/// the proof -/// -/// - The pre-instruction should call WithdrawData::verify_proof(&self) -/// - The actual program should check that `current_ct` is consistent with what is -/// currently stored in the confidential token account TODO: update this statement +/// The instruction data that is needed for the `ProofInstruction::VerifyWithdraw` instruction. /// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct WithdrawData { + /// The context data for the withdraw proof + pub context: WithdrawProofContext, // 128 bytes + + /// Range proof + pub proof: WithdrawProof, // 736 bytes +} + +/// The context data needed to verify a withdraw proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct WithdrawProofContext { /// The source account ElGamal pubkey pub pubkey: pod::ElGamalPubkey, // 32 bytes /// The source account available balance *after* the withdraw (encrypted by /// `source_pk` pub final_ciphertext: pod::ElGamalCiphertext, // 64 bytes - - /// Range proof - pub proof: WithdrawProof, // 736 bytes } +#[cfg(not(target_os = "solana"))] impl WithdrawData { - #[cfg(not(target_os = "solana"))] pub fn new( amount: u64, keypair: &ElGamalKeypair, @@ -64,31 +71,41 @@ impl WithdrawData { let pod_pubkey = pod::ElGamalPubkey((&keypair.public).to_bytes()); let pod_final_ciphertext: pod::ElGamalCiphertext = final_ciphertext.into(); - let mut transcript = WithdrawProof::transcript_new(&pod_pubkey, &pod_final_ciphertext); - let proof = WithdrawProof::new(keypair, final_balance, &final_ciphertext, &mut transcript); - Ok(Self { + let context = WithdrawProofContext { pubkey: pod_pubkey, final_ciphertext: pod_final_ciphertext, - proof, - }) + }; + + let mut transcript = WithdrawProof::transcript_new(&pod_pubkey, &pod_final_ciphertext); + let proof = WithdrawProof::new(keypair, final_balance, &final_ciphertext, &mut transcript); + + Ok(Self { context, proof }) } } -#[cfg(not(target_os = "solana"))] -impl Verifiable for WithdrawData { - fn verify(&self) -> Result<(), ProofError> { - let mut transcript = WithdrawProof::transcript_new(&self.pubkey, &self.final_ciphertext); +impl ZkProofData for WithdrawData { + const PROOF_TYPE: ProofType = ProofType::Withdraw; - let elgamal_pubkey = self.pubkey.try_into()?; - let final_balance_ciphertext = self.final_ciphertext.try_into()?; + fn context_data(&self) -> &WithdrawProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { + let mut transcript = + WithdrawProof::transcript_new(&self.context.pubkey, &self.context.final_ciphertext); + + let elgamal_pubkey = self.context.pubkey.try_into()?; + let final_balance_ciphertext = self.context.final_ciphertext.try_into()?; self.proof .verify(&elgamal_pubkey, &final_balance_ciphertext, &mut transcript) } } -/// This struct represents the cryptographic proof component that certifies the account's solvency -/// for withdrawal +/// The withdraw proof. +/// +/// It contains a ciphertext-commitment equality proof and a 64-bit range proof. #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] #[allow(non_snake_case)] @@ -97,7 +114,7 @@ pub struct WithdrawProof { pub commitment: pod::PedersenCommitment, /// Associated equality proof - pub equality_proof: pod::CtxtCommEqualityProof, + pub equality_proof: pod::CiphertextCommitmentEqualityProof, /// Associated range proof pub range_proof: pod::RangeProof64, // 672 bytes @@ -131,11 +148,11 @@ impl WithdrawProof { transcript.append_commitment(b"commitment", &pod_commitment); // generate equality_proof - let equality_proof = CtxtCommEqualityProof::new( + let equality_proof = CiphertextCommitmentEqualityProof::new( keypair, final_ciphertext, - final_balance, &opening, + final_balance, transcript, ); @@ -158,17 +175,13 @@ impl WithdrawProof { transcript.append_commitment(b"commitment", &self.commitment); let commitment: PedersenCommitment = self.commitment.try_into()?; - let equality_proof: CtxtCommEqualityProof = self.equality_proof.try_into()?; + let equality_proof: CiphertextCommitmentEqualityProof = self.equality_proof.try_into()?; let range_proof: RangeProof = self.range_proof.try_into()?; // verify equality proof - // - // TODO: we can also consider verifying equality and range proof in a batch equality_proof.verify(pubkey, final_ciphertext, &commitment, transcript)?; // verify range proof - // - // TODO: double compressing here - consider modifying range proof input type to `PedersenCommitment` range_proof.verify( vec![&commitment], vec![WITHDRAW_AMOUNT_BIT_LENGTH], @@ -200,7 +213,7 @@ mod test { ¤t_ciphertext, ) .unwrap(); - assert!(data.verify().is_ok()); + assert!(data.verify_proof().is_ok()); // generate and verify proof with wrong balance let wrong_balance: u64 = 99; @@ -211,6 +224,6 @@ mod test { ¤t_ciphertext, ) .unwrap(); - assert!(data.verify().is_err()); + assert!(data.verify_proof().is_err()); } } diff --git a/zk-token-sdk/src/instruction/withdraw_withheld.rs b/zk-token-sdk/src/instruction/withdraw_withheld.rs index ba137912959f2e..58049031e1fd3e 100644 --- a/zk-token-sdk/src/instruction/withdraw_withheld.rs +++ b/zk-token-sdk/src/instruction/withdraw_withheld.rs @@ -1,7 +1,3 @@ -use { - crate::zk_token_elgamal::pod, - bytemuck::{Pod, Zeroable}, -}; #[cfg(not(target_os = "solana"))] use { crate::{ @@ -10,13 +6,19 @@ use { pedersen::PedersenOpening, }, errors::ProofError, - instruction::Verifiable, sigma_proofs::equality_proof::CtxtCtxtEqualityProof, transcript::TranscriptProtocol, }, merlin::Transcript, std::convert::TryInto, }; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; /// This struct includes the cryptographic proof *and* the account data information needed to verify /// the proof @@ -28,19 +30,25 @@ use { #[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] pub struct WithdrawWithheldTokensData { - pub withdraw_withheld_authority_pubkey: pod::ElGamalPubkey, + pub context: WithdrawWithheldTokensProofContext, - pub destination_pubkey: pod::ElGamalPubkey, + pub proof: WithdrawWithheldTokensProof, +} + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct WithdrawWithheldTokensProofContext { + pub withdraw_withheld_authority_pubkey: pod::ElGamalPubkey, // 32 bytes - pub withdraw_withheld_authority_ciphertext: pod::ElGamalCiphertext, + pub destination_pubkey: pod::ElGamalPubkey, // 32 bytes - pub destination_ciphertext: pod::ElGamalCiphertext, + pub withdraw_withheld_authority_ciphertext: pod::ElGamalCiphertext, // 64 bytes - pub proof: WithdrawWithheldTokensProof, + pub destination_ciphertext: pod::ElGamalCiphertext, // 64 bytes } +#[cfg(not(target_os = "solana"))] impl WithdrawWithheldTokensData { - #[cfg(not(target_os = "solana"))] pub fn new( withdraw_withheld_authority_keypair: &ElGamalKeypair, destination_pubkey: &ElGamalPubkey, @@ -58,6 +66,13 @@ impl WithdrawWithheldTokensData { pod::ElGamalCiphertext(withdraw_withheld_authority_ciphertext.to_bytes()); let pod_destination_ciphertext = pod::ElGamalCiphertext(destination_ciphertext.to_bytes()); + let context = WithdrawWithheldTokensProofContext { + withdraw_withheld_authority_pubkey: pod_withdraw_withheld_authority_pubkey, + destination_pubkey: pod_destination_pubkey, + withdraw_withheld_authority_ciphertext: pod_withdraw_withheld_authority_ciphertext, + destination_ciphertext: pod_destination_ciphertext, + }; + let mut transcript = WithdrawWithheldTokensProof::transcript_new( &pod_withdraw_withheld_authority_pubkey, &pod_destination_pubkey, @@ -74,32 +89,34 @@ impl WithdrawWithheldTokensData { &mut transcript, ); - Ok(Self { - withdraw_withheld_authority_pubkey: pod_withdraw_withheld_authority_pubkey, - destination_pubkey: pod_destination_pubkey, - withdraw_withheld_authority_ciphertext: pod_withdraw_withheld_authority_ciphertext, - destination_ciphertext: pod_destination_ciphertext, - proof, - }) + Ok(Self { context, proof }) } } -#[cfg(not(target_os = "solana"))] -impl Verifiable for WithdrawWithheldTokensData { - fn verify(&self) -> Result<(), ProofError> { +impl ZkProofData for WithdrawWithheldTokensData { + const PROOF_TYPE: ProofType = ProofType::WithdrawWithheldTokens; + + fn context_data(&self) -> &WithdrawWithheldTokensProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { let mut transcript = WithdrawWithheldTokensProof::transcript_new( - &self.withdraw_withheld_authority_pubkey, - &self.destination_pubkey, - &self.withdraw_withheld_authority_ciphertext, - &self.destination_ciphertext, + &self.context.withdraw_withheld_authority_pubkey, + &self.context.destination_pubkey, + &self.context.withdraw_withheld_authority_ciphertext, + &self.context.destination_ciphertext, ); let withdraw_withheld_authority_pubkey = - self.withdraw_withheld_authority_pubkey.try_into()?; - let destination_pubkey = self.destination_pubkey.try_into()?; - let withdraw_withheld_authority_ciphertext = - self.withdraw_withheld_authority_ciphertext.try_into()?; - let destination_ciphertext = self.destination_ciphertext.try_into()?; + self.context.withdraw_withheld_authority_pubkey.try_into()?; + let destination_pubkey = self.context.destination_pubkey.try_into()?; + let withdraw_withheld_authority_ciphertext = self + .context + .withdraw_withheld_authority_ciphertext + .try_into()?; + let destination_ciphertext = self.context.destination_ciphertext.try_into()?; self.proof.verify( &withdraw_withheld_authority_pubkey, @@ -210,7 +227,7 @@ mod test { ) .unwrap(); - assert!(withdraw_withheld_tokens_data.verify().is_ok()); + assert!(withdraw_withheld_tokens_data.verify_proof().is_ok()); let amount: u64 = 55; let withdraw_withheld_authority_ciphertext = @@ -224,7 +241,7 @@ mod test { ) .unwrap(); - assert!(withdraw_withheld_tokens_data.verify().is_ok()); + assert!(withdraw_withheld_tokens_data.verify_proof().is_ok()); let amount = u64::max_value(); let withdraw_withheld_authority_ciphertext = @@ -238,6 +255,6 @@ mod test { ) .unwrap(); - assert!(withdraw_withheld_tokens_data.verify().is_ok()); + assert!(withdraw_withheld_tokens_data.verify_proof().is_ok()); } } diff --git a/zk-token-sdk/src/instruction/zero_balance.rs b/zk-token-sdk/src/instruction/zero_balance.rs new file mode 100644 index 00000000000000..0d676cb1318bc0 --- /dev/null +++ b/zk-token-sdk/src/instruction/zero_balance.rs @@ -0,0 +1,127 @@ +//! The zero-balance proof instruction. +//! +//! A zero-balance proof is defined with respect to a twisted ElGamal ciphertext. The proof +//! certifies that a given ciphertext encrypts the message 0 in the field (`Scalar::zero()`). To +//! generate the proof, a prover must provide the decryption key for the ciphertext. + +#[cfg(not(target_os = "solana"))] +use { + crate::{ + encryption::elgamal::{ElGamalCiphertext, ElGamalKeypair}, + errors::ProofError, + sigma_proofs::zero_balance_proof::ZeroBalanceProof, + transcript::TranscriptProtocol, + }, + merlin::Transcript, + std::convert::TryInto, +}; +use { + crate::{ + instruction::{ProofType, ZkProofData}, + zk_token_elgamal::pod, + }, + bytemuck::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the `ProofInstruction::ZeroBalance` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct ZeroBalanceProofData { + /// The context data for the zero-balance proof + pub context: ZeroBalanceProofContext, // 96 bytes + + /// Proof that the source account available balance is zero + pub proof: pod::ZeroBalanceProof, // 96 bytes +} + +/// The context data needed to verify a zero-balance proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct ZeroBalanceProofContext { + /// The source account ElGamal pubkey + pub pubkey: pod::ElGamalPubkey, // 32 bytes + + /// The source account available balance in encrypted form + pub ciphertext: pod::ElGamalCiphertext, // 64 bytes +} + +#[cfg(not(target_os = "solana"))] +impl ZeroBalanceProofData { + pub fn new( + keypair: &ElGamalKeypair, + ciphertext: &ElGamalCiphertext, + ) -> Result { + let pod_pubkey = pod::ElGamalPubkey(keypair.public.to_bytes()); + let pod_ciphertext = pod::ElGamalCiphertext(ciphertext.to_bytes()); + + let context = ZeroBalanceProofContext { + pubkey: pod_pubkey, + ciphertext: pod_ciphertext, + }; + + let mut transcript = ZeroBalanceProof::transcript_new(&pod_pubkey, &pod_ciphertext); + let proof = ZeroBalanceProof::new(keypair, ciphertext, &mut transcript).into(); + + Ok(ZeroBalanceProofData { context, proof }) + } +} + +impl ZkProofData for ZeroBalanceProofData { + const PROOF_TYPE: ProofType = ProofType::ZeroBalance; + + fn context_data(&self) -> &ZeroBalanceProofContext { + &self.context + } + + #[cfg(not(target_os = "solana"))] + fn verify_proof(&self) -> Result<(), ProofError> { + let mut transcript = + ZeroBalanceProof::transcript_new(&self.context.pubkey, &self.context.ciphertext); + + let pubkey = self.context.pubkey.try_into()?; + let ciphertext = self.context.ciphertext.try_into()?; + let proof: ZeroBalanceProof = self.proof.try_into()?; + proof + .verify(&pubkey, &ciphertext, &mut transcript) + .map_err(|e| e.into()) + } +} + +#[allow(non_snake_case)] +#[cfg(not(target_os = "solana"))] +impl ZeroBalanceProof { + fn transcript_new( + pubkey: &pod::ElGamalPubkey, + ciphertext: &pod::ElGamalCiphertext, + ) -> Transcript { + let mut transcript = Transcript::new(b"ZeroBalanceProof"); + + transcript.append_pubkey(b"pubkey", pubkey); + transcript.append_ciphertext(b"ciphertext", ciphertext); + + transcript + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_zero_balance_proof_instruction_correctness() { + let keypair = ElGamalKeypair::new_rand(); + + // general case: encryption of 0 + let ciphertext = keypair.public.encrypt(0_u64); + let zero_balance_proof_data = ZeroBalanceProofData::new(&keypair, &ciphertext).unwrap(); + assert!(zero_balance_proof_data.verify_proof().is_ok()); + + // general case: encryption of > 0 + let ciphertext = keypair.public.encrypt(1_u64); + let zero_balance_proof_data = ZeroBalanceProofData::new(&keypair, &ciphertext).unwrap(); + assert!(zero_balance_proof_data.verify_proof().is_err()); + } +} diff --git a/zk-token-sdk/src/lib.rs b/zk-token-sdk/src/lib.rs index c7a0391eaab8c8..14ce5b4380af47 100644 --- a/zk-token-sdk/src/lib.rs +++ b/zk-token-sdk/src/lib.rs @@ -37,3 +37,4 @@ pub mod instruction; pub mod zk_token_elgamal; pub mod zk_token_proof_instruction; pub mod zk_token_proof_program; +pub mod zk_token_proof_state; diff --git a/zk-token-sdk/src/macros.rs b/zk-token-sdk/src/macros.rs index 6351ba94224adb..cf7a3836e61d79 100644 --- a/zk-token-sdk/src/macros.rs +++ b/zk-token-sdk/src/macros.rs @@ -74,3 +74,13 @@ macro_rules! define_mul_variants { } }; } + +macro_rules! impl_from_transcript_error { + ($sigma_error_type:ty) => { + impl From for $sigma_error_type { + fn from(err: TranscriptError) -> Self { + ProofVerificationError::Transcript(err).into() + } + } + }; +} diff --git a/zk-token-sdk/src/range_proof/errors.rs b/zk-token-sdk/src/range_proof/errors.rs index b8766a2316a258..f832365fffc7ea 100644 --- a/zk-token-sdk/src/range_proof/errors.rs +++ b/zk-token-sdk/src/range_proof/errors.rs @@ -1,26 +1,10 @@ //! Errors related to proving and verifying range proofs. -use {crate::errors::TranscriptError, thiserror::Error}; +use { + crate::errors::{ProofVerificationError, TranscriptError}, + thiserror::Error, +}; #[derive(Error, Clone, Debug, Eq, PartialEq)] -pub enum RangeProofError { - #[error("the required algebraic relation does not hold")] - AlgebraicRelation, - #[error("malformed proof")] - Format, - #[error("attempted to create a proof with a non-power-of-two bitsize or bitsize too big")] - InvalidBitsize, - #[error("insufficient generators for the proof")] - InvalidGeneratorsLength, - #[error("multiscalar multiplication failed")] - MultiscalarMul, - #[error("transcript failed to produce a challenge")] - Transcript, - #[error("number of blinding factors do not match the number of values")] - WrongNumBlindingFactors, -} - -impl From for RangeProofError { - fn from(_err: TranscriptError) -> Self { - Self::Transcript - } -} +#[error("range proof verification failed: {0}")] +pub struct RangeProofError(#[from] pub(crate) ProofVerificationError); +impl_from_transcript_error!(RangeProofError); diff --git a/zk-token-sdk/src/range_proof/inner_product.rs b/zk-token-sdk/src/range_proof/inner_product.rs index 45df5d39b9bc6d..e61f077e4bd1fe 100644 --- a/zk-token-sdk/src/range_proof/inner_product.rs +++ b/zk-token-sdk/src/range_proof/inner_product.rs @@ -1,5 +1,6 @@ use { crate::{ + errors::ProofVerificationError, range_proof::{errors::RangeProofError, util}, transcript::TranscriptProtocol, }, @@ -7,7 +8,7 @@ use { curve25519_dalek::{ ristretto::{CompressedRistretto, RistrettoPoint}, scalar::Scalar, - traits::VartimeMultiscalarMul, + traits::{MultiscalarMul, VartimeMultiscalarMul}, }, merlin::Transcript, std::borrow::Borrow, @@ -85,7 +86,7 @@ impl InnerProductProof { let c_L = util::inner_product(a_L, b_R); let c_R = util::inner_product(a_R, b_L); - let L = RistrettoPoint::vartime_multiscalar_mul( + let L = RistrettoPoint::multiscalar_mul( a_L.iter() .zip(G_factors[n..2 * n].iter()) .map(|(a_L_i, g)| a_L_i * g) @@ -99,7 +100,7 @@ impl InnerProductProof { ) .compress(); - let R = RistrettoPoint::vartime_multiscalar_mul( + let R = RistrettoPoint::multiscalar_mul( a_R.iter() .zip(G_factors[0..n].iter()) .map(|(a_R_i, g)| a_R_i * g) @@ -125,11 +126,11 @@ impl InnerProductProof { for i in 0..n { a_L[i] = a_L[i] * u + u_inv * a_R[i]; b_L[i] = b_L[i] * u_inv + u * b_R[i]; - G_L[i] = RistrettoPoint::vartime_multiscalar_mul( + G_L[i] = RistrettoPoint::multiscalar_mul( &[u_inv * G_factors[i], u * G_factors[n + i]], &[G_L[i], G_R[i]], ); - H_L[i] = RistrettoPoint::vartime_multiscalar_mul( + H_L[i] = RistrettoPoint::multiscalar_mul( &[u * H_factors[i], u_inv * H_factors[n + i]], &[H_L[i], H_R[i]], ) @@ -151,13 +152,13 @@ impl InnerProductProof { let c_L = util::inner_product(a_L, b_R); let c_R = util::inner_product(a_R, b_L); - let L = RistrettoPoint::vartime_multiscalar_mul( + let L = RistrettoPoint::multiscalar_mul( a_L.iter().chain(b_R.iter()).chain(iter::once(&c_L)), G_R.iter().chain(H_L.iter()).chain(iter::once(Q)), ) .compress(); - let R = RistrettoPoint::vartime_multiscalar_mul( + let R = RistrettoPoint::multiscalar_mul( a_R.iter().chain(b_L.iter()).chain(iter::once(&c_R)), G_L.iter().chain(H_R.iter()).chain(iter::once(Q)), ) @@ -175,8 +176,8 @@ impl InnerProductProof { for i in 0..n { a_L[i] = a_L[i] * u + u_inv * a_R[i]; b_L[i] = b_L[i] * u_inv + u * b_R[i]; - G_L[i] = RistrettoPoint::vartime_multiscalar_mul(&[u_inv, u], &[G_L[i], G_R[i]]); - H_L[i] = RistrettoPoint::vartime_multiscalar_mul(&[u, u_inv], &[H_L[i], H_R[i]]); + G_L[i] = RistrettoPoint::multiscalar_mul(&[u_inv, u], &[G_L[i], G_R[i]]); + H_L[i] = RistrettoPoint::multiscalar_mul(&[u, u_inv], &[H_L[i], H_R[i]]); } a = a_L; @@ -208,10 +209,10 @@ impl InnerProductProof { if lg_n >= 32 { // 4 billion multiplications should be enough for anyone // and this check prevents overflow in 1<, _>>()?; let Rs = self .R_vec .iter() - .map(|p| p.decompress().ok_or(RangeProofError::Format)) + .map(|p| { + p.decompress() + .ok_or(ProofVerificationError::Deserialization) + }) .collect::, _>>()?; let expect_P = RistrettoPoint::vartime_multiscalar_mul( @@ -323,7 +330,7 @@ impl InnerProductProof { if expect_P == *P { Ok(()) } else { - Err(RangeProofError::AlgebraicRelation) + Err(ProofVerificationError::AlgebraicRelation.into()) } } @@ -360,18 +367,18 @@ impl InnerProductProof { pub fn from_bytes(slice: &[u8]) -> Result { let b = slice.len(); if b % 32 != 0 { - return Err(RangeProofError::Format); + return Err(ProofVerificationError::Deserialization.into()); } let num_elements = b / 32; if num_elements < 2 { - return Err(RangeProofError::Format); + return Err(ProofVerificationError::Deserialization.into()); } if (num_elements - 2) % 2 != 0 { - return Err(RangeProofError::Format); + return Err(ProofVerificationError::Deserialization.into()); } let lg_n = (num_elements - 2) / 2; if lg_n >= 32 { - return Err(RangeProofError::Format); + return Err(ProofVerificationError::Deserialization.into()); } let mut L_vec: Vec = Vec::with_capacity(lg_n); @@ -384,9 +391,9 @@ impl InnerProductProof { let pos = 2 * lg_n * 32; let a = Scalar::from_canonical_bytes(util::read32(&slice[pos..])) - .ok_or(RangeProofError::Format)?; + .ok_or(ProofVerificationError::Deserialization)?; let b = Scalar::from_canonical_bytes(util::read32(&slice[pos + 32..])) - .ok_or(RangeProofError::Format)?; + .ok_or(ProofVerificationError::Deserialization)?; Ok(InnerProductProof { L_vec, R_vec, a, b }) } diff --git a/zk-token-sdk/src/range_proof/mod.rs b/zk-token-sdk/src/range_proof/mod.rs index 2a19e8728e088e..f357bc88f7de0a 100644 --- a/zk-token-sdk/src/range_proof/mod.rs +++ b/zk-token-sdk/src/range_proof/mod.rs @@ -8,6 +8,7 @@ use { use { crate::{ encryption::pedersen::{G, H}, + errors::ProofVerificationError, range_proof::{ errors::RangeProofError, generators::BulletproofGens, inner_product::InnerProductProof, }, @@ -232,7 +233,7 @@ impl RangeProof { let bp_gens = BulletproofGens::new(nm); if !nm.is_power_of_two() { - return Err(RangeProofError::InvalidBitsize); + return Err(ProofVerificationError::InvalidBitSize.into()); } // append proof data to transcript and derive appropriate challenge scalars @@ -310,12 +311,12 @@ impl RangeProof { .chain(bp_gens.H(nm).map(|&x| Some(x))) .chain(comms.iter().map(|V| Some(*V.get_point()))), ) - .ok_or(RangeProofError::MultiscalarMul)?; + .ok_or(ProofVerificationError::MultiscalarMul)?; if mega_check.is_identity() { Ok(()) } else { - Err(RangeProofError::AlgebraicRelation) + Err(ProofVerificationError::AlgebraicRelation.into()) } } @@ -338,10 +339,10 @@ impl RangeProof { // changed. pub fn from_bytes(slice: &[u8]) -> Result { if slice.len() % 32 != 0 { - return Err(RangeProofError::Format); + return Err(ProofVerificationError::Deserialization.into()); } if slice.len() < 7 * 32 { - return Err(RangeProofError::Format); + return Err(ProofVerificationError::Deserialization.into()); } let A = CompressedRistretto(util::read32(&slice[0..])); @@ -350,11 +351,11 @@ impl RangeProof { let T_2 = CompressedRistretto(util::read32(&slice[3 * 32..])); let t_x = Scalar::from_canonical_bytes(util::read32(&slice[4 * 32..])) - .ok_or(RangeProofError::Format)?; + .ok_or(ProofVerificationError::Deserialization)?; let t_x_blinding = Scalar::from_canonical_bytes(util::read32(&slice[5 * 32..])) - .ok_or(RangeProofError::Format)?; + .ok_or(ProofVerificationError::Deserialization)?; let e_blinding = Scalar::from_canonical_bytes(util::read32(&slice[6 * 32..])) - .ok_or(RangeProofError::Format)?; + .ok_or(ProofVerificationError::Deserialization)?; let ipp_proof = InnerProductProof::from_bytes(&slice[7 * 32..])?; diff --git a/zk-token-sdk/src/sigma_proofs/equality_proof.rs b/zk-token-sdk/src/sigma_proofs/ctxt_comm_equality_proof.rs similarity index 50% rename from zk-token-sdk/src/sigma_proofs/equality_proof.rs rename to zk-token-sdk/src/sigma_proofs/ctxt_comm_equality_proof.rs index f3f4feffdead37..efbfaa87e04285 100644 --- a/zk-token-sdk/src/sigma_proofs/equality_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/ctxt_comm_equality_proof.rs @@ -1,20 +1,21 @@ -//! The equality sigma proof system. +//! The ciphertext-commitment equality sigma proof system. //! -//! An equality proof is defined with respect to two cryptographic objects: a twisted ElGamal -//! ciphertext and a Pedersen commitment. The proof certifies that a given ciphertext and -//! commitment pair encrypts/encodes the same message. To generate the proof, a prover must provide -//! the decryption key for the ciphertext and the Pedersen opening for the commitment. -//! -//! TODO: verify with respect to ciphertext +//! A ciphertext-commitment equality proof is defined with respect to a twisted ElGamal ciphertext +//! and a Pedersen commitment. The proof certifies that a given ciphertext and a commitment pair +//! encrypts/encodes the same message. To generate the proof, a prover must provide the decryption +//! key for the first ciphertext and the Pedersen opening for the commitment. //! //! The protocol guarantees computationally soundness (by the hardness of discrete log) and perfect //! zero-knowledge in the random oracle model. #[cfg(not(target_os = "solana"))] use { - crate::encryption::{ - elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, - pedersen::{PedersenCommitment, PedersenOpening, G, H}, + crate::{ + encryption::{ + elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, + pedersen::{PedersenCommitment, PedersenOpening, G, H}, + }, + errors::ProofVerificationError, }, curve25519_dalek::traits::MultiscalarMul, rand::rngs::OsRng, @@ -36,7 +37,7 @@ use { /// Contains all the elliptic curve and scalar components that make up the sigma protocol. #[allow(non_snake_case)] #[derive(Clone)] -pub struct CtxtCommEqualityProof { +pub struct CiphertextCommitmentEqualityProof { Y_0: CompressedRistretto, Y_1: CompressedRistretto, Y_2: CompressedRistretto, @@ -47,7 +48,7 @@ pub struct CtxtCommEqualityProof { #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] -impl CtxtCommEqualityProof { +impl CiphertextCommitmentEqualityProof { /// Equality proof constructor. The proof is with respect to a ciphertext and commitment. /// /// The function does *not* hash the public key, ciphertext, or commitment into the transcript. @@ -67,8 +68,8 @@ impl CtxtCommEqualityProof { pub fn new( source_keypair: &ElGamalKeypair, source_ciphertext: &ElGamalCiphertext, - amount: u64, opening: &PedersenOpening, + amount: u64, transcript: &mut Transcript, ) -> Self { transcript.equality_proof_domain_sep(); @@ -109,7 +110,7 @@ impl CtxtCommEqualityProof { y_x.zeroize(); y_r.zeroize(); - CtxtCommEqualityProof { + CiphertextCommitmentEqualityProof { Y_0, Y_1, Y_2, @@ -153,9 +154,18 @@ impl CtxtCommEqualityProof { let ww_negated = -&ww; // check that the required algebraic condition holds - let Y_0 = self.Y_0.decompress().ok_or(EqualityProofError::Format)?; - let Y_1 = self.Y_1.decompress().ok_or(EqualityProofError::Format)?; - let Y_2 = self.Y_2.decompress().ok_or(EqualityProofError::Format)?; + let Y_0 = self + .Y_0 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + let Y_1 = self + .Y_1 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + let Y_2 = self + .Y_2 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; let check = RistrettoPoint::vartime_multiscalar_mul( vec![ @@ -189,7 +199,7 @@ impl CtxtCommEqualityProof { if check.is_identity() { Ok(()) } else { - Err(EqualityProofError::AlgebraicRelation) + Err(ProofVerificationError::AlgebraicRelation.into()) } } @@ -205,242 +215,28 @@ impl CtxtCommEqualityProof { } pub fn from_bytes(bytes: &[u8]) -> Result { - let bytes = array_ref![bytes, 0, 192]; - let (Y_0, Y_1, Y_2, z_s, z_x, z_r) = array_refs![bytes, 32, 32, 32, 32, 32, 32]; - - let Y_0 = CompressedRistretto::from_slice(Y_0); - let Y_1 = CompressedRistretto::from_slice(Y_1); - let Y_2 = CompressedRistretto::from_slice(Y_2); - - let z_s = Scalar::from_canonical_bytes(*z_s).ok_or(EqualityProofError::Format)?; - let z_x = Scalar::from_canonical_bytes(*z_x).ok_or(EqualityProofError::Format)?; - let z_r = Scalar::from_canonical_bytes(*z_r).ok_or(EqualityProofError::Format)?; - - Ok(CtxtCommEqualityProof { - Y_0, - Y_1, - Y_2, - z_s, - z_x, - z_r, - }) - } -} - -/// Equality proof. -/// -/// Contains all the elliptic curve and scalar components that make up the sigma protocol. -#[allow(non_snake_case)] -#[derive(Clone)] -pub struct CtxtCtxtEqualityProof { - Y_0: CompressedRistretto, - Y_1: CompressedRistretto, - Y_2: CompressedRistretto, - Y_3: CompressedRistretto, - z_s: Scalar, - z_x: Scalar, - z_r: Scalar, -} - -#[allow(non_snake_case)] -#[cfg(not(target_os = "solana"))] -impl CtxtCtxtEqualityProof { - /// Equality proof constructor. The proof is with respect to two ciphertexts. - /// - /// The function does *not* hash the public key, ciphertext, or commitment into the transcript. - /// For security, the caller (the main protocol) should hash these public components prior to - /// invoking this constructor. - /// - /// This function is randomized. It uses `OsRng` internally to generate random scalars. - /// - /// Note that the proof constructor does not take the actual Pedersen commitment as input; it - /// takes the associated Pedersen opening instead. - /// - /// * `source_keypair` - The ElGamal keypair associated with the first ciphertext to be proved - /// * `destination_pubkey` - The ElGamal pubkey associated with the second ElGamal ciphertext - /// * `source_ciphertext` - The first ElGamal ciphertext - /// * `amount` - The message associated with the ElGamal ciphertext and Pedersen commitment - /// * `destination_opening` - The opening associated with the second ElGamal ciphertext - /// * `transcript` - The transcript that does the bookkeeping for the Fiat-Shamir heuristic - pub fn new( - source_keypair: &ElGamalKeypair, - destination_pubkey: &ElGamalPubkey, - source_ciphertext: &ElGamalCiphertext, - amount: u64, - destination_opening: &PedersenOpening, - transcript: &mut Transcript, - ) -> Self { - transcript.equality_proof_domain_sep(); - - // extract the relevant scalar and Ristretto points from the inputs - let P_source = source_keypair.public.get_point(); - let D_source = source_ciphertext.handle.get_point(); - let P_destination = destination_pubkey.get_point(); - - let s = source_keypair.secret.get_scalar(); - let x = Scalar::from(amount); - let r = destination_opening.get_scalar(); - - // generate random masking factors that also serves as nonces - let mut y_s = Scalar::random(&mut OsRng); - let mut y_x = Scalar::random(&mut OsRng); - let mut y_r = Scalar::random(&mut OsRng); - - let Y_0 = (&y_s * P_source).compress(); - let Y_1 = - RistrettoPoint::multiscalar_mul(vec![&y_x, &y_s], vec![&(*G), D_source]).compress(); - let Y_2 = RistrettoPoint::multiscalar_mul(vec![&y_x, &y_r], vec![&(*G), &(*H)]).compress(); - let Y_3 = (&y_r * P_destination).compress(); - - // record masking factors in the transcript - transcript.append_point(b"Y_0", &Y_0); - transcript.append_point(b"Y_1", &Y_1); - transcript.append_point(b"Y_2", &Y_2); - transcript.append_point(b"Y_3", &Y_3); - - let c = transcript.challenge_scalar(b"c"); - transcript.challenge_scalar(b"w"); - - // compute the masked values - let z_s = &(&c * s) + &y_s; - let z_x = &(&c * &x) + &y_x; - let z_r = &(&c * r) + &y_r; - - // zeroize random scalars - y_s.zeroize(); - y_x.zeroize(); - y_r.zeroize(); - - CtxtCtxtEqualityProof { - Y_0, - Y_1, - Y_2, - Y_3, - z_s, - z_x, - z_r, - } - } - - /// Equality proof verifier. The proof is with respect to two ciphertexts. - /// - /// * `source_pubkey` - The ElGamal pubkey associated with the first ciphertext to be proved - /// * `destination_pubkey` - The ElGamal pubkey associated with the second ciphertext to be proved - /// * `source_ciphertext` - The first ElGamal ciphertext to be proved - /// * `destination_ciphertext` - The second ElGamal ciphertext to be proved - /// * `transcript` - The transcript that does the bookkeeping for the Fiat-Shamir heuristic - pub fn verify( - self, - source_pubkey: &ElGamalPubkey, - destination_pubkey: &ElGamalPubkey, - source_ciphertext: &ElGamalCiphertext, - destination_ciphertext: &ElGamalCiphertext, - transcript: &mut Transcript, - ) -> Result<(), EqualityProofError> { - transcript.equality_proof_domain_sep(); - - // extract the relevant scalar and Ristretto points from the inputs - let P_source = source_pubkey.get_point(); - let C_source = source_ciphertext.commitment.get_point(); - let D_source = source_ciphertext.handle.get_point(); - - let P_destination = destination_pubkey.get_point(); - let C_destination = destination_ciphertext.commitment.get_point(); - let D_destination = destination_ciphertext.handle.get_point(); - - // include Y_0, Y_1, Y_2 to transcript and extract challenges - transcript.validate_and_append_point(b"Y_0", &self.Y_0)?; - transcript.validate_and_append_point(b"Y_1", &self.Y_1)?; - transcript.validate_and_append_point(b"Y_2", &self.Y_2)?; - transcript.validate_and_append_point(b"Y_3", &self.Y_3)?; - - let c = transcript.challenge_scalar(b"c"); - let w = transcript.challenge_scalar(b"w"); // w used for batch verification - let ww = &w * &w; - let www = &w * &ww; - - let w_negated = -&w; - let ww_negated = -&ww; - let www_negated = -&www; - - // check that the required algebraic condition holds - let Y_0 = self.Y_0.decompress().ok_or(EqualityProofError::Format)?; - let Y_1 = self.Y_1.decompress().ok_or(EqualityProofError::Format)?; - let Y_2 = self.Y_2.decompress().ok_or(EqualityProofError::Format)?; - let Y_3 = self.Y_3.decompress().ok_or(EqualityProofError::Format)?; - - let check = RistrettoPoint::vartime_multiscalar_mul( - vec![ - &self.z_s, // z_s - &(-&c), // -c - &(-&Scalar::one()), // -identity - &(&w * &self.z_x), // w * z_x - &(&w * &self.z_s), // w * z_s - &(&w_negated * &c), // -w * c - &w_negated, // -w - &(&ww * &self.z_x), // ww * z_x - &(&ww * &self.z_r), // ww * z_r - &(&ww_negated * &c), // -ww * c - &ww_negated, // -ww - &(&www * &self.z_r), // z_r - &(&www_negated * &c), // -www * c - &www_negated, - ], - vec![ - P_source, // P_source - &(*H), // H - &Y_0, // Y_0 - &(*G), // G - D_source, // D_source - C_source, // C_source - &Y_1, // Y_1 - &(*G), // G - &(*H), // H - C_destination, // C_destination - &Y_2, // Y_2 - P_destination, // P_destination - D_destination, // D_destination - &Y_3, // Y_3 - ], - ); - - if check.is_identity() { - Ok(()) - } else { - Err(EqualityProofError::AlgebraicRelation) + if bytes.len() != 192 { + return Err(ProofVerificationError::Deserialization.into()); } - } - - pub fn to_bytes(&self) -> [u8; 224] { - let mut buf = [0_u8; 224]; - buf[..32].copy_from_slice(self.Y_0.as_bytes()); - buf[32..64].copy_from_slice(self.Y_1.as_bytes()); - buf[64..96].copy_from_slice(self.Y_2.as_bytes()); - buf[96..128].copy_from_slice(self.Y_3.as_bytes()); - buf[128..160].copy_from_slice(self.z_s.as_bytes()); - buf[160..192].copy_from_slice(self.z_x.as_bytes()); - buf[192..224].copy_from_slice(self.z_r.as_bytes()); - buf - } - pub fn from_bytes(bytes: &[u8]) -> Result { - let bytes = array_ref![bytes, 0, 224]; - let (Y_0, Y_1, Y_2, Y_3, z_s, z_x, z_r) = array_refs![bytes, 32, 32, 32, 32, 32, 32, 32]; + let bytes = array_ref![bytes, 0, 192]; + let (Y_0, Y_1, Y_2, z_s, z_x, z_r) = array_refs![bytes, 32, 32, 32, 32, 32, 32]; let Y_0 = CompressedRistretto::from_slice(Y_0); let Y_1 = CompressedRistretto::from_slice(Y_1); let Y_2 = CompressedRistretto::from_slice(Y_2); - let Y_3 = CompressedRistretto::from_slice(Y_3); - let z_s = Scalar::from_canonical_bytes(*z_s).ok_or(EqualityProofError::Format)?; - let z_x = Scalar::from_canonical_bytes(*z_x).ok_or(EqualityProofError::Format)?; - let z_r = Scalar::from_canonical_bytes(*z_r).ok_or(EqualityProofError::Format)?; + let z_s = + Scalar::from_canonical_bytes(*z_s).ok_or(ProofVerificationError::Deserialization)?; + let z_x = + Scalar::from_canonical_bytes(*z_x).ok_or(ProofVerificationError::Deserialization)?; + let z_r = + Scalar::from_canonical_bytes(*z_r).ok_or(ProofVerificationError::Deserialization)?; - Ok(CtxtCtxtEqualityProof { + Ok(CiphertextCommitmentEqualityProof { Y_0, Y_1, Y_2, - Y_3, z_s, z_x, z_r, @@ -467,11 +263,11 @@ mod test { let mut prover_transcript = Transcript::new(b"Test"); let mut verifier_transcript = Transcript::new(b"Test"); - let proof = CtxtCommEqualityProof::new( + let proof = CiphertextCommitmentEqualityProof::new( &source_keypair, &source_ciphertext, - message, &destination_opening, + message, &mut prover_transcript, ); @@ -495,11 +291,11 @@ mod test { let mut prover_transcript = Transcript::new(b"Test"); let mut verifier_transcript = Transcript::new(b"Test"); - let proof = CtxtCommEqualityProof::new( + let proof = CiphertextCommitmentEqualityProof::new( &source_keypair, &source_ciphertext, - message, &destination_opening, + message, &mut prover_transcript, ); @@ -528,11 +324,11 @@ mod test { let mut prover_transcript = Transcript::new(b"Test"); let mut verifier_transcript = Transcript::new(b"Test"); - let proof = CtxtCommEqualityProof::new( + let proof = CiphertextCommitmentEqualityProof::new( &elgamal_keypair, &ciphertext, - message, &opening, + message, &mut prover_transcript, ); @@ -557,11 +353,11 @@ mod test { let mut prover_transcript = Transcript::new(b"Test"); let mut verifier_transcript = Transcript::new(b"Test"); - let proof = CtxtCommEqualityProof::new( + let proof = CiphertextCommitmentEqualityProof::new( &elgamal_keypair, &ciphertext, - message, &opening, + message, &mut prover_transcript, ); @@ -586,11 +382,11 @@ mod test { let mut prover_transcript = Transcript::new(b"Test"); let mut verifier_transcript = Transcript::new(b"Test"); - let proof = CtxtCommEqualityProof::new( + let proof = CiphertextCommitmentEqualityProof::new( &elgamal_keypair, &ciphertext, - message, &opening, + message, &mut prover_transcript, ); @@ -614,11 +410,11 @@ mod test { let mut prover_transcript = Transcript::new(b"Test"); let mut verifier_transcript = Transcript::new(b"Test"); - let proof = CtxtCommEqualityProof::new( + let proof = CiphertextCommitmentEqualityProof::new( &elgamal_keypair, &ciphertext, - message, &opening, + message, &mut prover_transcript, ); @@ -631,74 +427,4 @@ mod test { ) .is_ok()); } - - #[test] - fn test_ciphertext_ciphertext_equality_proof_correctness() { - // success case - let source_keypair = ElGamalKeypair::new_rand(); - let destination_keypair = ElGamalKeypair::new_rand(); - let message: u64 = 55; - - let source_ciphertext = source_keypair.public.encrypt(message); - - let destination_opening = PedersenOpening::new_rand(); - let destination_ciphertext = destination_keypair - .public - .encrypt_with(message, &destination_opening); - - let mut prover_transcript = Transcript::new(b"Test"); - let mut verifier_transcript = Transcript::new(b"Test"); - - let proof = CtxtCtxtEqualityProof::new( - &source_keypair, - &destination_keypair.public, - &source_ciphertext, - message, - &destination_opening, - &mut prover_transcript, - ); - - assert!(proof - .verify( - &source_keypair.public, - &destination_keypair.public, - &source_ciphertext, - &destination_ciphertext, - &mut verifier_transcript - ) - .is_ok()); - - // fail case: encrypted and committed messages are different - let source_message: u64 = 55; - let destination_message: u64 = 77; - - let source_ciphertext = source_keypair.public.encrypt(source_message); - - let destination_opening = PedersenOpening::new_rand(); - let destination_ciphertext = destination_keypair - .public - .encrypt_with(destination_message, &destination_opening); - - let mut prover_transcript = Transcript::new(b"Test"); - let mut verifier_transcript = Transcript::new(b"Test"); - - let proof = CtxtCtxtEqualityProof::new( - &source_keypair, - &destination_keypair.public, - &source_ciphertext, - message, - &destination_opening, - &mut prover_transcript, - ); - - assert!(proof - .verify( - &source_keypair.public, - &destination_keypair.public, - &source_ciphertext, - &destination_ciphertext, - &mut verifier_transcript - ) - .is_err()); - } } diff --git a/zk-token-sdk/src/sigma_proofs/ctxt_ctxt_equality_proof.rs b/zk-token-sdk/src/sigma_proofs/ctxt_ctxt_equality_proof.rs new file mode 100644 index 00000000000000..4cdeacb25723bd --- /dev/null +++ b/zk-token-sdk/src/sigma_proofs/ctxt_ctxt_equality_proof.rs @@ -0,0 +1,342 @@ +//! The ciphertext-ciphertext equality sigma proof system. +//! +//! The protocol guarantees computational soundness (by the hardness of discrete log) and perfect +//! zero-knowledge in the random oracle model. + +#[cfg(not(target_os = "solana"))] +use { + crate::{ + encryption::{ + elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, + pedersen::{PedersenOpening, G, H}, + }, + errors::ProofVerificationError, + }, + curve25519_dalek::traits::MultiscalarMul, + rand::rngs::OsRng, + zeroize::Zeroize, +}; +use { + crate::{sigma_proofs::errors::EqualityProofError, transcript::TranscriptProtocol}, + arrayref::{array_ref, array_refs}, + curve25519_dalek::{ + ristretto::{CompressedRistretto, RistrettoPoint}, + scalar::Scalar, + traits::{IsIdentity, VartimeMultiscalarMul}, + }, + merlin::Transcript, +}; + +/// The ciphertext-ciphertext equality proof. +/// +/// Contains all the elliptic curve and scalar components that make up the sigma protocol. +#[allow(non_snake_case)] +#[derive(Clone)] +pub struct CiphertextCiphertextEqualityProof { + Y_0: CompressedRistretto, + Y_1: CompressedRistretto, + Y_2: CompressedRistretto, + Y_3: CompressedRistretto, + z_s: Scalar, + z_x: Scalar, + z_r: Scalar, +} + +#[allow(non_snake_case)] +#[cfg(not(target_os = "solana"))] +impl CiphertextCiphertextEqualityProof { + /// The ciphertext-ciphertext equality proof constructor. + /// + /// The function does *not* hash the public key, ciphertext, or commitment into the transcript. + /// For security, the caller (the main protocol) should hash these public components prior to + /// invoking this constructor. + /// + /// This function is randomized. It uses `OsRng` internally to generate random scalars. + /// + /// * `source_keypair` - The ElGamal keypair associated with the first ciphertext to be proved + /// * `destination_pubkey` - The ElGamal pubkey associated with the second ElGamal ciphertext + /// * `source_ciphertext` - The first ElGamal ciphertext for which the prover knows a + /// decryption key for + /// * `destination_opening` - The opening (randomness) associated with the second ElGamal ciphertext + /// * `amount` - The message associated with the ElGamal ciphertext and Pedersen commitment + /// * `transcript` - The transcript that does the bookkeeping for the Fiat-Shamir heuristic + pub fn new( + source_keypair: &ElGamalKeypair, + destination_pubkey: &ElGamalPubkey, + source_ciphertext: &ElGamalCiphertext, + destination_opening: &PedersenOpening, + amount: u64, + transcript: &mut Transcript, + ) -> Self { + transcript.equality_proof_domain_sep(); + + // extract the relevant scalar and Ristretto points from the inputs + let P_source = source_keypair.public.get_point(); + let D_source = source_ciphertext.handle.get_point(); + let P_destination = destination_pubkey.get_point(); + + let s = source_keypair.secret.get_scalar(); + let x = Scalar::from(amount); + let r = destination_opening.get_scalar(); + + // generate random masking factors that also serves as nonces + let mut y_s = Scalar::random(&mut OsRng); + let mut y_x = Scalar::random(&mut OsRng); + let mut y_r = Scalar::random(&mut OsRng); + + let Y_0 = (&y_s * P_source).compress(); + let Y_1 = + RistrettoPoint::multiscalar_mul(vec![&y_x, &y_s], vec![&(*G), D_source]).compress(); + let Y_2 = RistrettoPoint::multiscalar_mul(vec![&y_x, &y_r], vec![&(*G), &(*H)]).compress(); + let Y_3 = (&y_r * P_destination).compress(); + + // record masking factors in the transcript + transcript.append_point(b"Y_0", &Y_0); + transcript.append_point(b"Y_1", &Y_1); + transcript.append_point(b"Y_2", &Y_2); + transcript.append_point(b"Y_3", &Y_3); + + let c = transcript.challenge_scalar(b"c"); + transcript.challenge_scalar(b"w"); + + // compute the masked values + let z_s = &(&c * s) + &y_s; + let z_x = &(&c * &x) + &y_x; + let z_r = &(&c * r) + &y_r; + + // zeroize random scalars + y_s.zeroize(); + y_x.zeroize(); + y_r.zeroize(); + + CiphertextCiphertextEqualityProof { + Y_0, + Y_1, + Y_2, + Y_3, + z_s, + z_x, + z_r, + } + } + + /// The ciphertext-ciphertext equality proof verifier. The proof is with respect to two + /// ciphertexts. + /// + /// * `source_pubkey` - The ElGamal pubkey associated with the first ciphertext to be proved + /// * `destination_pubkey` - The ElGamal pubkey associated with the second ciphertext to be proved + /// * `source_ciphertext` - The first ElGamal ciphertext to be proved + /// * `destination_ciphertext` - The second ElGamal ciphertext to be proved + /// * `transcript` - The transcript that does the bookkeeping for the Fiat-Shamir heuristic + pub fn verify( + self, + source_pubkey: &ElGamalPubkey, + destination_pubkey: &ElGamalPubkey, + source_ciphertext: &ElGamalCiphertext, + destination_ciphertext: &ElGamalCiphertext, + transcript: &mut Transcript, + ) -> Result<(), EqualityProofError> { + transcript.equality_proof_domain_sep(); + + // extract the relevant scalar and Ristretto points from the inputs + let P_source = source_pubkey.get_point(); + let C_source = source_ciphertext.commitment.get_point(); + let D_source = source_ciphertext.handle.get_point(); + + let P_destination = destination_pubkey.get_point(); + let C_destination = destination_ciphertext.commitment.get_point(); + let D_destination = destination_ciphertext.handle.get_point(); + + // include Y_0, Y_1, Y_2 to transcript and extract challenges + transcript.validate_and_append_point(b"Y_0", &self.Y_0)?; + transcript.validate_and_append_point(b"Y_1", &self.Y_1)?; + transcript.validate_and_append_point(b"Y_2", &self.Y_2)?; + transcript.validate_and_append_point(b"Y_3", &self.Y_3)?; + + let c = transcript.challenge_scalar(b"c"); + let w = transcript.challenge_scalar(b"w"); // w used for batch verification + let ww = &w * &w; + let www = &w * &ww; + + let w_negated = -&w; + let ww_negated = -&ww; + let www_negated = -&www; + + // check that the required algebraic condition holds + let Y_0 = self + .Y_0 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + let Y_1 = self + .Y_1 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + let Y_2 = self + .Y_2 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + let Y_3 = self + .Y_3 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + + let check = RistrettoPoint::vartime_multiscalar_mul( + vec![ + &self.z_s, // z_s + &(-&c), // -c + &(-&Scalar::one()), // -identity + &(&w * &self.z_x), // w * z_x + &(&w * &self.z_s), // w * z_s + &(&w_negated * &c), // -w * c + &w_negated, // -w + &(&ww * &self.z_x), // ww * z_x + &(&ww * &self.z_r), // ww * z_r + &(&ww_negated * &c), // -ww * c + &ww_negated, // -ww + &(&www * &self.z_r), // z_r + &(&www_negated * &c), // -www * c + &www_negated, + ], + vec![ + P_source, // P_source + &(*H), // H + &Y_0, // Y_0 + &(*G), // G + D_source, // D_source + C_source, // C_source + &Y_1, // Y_1 + &(*G), // G + &(*H), // H + C_destination, // C_destination + &Y_2, // Y_2 + P_destination, // P_destination + D_destination, // D_destination + &Y_3, // Y_3 + ], + ); + + if check.is_identity() { + Ok(()) + } else { + Err(ProofVerificationError::AlgebraicRelation.into()) + } + } + + pub fn to_bytes(&self) -> [u8; 224] { + let mut buf = [0_u8; 224]; + buf[..32].copy_from_slice(self.Y_0.as_bytes()); + buf[32..64].copy_from_slice(self.Y_1.as_bytes()); + buf[64..96].copy_from_slice(self.Y_2.as_bytes()); + buf[96..128].copy_from_slice(self.Y_3.as_bytes()); + buf[128..160].copy_from_slice(self.z_s.as_bytes()); + buf[160..192].copy_from_slice(self.z_x.as_bytes()); + buf[192..224].copy_from_slice(self.z_r.as_bytes()); + buf + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 224 { + return Err(ProofVerificationError::Deserialization.into()); + } + + let bytes = array_ref![bytes, 0, 224]; + let (Y_0, Y_1, Y_2, Y_3, z_s, z_x, z_r) = array_refs![bytes, 32, 32, 32, 32, 32, 32, 32]; + + let Y_0 = CompressedRistretto::from_slice(Y_0); + let Y_1 = CompressedRistretto::from_slice(Y_1); + let Y_2 = CompressedRistretto::from_slice(Y_2); + let Y_3 = CompressedRistretto::from_slice(Y_3); + + let z_s = + Scalar::from_canonical_bytes(*z_s).ok_or(ProofVerificationError::Deserialization)?; + let z_x = + Scalar::from_canonical_bytes(*z_x).ok_or(ProofVerificationError::Deserialization)?; + let z_r = + Scalar::from_canonical_bytes(*z_r).ok_or(ProofVerificationError::Deserialization)?; + + Ok(CiphertextCiphertextEqualityProof { + Y_0, + Y_1, + Y_2, + Y_3, + z_s, + z_x, + z_r, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_ciphertext_ciphertext_equality_proof_correctness() { + // success case + let source_keypair = ElGamalKeypair::new_rand(); + let destination_keypair = ElGamalKeypair::new_rand(); + let message: u64 = 55; + + let source_ciphertext = source_keypair.public.encrypt(message); + + let destination_opening = PedersenOpening::new_rand(); + let destination_ciphertext = destination_keypair + .public + .encrypt_with(message, &destination_opening); + + let mut prover_transcript = Transcript::new(b"Test"); + let mut verifier_transcript = Transcript::new(b"Test"); + + let proof = CiphertextCiphertextEqualityProof::new( + &source_keypair, + &destination_keypair.public, + &source_ciphertext, + &destination_opening, + message, + &mut prover_transcript, + ); + + assert!(proof + .verify( + &source_keypair.public, + &destination_keypair.public, + &source_ciphertext, + &destination_ciphertext, + &mut verifier_transcript + ) + .is_ok()); + + // fail case: encrypted and committed messages are different + let source_message: u64 = 55; + let destination_message: u64 = 77; + + let source_ciphertext = source_keypair.public.encrypt(source_message); + + let destination_opening = PedersenOpening::new_rand(); + let destination_ciphertext = destination_keypair + .public + .encrypt_with(destination_message, &destination_opening); + + let mut prover_transcript = Transcript::new(b"Test"); + let mut verifier_transcript = Transcript::new(b"Test"); + + let proof = CiphertextCiphertextEqualityProof::new( + &source_keypair, + &destination_keypair.public, + &source_ciphertext, + &destination_opening, + message, + &mut prover_transcript, + ); + + assert!(proof + .verify( + &source_keypair.public, + &destination_keypair.public, + &source_ciphertext, + &destination_ciphertext, + &mut verifier_transcript + ) + .is_err()); + } +} diff --git a/zk-token-sdk/src/sigma_proofs/errors.rs b/zk-token-sdk/src/sigma_proofs/errors.rs index 6a441b0f5928d4..117d5c44d382ee 100644 --- a/zk-token-sdk/src/sigma_proofs/errors.rs +++ b/zk-token-sdk/src/sigma_proofs/errors.rs @@ -1,74 +1,30 @@ //! Errors related to proving and verifying sigma proofs. -use {crate::errors::TranscriptError, thiserror::Error}; +use { + crate::errors::{ProofVerificationError, TranscriptError}, + thiserror::Error, +}; #[derive(Error, Clone, Debug, Eq, PartialEq)] -pub enum EqualityProofError { - #[error("the required algebraic relation does not hold")] - AlgebraicRelation, - #[error("malformed proof")] - Format, - #[error("multiscalar multiplication failed")] - MultiscalarMul, - #[error("transcript failed to produce a challenge")] - Transcript, -} - -impl From for EqualityProofError { - fn from(_err: TranscriptError) -> Self { - Self::Transcript - } -} +#[error("equality proof verification failed: {0}")] +pub struct EqualityProofError(#[from] pub(crate) ProofVerificationError); +impl_from_transcript_error!(EqualityProofError); #[derive(Error, Clone, Debug, Eq, PartialEq)] -pub enum ValidityProofError { - #[error("the required algebraic relation does not hold")] - AlgebraicRelation, - #[error("malformed proof")] - Format, - #[error("multiscalar multiplication failed")] - MultiscalarMul, - #[error("transcript failed to produce a challenge")] - Transcript, -} - -impl From for ValidityProofError { - fn from(_err: TranscriptError) -> Self { - Self::Transcript - } -} +#[error("validity proof verification failed: {0}")] +pub struct ValidityProofError(#[from] pub(crate) ProofVerificationError); +impl_from_transcript_error!(ValidityProofError); #[derive(Error, Clone, Debug, Eq, PartialEq)] -pub enum ZeroBalanceProofError { - #[error("the required algebraic relation does not hold")] - AlgebraicRelation, - #[error("malformed proof")] - Format, - #[error("multiscalar multiplication failed")] - MultiscalarMul, - #[error("transcript failed to produce a challenge")] - Transcript, -} - -impl From for ZeroBalanceProofError { - fn from(_err: TranscriptError) -> Self { - Self::Transcript - } -} +#[error("zero-balance proof verification failed: {0}")] +pub struct ZeroBalanceProofError(#[from] pub(crate) ProofVerificationError); +impl_from_transcript_error!(ZeroBalanceProofError); #[derive(Error, Clone, Debug, Eq, PartialEq)] -pub enum FeeSigmaProofError { - #[error("the required algebraic relation does not hold")] - AlgebraicRelation, - #[error("malformed proof")] - Format, - #[error("multiscalar multiplication failed")] - MultiscalarMul, - #[error("transcript failed to produce a challenge")] - Transcript, -} +#[error("fee sigma proof verification failed: {0}")] +pub struct FeeSigmaProofError(#[from] pub(crate) ProofVerificationError); +impl_from_transcript_error!(FeeSigmaProofError); -impl From for FeeSigmaProofError { - fn from(_err: TranscriptError) -> Self { - Self::Transcript - } -} +#[derive(Error, Clone, Debug, Eq, PartialEq)] +#[error("public key validity proof verification failed: {0}")] +pub struct PubkeyValidityProofError(#[from] pub(crate) ProofVerificationError); +impl_from_transcript_error!(PubkeyValidityProofError); diff --git a/zk-token-sdk/src/sigma_proofs/fee_proof.rs b/zk-token-sdk/src/sigma_proofs/fee_proof.rs index bb9342da6d0bc2..6602a6c15eb174 100644 --- a/zk-token-sdk/src/sigma_proofs/fee_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/fee_proof.rs @@ -8,7 +8,10 @@ use { rand::rngs::OsRng, }; use { - crate::{sigma_proofs::errors::FeeSigmaProofError, transcript::TranscriptProtocol}, + crate::{ + errors::ProofVerificationError, sigma_proofs::errors::FeeSigmaProofError, + transcript::TranscriptProtocol, + }, arrayref::{array_ref, array_refs}, curve25519_dalek::{ ristretto::{CompressedRistretto, RistrettoPoint}, @@ -40,6 +43,9 @@ impl FeeSigmaProof { /// Creates a fee sigma proof assuming that the committed fee is greater than the maximum fee /// bound. /// + /// Note: the proof is generated twice via `create_proof_fee_above_max` and + /// `create_proof_fee_below_max` to enforce constant time execution. + /// /// * `(fee_amount, fee_commitment, fee_opening)` - The amount, Pedersen commitment, and /// opening of the transfer fee /// * `(delta_fee, delta_commitment, delta_opening)` - The amount, Pedersen commitment, and @@ -76,24 +82,29 @@ impl FeeSigmaProof { let below_max = u64::ct_gt(&max_fee, &fee_amount); - // conditionally assign transcript; transcript is not conditionally selectable - if bool::from(below_max) { - *transcript = transcript_fee_below_max; - } else { - *transcript = transcript_fee_above_max; - } + // choose one of `proof_fee_above_max` or `proof_fee_below_max` according to whether the + // fee amount surpasses max fee + let fee_max_proof = FeeMaxProof::conditional_select( + &proof_fee_above_max.fee_max_proof, + &proof_fee_below_max.fee_max_proof, + below_max, + ); + + let fee_equality_proof = FeeEqualityProof::conditional_select( + &proof_fee_above_max.fee_equality_proof, + &proof_fee_below_max.fee_equality_proof, + below_max, + ); + + transcript.append_point(b"Y_max_proof", &fee_max_proof.Y_max_proof); + transcript.append_point(b"Y_delta", &fee_equality_proof.Y_delta); + transcript.append_point(b"Y_claimed", &fee_equality_proof.Y_claimed); + transcript.challenge_scalar(b"c"); + transcript.challenge_scalar(b"w"); Self { - fee_max_proof: FeeMaxProof::conditional_select( - &proof_fee_above_max.fee_max_proof, - &proof_fee_below_max.fee_max_proof, - below_max, - ), - fee_equality_proof: FeeEqualityProof::conditional_select( - &proof_fee_above_max.fee_equality_proof, - &proof_fee_below_max.fee_equality_proof, - below_max, - ), + fee_max_proof, + fee_equality_proof, } } @@ -276,19 +287,19 @@ impl FeeSigmaProof { .fee_max_proof .Y_max_proof .decompress() - .ok_or(FeeSigmaProofError::Format)?; + .ok_or(ProofVerificationError::Deserialization)?; let z_max = self.fee_max_proof.z_max_proof; let Y_delta_real = self .fee_equality_proof .Y_delta .decompress() - .ok_or(FeeSigmaProofError::Format)?; + .ok_or(ProofVerificationError::Deserialization)?; let Y_claimed = self .fee_equality_proof .Y_claimed .decompress() - .ok_or(FeeSigmaProofError::Format)?; + .ok_or(ProofVerificationError::Deserialization)?; let z_x = self.fee_equality_proof.z_x; let z_delta_real = self.fee_equality_proof.z_delta; let z_claimed = self.fee_equality_proof.z_claimed; @@ -334,7 +345,7 @@ impl FeeSigmaProof { if check.is_identity() { Ok(()) } else { - Err(FeeSigmaProofError::AlgebraicRelation) + Err(ProofVerificationError::AlgebraicRelation.into()) } } @@ -352,22 +363,28 @@ impl FeeSigmaProof { } pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 256 { + return Err(ProofVerificationError::Deserialization.into()); + } + let bytes = array_ref![bytes, 0, 256]; let (Y_max_proof, z_max_proof, c_max_proof, Y_delta, Y_claimed, z_x, z_delta, z_claimed) = array_refs![bytes, 32, 32, 32, 32, 32, 32, 32, 32]; let Y_max_proof = CompressedRistretto::from_slice(Y_max_proof); - let z_max_proof = - Scalar::from_canonical_bytes(*z_max_proof).ok_or(FeeSigmaProofError::Format)?; - let c_max_proof = - Scalar::from_canonical_bytes(*c_max_proof).ok_or(FeeSigmaProofError::Format)?; + let z_max_proof = Scalar::from_canonical_bytes(*z_max_proof) + .ok_or(ProofVerificationError::Deserialization)?; + let c_max_proof = Scalar::from_canonical_bytes(*c_max_proof) + .ok_or(ProofVerificationError::Deserialization)?; let Y_delta = CompressedRistretto::from_slice(Y_delta); let Y_claimed = CompressedRistretto::from_slice(Y_claimed); - let z_x = Scalar::from_canonical_bytes(*z_x).ok_or(FeeSigmaProofError::Format)?; - let z_delta = Scalar::from_canonical_bytes(*z_delta).ok_or(FeeSigmaProofError::Format)?; - let z_claimed = - Scalar::from_canonical_bytes(*z_claimed).ok_or(FeeSigmaProofError::Format)?; + let z_x = + Scalar::from_canonical_bytes(*z_x).ok_or(ProofVerificationError::Deserialization)?; + let z_delta = Scalar::from_canonical_bytes(*z_delta) + .ok_or(ProofVerificationError::Deserialization)?; + let z_claimed = Scalar::from_canonical_bytes(*z_claimed) + .ok_or(ProofVerificationError::Deserialization)?; Ok(Self { fee_max_proof: FeeMaxProof { diff --git a/zk-token-sdk/src/sigma_proofs/mod.rs b/zk-token-sdk/src/sigma_proofs/mod.rs index 53f5c7a9f296b2..2ccf2fdf372402 100644 --- a/zk-token-sdk/src/sigma_proofs/mod.rs +++ b/zk-token-sdk/src/sigma_proofs/mod.rs @@ -15,8 +15,10 @@ //! We refer to the zk-token paper for the formal details and security proofs of these argument //! systems. -pub mod equality_proof; +pub mod ctxt_comm_equality_proof; +pub mod ctxt_ctxt_equality_proof; pub mod errors; pub mod fee_proof; +pub mod pubkey_proof; pub mod validity_proof; pub mod zero_balance_proof; diff --git a/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs b/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs new file mode 100644 index 00000000000000..400e719e467a40 --- /dev/null +++ b/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs @@ -0,0 +1,170 @@ +//! The public-key (validity) proof system. +//! +//! The protocol guarantees computational soundness (by the hardness of discrete log) and perfect +//! zero-knowledge in the random oracle model. + +#[cfg(not(target_os = "solana"))] +use { + crate::encryption::{ + elgamal::{ElGamalKeypair, ElGamalPubkey}, + pedersen::H, + }, + rand::rngs::OsRng, + zeroize::Zeroize, +}; +use { + crate::{ + errors::ProofVerificationError, sigma_proofs::errors::PubkeyValidityProofError, + transcript::TranscriptProtocol, + }, + arrayref::{array_ref, array_refs}, + curve25519_dalek::{ + ristretto::{CompressedRistretto, RistrettoPoint}, + scalar::Scalar, + traits::{IsIdentity, VartimeMultiscalarMul}, + }, + merlin::Transcript, +}; + +/// Public-key proof. +/// +/// Contains all the elliptic curve and scalar components that make up the sigma protocol. +#[allow(non_snake_case)] +#[derive(Clone)] +pub struct PubkeyValidityProof { + Y: CompressedRistretto, + z: Scalar, +} + +#[allow(non_snake_case)] +#[cfg(not(target_os = "solana"))] +impl PubkeyValidityProof { + /// Public-key proof constructor. + /// + /// The function does *not* hash the public key and ciphertext into the transcript. For + /// security, the caller (the main protocol) should hash these public key components prior to + /// invoking this constructor. + /// + /// This function is randomized. It uses `OsRng` internally to generate random scalars. + /// + /// This function panics if the provided keypair is not valid (i.e. secret key is not + /// invertible). + /// + /// * `elgamal_keypair` = The ElGamal keypair that pertains to the ElGamal public key to be + /// proved + /// * `transcript` - The transcript that does the bookkeeping for the Fiat-Shamir heuristic + pub fn new(elgamal_keypair: &ElGamalKeypair, transcript: &mut Transcript) -> Self { + transcript.pubkey_proof_domain_sep(); + + // extract the relevant scalar and Ristretto points from the input + let s = elgamal_keypair.secret.get_scalar(); + + assert!(s != &Scalar::zero()); + let s_inv = s.invert(); + + // generate a random masking factor that also serves as a nonce + let mut y = Scalar::random(&mut OsRng); + let Y = (&y * &(*H)).compress(); + + // record masking factors in transcript and get challenges + transcript.append_point(b"Y", &Y); + let c = transcript.challenge_scalar(b"c"); + + // compute masked secret key + let z = &(&c * s_inv) + &y; + + y.zeroize(); + + Self { Y, z } + } + + /// Public-key proof verifier. + /// + /// * `elgamal_pubkey` - The ElGamal public key to be proved + /// * `transcript` - The transcript that does the bookkeeping for the Fiat-Shamir heuristic + pub fn verify( + self, + elgamal_pubkey: &ElGamalPubkey, + transcript: &mut Transcript, + ) -> Result<(), PubkeyValidityProofError> { + transcript.pubkey_proof_domain_sep(); + + // extract the relvant scalar and Ristretto points from the input + let P = elgamal_pubkey.get_point(); + + // include Y to transcript and extract challenge + transcript.validate_and_append_point(b"Y", &self.Y)?; + let c = transcript.challenge_scalar(b"c"); + + // check that the required algebraic condition holds + let Y = self + .Y + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + + let check = RistrettoPoint::vartime_multiscalar_mul( + vec![&self.z, &(-&c), &(-&Scalar::one())], + vec![&(*H), P, &Y], + ); + + if check.is_identity() { + Ok(()) + } else { + Err(ProofVerificationError::AlgebraicRelation.into()) + } + } + + pub fn to_bytes(&self) -> [u8; 64] { + let mut buf = [0_u8; 64]; + buf[..32].copy_from_slice(self.Y.as_bytes()); + buf[32..64].copy_from_slice(self.z.as_bytes()); + buf + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 64 { + return Err(ProofVerificationError::Deserialization.into()); + } + + let bytes = array_ref![bytes, 0, 64]; + let (Y, z) = array_refs![bytes, 32, 32]; + + let Y = CompressedRistretto::from_slice(Y); + let z = Scalar::from_canonical_bytes(*z).ok_or(ProofVerificationError::Deserialization)?; + + Ok(PubkeyValidityProof { Y, z }) + } +} + +#[cfg(test)] +mod test { + use { + super::*, + solana_sdk::{pubkey::Pubkey, signature::Keypair}, + }; + + #[test] + fn test_pubkey_proof_correctness() { + // random ElGamal keypair + let keypair = ElGamalKeypair::new_rand(); + + let mut prover_transcript = Transcript::new(b"test"); + let mut verifier_transcript = Transcript::new(b"test"); + + let proof = PubkeyValidityProof::new(&keypair, &mut prover_transcript); + assert!(proof + .verify(&keypair.public, &mut verifier_transcript) + .is_ok()); + + // derived ElGamal keypair + let keypair = ElGamalKeypair::new(&Keypair::new(), &Pubkey::default()).unwrap(); + + let mut prover_transcript = Transcript::new(b"test"); + let mut verifier_transcript = Transcript::new(b"test"); + + let proof = PubkeyValidityProof::new(&keypair, &mut prover_transcript); + assert!(proof + .verify(&keypair.public, &mut verifier_transcript) + .is_ok()); + } +} diff --git a/zk-token-sdk/src/sigma_proofs/validity_proof.rs b/zk-token-sdk/src/sigma_proofs/validity_proof.rs index 6084657612c20e..ee55d0c0236e9e 100644 --- a/zk-token-sdk/src/sigma_proofs/validity_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/validity_proof.rs @@ -10,9 +10,12 @@ #[cfg(not(target_os = "solana"))] use { - crate::encryption::{ - elgamal::{DecryptHandle, ElGamalPubkey}, - pedersen::{PedersenCommitment, PedersenOpening, G, H}, + crate::{ + encryption::{ + elgamal::{DecryptHandle, ElGamalPubkey}, + pedersen::{PedersenCommitment, PedersenOpening, G, H}, + }, + errors::ProofVerificationError, }, curve25519_dalek::traits::MultiscalarMul, rand::rngs::OsRng, @@ -137,9 +140,18 @@ impl ValidityProof { let ww_negated = -&ww; // check the required algebraic conditions - let Y_0 = self.Y_0.decompress().ok_or(ValidityProofError::Format)?; - let Y_1 = self.Y_1.decompress().ok_or(ValidityProofError::Format)?; - let Y_2 = self.Y_2.decompress().ok_or(ValidityProofError::Format)?; + let Y_0 = self + .Y_0 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + let Y_1 = self + .Y_1 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + let Y_2 = self + .Y_2 + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; let P_dest = destination_pubkey.get_point(); let P_auditor = auditor_pubkey.get_point(); @@ -178,7 +190,7 @@ impl ValidityProof { if check.is_identity() { Ok(()) } else { - Err(ValidityProofError::AlgebraicRelation) + Err(ProofVerificationError::AlgebraicRelation.into()) } } @@ -193,6 +205,10 @@ impl ValidityProof { } pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 160 { + return Err(ProofVerificationError::Deserialization.into()); + } + let bytes = array_ref![bytes, 0, 160]; let (Y_0, Y_1, Y_2, z_r, z_x) = array_refs![bytes, 32, 32, 32, 32, 32]; @@ -200,8 +216,10 @@ impl ValidityProof { let Y_1 = CompressedRistretto::from_slice(Y_1); let Y_2 = CompressedRistretto::from_slice(Y_2); - let z_r = Scalar::from_canonical_bytes(*z_r).ok_or(ValidityProofError::Format)?; - let z_x = Scalar::from_canonical_bytes(*z_x).ok_or(ValidityProofError::Format)?; + let z_r = + Scalar::from_canonical_bytes(*z_r).ok_or(ProofVerificationError::Deserialization)?; + let z_x = + Scalar::from_canonical_bytes(*z_x).ok_or(ProofVerificationError::Deserialization)?; Ok(ValidityProof { Y_0, diff --git a/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs b/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs index 642e8b57edb923..3108860544990b 100644 --- a/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs @@ -1,17 +1,16 @@ //! The zero-balance sigma proof system. //! -//! A zero-balance proof is defined with respect to a twisted ElGamal ciphertext. The proof -//! certifies that a given ciphertext encrypts the message 0 (`Scalar::zero()`). To generate the -//! proof, a prover must provide the decryption key for the ciphertext. -//! //! The protocol guarantees computationally soundness (by the hardness of discrete log) and perfect //! zero-knowledge in the random oracle model. #[cfg(not(target_os = "solana"))] use { - crate::encryption::{ - elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, - pedersen::H, + crate::{ + encryption::{ + elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, + pedersen::H, + }, + errors::ProofVerificationError, }, curve25519_dalek::traits::MultiscalarMul, rand::rngs::OsRng, @@ -61,6 +60,8 @@ impl ZeroBalanceProof { ciphertext: &ElGamalCiphertext, transcript: &mut Transcript, ) -> Self { + transcript.zero_balance_proof_domain_sep(); + // extract the relevant scalar and Ristretto points from the input let P = elgamal_keypair.public.get_point(); let s = elgamal_keypair.secret.get_scalar(); @@ -98,6 +99,8 @@ impl ZeroBalanceProof { ciphertext: &ElGamalCiphertext, transcript: &mut Transcript, ) -> Result<(), ZeroBalanceProofError> { + transcript.zero_balance_proof_domain_sep(); + // extract the relevant scalar and Ristretto points from the input let P = elgamal_pubkey.get_point(); let C = ciphertext.commitment.get_point(); @@ -112,9 +115,15 @@ impl ZeroBalanceProof { let w_negated = -&w; - // decompress R or return verification error - let Y_P = self.Y_P.decompress().ok_or(ZeroBalanceProofError::Format)?; - let Y_D = self.Y_D.decompress().ok_or(ZeroBalanceProofError::Format)?; + // decompress Y or return verification error + let Y_P = self + .Y_P + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; + let Y_D = self + .Y_D + .decompress() + .ok_or(ProofVerificationError::Deserialization)?; // check the required algebraic relation let check = RistrettoPoint::multiscalar_mul( @@ -139,7 +148,7 @@ impl ZeroBalanceProof { if check.is_identity() { Ok(()) } else { - Err(ZeroBalanceProofError::AlgebraicRelation) + Err(ProofVerificationError::AlgebraicRelation.into()) } } @@ -152,13 +161,17 @@ impl ZeroBalanceProof { } pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != 96 { + return Err(ProofVerificationError::Deserialization.into()); + } + let bytes = array_ref![bytes, 0, 96]; let (Y_P, Y_D, z) = array_refs![bytes, 32, 32, 32]; let Y_P = CompressedRistretto::from_slice(Y_P); let Y_D = CompressedRistretto::from_slice(Y_D); - let z = Scalar::from_canonical_bytes(*z).ok_or(ZeroBalanceProofError::Format)?; + let z = Scalar::from_canonical_bytes(*z).ok_or(ProofVerificationError::Deserialization)?; Ok(ZeroBalanceProof { Y_P, Y_D, z }) } diff --git a/zk-token-sdk/src/transcript.rs b/zk-token-sdk/src/transcript.rs index d0dfda9e42e373..c0ed1161666c9e 100644 --- a/zk-token-sdk/src/transcript.rs +++ b/zk-token-sdk/src/transcript.rs @@ -58,6 +58,9 @@ pub trait TranscriptProtocol { /// Append a domain separator for fee sigma proof. fn fee_sigma_proof_domain_sep(&mut self); + /// Append a domain separator for public-key proof. + fn pubkey_proof_domain_sep(&mut self); + /// Check that a point is not the identity, then append it to the /// transcript. Otherwise, return an error. fn validate_and_append_point( @@ -161,4 +164,8 @@ impl TranscriptProtocol for Transcript { fn fee_sigma_proof_domain_sep(&mut self) { self.append_message(b"dom-sep", b"fee-sigma-proof") } + + fn pubkey_proof_domain_sep(&mut self) { + self.append_message(b"dom-sep", b"pubkey-proof") + } } diff --git a/zk-token-sdk/src/zk_token_elgamal/convert.rs b/zk-token-sdk/src/zk_token_elgamal/convert.rs index 92e206359148b3..00a582be21c260 100644 --- a/zk-token-sdk/src/zk_token_elgamal/convert.rs +++ b/zk-token-sdk/src/zk_token_elgamal/convert.rs @@ -57,16 +57,18 @@ mod target_arch { elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalPubkey}, pedersen::PedersenCommitment, }, - errors::ProofError, + errors::{ProofError, ProofVerificationError}, instruction::{ transfer::{TransferAmountEncryption, TransferPubkeys}, transfer_with_fee::{FeeEncryption, FeeParameters, TransferWithFeePubkeys}, }, range_proof::{errors::RangeProofError, RangeProof}, sigma_proofs::{ - equality_proof::{CtxtCommEqualityProof, CtxtCtxtEqualityProof}, + ctxt_comm_equality_proof::CiphertextCommitmentEqualityProof, + ctxt_ctxt_equality_proof::CiphertextCiphertextEqualityProof, errors::*, fee_proof::FeeSigmaProof, + pubkey_proof::PubkeyValidityProof, validity_proof::{AggregatedValidityProof, ValidityProof}, zero_balance_proof::ZeroBalanceProof, }, @@ -81,9 +83,11 @@ mod target_arch { } } - impl From for Scalar { - fn from(pod: PodScalar) -> Self { - Scalar::from_bits(pod.0) + impl TryFrom for Scalar { + type Error = ProofError; + + fn try_from(pod: PodScalar) -> Result { + Scalar::from_canonical_bytes(pod.0).ok_or(ProofError::CiphertextDeserialization) } } @@ -97,7 +101,7 @@ mod target_arch { type Error = ProofError; fn try_from(ct: pod::ElGamalCiphertext) -> Result { - Self::from_bytes(&ct.0).ok_or(ProofError::InconsistentCTData) + Self::from_bytes(&ct.0).ok_or(ProofError::CiphertextDeserialization) } } @@ -111,7 +115,7 @@ mod target_arch { type Error = ProofError; fn try_from(pk: pod::ElGamalPubkey) -> Result { - Self::from_bytes(&pk.0).ok_or(ProofError::InconsistentCTData) + Self::from_bytes(&pk.0).ok_or(ProofError::CiphertextDeserialization) } } @@ -146,7 +150,7 @@ mod target_arch { type Error = ProofError; fn try_from(pod: pod::PedersenCommitment) -> Result { - Self::from_bytes(&pod.0).ok_or(ProofError::InconsistentCTData) + Self::from_bytes(&pod.0).ok_or(ProofError::CiphertextDeserialization) } } @@ -170,7 +174,7 @@ mod target_arch { type Error = ProofError; fn try_from(pod: pod::DecryptHandle) -> Result { - Self::from_bytes(&pod.0).ok_or(ProofError::InconsistentCTData) + Self::from_bytes(&pod.0).ok_or(ProofError::CiphertextDeserialization) } } @@ -184,34 +188,34 @@ mod target_arch { type Error = ProofError; fn try_from(ct: pod::AeCiphertext) -> Result { - Self::from_bytes(&ct.0).ok_or(ProofError::InconsistentCTData) + Self::from_bytes(&ct.0).ok_or(ProofError::CiphertextDeserialization) } } - impl From for pod::CtxtCommEqualityProof { - fn from(proof: CtxtCommEqualityProof) -> Self { + impl From for pod::CiphertextCommitmentEqualityProof { + fn from(proof: CiphertextCommitmentEqualityProof) -> Self { Self(proof.to_bytes()) } } - impl TryFrom for CtxtCommEqualityProof { + impl TryFrom for CiphertextCommitmentEqualityProof { type Error = EqualityProofError; - fn try_from(pod: pod::CtxtCommEqualityProof) -> Result { + fn try_from(pod: pod::CiphertextCommitmentEqualityProof) -> Result { Self::from_bytes(&pod.0) } } - impl From for pod::CtxtCtxtEqualityProof { - fn from(proof: CtxtCtxtEqualityProof) -> Self { + impl From for pod::CiphertextCiphertextEqualityProof { + fn from(proof: CiphertextCiphertextEqualityProof) -> Self { Self(proof.to_bytes()) } } - impl TryFrom for CtxtCtxtEqualityProof { + impl TryFrom for CiphertextCiphertextEqualityProof { type Error = EqualityProofError; - fn try_from(pod: pod::CtxtCtxtEqualityProof) -> Result { + fn try_from(pod: pod::CiphertextCiphertextEqualityProof) -> Result { Self::from_bytes(&pod.0) } } @@ -272,12 +276,26 @@ mod target_arch { } } + impl From for pod::PubkeyValidityProof { + fn from(proof: PubkeyValidityProof) -> Self { + Self(proof.to_bytes()) + } + } + + impl TryFrom for PubkeyValidityProof { + type Error = PubkeyValidityProofError; + + fn try_from(pod: pod::PubkeyValidityProof) -> Result { + Self::from_bytes(&pod.0) + } + } + impl TryFrom for pod::RangeProof64 { type Error = RangeProofError; fn try_from(proof: RangeProof) -> Result { if proof.ipp_proof.serialized_size() != 448 { - return Err(RangeProofError::Format); + return Err(ProofVerificationError::Deserialization.into()); } let mut buf = [0_u8; 672]; @@ -307,7 +325,7 @@ mod target_arch { fn try_from(proof: RangeProof) -> Result { if proof.ipp_proof.serialized_size() != 512 { - return Err(RangeProofError::Format); + return Err(ProofVerificationError::Deserialization.into()); } let mut buf = [0_u8; 736]; @@ -337,7 +355,7 @@ mod target_arch { fn try_from(proof: RangeProof) -> Result { if proof.ipp_proof.serialized_size() != 576 { - return Err(RangeProofError::Format); + return Err(ProofVerificationError::Deserialization.into()); } let mut buf = [0_u8; 800]; diff --git a/zk-token-sdk/src/zk_token_elgamal/ops.rs b/zk-token-sdk/src/zk_token_elgamal/ops.rs index cb5c1afe96d54e..fff1152f99b59d 100644 --- a/zk-token-sdk/src/zk_token_elgamal/ops.rs +++ b/zk-token-sdk/src/zk_token_elgamal/ops.rs @@ -1,226 +1,130 @@ -pub use target_arch::*; - -#[cfg(not(target_os = "solana"))] -mod target_arch { - use { - crate::{encryption::elgamal::ElGamalCiphertext, zk_token_elgamal::pod}, - curve25519_dalek::{constants::RISTRETTO_BASEPOINT_COMPRESSED, scalar::Scalar}, - std::convert::TryInto, - }; - pub const TWO_32: u64 = 4294967296; - - // On input two scalars x0, x1 and two ciphertexts ct0, ct1, - // returns `Some(x0*ct0 + x1*ct1)` or `None` if the input was invalid - fn add_ciphertexts( - scalar_0: Scalar, - ct_0: &pod::ElGamalCiphertext, - scalar_1: Scalar, - ct_1: &pod::ElGamalCiphertext, - ) -> Option { - let ct_0: ElGamalCiphertext = (*ct_0).try_into().ok()?; - let ct_1: ElGamalCiphertext = (*ct_1).try_into().ok()?; - - let ct_sum = ct_0 * scalar_0 + ct_1 * scalar_1; - Some(pod::ElGamalCiphertext::from(ct_sum)) - } - - pub(crate) fn combine_lo_hi( - ct_lo: &pod::ElGamalCiphertext, - ct_hi: &pod::ElGamalCiphertext, - ) -> Option { - add_ciphertexts(Scalar::one(), ct_lo, Scalar::from(TWO_32), ct_hi) - } - - pub fn add( - ct_0: &pod::ElGamalCiphertext, - ct_1: &pod::ElGamalCiphertext, - ) -> Option { - add_ciphertexts(Scalar::one(), ct_0, Scalar::one(), ct_1) - } - - pub fn add_with_lo_hi( - ct_0: &pod::ElGamalCiphertext, - ct_1_lo: &pod::ElGamalCiphertext, - ct_1_hi: &pod::ElGamalCiphertext, - ) -> Option { - let ct_1 = combine_lo_hi(ct_1_lo, ct_1_hi)?; - add_ciphertexts(Scalar::one(), ct_0, Scalar::one(), &ct_1) - } - - pub fn subtract( - ct_0: &pod::ElGamalCiphertext, - ct_1: &pod::ElGamalCiphertext, - ) -> Option { - add_ciphertexts(Scalar::one(), ct_0, -Scalar::one(), ct_1) - } - - pub fn subtract_with_lo_hi( - ct_0: &pod::ElGamalCiphertext, - ct_1_lo: &pod::ElGamalCiphertext, - ct_1_hi: &pod::ElGamalCiphertext, - ) -> Option { - let ct_1 = combine_lo_hi(ct_1_lo, ct_1_hi)?; - add_ciphertexts(Scalar::one(), ct_0, -Scalar::one(), &ct_1) - } - - pub fn add_to(ct: &pod::ElGamalCiphertext, amount: u64) -> Option { - let mut amount_as_ct = [0_u8; 64]; - amount_as_ct[..32].copy_from_slice(RISTRETTO_BASEPOINT_COMPRESSED.as_bytes()); - add_ciphertexts( - Scalar::one(), - ct, - Scalar::from(amount), - &pod::ElGamalCiphertext(amount_as_ct), - ) - } - - pub fn subtract_from( - ct: &pod::ElGamalCiphertext, - amount: u64, - ) -> Option { - let mut amount_as_ct = [0_u8; 64]; - amount_as_ct[..32].copy_from_slice(RISTRETTO_BASEPOINT_COMPRESSED.as_bytes()); - add_ciphertexts( - Scalar::one(), - ct, - -Scalar::from(amount), - &pod::ElGamalCiphertext(amount_as_ct), - ) - } +use crate::{ + curve25519::{ + ristretto::{add_ristretto, multiply_ristretto, subtract_ristretto, PodRistrettoPoint}, + scalar::PodScalar, + }, + zk_token_elgamal::pod, +}; + +const SHIFT_BITS: usize = 16; + +const G: PodRistrettoPoint = PodRistrettoPoint([ + 226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11, 106, 165, + 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118, +]); + +/// Add two ElGamal ciphertexts +pub fn add( + left_ciphertext: &pod::ElGamalCiphertext, + right_ciphertext: &pod::ElGamalCiphertext, +) -> Option { + let (left_commitment, left_handle): (pod::PedersenCommitment, pod::DecryptHandle) = + (*left_ciphertext).into(); + let (right_commitment, right_handle): (pod::PedersenCommitment, pod::DecryptHandle) = + (*right_ciphertext).into(); + + let result_commitment: pod::PedersenCommitment = + add_ristretto(&left_commitment.into(), &right_commitment.into())?.into(); + let result_handle: pod::DecryptHandle = + add_ristretto(&left_handle.into(), &right_handle.into())?.into(); + + Some((result_commitment, result_handle).into()) } -#[cfg(target_os = "solana")] -#[allow(unused_variables)] -mod target_arch { - use crate::{ - curve25519::{ - ristretto::{add_ristretto, multiply_ristretto, subtract_ristretto, PodRistrettoPoint}, - scalar::PodScalar, - }, - zk_token_elgamal::pod, - }; - - const SHIFT_BITS: usize = 16; - - const G: PodRistrettoPoint = PodRistrettoPoint([ - 226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11, 106, - 165, 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118, - ]); - - pub fn add( - left_ciphertext: &pod::ElGamalCiphertext, - right_ciphertext: &pod::ElGamalCiphertext, - ) -> Option { - let (left_commitment, left_handle): (pod::PedersenCommitment, pod::DecryptHandle) = - (*left_ciphertext).into(); - let (right_commitment, right_handle): (pod::PedersenCommitment, pod::DecryptHandle) = - (*right_ciphertext).into(); - - let result_commitment: pod::PedersenCommitment = - add_ristretto(&left_commitment.into(), &right_commitment.into())?.into(); - let result_handle: pod::DecryptHandle = - add_ristretto(&left_handle.into(), &right_handle.into())?.into(); +/// Multiply an ElGamal ciphertext by a scalar +pub fn multiply( + scalar: &PodScalar, + ciphertext: &pod::ElGamalCiphertext, +) -> Option { + let (commitment, handle): (pod::PedersenCommitment, pod::DecryptHandle) = (*ciphertext).into(); - Some((result_commitment, result_handle).into()) - } - - pub fn add_with_lo_hi( - left_ciphertext: &pod::ElGamalCiphertext, - right_ciphertext_lo: &pod::ElGamalCiphertext, - right_ciphertext_hi: &pod::ElGamalCiphertext, - ) -> Option { - let shift_scalar = to_scalar(1_u64 << SHIFT_BITS); - let shifted_right_ciphertext_hi = scalar_ciphertext(&shift_scalar, &right_ciphertext_hi)?; - let combined_right_ciphertext = add(right_ciphertext_lo, &shifted_right_ciphertext_hi)?; - add(left_ciphertext, &combined_right_ciphertext) - } + let commitment_point: PodRistrettoPoint = commitment.into(); + let handle_point: PodRistrettoPoint = handle.into(); - pub fn subtract( - left_ciphertext: &pod::ElGamalCiphertext, - right_ciphertext: &pod::ElGamalCiphertext, - ) -> Option { - let (left_commitment, left_handle): (pod::PedersenCommitment, pod::DecryptHandle) = - (*left_ciphertext).into(); - let (right_commitment, right_handle): (pod::PedersenCommitment, pod::DecryptHandle) = - (*right_ciphertext).into(); - - let result_commitment: pod::PedersenCommitment = - subtract_ristretto(&left_commitment.into(), &right_commitment.into())?.into(); - let result_handle: pod::DecryptHandle = - subtract_ristretto(&left_handle.into(), &right_handle.into())?.into(); - - Some((result_commitment, result_handle).into()) - } + let result_commitment: pod::PedersenCommitment = + multiply_ristretto(scalar, &commitment_point)?.into(); + let result_handle: pod::DecryptHandle = multiply_ristretto(scalar, &handle_point)?.into(); - pub fn subtract_with_lo_hi( - left_ciphertext: &pod::ElGamalCiphertext, - right_ciphertext_lo: &pod::ElGamalCiphertext, - right_ciphertext_hi: &pod::ElGamalCiphertext, - ) -> Option { - let shift_scalar = to_scalar(1_u64 << SHIFT_BITS); - let shifted_right_ciphertext_hi = scalar_ciphertext(&shift_scalar, &right_ciphertext_hi)?; - let combined_right_ciphertext = add(right_ciphertext_lo, &shifted_right_ciphertext_hi)?; - subtract(left_ciphertext, &combined_right_ciphertext) - } - - pub fn add_to( - ciphertext: &pod::ElGamalCiphertext, - amount: u64, - ) -> Option { - let amount_scalar = to_scalar(amount); - let amount_point = multiply_ristretto(&amount_scalar, &G)?; + Some((result_commitment, result_handle).into()) +} - let (commitment, handle): (pod::PedersenCommitment, pod::DecryptHandle) = - (*ciphertext).into(); - let commitment_point: PodRistrettoPoint = commitment.into(); +/// Compute `left_ciphertext + (right_ciphertext_lo + 2^16 * right_ciphertext_hi)` +pub fn add_with_lo_hi( + left_ciphertext: &pod::ElGamalCiphertext, + right_ciphertext_lo: &pod::ElGamalCiphertext, + right_ciphertext_hi: &pod::ElGamalCiphertext, +) -> Option { + let shift_scalar = to_scalar(1_u64 << SHIFT_BITS); + let shifted_right_ciphertext_hi = multiply(&shift_scalar, right_ciphertext_hi)?; + let combined_right_ciphertext = add(right_ciphertext_lo, &shifted_right_ciphertext_hi)?; + add(left_ciphertext, &combined_right_ciphertext) +} - let result_commitment: pod::PedersenCommitment = - add_ristretto(&commitment_point, &amount_point)?.into(); - Some((result_commitment, handle).into()) - } +/// Subtract two ElGamal ciphertexts +pub fn subtract( + left_ciphertext: &pod::ElGamalCiphertext, + right_ciphertext: &pod::ElGamalCiphertext, +) -> Option { + let (left_commitment, left_handle): (pod::PedersenCommitment, pod::DecryptHandle) = + (*left_ciphertext).into(); + let (right_commitment, right_handle): (pod::PedersenCommitment, pod::DecryptHandle) = + (*right_ciphertext).into(); + + let result_commitment: pod::PedersenCommitment = + subtract_ristretto(&left_commitment.into(), &right_commitment.into())?.into(); + let result_handle: pod::DecryptHandle = + subtract_ristretto(&left_handle.into(), &right_handle.into())?.into(); + + Some((result_commitment, result_handle).into()) +} - pub fn subtract_from( - ciphertext: &pod::ElGamalCiphertext, - amount: u64, - ) -> Option { - let amount_scalar = to_scalar(amount); - let amount_point = multiply_ristretto(&amount_scalar, &G)?; +/// Compute `left_ciphertext - (right_ciphertext_lo + 2^16 * right_ciphertext_hi)` +pub fn subtract_with_lo_hi( + left_ciphertext: &pod::ElGamalCiphertext, + right_ciphertext_lo: &pod::ElGamalCiphertext, + right_ciphertext_hi: &pod::ElGamalCiphertext, +) -> Option { + let shift_scalar = to_scalar(1_u64 << SHIFT_BITS); + let shifted_right_ciphertext_hi = multiply(&shift_scalar, right_ciphertext_hi)?; + let combined_right_ciphertext = add(right_ciphertext_lo, &shifted_right_ciphertext_hi)?; + subtract(left_ciphertext, &combined_right_ciphertext) +} - let (commitment, handle): (pod::PedersenCommitment, pod::DecryptHandle) = - (*ciphertext).into(); - let commitment_point: PodRistrettoPoint = commitment.into(); +/// Add a constant amount to a ciphertext +pub fn add_to(ciphertext: &pod::ElGamalCiphertext, amount: u64) -> Option { + let amount_scalar = to_scalar(amount); + let amount_point = multiply_ristretto(&amount_scalar, &G)?; - let result_commitment: pod::PedersenCommitment = - subtract_ristretto(&commitment_point, &amount_point)?.into(); - Some((result_commitment, handle).into()) - } + let (commitment, handle): (pod::PedersenCommitment, pod::DecryptHandle) = (*ciphertext).into(); + let commitment_point: PodRistrettoPoint = commitment.into(); - fn to_scalar(amount: u64) -> PodScalar { - let mut bytes = [0u8; 32]; - bytes[..8].copy_from_slice(&amount.to_le_bytes()); - PodScalar(bytes) - } - - fn scalar_ciphertext( - scalar: &PodScalar, - ciphertext: &pod::ElGamalCiphertext, - ) -> Option { - let (commitment, handle): (pod::PedersenCommitment, pod::DecryptHandle) = - (*ciphertext).into(); + let result_commitment: pod::PedersenCommitment = + add_ristretto(&commitment_point, &amount_point)?.into(); + Some((result_commitment, handle).into()) +} - let commitment_point: PodRistrettoPoint = commitment.into(); - let handle_point: PodRistrettoPoint = handle.into(); +/// Subtract a constant amount to a ciphertext +pub fn subtract_from( + ciphertext: &pod::ElGamalCiphertext, + amount: u64, +) -> Option { + let amount_scalar = to_scalar(amount); + let amount_point = multiply_ristretto(&amount_scalar, &G)?; - let result_commitment: pod::PedersenCommitment = - multiply_ristretto(scalar, &commitment_point)?.into(); - let result_handle: pod::DecryptHandle = multiply_ristretto(scalar, &handle_point)?.into(); + let (commitment, handle): (pod::PedersenCommitment, pod::DecryptHandle) = (*ciphertext).into(); + let commitment_point: PodRistrettoPoint = commitment.into(); - Some((result_commitment, result_handle).into()) - } + let result_commitment: pod::PedersenCommitment = + subtract_ristretto(&commitment_point, &amount_point)?.into(); + Some((result_commitment, handle).into()) } -pub const OP_ADD: u64 = 0; -pub const OP_SUB: u64 = 1; +/// Convert a `u64` amount into a curve25519 scalar +fn to_scalar(amount: u64) -> PodScalar { + let mut bytes = [0u8; 32]; + bytes[..8].copy_from_slice(&amount.to_le_bytes()); + PodScalar(bytes) +} #[cfg(test)] mod tests { @@ -230,6 +134,7 @@ mod tests { elgamal::{ElGamalCiphertext, ElGamalKeypair}, pedersen::{Pedersen, PedersenOpening}, }, + instruction::split_u64, zk_token_elgamal::{ops, pod}, }, bytemuck::Zeroable, @@ -237,6 +142,8 @@ mod tests { std::convert::TryInto, }; + const TWO_16: u64 = 65536; + #[test] fn test_zero_ct() { let spendable_balance = pod::ElGamalCiphertext::zeroed(); @@ -290,19 +197,11 @@ mod tests { assert_eq!(expected, subtracted_ct); } - /// Split u64 number into two u32 numbers - fn split_u64_into_u32(amt: u64) -> (u32, u32) { - let lo = amt as u32; - let hi = (amt >> 32) as u32; - - (lo, hi) - } - #[test] fn test_transfer_arithmetic() { // transfer amount let transfer_amount: u64 = 55; - let (amount_lo, amount_hi) = split_u64_into_u32(transfer_amount); + let (amount_lo, amount_hi) = split_u64(transfer_amount, 16); // generate public keys let source_pk = ElGamalKeypair::new_rand().public; @@ -335,38 +234,26 @@ mod tests { dest_pk.encrypt_with(77_u64, &dest_open).into(); // program arithmetic for the source account - - // 1. Combine commitments and handles let source_lo_ct: pod::ElGamalCiphertext = (comm_lo, handle_source_lo).into(); let source_hi_ct: pod::ElGamalCiphertext = (comm_hi, handle_source_hi).into(); - // 2. Combine lo and hi ciphertexts - let source_combined_ct = ops::combine_lo_hi(&source_lo_ct, &source_hi_ct).unwrap(); - - // 3. Subtract from available balance let final_source_spendable = - ops::subtract(&source_spendable_ct, &source_combined_ct).unwrap(); + ops::subtract_with_lo_hi(&source_spendable_ct, &source_lo_ct, &source_hi_ct).unwrap(); - // test let final_source_open = - source_open - (open_lo.clone() + open_hi.clone() * Scalar::from(ops::TWO_32)); + source_open - (open_lo.clone() + open_hi.clone() * Scalar::from(TWO_16)); let expected_source: pod::ElGamalCiphertext = source_pk.encrypt_with(22_u64, &final_source_open).into(); assert_eq!(expected_source, final_source_spendable); - // same for the destination account - - // 1. Combine commitments and handles + // program arithemtic for the destination account let dest_lo_ct: pod::ElGamalCiphertext = (comm_lo, handle_dest_lo).into(); let dest_hi_ct: pod::ElGamalCiphertext = (comm_hi, handle_dest_hi).into(); - // 2. Combine lo and hi ciphertexts - let dest_combined_ct = ops::combine_lo_hi(&dest_lo_ct, &dest_hi_ct).unwrap(); - - // 3. Add to pending balance - let final_dest_pending = ops::add(&dest_pending_ct, &dest_combined_ct).unwrap(); + let final_dest_pending = + ops::add_with_lo_hi(&dest_pending_ct, &dest_lo_ct, &dest_hi_ct).unwrap(); - let final_dest_open = dest_open + (open_lo + open_hi * Scalar::from(ops::TWO_32)); + let final_dest_open = dest_open + (open_lo + open_hi * Scalar::from(TWO_16)); let expected_dest_ct: pod::ElGamalCiphertext = dest_pk.encrypt_with(132_u64, &final_dest_open).into(); assert_eq!(expected_dest_ct, final_dest_pending); diff --git a/zk-token-sdk/src/zk_token_elgamal/pod.rs b/zk-token-sdk/src/zk_token_elgamal/pod.rs index cf1f87d65b52cd..171a28e3f19dcc 100644 --- a/zk-token-sdk/src/zk_token_elgamal/pod.rs +++ b/zk-token-sdk/src/zk_token_elgamal/pod.rs @@ -1,5 +1,10 @@ pub use bytemuck::{Pod, Zeroable}; -use std::fmt; +use { + crate::zk_token_proof_instruction::ProofType, + num_traits::{FromPrimitive, ToPrimitive}, + solana_program::instruction::InstructionError, + std::fmt, +}; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)] #[repr(transparent)] @@ -29,6 +34,22 @@ impl From for u64 { } } +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)] +#[repr(transparent)] +pub struct PodProofType(u8); +impl From for PodProofType { + fn from(proof_type: ProofType) -> Self { + Self(ToPrimitive::to_u8(&proof_type).unwrap()) + } +} +impl TryFrom for ProofType { + type Error = InstructionError; + + fn try_from(pod: PodProofType) -> Result { + FromPrimitive::from_u8(pod.0).ok_or(Self::Error::InvalidAccountData) + } +} + #[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)] #[repr(transparent)] pub struct CompressedRistretto(pub [u8; 32]); @@ -91,25 +112,25 @@ impl fmt::Debug for DecryptHandle { } } -/// Serialization of `CtxtCommEqualityProof` +/// Serialization of `CiphertextCommitmentEqualityProof` #[derive(Clone, Copy)] #[repr(transparent)] -pub struct CtxtCommEqualityProof(pub [u8; 192]); +pub struct CiphertextCommitmentEqualityProof(pub [u8; 192]); -// `CtxtCommEqualityProof` is a Pod and Zeroable. +// `CiphertextCommitmentEqualityProof` is a Pod and Zeroable. // Add the marker traits manually because `bytemuck` only adds them for some `u8` arrays -unsafe impl Zeroable for CtxtCommEqualityProof {} -unsafe impl Pod for CtxtCommEqualityProof {} +unsafe impl Zeroable for CiphertextCommitmentEqualityProof {} +unsafe impl Pod for CiphertextCommitmentEqualityProof {} /// Serialization of `CtxtCtxtEqualityProof` #[derive(Clone, Copy)] #[repr(transparent)] -pub struct CtxtCtxtEqualityProof(pub [u8; 224]); +pub struct CiphertextCiphertextEqualityProof(pub [u8; 224]); // `CtxtCtxtEqualityProof` is a Pod and Zeroable. // Add the marker traits manually because `bytemuck` only adds them for some `u8` arrays -unsafe impl Zeroable for CtxtCtxtEqualityProof {} -unsafe impl Pod for CtxtCtxtEqualityProof {} +unsafe impl Zeroable for CiphertextCiphertextEqualityProof {} +unsafe impl Pod for CiphertextCiphertextEqualityProof {} /// Serialization of validity proofs #[derive(Clone, Copy)] @@ -146,6 +167,11 @@ unsafe impl Pod for ZeroBalanceProof {} #[repr(transparent)] pub struct FeeSigmaProof(pub [u8; 256]); +/// Serialization of public-key sigma proof +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(transparent)] +pub struct PubkeyValidityProof(pub [u8; 64]); + /// Serialization of range proofs for 64-bit numbers (for `Withdraw` instruction) #[derive(Clone, Copy)] #[repr(transparent)] diff --git a/zk-token-sdk/src/zk_token_proof_instruction.rs b/zk-token-sdk/src/zk_token_proof_instruction.rs index d983454eb1b0d5..fee9bb8a22c54d 100644 --- a/zk-token-sdk/src/zk_token_proof_instruction.rs +++ b/zk-token-sdk/src/zk_token_proof_instruction.rs @@ -1,28 +1,59 @@ ///! Instructions provided by the ZkToken Proof program pub use crate::instruction::*; use { - bytemuck::{bytes_of, Pod}, + bytemuck::bytes_of, num_derive::{FromPrimitive, ToPrimitive}, num_traits::{FromPrimitive, ToPrimitive}, - solana_program::instruction::Instruction, + solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + }, }; #[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive, PartialEq, Eq)] #[repr(u8)] pub enum ProofInstruction { - /// Verify a `CloseAccountData` struct + /// Close a zero-knowledge proof context state. /// /// Accounts expected by this instruction: + /// 0. `[writable]` The proof context account to close + /// 1. `[writable]` The destination account for lamports + /// 2. `[signer]` The context account's owner + /// + /// Data expected by this instruction: + /// None + /// + CloseContextState, + + /// Verify a zero-balance proof. + /// + /// This instruction can be configured to optionally create a proof context state account. + /// + /// Accounts expected by this instruction: + /// + /// * Creating a proof context account + /// 0. `[writable]` The proof context account + /// 1. `[]` The proof context account owner + /// + /// * Otherwise /// None /// /// Data expected by this instruction: - /// `CloseAccountData` + /// `ZeroBalanceProofData` /// - VerifyCloseAccount, + VerifyZeroBalance, - /// Verify a `WithdrawData` struct + /// Verify a withdraw zero-knowledge proof. + /// + /// This instruction can be configured to optionally create a proof context state account. /// /// Accounts expected by this instruction: + /// + /// * Creating a proof context account + /// 0. `[writable]` The proof context account + /// 1. `[]` The proof context account owner + /// + /// * Otherwise /// None /// /// Data expected by this instruction: @@ -30,19 +61,35 @@ pub enum ProofInstruction { /// VerifyWithdraw, - /// Verify a `WithdrawWithheldTokensData` struct + /// Verify a ciphertext-ciphertext equality proof. + /// + /// This instruction can be configured to optionally create a proof context state account. /// /// Accounts expected by this instruction: + /// + /// * Creating a proof context account + /// 0. `[writable]` The proof context account + /// 1. `[]` The proof context account owner + /// + /// * Otherwise /// None /// /// Data expected by this instruction: - /// `WithdrawWithheldTokensData` + /// `CiphertextCiphertextEqualityProofData` /// - VerifyWithdrawWithheldTokens, + VerifyCiphertextCiphertextEquality, - /// Verify a `TransferData` struct + /// Verify a transfer zero-knowledge proof. + /// + /// This instruction can be configured to optionally create a proof context state account. /// /// Accounts expected by this instruction: + /// + /// * Creating a proof context account + /// 0. `[writable]` The proof context account + /// 1. `[]` The proof context account owner + /// + /// * Otherwise /// None /// /// Data expected by this instruction: @@ -50,57 +97,161 @@ pub enum ProofInstruction { /// VerifyTransfer, - /// Verify a `TransferWithFeeData` struct + /// Verify a transfer with fee zero-knowledge proof. + /// + /// This instruction can be configured to optionally create a proof context state account. /// /// Accounts expected by this instruction: + /// + /// * Creating a proof context account + /// 0. `[writable]` The proof context account + /// 1. `[]` The proof context account owner + /// + /// * Otherwise /// None /// /// Data expected by this instruction: /// `TransferWithFeeData` /// VerifyTransferWithFee, + + /// Verify a pubkey validity zero-knowledge proof. + /// + /// This instruction can be configured to optionally create a proof context state account. + /// + /// Accounts expected by this instruction: + /// + /// * Creating a proof context account + /// 0. `[writable]` The proof context account + /// 1. `[]` The proof context account owner + /// + /// * Otherwise + /// None + /// + /// Data expected by this instruction: + /// `PubkeyValidityData` + /// + VerifyPubkeyValidity, } -impl ProofInstruction { - pub fn encode(&self, proof: &T) -> Instruction { - let mut data = vec![ToPrimitive::to_u8(self).unwrap()]; - data.extend_from_slice(bytes_of(proof)); - Instruction { - program_id: crate::zk_token_proof_program::id(), - accounts: vec![], - data, - } - } +/// Pubkeys associated with a context state account to be used as parameters to functions. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct ContextStateInfo<'a> { + pub context_state_account: &'a Pubkey, + pub context_state_authority: &'a Pubkey, +} - pub fn decode_type(input: &[u8]) -> Option { - input.first().and_then(|x| FromPrimitive::from_u8(*x)) - } +/// Create a `CloseContextState` instruction. +pub fn close_context_state( + context_state_info: ContextStateInfo, + destination_account: &Pubkey, +) -> Instruction { + let accounts = vec![ + AccountMeta::new(*context_state_info.context_state_account, false), + AccountMeta::new(*destination_account, false), + AccountMeta::new_readonly(*context_state_info.context_state_authority, true), + ]; - pub fn decode_data(input: &[u8]) -> Option<&T> { - if input.is_empty() { - None - } else { - bytemuck::try_from_bytes(&input[1..]).ok() - } + let data = vec![ToPrimitive::to_u8(&ProofInstruction::CloseContextState).unwrap()]; + + Instruction { + program_id: crate::zk_token_proof_program::id(), + accounts, + data, } } -pub fn verify_close_account(proof_data: &CloseAccountData) -> Instruction { - ProofInstruction::VerifyCloseAccount.encode(proof_data) +/// Create a `VerifyZeroBalance` instruction. +pub fn verify_zero_balance( + context_state_info: Option, + proof_data: &ZeroBalanceProofData, +) -> Instruction { + ProofInstruction::VerifyZeroBalance.encode_verify_proof(context_state_info, proof_data) +} + +/// Create a `VerifyWithdraw` instruction. +pub fn verify_withdraw( + context_state_info: Option, + proof_data: &WithdrawData, +) -> Instruction { + ProofInstruction::VerifyWithdraw.encode_verify_proof(context_state_info, proof_data) } -pub fn verify_withdraw(proof_data: &WithdrawData) -> Instruction { - ProofInstruction::VerifyWithdraw.encode(proof_data) +/// Create a `VerifyCiphertextCiphertextEquality` instruction. +pub fn verify_ciphertext_ciphertext_equality( + context_state_info: Option, + proof_data: &CiphertextCiphertextEqualityProofData, +) -> Instruction { + ProofInstruction::VerifyCiphertextCiphertextEquality + .encode_verify_proof(context_state_info, proof_data) } -pub fn verify_withdraw_withheld_tokens(proof_data: &WithdrawWithheldTokensData) -> Instruction { - ProofInstruction::VerifyWithdrawWithheldTokens.encode(proof_data) +/// Create a `VerifyTransfer` instruction. +pub fn verify_transfer( + context_state_info: Option, + proof_data: &TransferData, +) -> Instruction { + ProofInstruction::VerifyTransfer.encode_verify_proof(context_state_info, proof_data) } -pub fn verify_transfer(proof_data: &TransferData) -> Instruction { - ProofInstruction::VerifyTransfer.encode(proof_data) +/// Create a `VerifyTransferWithFee` instruction. +pub fn verify_transfer_with_fee( + context_state_info: Option, + proof_data: &TransferWithFeeData, +) -> Instruction { + ProofInstruction::VerifyTransferWithFee.encode_verify_proof(context_state_info, proof_data) } -pub fn verify_transfer_with_fee(proof_data: &TransferWithFeeData) -> Instruction { - ProofInstruction::VerifyTransferWithFee.encode(proof_data) +/// Create a `VerifyPubkeyValidity` instruction. +pub fn verify_pubkey_validity( + context_state_info: Option, + proof_data: &PubkeyValidityData, +) -> Instruction { + ProofInstruction::VerifyPubkeyValidity.encode_verify_proof(context_state_info, proof_data) +} + +impl ProofInstruction { + pub fn encode_verify_proof( + &self, + context_state_info: Option, + proof_data: &T, + ) -> Instruction + where + T: Pod + ZkProofData, + U: Pod, + { + let accounts = if let Some(context_state_info) = context_state_info { + vec![ + AccountMeta::new(*context_state_info.context_state_account, false), + AccountMeta::new_readonly(*context_state_info.context_state_authority, false), + ] + } else { + vec![] + }; + + let mut data = vec![ToPrimitive::to_u8(self).unwrap()]; + data.extend_from_slice(bytes_of(proof_data)); + + Instruction { + program_id: crate::zk_token_proof_program::id(), + accounts, + data, + } + } + + pub fn instruction_type(input: &[u8]) -> Option { + input + .first() + .and_then(|instruction| FromPrimitive::from_u8(*instruction)) + } + + pub fn proof_data(input: &[u8]) -> Option<&T> + where + T: Pod + ZkProofData, + U: Pod, + { + input + .get(1..) + .and_then(|data| bytemuck::try_from_bytes(data).ok()) + } } diff --git a/zk-token-sdk/src/zk_token_proof_state.rs b/zk-token-sdk/src/zk_token_proof_state.rs new file mode 100644 index 00000000000000..d95aa4f11ec1c3 --- /dev/null +++ b/zk-token-sdk/src/zk_token_proof_state.rs @@ -0,0 +1,72 @@ +use { + crate::{zk_token_elgamal::pod::PodProofType, zk_token_proof_instruction::ProofType}, + bytemuck::{bytes_of, Pod, Zeroable}, + num_traits::ToPrimitive, + solana_program::{ + instruction::{InstructionError, InstructionError::InvalidAccountData}, + pubkey::Pubkey, + }, + std::mem::size_of, +}; + +/// The proof context account state +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(C)] +pub struct ProofContextState { + /// The proof context authority that can close the account + pub context_state_authority: Pubkey, + /// The proof type for the context data + pub proof_type: PodProofType, + /// The proof context data + pub proof_context: T, +} + +// `bytemuck::Pod` cannot be derived for generic structs unless the struct is marked +// `repr(packed)`, which may cause unnecessary complications when referencing its fields. Directly +// mark `ProofContextState` as `Zeroable` and `Pod` since since none of its fields has an alignment +// requirement greater than 1 and therefore, guaranteed to be `packed`. +unsafe impl Zeroable for ProofContextState {} +unsafe impl Pod for ProofContextState {} + +impl ProofContextState { + pub fn encode( + context_state_authority: &Pubkey, + proof_type: ProofType, + proof_context: &T, + ) -> Vec { + let mut buf = Vec::with_capacity(size_of::()); + buf.extend_from_slice(context_state_authority.as_ref()); + buf.push(ToPrimitive::to_u8(&proof_type).unwrap()); + buf.extend_from_slice(bytes_of(proof_context)); + buf + } + + /// Interpret a slice as a `ProofContextState`. + /// + /// This function requires a generic parameter. To access only the generic-independent fields + /// in `ProofContextState` without a generic parameter, use + /// `ProofContextStateMeta::try_from_bytes` instead. + pub fn try_from_bytes(input: &[u8]) -> Result<&Self, InstructionError> { + bytemuck::try_from_bytes(input).map_err(|_| InvalidAccountData) + } +} + +/// The `ProofContextState` without the proof context itself. This struct exists to facilitate the +/// decoding of generic-independent fields in `ProofContextState`. +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] +#[repr(C)] +pub struct ProofContextStateMeta { + /// The proof context authority that can close the account + pub context_state_authority: Pubkey, + /// The proof type for the context data + pub proof_type: PodProofType, +} + +impl ProofContextStateMeta { + pub fn try_from_bytes(input: &[u8]) -> Result<&Self, InstructionError> { + input + .get(..size_of::()) + .and_then(|data| bytemuck::try_from_bytes(data).ok()) + .ok_or(InvalidAccountData) + } +}