diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..3bdeb943cc --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,44 @@ +/ @jolestar @wow-sven @baichuan3 @templexxx @stevenlaw123 +/.github @yourmoonlight @jolestar +/scripts @yourmoonlight @jolestar +/kube @yourmoonlight @jolestar +/docker @yourmoonlight @jolestar + +/moveos @stevenlaw123 @jolestar +/moveos/metrics @baichuan3 @stevenlaw123 +/moveos/moveos @stevenlaw123 @jolestar +/moveos/moveos-commons @baichuan3 @wow-sven +/moveos/moveos-config @baichuan3 @wow-sven +/moveos/moveos-stdlib @baichuan3 @wow-sven @jolestar +/moveos/moveos-store @baichuan3 @templexxx +/moveos/moveos-types @jolestar @baichuan3 +/moveos/moveos-verfier @stevenlaw123 @jolestar +/moveos/raw-store @baichuan3 @templexxx +/moveos/smt @baichuan3 @templexxx @jolestar + +/crates @baichuan3 @jolestar +/crates/rooch @wow-sven @pause125 +/crates/rooch-config @wow-sven @baichuan3 +/crates/rooch-da @templexxx @jolestar +/crates/rooch-executor @templexxx @jolestar +/crates/rooch-framework @jolestar @baichuan3 +/crates/rooch-genesis @jolestar @baichuan3 +/crates/rooch-integration-test-runner @stevenlaw123 @jolestar +/crates/rooch-key @wow-sven @templexxx +/crates/rooch-open-rpc @wow-sven @baichuan3 +/crates/rooch-open-rpc-macros @wow-sven @baichuan3 +/crates/rooch-open-rpc-spec @wow-sven @baichuan3 +/crates/rooch-proposer @jolestar @templexxx +/crates/rooch-rpc-api @wow-sven @baichuan3 +/crates/rooch-rpc-client @wow-sven @baichuan3 +/crates/rooch-rpc-server @wow-sven @baichuan3 +/crates/rooch-sequencer @templexxx @jolestar +/crates/rooch-store @baichuan3 @templexxx +/crates/rooch-types @jolestar @baichuan3 +/crates/testsuite @pause125 @jolestar + +/examples @jolestar @wow-sven @geometryolife +/examples/blog @wubuku @jolestar +/sdk @wow-sven @yubing744 +/dashboards @wow-sven @yubing744 @Mine77 +/docs @wow-sven @geometryolife @Mine77 diff --git a/.github/workflows/check_build_test.yml b/.github/workflows/check_build_test.yml index 90ca1bb5ab..6e8bce3f5b 100644 --- a/.github/workflows/check_build_test.yml +++ b/.github/workflows/check_build_test.yml @@ -7,7 +7,6 @@ on: - 'docs/**' - 'fixtures/**' - 'kube/**' - - 'scripts/**' - '**.md' pull_request: branches: ["main"] @@ -15,7 +14,6 @@ on: - 'docs/**' - 'fixtures/**' - 'kube/**' - - 'scripts/**' - '**.md' env: @@ -44,8 +42,6 @@ jobs: # Use cargo test to run CLI integration tests. # TODO: FIXME run: cargo test -p testsuite --test integration - - name: Export rooch_types.yaml - run: cargo run --bin rooch abi export-rooch-types - name: Run Rooch init run: cargo run --bin rooch init - name: Execute stdlib tests @@ -54,8 +50,30 @@ jobs: run: cargo run --bin rooch move test -p crates/rooch-framework/ - name: Build and test example projects run: ./scripts/pr.sh -e + ## Build and test sdk start + - name: Use Node.js + uses: actions/setup-node@v2 + with: + node-version: '18.x' + - name: Cache Node.js modules + uses: actions/cache@v2 + with: + path: ~/.pnpm-store + key: ${{ runner.OS }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.OS }}-pnpm- + - name: Install SDK dependencies + run: cd sdk/typescript && npm install pnpm -g && pnpm install + - name: Gen SDK dependencie Code + run: cd sdk/typescript && pnpm gen + - name: Build SDK + run: cd sdk/typescript && pnpm run build + - name: Test SDK + run: cd sdk/typescript && pnpm run test + ## Build and test sdk end - uses: CatChen/check-git-status-action@v1 with: fail-if-not-clean: true # optional push-if-not-clean: false # optional targets: "." #optional + diff --git a/CONTRIBUTING-zh.md b/CONTRIBUTING-zh.md new file mode 100644 index 0000000000..e6cde5b828 --- /dev/null +++ b/CONTRIBUTING-zh.md @@ -0,0 +1,71 @@ +# 为 Rooch 做出贡献 + +感谢您有兴趣为 Rooch 做出贡献!做出贡献的方式有很多种,我们感谢所有这些方式。 + +- 在我们的 [Discord 频道](https://discord.gg/kgXEmHGB)交流,分享您的新想法,交流技术 +- 学习 Rooch +- 报告 Bug +- 请求新特性 +- 提交 PR + +## 通过 GitHub 来贡献 + +为 Rooch 源代码或文档做出贡献,您需要拥有一个 GitHub 帐户。 + +提交 PR 的方式通常有两种: + +- 一种是直接在 GitHub 网站上的仓库里进行编辑,提交更改后,直接推送到主分支上。这种方式特别适合提交修改错别字等简单的 PR,对于涉及多个文件或者更改的内容比较多时,不推荐使用这种方式。 +- 另一种方式是,将 Rooch 的仓库 `fork` 到您自己的账户下,并将其 `clone` 到本地,完成修改后再将其推送到上游 `main` 分支(指 Rooch 组织),您自己 GitHub 上的分支称为远程分支。 + +## 方式二的详细操作流程 + +### 创建一个新分叉 + +首先,在您自己的帐户中创建 `rooch` 仓库的分支,以便您可以使用自己的副本。 + +1. 登录您的 Github 帐户。 +2. 浏览 GitHub 上的 [Rooch 存储库](https:github.comrooch-networkrooch)。 +3. 选择右上角的 `Fork`,然后选择 `Create a new fork`。 +4. 对于**所有者**,选择您的用户名。 +5. 对于**存储库名称**,我们建议保留名称 `rooch`,但您可以使用任何名称。 +6. 可选。要做出贡献,您只需要存储库的主分支。要包含所有分支,请取消选中**仅复制 `main` 分支**的复选框。 +7. 单击 `Create fork`。 + +### 克隆您的分叉 + +接下来,将存储库的分支克隆到本地工作区。 + +1. 打开您分叉的仓库页面,然后单击 `Sync fork` 按钮(刚刚分叉通常不需要操作,如果你的分叉仓库提交落后上游仓库时,才需要同步)。 +2. 单击 `Code`,然后单击 `HTTPS` 并复制显示的 Web URL。 +3. 打开终端会话并导航到要使用的文件夹,然后运行以下命令,将 URL 替换为您从 GitHub 页面复制的 URL: + +```shell +git clone https://github.com//rooch.git` +``` + +### 新建分支 + +克隆完成后,就可以对 `rooch` 目录内的任意文件进行修改了,默认情况下克隆的是默认分支(可以在 GitHub 设定),通常情况下是 `main` 分支。 + +对 Rooch 的项目进行相应的修改前,需要进行**最重要的一步**:新建主题分支。 + +通常情况下,不建议您直接使用 `main` 分支向 Rooch 的上游提交 PR,这不利于 Rooch 的维护者与你进行协作。 + + +## 新建拉取请求 + +当你完成了修改,将更改提交并推送到远程仓库后,你通常可以在 GitHub 的页面上看到一个 `Compare & pull request` 的弹出按钮,点击并填写相应信息即可。 + +内容通常会自动填充,如果你觉得标题或者这次 PR 的内容描述不够准确,可以继续修改。 + +修改完成后,点击下方的 `Create pull request` 按钮后,你的 PR 就会出现在 Rooch 的 `Pull requests` 页面了。 + +## 提交新问题 + +报告问题和提交特性请求通常在 `Issues` 页面提交对应的 issue 帖子即可。 + +**当您要报告 Bug 时**,请打开 [Rooch 的 GitHub Issues 页面](https://github.com/rooch-network/rooch/issues)。在上方的搜索栏,简单搜索一下您的问题,可能您发现的问题可能其他人已经提交过了,避免重复提交。如果没有您想报告的 Bug 帖子,此时请点击右上方的 `New issue` 按钮,输入描述当前 Bug 的简要描述作为标题,并在内容框里填写当前 Bug 的**详细描述**,包括*您的操作(系统)环境*,*使用的 Rooch 版本*以及*复现问题的流程*等。 + +**当您要请求一个新特性时**,在当前的 Issues 页面标题栏上填写特性的简介描述作为标题,并在标题前加上 `[Feature Request]` 作为标识。 + +填写好相关信息后,点击右侧的 `Lables`,打上相应的标签,并点击下方的 `Submit new issue` 按钮。 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe9a0e8585..b8af4fc26b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,59 +1,72 @@ +# Contributing to Rooch -# Contributing +Thank you for your interest in contributing to Rooch! There are many ways to contribute and we appreciate all of them. -Our goal is to make contributing to Rooch Network easy and transparent. +- Communicating in our [Discord channel](https://discord.gg/kgXEmHGB), share your new ideas, talk technologies +- Learning Rooch +- Reporting bugs +- Requesting new features +- Submitting PRs -## Install Rooch to contribute +## Contributing via GitHub -To contribute to Rooch source code or documentation, you need only a GitHub account. You can commit updates and then submit a PR directly from the Github website, or create a fork of the repo to your local environment and use your favorite tools to make changes. Always submit PRs to the `main` branch. +To contribute to the Rooch source code or documentation, you need a GitHub account. -### Create a fork +There are usually two ways to submit a PR: -First, create a fork of the Rooch Network Rooch repo in your own account so that you can work with your own copy. +- One is to edit directly in the repository on the GitHub website, and after submitting the changes, push them directly to the main branch. This method is especially suitable for submitting simple PRs such as correcting typos. It is not recommended to use this method when multiple files are involved or there are many changes. -**To create a fork using the website** +- Another way is to `fork` the Rooch repository under your own account, and `clone` it locally, and then push it to the upstream `main` branch (referring to the Rooch organization) after the modification is completed. The branch on your own GitHub is called a remote branch. + +## Detailed operation process of method 2 + +### Create a new fork + +First, create a fork of the `rooch` repository in your own account so that you can use your own copy. 1. Log in to your Github account. -1. Browse to the [Rooch repo](https://github.com/rooch-network/rooch) on GitHub. -1. Choose **Fork** in the top-right, then choose **Create new fork**. -1. For **Owner**, select your username. -1. For **Repository name**, we suggest keeping the name rooch, but you can use any name. -1. Optional. To contribute you need only the main branch of the repo. To include all branches, unselect the checkbox for **Copy the `main` branch only**. -1. Click **Create fork**. +2. Browse the [Rooch repo](https://github.com/rooch-network/rooch) on GitHub. +3. Select `Fork` in the upper right corner, then select `Create a new fork`. +4. For **Owner**, select your username. +5. For the **Repository name**, we recommend keeping the name `rooch`, but you can use any name. +6. Optional. To contribute, you only need the main branch of the repository. To include all branches, uncheck the checkbox for **Copy the `main` branch only**. +7. Click `Create fork`. ### Clone your fork -Next, clone your fork of the repo to your local workspace. +Next, clone the fork of the repository to your local repository. + +1. Open the repository page of your fork, and click the `Sync fork` button (no operation is usually required for the fork just now, and synchronization is only required if your forked repository commits lags behind the upstream repository). +2. Click on `Code`, then click on `HTTPS` and copy the displayed web URL. +3. Open a terminal session and navigate to the folder you want to use, then run the following command, replacing the URL with the one you copied from the GitHub page: -**To clone your fork to your local workspace** -1. Open the GitHub page for your fork of the repo, then click **Sync fork**. -1. Click **Code**, then click **HTTPS** and copy the web URL displayed. -1. Open a terminal session and navigate to the folder to use, then run the following command, replacing the URL with the URL you copied from the Git page: +```shell +git clone https://github.com//rooch.git` +``` -`git clone https://github.com/github-user-name/rooch.git` +### Create a new branch -The repo is automatically cloned into the `rooch` folder in your workspace. -Create a branch of your fork with following command (or follow the [GitHub topic on branching](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-and-deleting-branches-within-your-repository)) +After cloning is completed, you can modify any file in the `rooch` directory. By default, the clone is the default branch (can be set in GitHub), usually the `main` branch. -`git checkout -b your-branch-name` +Before making corresponding changes to Rooch's project, you need to perform **the most important step**: creating a new topic branch. -Use the following command to set the [remote upstream repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/configuring-a-remote-repository-for-a-fork): +In general, it is not recommended to submit a PR to Rooch's upstream directly using the `main` branch, which is not conducive to the collaboration of Rooch maintainers with you. -`git remote add upstream https://github.com/rooch-network/rooch.git` +## Create a new pull request -When you're under the rooch dir, you can locally run `./scripts/dev_setup.sh` to ensure you have all development dependencies required for our workflows. -## Issues +When you have completed the modification, submitted and pushed the changes to the remote repository, you can usually see a `Compare & pull request` pop-up button on the GitHub page, just click and fill in the corresponding information. -Rooch Network uses [GitHub issues](https://github.com/rooch-network/rooch/issues) to track bugs. Please include necessary information and instructions to reproduce your issue. +The content is usually filled in automatically. If you feel that the title or the content description of this PR is not accurate enough, you can continue to modify it. +After the modification is complete, click the `Create pull request` button below, and your PR will appear on Rooch's `Pull requests` page. -## Pull Request Requirements +## Submitting new issues -You now have a fork of the Rooch repo set up in your local workspace. You can make changes to the files in the workspace, add commits, then push your changes to your fork of the repo to then create a Pull Request. +Reporting issues and submitting feature requests is usually done by submitting corresponding issue posts on the `Issues` page. -Rooch Network welcomes everyone to participate and contribute, after reading the contribution guidelines, we also invite you to take a look at the requirements for Pull Requests [PR-Guidelines](./docs/contribute/pr-requirements.md). +**When you want to report a bug**, please open [Rooch's GitHub Issues page](https://github.com/rooch-network/rooch/issues). In the search bar above, simply search for your question, maybe the question you found may have been submitted by others, so avoid duplicating submissions.If there is no bug post you want to report, please click the `New issue` button on the upper right, enter a brief description of the current bug as the title, and fill in the **detailed description** of the current bug in the content box, including *your operating (system) environment*, *the version of Rooch used*, and *the process of reproducing the problem*, etc. -## Coding Guidelines for Move Language +**When you want to request a new feature**, fill in the brief description of the feature on the title bar of the current Issues page as the title, and add `[Feature Request]` as the identifier before the title. -Please refer to [Move Coding Conventions](./docs/contribute/coding-conventions.md). \ No newline at end of file +After filling in the relevant information, click on `Lables` on the right, label it accordingly, and click on the `Submit new issue` button below. diff --git a/Cargo.toml b/Cargo.toml index 8c52270ba6..fadb2f16db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,11 @@ members = [ "moveos/moveos-stdlib", "moveos/moveos-stdlib-builder", "moveos/moveos", - "moveos/moveos-common", + "moveos/moveos-commons/moveos-common", + "moveos/moveos-commons/timeout-join-handler", "moveos/raw-store", + "moveos/metrics", + "moveos/moveos-config", "crates/rooch-key", "crates/rooch-types", "crates/rooch-framework", @@ -60,9 +63,12 @@ moveos-stdlib = { path = "moveos/moveos-stdlib" } moveos-stdlib-builder = { path = "moveos/moveos-stdlib-builder" } moveos = { path = "moveos/moveos" } moveos-cli = { path = "moveos/moveos-cli" } -moveos-common = { path = "moveos/moveos-common" } +moveos-common = { path = "moveos/moveos-commons/moveos-common" } +timeout-join-handler = { path = "moveos/moveos-commons/timeout-join-handler" } moveos-verifier = { path = "moveos/moveos-verifier" } raw-store = { path = "moveos/raw-store" } +metrics = { path = "moveos/metrics" } +moveos-config = { path = "moveos/moveos-config" } # crates for Rooch rooch = { path = "crates/rooch" } @@ -109,10 +115,12 @@ enum_dispatch = "^0.3" ethereum-types = "0.14.1" ethers = { version = "2.0.7", features = ["legacy"] } eyre = "0.6.8" -fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "c27e87fb539c88b6fe1eac7dd82561254bb1e07a" } +fastcrypto = { git = "https://github.com/rooch-network/fastcrypto", rev = "aa5f9f308b6598779820db8b673050c10cfcc3c1" } futures = "0.3" hex = "0.4.3" rustc-hex = "1.0" +rust-embed = "6.8.1" +rocket = { version = "0.5.0-rc.2", default-features = false } itertools = "0.10.5" jsonrpsee = { version = "0.16", features = ["full"] } jpst = "0.1.1" @@ -169,79 +177,90 @@ tempfile = "3.2.0" regex = "1.8.4" walkdir = "2.3.3" rocksdb = { version = "0.21.0", features = ["snappy", "lz4", "zstd", "zlib", "multi-threaded-cf"], default-features = false } -bincode = "1.3.3" -collectable = "0.0.2" -fdlimit = "0.2.1" -tap = "1.0.1" -num_cpus = "1.14.0" prometheus = "0.13.3" -hdrhistogram = "7.5.1" -ouroboros = "0.15.5" -rstest = "0.16.0" +coarsetime = "0.1.22" +hyper = { version = "0.14.12", features = ["full"] } +num_enum = "0.5.7" +libc = "^0.2" nostr = "0.22" serde-reflection = "0.3.6" serde-generate = "0.25.1" +rust_secp256k1 = { version = "0.27.0", package = "secp256k1", features = ["recovery", "rand-std", "bitcoin_hashes", "global-context"] } +bcs-ext = { path = "moveos/moveos-commons/bcs_ext" } +tower = { version = "0.4.12", features = ["full", "util", "timeout", "load-shed", "limit"] } +tower-http = { version = "0.3.4", features = ["cors", "full", "trace", "set-header", "propagate-header"] } # Note: the BEGIN and END comments below are required for external tooling. Do not remove. # BEGIN MOVE DEPENDENCIES -move-abigen = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-binary-format = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-bytecode-verifier = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-bytecode-utils = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-cli = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-command-line-common = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-compiler ={ git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-core-types = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c", features = ["address32"] } -move-coverage = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-disassembler = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-docgen = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-errmapgen = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-ir-compiler = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-model = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-package = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-prover = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-prover-boogie-backend = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-stackless-bytecode = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-prover-test-utils = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-resource-viewer = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-stackless-bytecode-interpreter = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-stdlib = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c", features = ["address32", "testing"] } -move-symbol-pool = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -#move-table-extension = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-transactional-test-runner = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-unit-test = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c", features = ["table-extension"] } -move-vm-runtime = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c", features = ["lazy_natives"] } -move-vm-test-utils = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c", features = ["table-extension"] } -move-vm-types = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -read-write-set = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -read-write-set-dynamic = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-bytecode-source-map = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -move-ir-types = { git = "https://github.com/rooch-network/move", rev = "5597bcfd78d952f428360ba2eae81f2f79d2627c" } -# END MOVE DEPENDENCIES +move-abigen = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-binary-format = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-bytecode-verifier = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-bytecode-utils = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-cli = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-command-line-common = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-compiler ={ git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-core-types = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a", features = ["address32"] } +move-coverage = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-disassembler = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-docgen = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-errmapgen = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-ir-compiler = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-model = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-package = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-prover = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-prover-boogie-backend = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-stackless-bytecode = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-prover-test-utils = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-resource-viewer = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-stackless-bytecode-interpreter = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-stdlib = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a", features = ["address32", "testing"] } +move-symbol-pool = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +#move-table-extension = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-transactional-test-runner = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-unit-test = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a", features = ["table-extension"] } +move-vm-runtime = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a", features = ["lazy_natives"] } +move-vm-test-utils = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a", features = ["table-extension"] } +move-vm-types = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +read-write-set = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +read-write-set-dynamic = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-bytecode-source-map = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" } +move-ir-types = { git = "https://github.com/rooch-network/move", rev = "55b8bf6967e76ff11933fffaccb76d5a4e15a23a" }# END MOVE DEPENDENCIES # keep this for convenient debug Move in local repo # [patch.'https://github.com/rooch-network/move'] -# move-abigen = { path = "../../move-language/move/language/move-prover/move-abigen" } -# move-prover = { path = "../../move-language/move/language/move-prover" } -# move-package = { path = "../../move-language/move/language/tools/move-package" } -# move-stdlib = { path = "../../move-language/move/language/move-stdlib" } -# move-unit-test = { path = "../../move-language/move/language/tools/move-unit-test" } -# move-cli = { path = "../../move-language/move/language/tools/move-cli" } -# move-command-line-common = { path = "../../move-language/move/language/move-command-line-common" } -# move-ir-types = { path = "../../move-language/move/language/move-ir/types" } -# move-binary-format = { path = "../../move-language/move/language/move-binary-format" } -# move-core-types = { path = "../../move-language/move/language/move-core/types" } -# move-bytecode-verifier = { path = "../../move-language/move/language/move-bytecode-verifier" } -# move-ir-compiler = { path = "../../move-language/move/language/move-ir-compiler" } -# move-ir-to-bytecode = { path = "../../move-language/move/language/move-ir-compiler/move-ir-to-bytecode" } -# move-bytecode-source-map = { path = "../../move-language/move/language/move-ir-compiler/move-bytecode-source-map" } -# move-compiler = { path = "../../move-language/move/language/move-compiler" } -# move-vm-runtime = { path = "../../move-language/move/language/move-vm/runtime" } -# move-vm-types = { path = "../../move-language/move/language/move-vm/types" } -# move-model = { path = "../../move-language/move/language/move-model" } -# move-vm-test-utils = { path = "../../move-language/move/language/move-vm/test-utils" } -# move-transactional-test-runner = { path = "../../move-language/move/language/testing-infra/transactional-test-runner" } - +# move-abigen = { path = "../move/language/move-prover/move-abigen" } +# move-binary-format = { path = "../move/language/move-binary-format" } +# move-bytecode-verifier = { path = "../move/language/move-bytecode-verifier" } +# move-bytecode-utils = { path = "../move/language/tools/move-bytecode-utils" } +# move-cli = { path = "../move/language/tools/move-cli" } +# move-command-line-common = { path = "../move/language/move-command-line-common" } +# move-compiler ={ path = "../move/language/move-compiler" } +# move-core-types = { path = "../move/language/move-core/types", features = ["address32"] } +# move-coverage = { path = "../move/language/tools/move-coverage" } +# move-disassembler = { path = "../move/language/tools/move-disassembler" } +# move-docgen = { path = "../move/language/move-prover/move-docgen" } +# move-errmapgen = { path = "../move/language/move-prover/move-errmapgen" } +# move-ir-compiler = { path = "../move/language/move-ir-compiler" } +# move-model = { path = "../move/language/move-model" } +# move-package = { path = "../move/language/tools/move-package" } +# move-prover = { path = "../move/language/move-prover" } +# move-prover-boogie-backend = { path = "../move/language/move-prover/boogie-backend" } +# move-stackless-bytecode = { path = "../move/language/move-prover/bytecode" } +# move-prover-test-utils = { path = "../move/language/move-prover/test-utils" } +# move-resource-viewer = { path = "../move/language/tools/move-resource-viewer" } +# move-stackless-bytecode-interpreter = { path = "../move/language/move-prover/interpreter" } +# move-stdlib = { path = "../move/language/move-stdlib", features = ["address32", "testing"] } +# move-symbol-pool = { path = "../move/language/move-symbol-pool" } +# #move-table-extension = { path = "../move/language/" } +# move-transactional-test-runner = { path = "../move/language/testing-infra/transactional-test-runner" } +# move-unit-test = { path = "../move/language/tools/move-unit-test", features = ["table-extension"] } +# move-vm-runtime = { path = "../move/language/move-vm/runtime", features = ["lazy_natives"] } +# move-vm-test-utils = { path = "../move/language/move-vm/test-utils", features = ["table-extension"] } +# move-vm-types = { path = "../move/language/move-vm/types" } +# read-write-set = { path = "../move/language/tools/read-write-set" } +# read-write-set-dynamic = { path = "../move/language/read-write-set/dynamic" } +# move-bytecode-source-map = { path = "../move/language/move-ir-compiler/move-bytecode-source-map" } +# move-ir-types = { path = "../move/language/move-ir/types" } [profile.release] debug = false overflow-checks = true diff --git a/crates/rooch-config/Cargo.toml b/crates/rooch-config/Cargo.toml index 80b9fbd8e0..2a39ca0a48 100644 --- a/crates/rooch-config/Cargo.toml +++ b/crates/rooch-config/Cargo.toml @@ -15,5 +15,9 @@ thiserror = { workspace = true } tonic = { workspace = true } coerce = { workspace = true } dirs = { workspace = true } +once_cell = { workspace = true } +clap = { workspace = true } + rooch-key = { workspace = true } rooch-types = { workspace = true } +moveos-config = { workspace = true } diff --git a/crates/rooch-config/src/lib.rs b/crates/rooch-config/src/lib.rs index 310b6b7fa4..50334e62fd 100644 --- a/crates/rooch-config/src/lib.rs +++ b/crates/rooch-config/src/lib.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 pub mod rpc; +pub mod store_config; use anyhow::{Context, Result}; use serde::{de::DeserializeOwned, Serialize}; @@ -14,6 +15,15 @@ pub const ROOCH_SERVER_CONFIG: &str = "server.yaml"; pub const ROOCH_KEYSTORE_FILENAME: &str = "rooch.keystore"; pub fn rooch_config_dir() -> anyhow::Result { + get_rooch_config_dir().and_then(|dir| { + if !dir.exists() { + fs::create_dir_all(dir.clone())?; + } + Ok(dir) + }) +} + +pub fn get_rooch_config_dir() -> anyhow::Result { match std::env::var_os("ROOCH_CONFIG_DIR") { Some(config_env) => Ok(config_env.into()), None => match dirs::home_dir() { @@ -21,12 +31,6 @@ pub fn rooch_config_dir() -> anyhow::Result { None => anyhow::bail!("Cannot obtain home directory path"), }, } - .and_then(|dir| { - if !dir.exists() { - fs::create_dir_all(dir.clone())?; - } - Ok(dir) - }) } pub trait Config diff --git a/crates/rooch-config/src/store_config.rs b/crates/rooch-config/src/store_config.rs new file mode 100644 index 0000000000..843758df6b --- /dev/null +++ b/crates/rooch-config/src/store_config.rs @@ -0,0 +1,109 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +// Copyright (c) The Starcoin Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::get_rooch_config_dir; +use anyhow::Result; +use clap::Parser; +use moveos_config::store_config::RocksdbConfig; +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; + +static R_DEFAULT_DB_DIR: Lazy = Lazy::new(|| PathBuf::from("roochdb")); +static R_DEFAULT_DB_MOVEOS_SUBDIR: Lazy = Lazy::new(|| PathBuf::from("moveos_store")); +static R_DEFAULT_DB_ROOCH_SUBDIR: Lazy = Lazy::new(|| PathBuf::from("rooch_store")); +pub const DEFAULT_CACHE_SIZE: usize = 20000; + +#[derive(Clone, Default, Debug, Deserialize, PartialEq, Serialize, Parser)] +#[serde(deny_unknown_fields)] +pub struct StoreConfig { + #[serde(skip_serializing_if = "Option::is_none")] + #[clap(name = "rocksdb-max-open-files", long, help = "rocksdb max open files")] + pub max_open_files: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + #[clap( + name = "rocksdb-max-total-wal-sizes", + long, + help = "rocksdb max total WAL sizes" + )] + pub max_total_wal_size: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + #[clap(name = "cache-sizes", long, help = "cache sizes")] + pub cache_size: Option, + + // #[serde(skip)] + // #[clap(skip)] + // base: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + #[clap( + name = "rocksdb-wal-bytes-per-sync", + long, + help = "rocksdb wal bytes per sync" + )] + pub wal_bytes_per_sync: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + #[clap(name = "rocksdb-bytes-per-sync", long, help = "rocksdb bytes per sync")] + pub bytes_per_sync: Option, +} + +//TODO implement store dir +impl StoreConfig { + pub fn init() -> Result<()> { + let rooch_db_dir = Self::get_rooch_store_dir(); + let moveos_db_dir = Self::get_moveos_store_dir(); + if !rooch_db_dir.exists() { + fs::create_dir_all(rooch_db_dir.clone())?; + } + if !moveos_db_dir.exists() { + fs::create_dir_all(moveos_db_dir.clone())?; + } + println!( + "StoreConfig init store dir {:?} {:?}", + rooch_db_dir, moveos_db_dir + ); + Ok(()) + } + + //TODO load from config + pub fn get_moveos_store_dir() -> PathBuf { + get_rooch_config_dir() + .unwrap() + .parent() + .unwrap() + .join(R_DEFAULT_DB_DIR.as_path()) + .join(R_DEFAULT_DB_MOVEOS_SUBDIR.as_path()) + } + + pub fn get_rooch_store_dir() -> PathBuf { + get_rooch_config_dir() + .unwrap() + .parent() + .unwrap() + .join(R_DEFAULT_DB_DIR.as_path()) + .join(R_DEFAULT_DB_ROOCH_SUBDIR.as_path()) + } + + pub fn rocksdb_config(&self) -> RocksdbConfig { + let default = RocksdbConfig::default(); + RocksdbConfig { + max_open_files: self.max_open_files.unwrap_or(default.max_open_files), + max_total_wal_size: self + .max_total_wal_size + .unwrap_or(default.max_total_wal_size), + bytes_per_sync: self.bytes_per_sync.unwrap_or(default.bytes_per_sync), + wal_bytes_per_sync: self + .wal_bytes_per_sync + .unwrap_or(default.wal_bytes_per_sync), + } + } + pub fn cache_size(&self) -> usize { + self.cache_size.unwrap_or(DEFAULT_CACHE_SIZE) + } +} diff --git a/crates/rooch-executor/Cargo.toml b/crates/rooch-executor/Cargo.toml index 2240bcd177..e1de182042 100644 --- a/crates/rooch-executor/Cargo.toml +++ b/crates/rooch-executor/Cargo.toml @@ -43,4 +43,5 @@ moveos-common = { workspace = true } rooch-types = { workspace = true } rooch-framework = { workspace = true } rooch-genesis = { workspace = true } -rooch-store = { workspace = true } \ No newline at end of file +rooch-store = { workspace = true } +rooch-config = { workspace = true } \ No newline at end of file diff --git a/crates/rooch-executor/src/actor/executor.rs b/crates/rooch-executor/src/actor/executor.rs index 2588b01f59..9a1f344062 100644 --- a/crates/rooch-executor/src/actor/executor.rs +++ b/crates/rooch-executor/src/actor/executor.rs @@ -6,7 +6,10 @@ use super::messages::{ ExecuteViewFunctionMessage, GetEventsByEventHandleMessage, GetEventsMessage, ResolveMessage, StatesMessage, ValidateTransactionMessage, }; -use crate::actor::messages::{GetTransactionInfosByTxHashMessage, GetTxSeqMappingByTxOrderMessage}; +use crate::actor::messages::{ + GetTransactionInfosByTxHashMessage, GetTxSeqMappingByTxOrderMessage, + ListAnnotatedStatesMessage, ListStatesMessage, +}; use anyhow::bail; use anyhow::Result; use async_trait::async_trait; @@ -15,7 +18,8 @@ use move_core_types::account_address::AccountAddress; use move_resource_viewer::MoveValueAnnotator; use moveos::moveos::MoveOS; use moveos_common::accumulator::InMemoryAccumulator; -use moveos_store::MoveOSDB; +use moveos_store::transaction_store::TransactionStore; +use moveos_store::MoveOSStore; use moveos_types::event::AnnotatedMoveOSEvent; use moveos_types::event::EventHandle; use moveos_types::function_return_value::AnnotatedFunctionReturnValue; @@ -23,46 +27,46 @@ use moveos_types::module_binding::MoveFunctionCaller; use moveos_types::move_types::as_struct_tag; use moveos_types::state::{AnnotatedState, State}; use moveos_types::state_resolver::{AnnotatedStateReader, StateReader}; +use moveos_types::transaction::FunctionCall; use moveos_types::transaction::TransactionExecutionInfo; use moveos_types::transaction::VerifiedMoveOSTransaction; +use moveos_types::tx_context::TxContext; use rooch_framework::bindings::address_mapping::AddressMapping; +use rooch_framework::bindings::auth_validator::AuthValidatorCaller; use rooch_framework::bindings::transaction_validator::TransactionValidator; use rooch_genesis::RoochGenesis; -use rooch_store::RoochDB; +use rooch_store::RoochStore; use rooch_types::address::MultiChainAddress; +use rooch_types::framework::auth_validator::TxValidateResult; +use rooch_types::transaction::AuthenticatorInfo; use rooch_types::transaction::{AbstractTransaction, TransactionSequenceMapping}; pub struct ExecutorActor { moveos: MoveOS, - rooch_db: RoochDB, + rooch_store: RoochStore, } impl ExecutorActor { - pub fn new(rooch_db: RoochDB) -> Result { - let moveosdb = MoveOSDB::new_with_memory_store(); + pub fn new(moveos_store: MoveOSStore, rooch_store: RoochStore) -> Result { let genesis: &RoochGenesis = &rooch_genesis::ROOCH_GENESIS; - let mut moveos = MoveOS::new(moveosdb, genesis.all_natives(), genesis.config.clone())?; + let mut moveos = MoveOS::new(moveos_store, genesis.all_natives(), genesis.config.clone())?; if moveos.state().is_genesis() { moveos.init_genesis(genesis.genesis_txs())?; } - Ok(Self { moveos, rooch_db }) + Ok(Self { + moveos, + rooch_store, + }) } - pub fn resolve_address( + pub fn resolve_or_generate( &self, multi_chain_address_sender: MultiChainAddress, ) -> Result { let resolved_sender = { let address_mapping = self.moveos.as_module_bundle::(); - address_mapping - .resolve(multi_chain_address_sender.clone())? - .ok_or_else(|| { - anyhow::anyhow!( - "the multiaddress sender({}) mapping record is not exists.", - multi_chain_address_sender - ) - })? + address_mapping.resovle_or_generate(multi_chain_address_sender)? }; Ok(resolved_sender) @@ -71,23 +75,28 @@ impl ExecutorActor { pub fn validate(&self, tx: T) -> Result { let multi_chain_address_sender = tx.sender(); - let resolved_sender = self.resolve_address(multi_chain_address_sender.clone()); + let resolved_sender = self.resolve_or_generate(multi_chain_address_sender.clone()); let authenticator = tx.authenticator_info(); let mut moveos_tx = tx.construct_moveos_transaction(resolved_sender?)?; - let result = { - let tx_validator = self.moveos.as_module_bundle::(); - tx_validator.validate(&moveos_tx.ctx, authenticator) - }; + let result = self.validate_authenticator(&moveos_tx.ctx, authenticator); match result { - Ok(_) => { + Ok((tx_validate_result, pre_execute_functions, post_execute_functions)) => { // Add the original multichain address to the context moveos_tx .ctx .add(multi_chain_address_sender) .expect("add sender to context failed"); + // Add the tx_validate_result to the context + moveos_tx + .ctx + .add(tx_validate_result) + .expect("add tx_validate_result failed"); + + moveos_tx.append_pre_execute_functions(pre_execute_functions); + moveos_tx.append_post_execute_functions(post_execute_functions); Ok(self.moveos.verify(moveos_tx)?) } Err(e) => { @@ -100,6 +109,84 @@ impl ExecutorActor { } } } + + pub fn validate_authenticator( + &self, + ctx: &TxContext, + authenticator: AuthenticatorInfo, + ) -> Result<(TxValidateResult, Vec, Vec)> { + let tx_validator = self.moveos.as_module_bundle::(); + let tx_validate_result = tx_validator.validate(ctx, authenticator.clone())?; + let auth_validator_option = tx_validate_result.auth_validator(); + match auth_validator_option { + Some(auth_validator) => { + let auth_validator_caller = AuthValidatorCaller::new(&self.moveos, auth_validator); + auth_validator_caller.validate(ctx, authenticator.authenticator.payload)?; + // pre_execute_function: TransactionValidator first, then AuthValidator + let pre_execute_functions = vec![ + TransactionValidator::pre_execute_function_call(), + auth_validator_caller.pre_execute_function_call(), + ]; + // post_execute_function: AuthValidator first, then TransactionValidator + let post_execute_functions = vec![ + auth_validator_caller.post_execute_function_call(), + TransactionValidator::post_execute_function_call(), + ]; + Ok(( + tx_validate_result, + pre_execute_functions, + post_execute_functions, + )) + } + None => { + let pre_execute_functions = vec![TransactionValidator::pre_execute_function_call()]; + let post_execute_functions = + vec![TransactionValidator::post_execute_function_call()]; + Ok(( + tx_validate_result, + pre_execute_functions, + post_execute_functions, + )) + } + } + } + + pub fn get_rooch_store(&self) -> RoochStore { + self.rooch_store.clone() + } + + pub fn moveos(&self) -> &MoveOS { + &self.moveos + } + + pub fn execute(&mut self, tx: VerifiedMoveOSTransaction) -> Result { + let tx_hash = tx.ctx.tx_hash(); + let (state_root, output) = self.moveos.execute_and_apply(tx)?; + let event_hashes: Vec<_> = output.events.iter().map(|e| e.hash()).collect(); + let event_root = InMemoryAccumulator::from_leaves(event_hashes.as_slice()).root_hash(); + + let transaction_info = TransactionExecutionInfo::new( + tx_hash, + state_root, + event_root, + 0, + output.status.clone(), + ); + self.moveos + .transaction_store() + .save_tx_exec_info(transaction_info.clone()) + .map_err(|e| { + anyhow::anyhow!( + "ExecuteTransactionMessage handler save tx info failed: {:?} {}", + transaction_info, + e + ) + })?; + Ok(ExecuteTransactionResult { + output, + transaction_info, + }) + } } impl Actor for ExecutorActor {} @@ -125,25 +212,7 @@ impl Handler for ExecutorActor { msg: ExecuteTransactionMessage, _ctx: &mut ActorContext, ) -> Result { - let tx_hash = msg.tx.ctx.tx_hash(); - let (state_root, output) = self.moveos.execute(msg.tx)?; - let event_hashes: Vec<_> = output.events.iter().map(|e| e.hash()).collect(); - let event_root = InMemoryAccumulator::from_leaves(event_hashes.as_slice()).root_hash(); - - let transaction_info = TransactionExecutionInfo::new( - tx_hash, - state_root, - event_root, - 0, - output.status.clone(), - ); - self.moveos - .transaction_store() - .save_tx_exec_info(transaction_info.clone()); - Ok(ExecuteTransactionResult { - output, - transaction_info, - }) + self.execute(msg.tx) } } @@ -177,7 +246,7 @@ impl Handler for ExecutorActor { msg: ResolveMessage, _ctx: &mut ActorContext, ) -> Result { - self.resolve_address(msg.address) + self.resolve_or_generate(msg.address) } } @@ -205,6 +274,30 @@ impl Handler for ExecutorActor { } } +#[async_trait] +impl Handler for ExecutorActor { + async fn handle( + &mut self, + msg: ListStatesMessage, + _ctx: &mut ActorContext, + ) -> Result, State)>>, anyhow::Error> { + let statedb = self.moveos.moveos_resolver(); + statedb.list_states(msg.access_path, msg.cursor, msg.limit) + } +} + +#[async_trait] +impl Handler for ExecutorActor { + async fn handle( + &mut self, + msg: ListAnnotatedStatesMessage, + _ctx: &mut ActorContext, + ) -> Result, AnnotatedState)>>, anyhow::Error> { + let statedb = self.moveos.moveos_resolver(); + statedb.list_annotated_states(msg.access_path, msg.cursor, msg.limit) + } +} + #[async_trait] impl Handler for ExecutorActor { async fn handle( @@ -284,9 +377,8 @@ impl Handler for ExecutorActor { _ctx: &mut ActorContext, ) -> Result> { let GetTxSeqMappingByTxOrderMessage { cursor, limit } = msg; - let rooch_tx_store = self.rooch_db.get_transaction_store(); - let result = rooch_tx_store.get_tx_seq_mapping_by_tx_order(cursor, limit); - Ok(result) + let rooch_tx_store = self.rooch_store.get_transaction_store(); + rooch_tx_store.get_tx_seq_mapping_by_tx_order(cursor, limit) } } @@ -298,8 +390,8 @@ impl Handler for ExecutorActor { _ctx: &mut ActorContext, ) -> Result>> { let GetTransactionInfosByTxHashMessage { tx_hashes } = msg; - let moveos_tx_store = self.moveos.transaction_store(); - let result = moveos_tx_store.multi_get_tx_exec_infos(tx_hashes); - Ok(result) + self.moveos + .transaction_store() + .multi_get_tx_exec_infos(tx_hashes) } } diff --git a/crates/rooch-executor/src/actor/messages.rs b/crates/rooch-executor/src/actor/messages.rs index 3d20347d7d..f32ade2c83 100644 --- a/crates/rooch-executor/src/actor/messages.rs +++ b/crates/rooch-executor/src/actor/messages.rs @@ -81,6 +81,28 @@ impl Message for AnnotatedStatesMessage { type Result = Result>>; } +#[derive(Debug, Serialize, Deserialize)] +pub struct ListStatesMessage { + pub access_path: AccessPath, + pub cursor: Option>, + pub limit: usize, +} + +impl Message for ListStatesMessage { + type Result = Result, State)>>>; +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ListAnnotatedStatesMessage { + pub access_path: AccessPath, + pub cursor: Option>, + pub limit: usize, +} + +impl Message for ListAnnotatedStatesMessage { + type Result = Result, AnnotatedState)>>>; +} + #[derive(Debug, Serialize, Deserialize)] pub struct GetEventsByEventHandleMessage { pub event_handle_type: StructTag, diff --git a/crates/rooch-executor/src/proxy/mod.rs b/crates/rooch-executor/src/proxy/mod.rs index 0c13c7dc5a..6679320a58 100644 --- a/crates/rooch-executor/src/proxy/mod.rs +++ b/crates/rooch-executor/src/proxy/mod.rs @@ -1,7 +1,10 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use crate::actor::messages::{GetTransactionInfosByTxHashMessage, GetTxSeqMappingByTxOrderMessage}; +use crate::actor::messages::{ + GetTransactionInfosByTxHashMessage, GetTxSeqMappingByTxOrderMessage, + ListAnnotatedStatesMessage, ListStatesMessage, +}; use crate::actor::{ executor::ExecutorActor, messages::{ @@ -82,6 +85,36 @@ impl ExecutorProxy { .await? } + pub async fn list_states( + &self, + access_path: AccessPath, + cursor: Option>, + limit: usize, + ) -> Result, State)>>> { + self.actor + .send(ListStatesMessage { + access_path, + cursor, + limit, + }) + .await? + } + + pub async fn list_annotated_states( + &self, + access_path: AccessPath, + cursor: Option>, + limit: usize, + ) -> Result, AnnotatedState)>>> { + self.actor + .send(ListAnnotatedStatesMessage { + access_path, + cursor, + limit, + }) + .await? + } + pub async fn get_events_by_event_handle( &self, event_handle_type: StructTag, diff --git a/crates/rooch-framework-tests/Cargo.toml b/crates/rooch-framework-tests/Cargo.toml index 1ecf6fe842..2fb43710f8 100644 --- a/crates/rooch-framework-tests/Cargo.toml +++ b/crates/rooch-framework-tests/Cargo.toml @@ -47,10 +47,14 @@ rooch-framework = { workspace = true } rooch-genesis = { workspace = true } rooch-types = { workspace = true } rooch-key = { workspace = true } +rooch-executor = { workspace = true } +rooch-store = { workspace = true } [dev-dependencies] rooch-integration-test-runner = { workspace = true } datatest-stable = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } [[test]] harness = false diff --git a/crates/rooch-framework-tests/src/binding_test.rs b/crates/rooch-framework-tests/src/binding_test.rs index 49fd8b1d6b..2049e8b9c6 100644 --- a/crates/rooch-framework-tests/src/binding_test.rs +++ b/crates/rooch-framework-tests/src/binding_test.rs @@ -1,29 +1,40 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use anyhow::Result; -use moveos::moveos::MoveOS; -use moveos_store::MoveOSDB; +use anyhow::{bail, Result}; +use move_core_types::vm_status::KeptVMStatus; +use moveos_store::MoveOSStore; use moveos_types::module_binding::{ModuleBundle, MoveFunctionCaller}; -use rooch_genesis::RoochGenesis; +use rooch_executor::actor::executor::ExecutorActor; +use rooch_store::RoochStore; +use rooch_types::transaction::AbstractTransaction; pub struct RustBindingTest { - moveos: MoveOS, + executor: ExecutorActor, } impl RustBindingTest { pub fn new() -> Result { - let moveosdb = MoveOSDB::new_with_memory_store(); - let genesis: &RoochGenesis = &rooch_genesis::ROOCH_GENESIS; - - let mut moveos = MoveOS::new(moveosdb, genesis.all_natives(), genesis.config.clone())?; - if moveos.state().is_genesis() { - moveos.init_genesis(genesis.genesis_txs())?; - } - Ok(Self { moveos }) + let moveos_store = MoveOSStore::mock_moveos_store()?; + let rooch_store = RoochStore::mock_rooch_store(); + let executor = ExecutorActor::new(moveos_store, rooch_store)?; + Ok(Self { executor }) } pub fn as_module_bundle<'a, M: ModuleBundle<'a>>(&'a self) -> M { - self.moveos.as_module_bundle::() + self.executor.moveos().as_module_bundle::() + } + + //TODO let the module bundle to execute the function + pub fn execute(&mut self, tx: T) -> Result<()> { + let verified_tx = self.executor.validate(tx)?; + let execute_result = self.executor.execute(verified_tx)?; + if execute_result.transaction_info.status != KeptVMStatus::Executed { + bail!( + "tx should success, error: {:?}", + execute_result.transaction_info.status + ); + } + Ok(()) } } diff --git a/crates/rooch-framework-tests/src/tests/ecdsa_k1_recoverable_validator_tests.rs b/crates/rooch-framework-tests/src/tests/ecdsa_k1_recoverable_validator_tests.rs new file mode 100644 index 0000000000..f2b268ec83 --- /dev/null +++ b/crates/rooch-framework-tests/src/tests/ecdsa_k1_recoverable_validator_tests.rs @@ -0,0 +1,35 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use moveos_types::transaction::MoveAction; +use rooch_framework::bindings::empty::Empty; +use rooch_key::keystore::{AccountKeystore, InMemKeystore}; +use rooch_types::{ + crypto::BuiltinScheme, + transaction::{rooch::RoochTransactionData, AbstractTransaction}, +}; + +use crate::binding_test; + +#[test] +fn test_validate() { + let binding_test = binding_test::RustBindingTest::new().unwrap(); + let ecdsa_k1_recoverable_validator = binding_test + .as_module_bundle::( + ); + + let keystore = InMemKeystore::new_ecdsa_recoverable_insecure_for_tests(1); + let sender = keystore.addresses()[0]; + let sequence_number = 0; + let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); + let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx = keystore + .sign_transaction(&sender, tx_data, BuiltinScheme::EcdsaRecoverable) + .unwrap(); + let auth_info = tx.authenticator_info(); + let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); + + ecdsa_k1_recoverable_validator + .validate(&move_tx.ctx, auth_info.authenticator.payload) + .unwrap() +} diff --git a/crates/rooch-framework-tests/src/tests/ecdsa_k1_validator_tests.rs b/crates/rooch-framework-tests/src/tests/ecdsa_k1_validator_tests.rs new file mode 100644 index 0000000000..1c32e1f31d --- /dev/null +++ b/crates/rooch-framework-tests/src/tests/ecdsa_k1_validator_tests.rs @@ -0,0 +1,35 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use moveos_types::transaction::MoveAction; +use rooch_framework::bindings::empty::Empty; +use rooch_key::keystore::{AccountKeystore, InMemKeystore}; +use rooch_types::{ + crypto::BuiltinScheme, + transaction::{rooch::RoochTransactionData, AbstractTransaction}, +}; + +use crate::binding_test; + +#[test] +fn test_validate() { + let binding_test = binding_test::RustBindingTest::new().unwrap(); + let ecdsa_k1_validator = binding_test + .as_module_bundle::( + ); + + let keystore = InMemKeystore::new_ecdsa_insecure_for_tests(1); + let sender = keystore.addresses()[0]; + let sequence_number = 0; + let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); + let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx = keystore + .sign_transaction(&sender, tx_data, BuiltinScheme::Ecdsa) + .unwrap(); + let auth_info = tx.authenticator_info(); + let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); + + ecdsa_k1_validator + .validate(&move_tx.ctx, auth_info.authenticator.payload) + .unwrap() +} diff --git a/crates/rooch-framework-tests/src/tests/ed25519_validator_tests.rs b/crates/rooch-framework-tests/src/tests/ed25519_validator_tests.rs index c39f537242..b46602d29d 100644 --- a/crates/rooch-framework-tests/src/tests/ed25519_validator_tests.rs +++ b/crates/rooch-framework-tests/src/tests/ed25519_validator_tests.rs @@ -4,7 +4,10 @@ use moveos_types::transaction::MoveAction; use rooch_framework::bindings::empty::Empty; use rooch_key::keystore::{AccountKeystore, InMemKeystore}; -use rooch_types::transaction::{rooch::RoochTransactionData, AbstractTransaction}; +use rooch_types::{ + crypto::BuiltinScheme, + transaction::{rooch::RoochTransactionData, AbstractTransaction}, +}; use crate::binding_test; @@ -15,12 +18,14 @@ fn test_validate() { .as_module_bundle::( ); - let keystore = InMemKeystore::new_insecure_for_tests(1); + let keystore = InMemKeystore::new_ed25519_insecure_for_tests(1); let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); let tx_data = RoochTransactionData::new(sender, sequence_number, action); - let tx = keystore.sign_transaction(&sender, tx_data).unwrap(); + let tx = keystore + .sign_transaction(&sender, tx_data, BuiltinScheme::Ed25519) + .unwrap(); let auth_info = tx.authenticator_info(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); diff --git a/crates/rooch-framework-tests/src/tests/mod.rs b/crates/rooch-framework-tests/src/tests/mod.rs index b645a9fb3d..e5792da86c 100644 --- a/crates/rooch-framework-tests/src/tests/mod.rs +++ b/crates/rooch-framework-tests/src/tests/mod.rs @@ -1,6 +1,9 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 +mod ecdsa_k1_recoverable_validator_tests; +mod ecdsa_k1_validator_tests; mod ed25519_validator_tests; mod empty_tests; +mod schnorr_validator_tests; mod transaction_validator_tests; diff --git a/crates/rooch-framework-tests/src/tests/schnorr_validator_tests.rs b/crates/rooch-framework-tests/src/tests/schnorr_validator_tests.rs new file mode 100644 index 0000000000..cd9dbf28fe --- /dev/null +++ b/crates/rooch-framework-tests/src/tests/schnorr_validator_tests.rs @@ -0,0 +1,35 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use moveos_types::transaction::MoveAction; +use rooch_framework::bindings::empty::Empty; +use rooch_key::keystore::{AccountKeystore, InMemKeystore}; +use rooch_types::{ + crypto::BuiltinScheme, + transaction::{rooch::RoochTransactionData, AbstractTransaction}, +}; + +use crate::binding_test; + +#[test] +fn test_validate() { + let binding_test = binding_test::RustBindingTest::new().unwrap(); + let schnorr_validator = binding_test + .as_module_bundle::( + ); + + let keystore = InMemKeystore::new_schnorr_insecure_for_tests(1); + let sender = keystore.addresses()[0]; + let sequence_number = 0; + let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); + let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx = keystore + .sign_transaction(&sender, tx_data, BuiltinScheme::Schnorr) + .unwrap(); + let auth_info = tx.authenticator_info(); + let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); + + schnorr_validator + .validate(&move_tx.ctx, auth_info.authenticator.payload) + .unwrap() +} diff --git a/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs b/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs index 0c6349bdb7..44a79392fb 100644 --- a/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs +++ b/crates/rooch-framework-tests/src/tests/transaction_validator_tests.rs @@ -1,24 +1,51 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use moveos_types::transaction::MoveAction; -use rooch_framework::bindings::empty::Empty; +use moveos_types::{module_binding::ModuleBundle, transaction::MoveAction}; +use rooch_framework::{bindings::empty::Empty, ROOCH_FRAMEWORK_ADDRESS}; use rooch_key::keystore::{AccountKeystore, InMemKeystore}; -use rooch_types::transaction::{rooch::RoochTransactionData, AbstractTransaction}; +use rooch_types::{ + crypto::BuiltinScheme, + framework::session_key::SessionScope, + transaction::{rooch::RoochTransactionData, AbstractTransaction}, +}; use crate::binding_test; #[test] -fn test_validate() { +fn test_validate_ed25519() { let binding_test = binding_test::RustBindingTest::new().unwrap(); let transaction_validator = binding_test.as_module_bundle::(); - let keystore = InMemKeystore::new_insecure_for_tests(1); + let keystore = InMemKeystore::new_ed25519_insecure_for_tests(1); let sender = keystore.addresses()[0]; let sequence_number = 0; let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); let tx_data = RoochTransactionData::new(sender, sequence_number, action); - let tx = keystore.sign_transaction(&sender, tx_data).unwrap(); + let tx = keystore + .sign_transaction(&sender, tx_data, BuiltinScheme::Ed25519) + .unwrap(); + let auth_info = tx.authenticator_info(); + let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); + + transaction_validator + .validate(&move_tx.ctx, auth_info) + .unwrap(); +} + +#[test] +fn test_validate_ecdsa() { + let binding_test = binding_test::RustBindingTest::new().unwrap(); + let transaction_validator = binding_test.as_module_bundle::(); + + let keystore = InMemKeystore::new_ecdsa_insecure_for_tests(1); + let sender = keystore.addresses()[0]; + let sequence_number = 0; + let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); + let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx = keystore + .sign_transaction(&sender, tx_data, BuiltinScheme::Ecdsa) + .unwrap(); let auth_info = tx.authenticator_info(); let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); @@ -26,3 +53,89 @@ fn test_validate() { .validate(&move_tx.ctx, auth_info) .unwrap(); } + +#[test] +fn test_validate_ecdsa_recoverable() { + let binding_test = binding_test::RustBindingTest::new().unwrap(); + let transaction_validator = binding_test.as_module_bundle::(); + + let keystore = InMemKeystore::new_ecdsa_recoverable_insecure_for_tests(1); + let sender = keystore.addresses()[0]; + let sequence_number = 0; + let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); + let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx = keystore + .sign_transaction(&sender, tx_data, BuiltinScheme::EcdsaRecoverable) + .unwrap(); + let auth_info = tx.authenticator_info(); + let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); + + transaction_validator + .validate(&move_tx.ctx, auth_info) + .unwrap(); +} + +#[test] +fn test_validate_schnorr() { + let binding_test = binding_test::RustBindingTest::new().unwrap(); + let transaction_validator = binding_test.as_module_bundle::(); + + let keystore = InMemKeystore::new_schnorr_insecure_for_tests(1); + let sender = keystore.addresses()[0]; + let sequence_number = 0; + let action = MoveAction::new_function_call(Empty::empty_function_id(), vec![], vec![]); + let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx = keystore + .sign_transaction(&sender, tx_data, BuiltinScheme::Schnorr) + .unwrap(); + let auth_info = tx.authenticator_info(); + let move_tx = tx.construct_moveos_transaction(sender.into()).unwrap(); + + transaction_validator + .validate(&move_tx.ctx, auth_info) + .unwrap(); +} + +#[test] +fn test_session_key_ed25519() { + tracing_subscriber::fmt::init(); + let mut binding_test = binding_test::RustBindingTest::new().unwrap(); + + let keystore = InMemKeystore::new_ed25519_insecure_for_tests(1); + let sender = keystore.addresses()[0]; + let sequence_number = 0; + + let auth_key = vec![1u8; 32]; + let session_scope = SessionScope::new( + ROOCH_FRAMEWORK_ADDRESS, + Empty::MODULE_NAME.as_str(), + Empty::EMPTY_FUNCTION_NAME.as_str(), + ); + let expiration_time = 100; + let max_inactive_interval = 100; + let action = + rooch_framework::bindings::session_key::SessionKeyModule::create_session_key_action( + auth_key.clone(), + BuiltinScheme::Ed25519, + session_scope.clone(), + expiration_time, + max_inactive_interval, + ); + let tx_data = RoochTransactionData::new(sender, sequence_number, action); + let tx = keystore + .sign_transaction(&sender, tx_data, BuiltinScheme::Ed25519) + .unwrap(); + binding_test.execute(tx).unwrap(); + + let session_key_module = + binding_test.as_module_bundle::(); + let session_key_option = session_key_module + .get_session_key(sender.into(), auth_key) + .unwrap(); + assert!(session_key_option.is_some(), "Session key not found"); + let session_key = session_key_option.unwrap(); + assert_eq!(session_key.scheme, BuiltinScheme::Ed25519.flag() as u64); + assert_eq!(session_key.scopes, vec![session_scope]); + assert_eq!(session_key.expiration_time, expiration_time); + assert_eq!(session_key.max_inactive_interval, max_inactive_interval); +} diff --git a/crates/rooch-framework-tests/tests/cases/event/basic.exp b/crates/rooch-framework-tests/tests/cases/event/basic.exp index abd79a0902..1954ab2054 100644 --- a/crates/rooch-framework-tests/tests/cases/event/basic.exp +++ b/crates/rooch-framework-tests/tests/cases/event/basic.exp @@ -3,5 +3,5 @@ processed 3 tasks task 1 'publish'. lines 3-18: status EXECUTED -task 2 'run'. lines 19-31: +task 2 'run'. lines 19-29: status EXECUTED diff --git a/crates/rooch-framework-tests/tests/cases/event/basic.move b/crates/rooch-framework-tests/tests/cases/event/basic.move index 7df03e7193..5be8bf1b8c 100644 --- a/crates/rooch-framework-tests/tests/cases/event/basic.move +++ b/crates/rooch-framework-tests/tests/cases/event/basic.move @@ -2,16 +2,16 @@ //# publish module test::m { - struct WithdrawEvent has key{ + use moveos_std::event; + use moveos_std::storage_context::StorageContext; + struct WithdrawEvent{ addr: address, amount: u64 } - public fun new_test_struct(addr: address, amount: u64): WithdrawEvent { - WithdrawEvent{ - addr, - amount, - } + public fun emit_withdraw_event(ctx: &mut StorageContext, addr: address, amount: u64) { + let withdraw_event = WithdrawEvent{addr, amount}; + event::emit(ctx, withdraw_event); } } @@ -20,12 +20,10 @@ module test::m { script { use moveos_std::storage_context::{Self, StorageContext}; use moveos_std::tx_context; - use moveos_std::event; - use test::m::{Self, WithdrawEvent}; + use test::m; fun main(ctx: &mut StorageContext) { let sender_addr = tx_context::sender(storage_context::tx_context(ctx)); - let withdraw_event = m::new_test_struct(sender_addr, 100); - event::emit_event(ctx, withdraw_event); + m::emit_withdraw_event(ctx, sender_addr, 100); } } diff --git a/crates/rooch-framework-tests/tests/cases/global_storage_access/move_to.exp b/crates/rooch-framework-tests/tests/cases/global_storage_access/move_to.exp new file mode 100644 index 0000000000..54690e7f3a --- /dev/null +++ b/crates/rooch-framework-tests/tests/cases/global_storage_access/move_to.exp @@ -0,0 +1,27 @@ +processed 3 tasks + +task 1 'publish'. lines 3-12: +Error: error: Access to Move global storage is not allowed. Found in function publish_foo: [MoveTo(StructDefinitionIndex(0))] + ┌─ /tmp/tempfile:8:5 + │ + 8 │ ╭ public fun publish_foo(s: &signer) { + 9 │ │ move_to(s, Foo { x: 500 }) +10 │ │ } + │ ╰─────^ + + + +task 2 'run'. lines 14-21: +Error: error[E03002]: unbound module + ┌─ /tmp/tempfile:16:9 + │ +16 │ use creator::test::{Self, Foo}; + │ ^^^^^^^^^^^^^ Invalid 'use'. Unbound module: '(creator=0x42)::test' + +error[E03002]: unbound module + ┌─ /tmp/tempfile:18:9 + │ +18 │ test::publish_foo(&s); + │ ^^^^ Unbound module alias 'test' + + diff --git a/crates/rooch-framework-tests/tests/cases/global_storage_access/move_to.move b/crates/rooch-framework-tests/tests/cases/global_storage_access/move_to.move new file mode 100644 index 0000000000..8cd56eee5f --- /dev/null +++ b/crates/rooch-framework-tests/tests/cases/global_storage_access/move_to.move @@ -0,0 +1,21 @@ +//# init --addresses creator=0x42 A=0x43 + +//# publish +module creator::test { + struct Foo has key, drop { + x: u64, + } + + public fun publish_foo(s: &signer) { + move_to(s, Foo { x: 500 }) + } +} + +//# run --signers creator +script { + use creator::test::{Self, Foo}; + + fun main(s: signer) { + test::publish_foo(&s); + } +} diff --git a/crates/rooch-framework-tests/tests/cases/private_generics/inside_module.exp b/crates/rooch-framework-tests/tests/cases/private_generics/inside_module.exp index f7607a3429..9169589c50 100644 --- a/crates/rooch-framework-tests/tests/cases/private_generics/inside_module.exp +++ b/crates/rooch-framework-tests/tests/cases/private_generics/inside_module.exp @@ -1,7 +1,7 @@ processed 3 tasks -task 1 'publish'. lines 3-17: +task 1 'publish'. lines 3-20: status EXECUTED -task 2 'run'. lines 19-26: +task 2 'run'. lines 22-30: status EXECUTED diff --git a/crates/rooch-framework-tests/tests/cases/private_generics/inside_module.move b/crates/rooch-framework-tests/tests/cases/private_generics/inside_module.move index 2cf8ecf894..049c088f00 100644 --- a/crates/rooch-framework-tests/tests/cases/private_generics/inside_module.move +++ b/crates/rooch-framework-tests/tests/cases/private_generics/inside_module.move @@ -2,25 +2,29 @@ //# publish module creator::test { + use moveos_std::storage_context::StorageContext; + use moveos_std::account_storage; + struct Foo has key { x: u64, } #[private_generics(T)] - fun publish_foo(s: &signer) { - move_to(s, Foo { x: 500 }) + fun publish_foo(ctx: &mut StorageContext, s: &signer) { + account_storage::global_move_to(ctx, s, Foo { x: 500}) } - public fun invoke_publish_foo(s: &signer) { - publish_foo(s); + public fun invoke_publish_foo(ctx: &mut StorageContext, s: &signer) { + publish_foo(ctx, s); } } //# run --signers creator script { + use moveos_std::storage_context::StorageContext; use creator::test; - fun main(s: signer) { - test::invoke_publish_foo(&s); + fun main(ctx: &mut StorageContext, s: signer) { + test::invoke_publish_foo(ctx, &s); } } diff --git a/crates/rooch-framework-tests/tests/cases/private_generics/outside_module.move b/crates/rooch-framework-tests/tests/cases/private_generics/outside_module.move index 8cff2796d4..462ac1afcc 100644 --- a/crates/rooch-framework-tests/tests/cases/private_generics/outside_module.move +++ b/crates/rooch-framework-tests/tests/cases/private_generics/outside_module.move @@ -7,8 +7,8 @@ module creator::test { } #[private_generics(T)] - public fun publish_foo(s: &signer) { - move_to(s, Foo { x: 500 }) + public fun publish_foo(_s: &signer) { + // move_to(s, Foo { x: 500 }) } } @@ -21,8 +21,8 @@ module A::m { } #[private_generics(V)] - fun publish_bar(s: &signer) { - move_to(s, Bar { v: 100 }) + fun publish_bar(_s: &signer) { + // move_to(s, Bar { v: 100 }) } public fun invoke_publish_bar(s: &signer) { @@ -39,4 +39,4 @@ script { fun main(s: signer) { test::publish_foo(&s); } -} \ No newline at end of file +} diff --git a/crates/rooch-framework/Cargo.toml b/crates/rooch-framework/Cargo.toml index 8a0995a53c..84ca47b847 100644 --- a/crates/rooch-framework/Cargo.toml +++ b/crates/rooch-framework/Cargo.toml @@ -25,6 +25,7 @@ serde_bytes = { workspace = true } sha3 = { workspace = true } smallvec = { workspace = true } hex = { workspace = true } +rust_secp256k1 = { workspace = true } move-binary-format = { workspace = true } move-bytecode-utils = { workspace = true } diff --git a/crates/rooch-framework/doc/README.md b/crates/rooch-framework/doc/README.md index bcd1cc8325..1c9c140958 100644 --- a/crates/rooch-framework/doc/README.md +++ b/crates/rooch-framework/doc/README.md @@ -20,13 +20,18 @@ This is the reference documentation of the Rooch Framework. - [`0x3::builtin_validators`](builtin_validators.md#0x3_builtin_validators) - [`0x3::core_addresses`](core_addresses.md#0x3_core_addresses) - [`0x3::ecdsa_k1`](ecdsa_k1.md#0x3_ecdsa_k1) -- [`0x3::ecdsa_validator`](ecdsa_validator.md#0x3_ecdsa_validator) +- [`0x3::ecdsa_k1_recoverable`](ecdsa_k1_recoverable.md#0x3_ecdsa_k1_recoverable) +- [`0x3::ecdsa_k1_recoverable_validator`](ecdsa_k1_recoverable_validator.md#0x3_ecdsa_k1_recoverable_validator) +- [`0x3::ecdsa_k1_validator`](ecdsa_k1_validator.md#0x3_ecdsa_k1_validator) - [`0x3::ed25519`](ed25519.md#0x3_ed25519) - [`0x3::ed25519_validator`](ed25519_validator.md#0x3_ed25519_validator) - [`0x3::empty`](empty.md#0x3_empty) - [`0x3::genesis`](genesis.md#0x3_genesis) - [`0x3::hash`](hash.md#0x3_hash) - [`0x3::multi_ed25519_validator`](multi_ed25519_validator.md#0x3_multi_ed25519_validator) +- [`0x3::schnorr`](schnorr.md#0x3_schnorr) +- [`0x3::schnorr_validator`](schnorr_validator.md#0x3_schnorr_validator) +- [`0x3::session_key`](session_key.md#0x3_session_key) - [`0x3::transaction_validator`](transaction_validator.md#0x3_transaction_validator) diff --git a/crates/rooch-framework/doc/account.md b/crates/rooch-framework/doc/account.md index 49d4af37b1..0780be83d7 100644 --- a/crates/rooch-framework/doc/account.md +++ b/crates/rooch-framework/doc/account.md @@ -33,6 +33,7 @@ use 0x2::account_storage; use 0x2::bcs; use 0x2::storage_context; +use 0x3::account_authentication; @@ -280,7 +281,11 @@ A entry function to create an account under new_address
public entry fun create_account_entry(ctx: &mut StorageContext, new_address: address){
-   Self::create_account(ctx, new_address);
+   // If account already exists, do nothing
+   // Because if the new address is the same as the sender, the account must already created in the `transaction_validator::pre_execute` function
+   if(!exists_at(ctx, new_address)){
+      create_account(ctx, new_address);
+   };
 }
 
@@ -313,7 +318,10 @@ is returned. This way, the caller of this function can publish additional resour ); // there cannot be an Account resource under new_addr already. - assert!(!exists<Account>(new_address), error::already_exists(EAccountAlreadyExists)); + assert!( + !account_storage::global_exists<Account>(ctx, new_address), + error::already_exists(EAccountAlreadyExists) + ); create_account_unchecked(ctx, new_address) } diff --git a/crates/rooch-framework/doc/account_authentication.md b/crates/rooch-framework/doc/account_authentication.md index cdadb23302..5a88a6489f 100644 --- a/crates/rooch-framework/doc/account_authentication.md +++ b/crates/rooch-framework/doc/account_authentication.md @@ -8,11 +8,13 @@ Migrate their from the account module for simplyfying the account module. - [Resource `AuthenticationKey`](#0x3_account_authentication_AuthenticationKey) +- [Resource `AuthenticationKeys`](#0x3_account_authentication_AuthenticationKeys) - [Resource `InstalledAuthValidator`](#0x3_account_authentication_InstalledAuthValidator) - [Constants](#@Constants_0) +- [Function `init_authentication_keys`](#0x3_account_authentication_init_authentication_keys) - [Function `get_authentication_key`](#0x3_account_authentication_get_authentication_key) - [Function `rotate_authentication_key`](#0x3_account_authentication_rotate_authentication_key) -- [Function `rotate_authentication_key_internal`](#0x3_account_authentication_rotate_authentication_key_internal) +- [Function `remove_authentication_key`](#0x3_account_authentication_remove_authentication_key) - [Function `is_auth_validator_installed`](#0x3_account_authentication_is_auth_validator_installed) - [Function `install_auth_validator`](#0x3_account_authentication_install_auth_validator) - [Function `install_auth_validator_entry`](#0x3_account_authentication_install_auth_validator_entry) @@ -24,6 +26,9 @@ Migrate their from the account module for simplyfying the account module. use 0x1::vector; use 0x2::account_storage; use 0x2::storage_context; +use 0x2::tx_context; +use 0x2::type_table; +use 0x3::auth_validator; use 0x3::auth_validator_registry; @@ -37,7 +42,7 @@ A resource that holds the authentication key for this account. ValidatorType is a phantom type parameter that is used to distinguish between different auth validator types. -
struct AuthenticationKey<ValidatorType> has key
+
struct AuthenticationKey<ValidatorType> has drop, key
 
@@ -56,6 +61,34 @@ ValidatorType is a phantom type parameter that is used to distinguish between di + + + + +## Resource `AuthenticationKeys` + +A resource that holds the authentication keys for this account. + + +
struct AuthenticationKeys has key
+
+ + + +
+Fields + + +
+
+authentication_keys: type_table::TypeTable +
+
+ +
+
+ +
@@ -100,6 +133,16 @@ A resource tha holds the auth validator ids for this account has installed. + + +The authentication key has not been found for the specified validator + + +
const EAuthenticationKeyNotFound: u64 = 3;
+
+ + + The provided authentication key has an invalid length @@ -120,6 +163,33 @@ max authentication key length + + +## Function `init_authentication_keys` + + + +
public(friend) fun init_authentication_keys(ctx: &mut storage_context::StorageContext, account: &signer)
+
+ + + +
+Implementation + + +
public(friend) fun init_authentication_keys(ctx: &mut StorageContext, account: &signer) {
+   let authentication_keys = AuthenticationKeys {
+      authentication_keys: type_table::new(storage_context::tx_context_mut(ctx)),
+   };
+   account_storage::global_move_to<AuthenticationKeys>(ctx, account, authentication_keys);
+}
+
+ + + +
+ ## Function `get_authentication_key` @@ -136,10 +206,15 @@ max authentication key length
public fun get_authentication_key<ValidatorType>(ctx: &StorageContext, account_addr: address): Option<vector<u8>> {
-   if(!account_storage::global_exists<AuthenticationKey<ValidatorType>>(ctx, account_addr)){
+   if(!account_storage::global_exists<AuthenticationKeys>(ctx, account_addr)){
       option::none<vector<u8>>()
    }else{
-      option::some(account_storage::global_borrow<AuthenticationKey<ValidatorType>>(ctx, account_addr).authentication_key)
+      let authentication_keys = account_storage::global_borrow<AuthenticationKeys>(ctx, account_addr);
+      if(type_table::contains<AuthenticationKey<ValidatorType>>(&authentication_keys.authentication_keys)){
+         option::some(type_table::borrow<AuthenticationKey<ValidatorType>>(&authentication_keys.authentication_keys).authentication_key)
+      }else{
+         option::none<vector<u8>>()
+      }
    }
 }
 
@@ -155,7 +230,7 @@ max authentication key length This function is used to rotate a resource account's authentication key, only the module which define the ValidatorType can call this function. -
public fun rotate_authentication_key<ValidatorType>(ctx: &mut storage_context::StorageContext, account: &signer, new_auth_key: vector<u8>)
+
public fun rotate_authentication_key<ValidatorType>(ctx: &mut storage_context::StorageContext, account_addr: address, new_auth_key: vector<u8>)
 
@@ -164,8 +239,23 @@ This function is used to rotate a resource account's authentication key, only th Implementation -
public fun rotate_authentication_key<ValidatorType>(ctx: &mut StorageContext, account: &signer, new_auth_key: vector<u8>) {
-   rotate_authentication_key_internal<ValidatorType>(ctx, account, new_auth_key);
+
public fun rotate_authentication_key<ValidatorType>(ctx: &mut StorageContext, account_addr: address, new_auth_key: vector<u8>) {
+
+   assert!(
+      vector::length(&new_auth_key) <= MAX_AUTHENTICATION_KEY_LENGTH,
+      error::invalid_argument(EMalformedAuthenticationKey)
+   );
+   //We need to ensure the AuthenticationKeys resource exists before we can rotate the authentication key.
+   let authentication_keys = account_storage::global_borrow_mut<AuthenticationKeys>(ctx, account_addr);
+   if(type_table::contains<AuthenticationKey<ValidatorType>>(&authentication_keys.authentication_keys)){
+      let authentication_key = type_table::borrow_mut<AuthenticationKey<ValidatorType>>(&mut authentication_keys.authentication_keys);
+      authentication_key.authentication_key = new_auth_key;
+   }else{
+      let authentication_key = AuthenticationKey<ValidatorType> {
+         authentication_key: new_auth_key,
+      };
+      type_table::add(&mut authentication_keys.authentication_keys, authentication_key);
+   };
 }
 
@@ -173,13 +263,14 @@ This function is used to rotate a resource account's authentication key, only th - + -## Function `rotate_authentication_key_internal` +## Function `remove_authentication_key` +This function is used to remove a resource account's authentication key, only the module which define the ValidatorType can call this function. -
public(friend) fun rotate_authentication_key_internal<ValidatorType>(ctx: &mut storage_context::StorageContext, account: &signer, new_auth_key: vector<u8>)
+
public fun remove_authentication_key<ValidatorType>(ctx: &mut storage_context::StorageContext, account_addr: address): account_authentication::AuthenticationKey<ValidatorType>
 
@@ -188,23 +279,19 @@ This function is used to rotate a resource account's authentication key, only th Implementation -
public(friend) fun rotate_authentication_key_internal<ValidatorType>(ctx: &mut StorageContext, account: &signer, new_auth_key: vector<u8>) {
-   let account_addr = signer::address_of(account);
-
+
public fun remove_authentication_key<ValidatorType>(ctx: &mut StorageContext, account_addr: address): AuthenticationKey<ValidatorType> {
    assert!(
-      vector::length(&new_auth_key) <= MAX_AUTHENTICATION_KEY_LENGTH,
-      error::invalid_argument(EMalformedAuthenticationKey)
+      account_storage::global_exists<AuthenticationKeys>(ctx, account_addr),
+      error::not_found(EAuthenticationKeyNotFound)
+   );
+   let authentication_keys = account_storage::global_borrow_mut<AuthenticationKeys>(ctx, account_addr);
+   assert!(
+      type_table::contains<AuthenticationKey<ValidatorType>>(&authentication_keys.authentication_keys),
+      error::not_found(EAuthenticationKeyNotFound)
    );
 
-   if(account_storage::global_exists<AuthenticationKey<ValidatorType>>(ctx, account_addr)){
-      let authentication_key = account_storage::global_borrow_mut<AuthenticationKey<ValidatorType>>(ctx, account_addr);
-      authentication_key.authentication_key = new_auth_key;
-   }else{
-      let authentication_key = AuthenticationKey<ValidatorType> {
-         authentication_key: new_auth_key,
-      };
-      account_storage::global_move_to(ctx, account, authentication_key);
-   }
+   let removed_authentication_key = type_table::remove<AuthenticationKey<ValidatorType>>(&mut authentication_keys.authentication_keys);
+   removed_authentication_key
 }
 
@@ -259,7 +346,7 @@ Return the authentication validator is installed for the account at accoun
public fun install_auth_validator<ValidatorType: store>(ctx: &mut StorageContext, account_signer: &signer) {
    let validator = auth_validator_registry::borrow_validator_by_type<ValidatorType>(ctx);
-   let validator_id = auth_validator_registry::validator_id(validator);
+   let validator_id = auth_validator::validator_id(validator);
    let account_addr = signer::address_of(account_signer);
 
    assert!(
diff --git a/crates/rooch-framework/doc/address_mapping.md b/crates/rooch-framework/doc/address_mapping.md
index a9696841b9..4e973e4717 100644
--- a/crates/rooch-framework/doc/address_mapping.md
+++ b/crates/rooch-framework/doc/address_mapping.md
@@ -10,6 +10,7 @@
 -  [Constants](#@Constants_0)
 -  [Function `is_rooch_address`](#0x3_address_mapping_is_rooch_address)
 -  [Function `resolve`](#0x3_address_mapping_resolve)
+-  [Function `resolve_or_generate`](#0x3_address_mapping_resolve_or_generate)
 -  [Function `exists_mapping`](#0x3_address_mapping_exists_mapping)
 -  [Function `bind`](#0x3_address_mapping_bind)
 -  [Function `bind_no_check`](#0x3_address_mapping_bind_no_check)
@@ -178,7 +179,37 @@ Resolve a multi-chain address to a rooch address
         let addr = table::borrow(&am.mapping, maddress);
         option::some(*addr)
     }else{
-        default_rooch_address(maddress)
+        option::none()
+    }
+}
+
+ + + + + + + +## Function `resolve_or_generate` + +Resolve a multi-chain address to a rooch address, if not exists, generate a new rooch address + + +
public fun resolve_or_generate(ctx: &storage_context::StorageContext, maddress: address_mapping::MultiChainAddress): address
+
+ + + +
+Implementation + + +
public fun resolve_or_generate(ctx: &StorageContext, maddress: MultiChainAddress): address {
+    let addr = resolve(ctx, maddress);
+    if(option::is_none(&addr)){
+        generate_rooch_address(maddress)
+    }else{
+        option::extract(&mut addr)
     }
 }
 
diff --git a/crates/rooch-framework/doc/auth_validator.md b/crates/rooch-framework/doc/auth_validator.md index a9c795be4d..77f1e31bd9 100644 --- a/crates/rooch-framework/doc/auth_validator.md +++ b/crates/rooch-framework/doc/auth_validator.md @@ -5,24 +5,132 @@ This module contains the error code for auth_validator module The auth_validator implementation should contain the following functions -public fun validate(ctx: &StorageContext, payload: vector) +public fun validate(ctx: &StorageContext, authenticator_payload: vector) +fun pre_execute(ctx: &mut StorageContext) +fun post_execute(ctx: &mut StorageContext) +- [Struct `AuthValidator`](#0x3_auth_validator_AuthValidator) +- [Struct `TxValidateResult`](#0x3_auth_validator_TxValidateResult) - [Constants](#@Constants_0) - [Function `error_invalid_account_auth_key`](#0x3_auth_validator_error_invalid_account_auth_key) - [Function `error_invalid_authenticator`](#0x3_auth_validator_error_invalid_authenticator) +- [Function `new_auth_validator`](#0x3_auth_validator_new_auth_validator) +- [Function `validator_id`](#0x3_auth_validator_validator_id) +- [Function `validator_module_address`](#0x3_auth_validator_validator_module_address) +- [Function `validator_module_name`](#0x3_auth_validator_validator_module_name) +- [Function `new_tx_validate_result`](#0x3_auth_validator_new_tx_validate_result) +- [Function `get_validate_result_from_tx_ctx`](#0x3_auth_validator_get_validate_result_from_tx_ctx) +- [Function `get_validator_scheme_from_tx_ctx`](#0x3_auth_validator_get_validator_scheme_from_tx_ctx) +- [Function `get_session_key_from_tx_ctx_option`](#0x3_auth_validator_get_session_key_from_tx_ctx_option) +- [Function `is_validate_via_session_key`](#0x3_auth_validator_is_validate_via_session_key) +- [Function `get_session_key_from_tx_ctx`](#0x3_auth_validator_get_session_key_from_tx_ctx) + + +
use 0x1::ascii;
+use 0x1::error;
+use 0x1::option;
+use 0x2::storage_context;
+
+ + + + +## Struct `AuthValidator` -
use 0x1::error;
+The Authentication Validator
+
+
+
struct AuthValidator has copy, drop, store
 
+
+Fields + + +
+
+id: u64 +
+
+ +
+
+module_address: address +
+
+ +
+
+module_name: ascii::String +
+
+ +
+
+ + +
+ + + +## Struct `TxValidateResult` + +The Transaction Validate Result +this result will be stored in the TxContext + + +
struct TxValidateResult has copy, drop, store
+
+ + + +
+Fields + + +
+
+scheme: u64 +
+
+ The auth validator's scheme that validate the transaction +
+
+auth_validator: option::Option<auth_validator::AuthValidator> +
+
+ +
+
+session_key: option::Option<vector<u8>> +
+
+ +
+
+ + +
+ ## Constants + + +The function must be executed after the transaction is validated + + +
const EMustExecuteAfterValidate: u64 = 1;
+
+ + + The AuthKey in transaction's authenticator do not match with the sender's account auth key @@ -59,7 +167,7 @@ InvalidAuthenticator, include invalid signature
public fun error_invalid_account_auth_key(): u64 {
-   error::invalid_argument(EValidateInvalidAccountAuthKey)
+    error::invalid_argument(EValidateInvalidAccountAuthKey)
 }
 
@@ -83,7 +191,281 @@ InvalidAuthenticator, include invalid signature
public fun error_invalid_authenticator(): u64 {
-   error::invalid_argument(EValidateInvalidAuthenticator)
+    error::invalid_argument(EValidateInvalidAuthenticator)
+}
+
+ + + +
+ + + +## Function `new_auth_validator` + + + +
public(friend) fun new_auth_validator(id: u64, module_address: address, module_name: ascii::String): auth_validator::AuthValidator
+
+ + + +
+Implementation + + +
public(friend) fun new_auth_validator(
+    id: u64,
+    module_address: address,
+    module_name: std::ascii::String
+): AuthValidator {
+    AuthValidator {
+        id: id,
+        module_address: module_address,
+        module_name: module_name,
+    }
+}
+
+ + + +
+ + + +## Function `validator_id` + + + +
public fun validator_id(validator: &auth_validator::AuthValidator): u64
+
+ + + +
+Implementation + + +
public fun validator_id(validator: &AuthValidator): u64 {
+    validator.id
+}
+
+ + + +
+ + + +## Function `validator_module_address` + + + +
public fun validator_module_address(validator: &auth_validator::AuthValidator): address
+
+ + + +
+Implementation + + +
public fun validator_module_address(validator: &AuthValidator): address {
+    validator.module_address
+}
+
+ + + +
+ + + +## Function `validator_module_name` + + + +
public fun validator_module_name(validator: &auth_validator::AuthValidator): ascii::String
+
+ + + +
+Implementation + + +
public fun validator_module_name(validator: &AuthValidator): std::ascii::String {
+    validator.module_name
+}
+
+ + + +
+ + + +## Function `new_tx_validate_result` + + + +
public(friend) fun new_tx_validate_result(scheme: u64, auth_validator: option::Option<auth_validator::AuthValidator>, session_key: option::Option<vector<u8>>): auth_validator::TxValidateResult
+
+ + + +
+Implementation + + +
public(friend) fun new_tx_validate_result(
+    scheme: u64,
+    auth_validator: Option<AuthValidator>,
+    session_key: Option<vector<u8>>
+): TxValidateResult {
+    TxValidateResult {
+        scheme: scheme,
+        auth_validator: auth_validator,
+        session_key: session_key,
+    }
+}
+
+ + + +
+ + + +## Function `get_validate_result_from_tx_ctx` + +Get the TxValidateResult from the TxContext, Only can be called after the transaction is validated + + +
public fun get_validate_result_from_tx_ctx(ctx: &storage_context::StorageContext): auth_validator::TxValidateResult
+
+ + + +
+Implementation + + +
public fun get_validate_result_from_tx_ctx(ctx: &StorageContext): TxValidateResult {
+    let validate_result_opt = storage_context::get<TxValidateResult>(ctx);
+    assert!(option::is_some(&validate_result_opt), error::invalid_state(EMustExecuteAfterValidate));
+    option::extract(&mut validate_result_opt)
+}
+
+ + + +
+ + + +## Function `get_validator_scheme_from_tx_ctx` + +Get the auth validator's scheme from the TxValidateResult in the TxContext + + +
public fun get_validator_scheme_from_tx_ctx(ctx: &storage_context::StorageContext): u64
+
+ + + +
+Implementation + + +
public fun get_validator_scheme_from_tx_ctx(ctx: &StorageContext): u64 {
+    let validate_result = get_validate_result_from_tx_ctx(ctx);
+    validate_result.scheme
+}
+
+ + + +
+ + + +## Function `get_session_key_from_tx_ctx_option` + +Get the session key from the TxValidateResult in the TxContext +If the TxValidateResult is None or SessionKey is None, return None + + +
public fun get_session_key_from_tx_ctx_option(ctx: &storage_context::StorageContext): option::Option<vector<u8>>
+
+ + + +
+Implementation + + +
public fun get_session_key_from_tx_ctx_option(ctx: &StorageContext): Option<vector<u8>> {
+    let validate_result_opt = storage_context::get<TxValidateResult>(ctx);
+    if (option::is_some(&validate_result_opt)) {
+        let validate_result = option::extract(&mut validate_result_opt);
+        validate_result.session_key
+    }else {
+        option::none<vector<u8>>()
+    }
+}
+
+ + + +
+ + + +## Function `is_validate_via_session_key` + +The current tx is validate via the session key or not + + +
public fun is_validate_via_session_key(ctx: &storage_context::StorageContext): bool
+
+ + + +
+Implementation + + +
public fun is_validate_via_session_key(ctx: &StorageContext): bool {
+    option::is_some(&get_session_key_from_tx_ctx_option(ctx))
+}
+
+ + + +
+ + + +## Function `get_session_key_from_tx_ctx` + +Get the session key from the TxValidateResult in the TxContext +Only can be called after the transaction is validated + + +
public fun get_session_key_from_tx_ctx(ctx: &storage_context::StorageContext): vector<u8>
+
+ + + +
+Implementation + + +
public fun get_session_key_from_tx_ctx(ctx: &StorageContext): vector<u8> {
+    let sesion_key_option = get_session_key_from_tx_ctx_option(ctx);
+    assert!(option::is_some(&sesion_key_option), error::invalid_state(EMustExecuteAfterValidate));
+    option::extract(&mut sesion_key_option)
 }
 
diff --git a/crates/rooch-framework/doc/auth_validator_registry.md b/crates/rooch-framework/doc/auth_validator_registry.md index 91af9ba15f..abb9cab664 100644 --- a/crates/rooch-framework/doc/auth_validator_registry.md +++ b/crates/rooch-framework/doc/auth_validator_registry.md @@ -5,7 +5,6 @@ -- [Struct `AuthValidator`](#0x3_auth_validator_registry_AuthValidator) - [Resource `AuthValidatorWithType`](#0x3_auth_validator_registry_AuthValidatorWithType) - [Resource `ValidatorRegistry`](#0x3_auth_validator_registry_ValidatorRegistry) - [Constants](#@Constants_0) @@ -14,9 +13,6 @@ - [Function `register_internal`](#0x3_auth_validator_registry_register_internal) - [Function `borrow_validator`](#0x3_auth_validator_registry_borrow_validator) - [Function `borrow_validator_by_type`](#0x3_auth_validator_registry_borrow_validator_by_type) -- [Function `validator_id`](#0x3_auth_validator_registry_validator_id) -- [Function `validator_module_address`](#0x3_auth_validator_registry_validator_module_address) -- [Function `validator_module_name`](#0x3_auth_validator_registry_validator_module_name)
use 0x1::ascii;
@@ -27,49 +23,11 @@
 use 0x2::tx_context;
 use 0x2::type_info;
 use 0x2::type_table;
+use 0x3::auth_validator;
 
- - -## Struct `AuthValidator` - - - -
struct AuthValidator has store
-
- - - -
-Fields - - -
-
-id: u64 -
-
- -
-
-module_address: address -
-
- -
-
-module_name: ascii::String -
-
- -
-
- - -
- ## Resource `AuthValidatorWithType` @@ -120,7 +78,7 @@ How many validators are registered
-validators: table::Table<u64, auth_validator_registry::AuthValidator> +validators: table::Table<u64, auth_validator::AuthValidator>
@@ -244,11 +202,11 @@ Init function called by genesis. }; type_table::add(&mut registry.validators_with_type, validator_with_type); - let validator = AuthValidator { + let validator = auth_validator::new_auth_validator( id, - module_address: module_address, - module_name: module_name, - }; + module_address, + module_name, + ); table::add(&mut registry.validators, id, validator); registry.validator_num = registry.validator_num + 1; @@ -266,7 +224,7 @@ Init function called by genesis. -
public fun borrow_validator(ctx: &storage_context::StorageContext, id: u64): &auth_validator_registry::AuthValidator
+
public fun borrow_validator(ctx: &storage_context::StorageContext, id: u64): &auth_validator::AuthValidator
 
@@ -275,7 +233,7 @@ Init function called by genesis. Implementation -
public fun borrow_validator(ctx: &StorageContext, id: u64): &AuthValidator {
+
public fun borrow_validator(ctx: &StorageContext, id: u64): &AuthValidator {
     let registry = account_storage::global_borrow<ValidatorRegistry>(ctx, @rooch_framework);
     table::borrow(®istry.validators, id)
 }
@@ -291,7 +249,7 @@ Init function called by genesis.
 
 
 
-
public fun borrow_validator_by_type<ValidatorType: store>(ctx: &storage_context::StorageContext): &auth_validator_registry::AuthValidator
+
public fun borrow_validator_by_type<ValidatorType: store>(ctx: &storage_context::StorageContext): &auth_validator::AuthValidator
 
@@ -300,7 +258,7 @@ Init function called by genesis. Implementation -
public fun borrow_validator_by_type<ValidatorType: store>(ctx: &StorageContext): &AuthValidator {
+
public fun borrow_validator_by_type<ValidatorType: store>(ctx: &StorageContext): &AuthValidator {
     let registry = account_storage::global_borrow<ValidatorRegistry>(ctx, @rooch_framework);
     assert!(type_table::contains<AuthValidatorWithType<ValidatorType>>(®istry.validators_with_type), error::not_found(EValidatorUnregistered));
     let validator_with_type = type_table::borrow<AuthValidatorWithType<ValidatorType>>(®istry.validators_with_type);
@@ -311,76 +269,4 @@ Init function called by genesis.
 
 
 
-
- - - -## Function `validator_id` - - - -
public fun validator_id(validator: &auth_validator_registry::AuthValidator): u64
-
- - - -
-Implementation - - -
public fun validator_id(validator: &AuthValidator): u64 {
-    validator.id
-}
-
- - - -
- - - -## Function `validator_module_address` - - - -
public fun validator_module_address(validator: &auth_validator_registry::AuthValidator): address
-
- - - -
-Implementation - - -
public fun validator_module_address(validator: &AuthValidator): address {
-    validator.module_address
-}
-
- - - -
- - - -## Function `validator_module_name` - - - -
public fun validator_module_name(validator: &auth_validator_registry::AuthValidator): ascii::String
-
- - - -
-Implementation - - -
public fun validator_module_name(validator: &AuthValidator): std::ascii::String {
-    validator.module_name
-}
-
- - -
diff --git a/crates/rooch-framework/doc/builtin_validators.md b/crates/rooch-framework/doc/builtin_validators.md index f2fd3073b4..dd2e285083 100644 --- a/crates/rooch-framework/doc/builtin_validators.md +++ b/crates/rooch-framework/doc/builtin_validators.md @@ -7,15 +7,17 @@ - [Constants](#@Constants_0) - [Function `genesis_init`](#0x3_builtin_validators_genesis_init) -- [Function `is_builtin`](#0x3_builtin_validators_is_builtin) +- [Function `is_builtin_scheme`](#0x3_builtin_validators_is_builtin_scheme)
use 0x1::error;
 use 0x2::storage_context;
 use 0x3::auth_validator_registry;
-use 0x3::ecdsa_validator;
+use 0x3::ecdsa_k1_recoverable_validator;
+use 0x3::ecdsa_k1_validator;
 use 0x3::ed25519_validator;
 use 0x3::multi_ed25519_validator;
+use 0x3::schnorr_validator;
 
@@ -57,8 +59,14 @@ let id = auth_validator_registry::register_internal<multi_ed25519_validator::MultiEd25519Validator>(ctx); assert!(id == multi_ed25519_validator::scheme(), std::error::internal(E_GENESIS_INIT)); //SCHEME_ECDSA: u64 = 2; - let id = auth_validator_registry::register_internal<ecdsa_validator::EcdsaValidator>(ctx); - assert!(id == ecdsa_validator::scheme(), std::error::internal(E_GENESIS_INIT)); + let id = auth_validator_registry::register_internal<ecdsa_k1_validator::EcdsaK1Validator>(ctx); + assert!(id == ecdsa_k1_validator::scheme(), std::error::internal(E_GENESIS_INIT)); + //SCHEME_ECDSA_RECOVERABLE: u64 = 3; + let id = auth_validator_registry::register_internal<ecdsa_k1_recoverable_validator::EcdsaK1RecoverableValidator>(ctx); + assert!(id == ecdsa_k1_recoverable_validator::scheme(), std::error::internal(E_GENESIS_INIT)); + //SCHEME_SCHNORR: u64 = 4; + let id = auth_validator_registry::register_internal<schnorr_validator::SchnorrValidator>(ctx); + assert!(id == schnorr_validator::scheme(), std::error::internal(E_GENESIS_INIT)); }
@@ -66,13 +74,13 @@ - + -## Function `is_builtin` +## Function `is_builtin_scheme` -
public fun is_builtin(scheme: u64): bool
+
public fun is_builtin_scheme(scheme: u64): bool
 
@@ -81,8 +89,8 @@ Implementation -
public fun is_builtin(scheme: u64): bool {
-    scheme == ed25519_validator::scheme() || scheme == multi_ed25519_validator::scheme() || scheme == ecdsa_validator::scheme()
+
public fun is_builtin_scheme(scheme: u64): bool {
+    scheme == ed25519_validator::scheme() || scheme == multi_ed25519_validator::scheme() || scheme == ecdsa_k1_validator::scheme() || scheme == ecdsa_k1_recoverable_validator::scheme() || scheme == schnorr_validator::scheme()
 }
 
diff --git a/crates/rooch-framework/doc/ecdsa_k1.md b/crates/rooch-framework/doc/ecdsa_k1.md index d9e3960325..65374923ed 100644 --- a/crates/rooch-framework/doc/ecdsa_k1.md +++ b/crates/rooch-framework/doc/ecdsa_k1.md @@ -6,13 +6,10 @@ - [Constants](#@Constants_0) -- [Function `ecrecover`](#0x3_ecdsa_k1_ecrecover) -- [Function `decompress_pubkey`](#0x3_ecdsa_k1_decompress_pubkey) - [Function `verify`](#0x3_ecdsa_k1_verify) -
use 0x3::hash;
-
+
@@ -21,12 +18,12 @@ ## Constants - + -Error if the public key cannot be recovered from the signature. +Error if the public key is invalid. -
const EFailToRecoverPubKey: u64 = 0;
+
const EInvalidPubKey: u64 = 1;
 
@@ -36,7 +33,7 @@ Error if the public key cannot be recovered from the signature. Error if the signature is invalid. -
const EInvalidSignature: u64 = 1;
+
const EInvalidSignature: u64 = 0;
 
@@ -60,75 +57,20 @@ Hash function name that are valid for ecrecover and verify. - - -## Function `ecrecover` - -@param signature: A 65-bytes signature in form (r, s, v) that is signed using -The accepted v values are {0, 1, 2, 3}. -@param msg: The message that the signature is signed against, this is raw message without hashing. -@param hash: The hash function used to hash the message when signing. - -If the signature is valid, return the corresponding recovered Secpk256k1 public -key, otherwise throw error. This is similar to ecrecover in Ethereum, can only be -applied to Ecdsa signatures. - - -
public fun ecrecover(signature: &vector<u8>, msg: &vector<u8>, hash: u8): vector<u8>
-
- - - -
-Implementation - - -
public native fun ecrecover(signature: &vector<u8>, msg: &vector<u8>, hash: u8): vector<u8>;
-
- - - -
- - - -## Function `decompress_pubkey` - -@param pubkey: A 33-bytes compressed public key, a prefix either 0x02 or 0x03 and a 256-bit integer. - -If the compressed public key is valid, return the 65-bytes uncompressed public key, -otherwise throw error. - - -
public fun decompress_pubkey(pubkey: &vector<u8>): vector<u8>
-
- - - -
-Implementation - - -
public native fun decompress_pubkey(pubkey: &vector<u8>): vector<u8>;
-
- - - -
- ## Function `verify` -@param signature: A 65-bytes signature in form (r, s, v) that is signed using +@param signature: A 64-bytes signature in form (r, s) that is signed using Ecdsa. This is an non-recoverable signature without recovery id. +@param public_key: A 33-bytes public key that is used to sign messages. @param msg: The message that the signature is signed against. @param hash: The hash function used to hash the message when signing. If the signature is valid to the pubkey and hashed message, return true. Else false. -
public fun verify(signature: &vector<u8>, msg: &vector<u8>, hash: u8): bool
+
public fun verify(signature: &vector<u8>, public_key: &vector<u8>, msg: &vector<u8>, hash: u8): bool
 
@@ -139,6 +81,7 @@ If the signature is valid to the pubkey and hashed message, return true. Else fa
public native fun verify(
    signature: &vector<u8>,
+   public_key: &vector<u8>,
    msg: &vector<u8>,
    hash: u8
 ): bool;
diff --git a/crates/rooch-framework/doc/ecdsa_k1_recoverable.md b/crates/rooch-framework/doc/ecdsa_k1_recoverable.md
new file mode 100644
index 0000000000..4411773031
--- /dev/null
+++ b/crates/rooch-framework/doc/ecdsa_k1_recoverable.md
@@ -0,0 +1,159 @@
+
+
+
+# Module `0x3::ecdsa_k1_recoverable`
+
+
+
+-  [Constants](#@Constants_0)
+-  [Function `ecrecover`](#0x3_ecdsa_k1_recoverable_ecrecover)
+-  [Function `decompress_pubkey`](#0x3_ecdsa_k1_recoverable_decompress_pubkey)
+-  [Function `verify`](#0x3_ecdsa_k1_recoverable_verify)
+
+
+
use 0x3::hash;
+
+ + + + + +## Constants + + + + +Error if the public key is invalid. + + +
const EInvalidPubKey: u64 = 2;
+
+ + + + + +Error if the signature is invalid. + + +
const EInvalidSignature: u64 = 1;
+
+ + + + + +Hash function name that are valid for ecrecover and verify. + + +
const KECCAK256: u8 = 0;
+
+ + + + + + + +
const SHA256: u8 = 1;
+
+ + + + + +Error if the public key cannot be recovered from the signature. + + +
const EFailToRecoverPubKey: u64 = 0;
+
+ + + + + +## Function `ecrecover` + +@param signature: A 65-bytes signature in form (r, s, v) that is signed using +The accepted v values are {0, 1, 2, 3}. +@param msg: The message that the signature is signed against, this is raw message without hashing. +@param hash: The hash function used to hash the message when signing. + +If the signature is valid, return the corresponding recovered Secpk256k1 public +key, otherwise throw error. This is similar to ecrecover in Ethereum, can only be +applied to Ecdsa signatures. + + +
public fun ecrecover(signature: &vector<u8>, msg: &vector<u8>, hash: u8): vector<u8>
+
+ + + +
+Implementation + + +
public native fun ecrecover(signature: &vector<u8>, msg: &vector<u8>, hash: u8): vector<u8>;
+
+ + + +
+ + + +## Function `decompress_pubkey` + +@param pubkey: A 33-bytes compressed public key, a prefix either 0x02 or 0x03 and a 256-bit integer. + +If the compressed public key is valid, return the 65-bytes uncompressed public key, +otherwise throw error. + + +
public fun decompress_pubkey(pubkey: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public native fun decompress_pubkey(pubkey: &vector<u8>): vector<u8>;
+
+ + + +
+ + + +## Function `verify` + +@param signature: A 65-bytes signature in form (r, s, v) that is signed using +Ecdsa. This is a recoverable signature with a recovery id. +@param msg: The message that the signature is signed against. +@param hash: The hash function used to hash the message when signing. + +If the signature is valid to the pubkey and hashed message, return true. Else false. + + +
public fun verify(signature: &vector<u8>, msg: &vector<u8>, hash: u8): bool
+
+ + + +
+Implementation + + +
public native fun verify(
+   signature: &vector<u8>,
+   msg: &vector<u8>,
+   hash: u8
+): bool;
+
+ + + +
diff --git a/crates/rooch-framework/doc/ecdsa_k1_recoverable_validator.md b/crates/rooch-framework/doc/ecdsa_k1_recoverable_validator.md new file mode 100644 index 0000000000..6d1d98d413 --- /dev/null +++ b/crates/rooch-framework/doc/ecdsa_k1_recoverable_validator.md @@ -0,0 +1,431 @@ + + + +# Module `0x3::ecdsa_k1_recoverable_validator` + +This module implements the ECDSA Recoverable over Secpk256k1 validator scheme. + + +- [Struct `EcdsaK1RecoverableValidator`](#0x3_ecdsa_k1_recoverable_validator_EcdsaK1RecoverableValidator) +- [Constants](#@Constants_0) +- [Function `scheme`](#0x3_ecdsa_k1_recoverable_validator_scheme) +- [Function `rotate_authentication_key_entry`](#0x3_ecdsa_k1_recoverable_validator_rotate_authentication_key_entry) +- [Function `remove_authentication_key_entry`](#0x3_ecdsa_k1_recoverable_validator_remove_authentication_key_entry) +- [Function `ecdsa_k1_recoverable_public_key`](#0x3_ecdsa_k1_recoverable_validator_ecdsa_k1_recoverable_public_key) +- [Function `ecdsa_k1_recoverable_signature`](#0x3_ecdsa_k1_recoverable_validator_ecdsa_k1_recoverable_signature) +- [Function `ecdsa_k1_recoverable_authentication_key`](#0x3_ecdsa_k1_recoverable_validator_ecdsa_k1_recoverable_authentication_key) +- [Function `ecdsa_k1_recoverable_public_key_to_address`](#0x3_ecdsa_k1_recoverable_validator_ecdsa_k1_recoverable_public_key_to_address) +- [Function `get_authentication_key`](#0x3_ecdsa_k1_recoverable_validator_get_authentication_key) +- [Function `validate`](#0x3_ecdsa_k1_recoverable_validator_validate) + + +
use 0x1::error;
+use 0x1::option;
+use 0x1::signer;
+use 0x1::vector;
+use 0x2::bcs;
+use 0x2::storage_context;
+use 0x3::account_authentication;
+use 0x3::auth_validator;
+use 0x3::ecdsa_k1_recoverable;
+use 0x3::hash;
+
+ + + + + +## Struct `EcdsaK1RecoverableValidator` + + + +
struct EcdsaK1RecoverableValidator has drop, store
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const EMalformedAuthenticationKey: u64 = 1002;
+
+ + + + + +Hash function name that are valid for ecrecover and verify. + + +
const KECCAK256: u8 = 0;
+
+ + + + + + + +
const SHA256: u8 = 1;
+
+ + + + + +error code + + +
const EMalformedAccount: u64 = 1001;
+
+ + + + + + + +
const SCHEME_ECDSA_RECOVERABLE: u64 = 3;
+
+ + + + + + + +
const V_ECDSA_RECOVERABLE_HASH_LENGTH: u64 = 1;
+
+ + + + + + + +
const V_ECDSA_RECOVERABLE_PUBKEY_LENGTH: u64 = 33;
+
+ + + + + + + +
const V_ECDSA_RECOVERABLE_SCHEME_LENGTH: u64 = 1;
+
+ + + + + + + +
const V_ECDSA_RECOVERABLE_SIG_LENGTH: u64 = 65;
+
+ + + + + +## Function `scheme` + + + +
public fun scheme(): u64
+
+ + + +
+Implementation + + +
public fun scheme(): u64 {
+    SCHEME_ECDSA_RECOVERABLE
+}
+
+ + + +
+ + + +## Function `rotate_authentication_key_entry` + + + +
public entry fun rotate_authentication_key_entry<EcdsaK1RecoverableValidator>(ctx: &mut storage_context::StorageContext, account: &signer, public_key: vector<u8>)
+
+ + + +
+Implementation + + +
public entry fun rotate_authentication_key_entry<EcdsaK1RecoverableValidator>(
+    ctx: &mut StorageContext,
+    account: &signer,
+    public_key: vector<u8>
+) {
+    // compare newly passed public key with ecdsa recoverable public key length to ensure it's compatible
+    assert!(
+        vector::length(&public_key) == V_ECDSA_RECOVERABLE_PUBKEY_LENGTH,
+        error::invalid_argument(EMalformedAuthenticationKey)
+    );
+
+    // ensure that the ecdsa recoverable public key to address isn't matched with the ed25519 account address
+    let account_addr = signer::address_of(account);
+    let ecdsa_recoverable_addr = ecdsa_k1_recoverable_public_key_to_address(public_key);
+    assert!(
+        account_addr != ecdsa_recoverable_addr,
+        error::invalid_argument(EMalformedAccount)
+    );
+
+    // serialize the address to an auth key and rotate it by calling rotate_authentication_key
+    let ecdsa_k1_recoverable_authentication_key = moveos_std::bcs::to_bytes(&ecdsa_recoverable_addr);
+    account_authentication::rotate_authentication_key<EcdsaK1RecoverableValidator>(
+        ctx,
+        account_addr,
+        ecdsa_k1_recoverable_authentication_key
+    );
+}
+
+ + + +
+ + + +## Function `remove_authentication_key_entry` + + + +
public entry fun remove_authentication_key_entry<EcdsaK1RecoverableValidator>(ctx: &mut storage_context::StorageContext, account: &signer)
+
+ + + +
+Implementation + + +
public entry fun remove_authentication_key_entry<EcdsaK1RecoverableValidator>(ctx: &mut StorageContext, account: &signer) {
+    let account_addr = signer::address_of(account);
+    account_authentication::remove_authentication_key<EcdsaK1RecoverableValidator>(ctx, account_addr);
+}
+
+ + + +
+ + + +## Function `ecdsa_k1_recoverable_public_key` + + + +
public fun ecdsa_k1_recoverable_public_key(authenticator_payload: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun ecdsa_k1_recoverable_public_key(authenticator_payload: &vector<u8>): vector<u8> {
+    let public_key = vector::empty<u8>();
+    let i = V_ECDSA_RECOVERABLE_SCHEME_LENGTH + V_ECDSA_RECOVERABLE_SIG_LENGTH;
+    while (i < V_ECDSA_RECOVERABLE_SCHEME_LENGTH + V_ECDSA_RECOVERABLE_SIG_LENGTH + V_ECDSA_RECOVERABLE_PUBKEY_LENGTH) {
+        let value = vector::borrow(authenticator_payload, i);
+        vector::push_back(&mut public_key, *value);
+        i = i + 1;
+    };
+
+    public_key
+}
+
+ + + +
+ + + +## Function `ecdsa_k1_recoverable_signature` + + + +
public fun ecdsa_k1_recoverable_signature(authenticator_payload: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun ecdsa_k1_recoverable_signature(authenticator_payload: &vector<u8>): vector<u8> {
+    let sign = vector::empty<u8>();
+    let i = V_ECDSA_RECOVERABLE_SCHEME_LENGTH;
+    while (i < V_ECDSA_RECOVERABLE_SIG_LENGTH + 1) {
+        let value = vector::borrow(authenticator_payload, i);
+        vector::push_back(&mut sign, *value);
+        i = i + 1;
+    };
+
+    sign
+}
+
+ + + +
+ + + +## Function `ecdsa_k1_recoverable_authentication_key` + +Get the authentication key of the given authenticator. + + +
public fun ecdsa_k1_recoverable_authentication_key(authenticator_payload: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun ecdsa_k1_recoverable_authentication_key(authenticator_payload: &vector<u8>): vector<u8> {
+    let public_key = ecdsa_k1_recoverable_public_key(authenticator_payload);
+    let addr = ecdsa_k1_recoverable_public_key_to_address(public_key);
+    moveos_std::bcs::to_bytes(&addr)
+}
+
+ + + +
+ + + +## Function `ecdsa_k1_recoverable_public_key_to_address` + + + +
public fun ecdsa_k1_recoverable_public_key_to_address(public_key: vector<u8>): address
+
+ + + +
+Implementation + + +
public fun ecdsa_k1_recoverable_public_key_to_address(public_key: vector<u8>): address {
+    let bytes = vector::singleton((SCHEME_ECDSA_RECOVERABLE as u8));
+    vector::append(&mut bytes, public_key);
+    moveos_std::bcs::to_address(hash::blake2b256(&bytes))
+}
+
+ + + +
+ + + +## Function `get_authentication_key` + + + +
public fun get_authentication_key(ctx: &storage_context::StorageContext, addr: address): vector<u8>
+
+ + + +
+Implementation + + +
public fun get_authentication_key(ctx: &StorageContext, addr: address): vector<u8> {
+    let auth_key_option = account_authentication::get_authentication_key<EcdsaK1RecoverableValidator>(ctx, addr);
+    if (option::is_some(&auth_key_option)) {
+        option::extract(&mut auth_key_option)
+    }else {
+        //if AuthenticationKey does not exist, return addr as authentication key
+        moveos_std::bcs::to_bytes(&addr)
+    }
+}
+
+ + + +
+ + + +## Function `validate` + + + +
public fun validate(ctx: &storage_context::StorageContext, authenticator_payload: vector<u8>)
+
+ + + +
+Implementation + + +
public fun validate(ctx: &StorageContext, authenticator_payload: vector<u8>) {
+    // TODO handle non-ed25519 auth key and address relationship
+    // let auth_key = ecdsa_k1_recoverable_authentication_key(&authenticator_payload);
+    // let auth_key_in_account = get_authentication_key(ctx, storage_context::sender(ctx));
+    // assert!(
+    //    auth_key_in_account == auth_key,
+    //    auth_validator::error_invalid_account_auth_key()
+    // );
+    assert!(
+        ecdsa_k1_recoverable::verify(
+            &ecdsa_k1_recoverable_signature(&authenticator_payload),
+            &storage_context::tx_hash(ctx),
+            SHA256, // KECCAK256:0, SHA256:1, TODO: The hash type may need to be passed through the authenticator
+        ),
+        auth_validator::error_invalid_authenticator()
+    );
+}
+
+ + + +
diff --git a/crates/rooch-framework/doc/ecdsa_k1_validator.md b/crates/rooch-framework/doc/ecdsa_k1_validator.md new file mode 100644 index 0000000000..1aaeab9295 --- /dev/null +++ b/crates/rooch-framework/doc/ecdsa_k1_validator.md @@ -0,0 +1,428 @@ + + + +# Module `0x3::ecdsa_k1_validator` + +This module implements the ECDSA over Secpk256k1 validator scheme. + + +- [Struct `EcdsaK1Validator`](#0x3_ecdsa_k1_validator_EcdsaK1Validator) +- [Constants](#@Constants_0) +- [Function `scheme`](#0x3_ecdsa_k1_validator_scheme) +- [Function `rotate_authentication_key_entry`](#0x3_ecdsa_k1_validator_rotate_authentication_key_entry) +- [Function `remove_authentication_key_entry`](#0x3_ecdsa_k1_validator_remove_authentication_key_entry) +- [Function `ecdsa_k1_public_key`](#0x3_ecdsa_k1_validator_ecdsa_k1_public_key) +- [Function `ecdsa_k1_signature`](#0x3_ecdsa_k1_validator_ecdsa_k1_signature) +- [Function `ecdsa_k1_authentication_key`](#0x3_ecdsa_k1_validator_ecdsa_k1_authentication_key) +- [Function `ecdsa_k1_public_key_to_address`](#0x3_ecdsa_k1_validator_ecdsa_k1_public_key_to_address) +- [Function `get_authentication_key`](#0x3_ecdsa_k1_validator_get_authentication_key) +- [Function `validate`](#0x3_ecdsa_k1_validator_validate) + + +
use 0x1::error;
+use 0x1::option;
+use 0x1::signer;
+use 0x1::vector;
+use 0x2::bcs;
+use 0x2::storage_context;
+use 0x3::account_authentication;
+use 0x3::auth_validator;
+use 0x3::ecdsa_k1;
+use 0x3::hash;
+
+ + + + + +## Struct `EcdsaK1Validator` + + + +
struct EcdsaK1Validator has drop, store
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const EMalformedAuthenticationKey: u64 = 1002;
+
+ + + + + +Hash function name that are valid for ecrecover and verify. + + +
const KECCAK256: u8 = 0;
+
+ + + + + + + +
const SHA256: u8 = 1;
+
+ + + + + +error code + + +
const EMalformedAccount: u64 = 1001;
+
+ + + + + + + +
const SCHEME_ECDSA: u64 = 2;
+
+ + + + + + + +
const V_ECDSA_HASH_LENGTH: u64 = 1;
+
+ + + + + + + +
const V_ECDSA_PUBKEY_LENGTH: u64 = 33;
+
+ + + + + + + +
const V_ECDSA_SCHEME_LENGTH: u64 = 1;
+
+ + + + + + + +
const V_ECDSA_SIG_LENGTH: u64 = 64;
+
+ + + + + +## Function `scheme` + + + +
public fun scheme(): u64
+
+ + + +
+Implementation + + +
public fun scheme(): u64 {
+    SCHEME_ECDSA
+}
+
+ + + +
+ + + +## Function `rotate_authentication_key_entry` + + + +
public entry fun rotate_authentication_key_entry<EcdsaK1Validator>(ctx: &mut storage_context::StorageContext, account: &signer, public_key: vector<u8>)
+
+ + + +
+Implementation + + +
public entry fun rotate_authentication_key_entry<EcdsaK1Validator>(
+    ctx: &mut StorageContext,
+    account: &signer,
+    public_key: vector<u8>
+) {
+    // compare newly passed public key with ecdsa public key length to ensure it's compatible
+    assert!(
+        vector::length(&public_key) == V_ECDSA_PUBKEY_LENGTH,
+        error::invalid_argument(EMalformedAuthenticationKey)
+    );
+
+    // ensure that the ecdsa public key to address isn't matched with the ed25519 account address
+    let account_addr = signer::address_of(account);
+    let ecdsa_addr = ecdsa_k1_public_key_to_address(public_key);
+    assert!(
+        account_addr != ecdsa_addr,
+        error::invalid_argument(EMalformedAccount)
+    );
+
+    // serialize the address to an auth key and rotate it by calling rotate_authentication_key
+    let ecdsa_k1_authentication_key = moveos_std::bcs::to_bytes(&ecdsa_addr);
+    account_authentication::rotate_authentication_key<EcdsaK1Validator>(ctx, account_addr, ecdsa_k1_authentication_key);
+}
+
+ + + +
+ + + +## Function `remove_authentication_key_entry` + + + +
public entry fun remove_authentication_key_entry<EcdsaK1Validator>(ctx: &mut storage_context::StorageContext, account: &signer)
+
+ + + +
+Implementation + + +
public entry fun remove_authentication_key_entry<EcdsaK1Validator>(ctx: &mut StorageContext, account: &signer) {
+    let account_addr = signer::address_of(account);
+    account_authentication::remove_authentication_key<EcdsaK1Validator>(ctx, account_addr);
+}
+
+ + + +
+ + + +## Function `ecdsa_k1_public_key` + + + +
public fun ecdsa_k1_public_key(authenticator_payload: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun ecdsa_k1_public_key(authenticator_payload: &vector<u8>): vector<u8> {
+    let public_key = vector::empty<u8>();
+    let i = V_ECDSA_SCHEME_LENGTH + V_ECDSA_SIG_LENGTH;
+    while (i < V_ECDSA_SCHEME_LENGTH + V_ECDSA_SIG_LENGTH + V_ECDSA_PUBKEY_LENGTH) {
+        let value = vector::borrow(authenticator_payload, i);
+        vector::push_back(&mut public_key, *value);
+        i = i + 1;
+    };
+
+    public_key
+}
+
+ + + +
+ + + +## Function `ecdsa_k1_signature` + + + +
public fun ecdsa_k1_signature(authenticator_payload: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun ecdsa_k1_signature(authenticator_payload: &vector<u8>): vector<u8> {
+    let sign = vector::empty<u8>();
+    let i = V_ECDSA_SCHEME_LENGTH;
+    while (i < V_ECDSA_SIG_LENGTH + 1) {
+        let value = vector::borrow(authenticator_payload, i);
+        vector::push_back(&mut sign, *value);
+        i = i + 1;
+    };
+
+    sign
+}
+
+ + + +
+ + + +## Function `ecdsa_k1_authentication_key` + +Get the authentication key of the given authenticator. + + +
public fun ecdsa_k1_authentication_key(authenticator_payload: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun ecdsa_k1_authentication_key(authenticator_payload: &vector<u8>): vector<u8> {
+    let public_key = ecdsa_k1_public_key(authenticator_payload);
+    let addr = ecdsa_k1_public_key_to_address(public_key);
+    moveos_std::bcs::to_bytes(&addr)
+}
+
+ + + +
+ + + +## Function `ecdsa_k1_public_key_to_address` + + + +
public fun ecdsa_k1_public_key_to_address(public_key: vector<u8>): address
+
+ + + +
+Implementation + + +
public fun ecdsa_k1_public_key_to_address(public_key: vector<u8>): address {
+    let bytes = vector::singleton((SCHEME_ECDSA as u8));
+    vector::append(&mut bytes, public_key);
+    moveos_std::bcs::to_address(hash::blake2b256(&bytes))
+}
+
+ + + +
+ + + +## Function `get_authentication_key` + + + +
public fun get_authentication_key(ctx: &storage_context::StorageContext, addr: address): vector<u8>
+
+ + + +
+Implementation + + +
public fun get_authentication_key(ctx: &StorageContext, addr: address): vector<u8> {
+    let auth_key_option = account_authentication::get_authentication_key<EcdsaK1Validator>(ctx, addr);
+    if (option::is_some(&auth_key_option)) {
+        option::extract(&mut auth_key_option)
+    }else {
+        //if AuthenticationKey does not exist, return addr as authentication key
+        moveos_std::bcs::to_bytes(&addr)
+    }
+}
+
+ + + +
+ + + +## Function `validate` + + + +
public fun validate(ctx: &storage_context::StorageContext, authenticator_payload: vector<u8>)
+
+ + + +
+Implementation + + +
public fun validate(ctx: &StorageContext, authenticator_payload: vector<u8>) {
+    // TODO handle non-ed25519 auth key and address relationship
+    // let auth_key = ecdsa_k1_authentication_key(&authenticator_payload);
+    // let auth_key_in_account = get_authentication_key(ctx, storage_context::sender(ctx));
+    // assert!(
+    //    auth_key_in_account == auth_key,
+    //    auth_validator::error_invalid_account_auth_key()
+    // );
+    assert!(
+        ecdsa_k1::verify(
+            &ecdsa_k1_signature(&authenticator_payload),
+            &ecdsa_k1_public_key(&authenticator_payload),
+            &storage_context::tx_hash(ctx),
+            SHA256, // KECCAK256:0, SHA256:1, TODO: The hash type may need to be passed through the authenticator
+        ),
+        auth_validator::error_invalid_authenticator()
+    );
+}
+
+ + + +
diff --git a/crates/rooch-framework/doc/ed25519_validator.md b/crates/rooch-framework/doc/ed25519_validator.md index 01d4cfe311..b12aa26c1c 100644 --- a/crates/rooch-framework/doc/ed25519_validator.md +++ b/crates/rooch-framework/doc/ed25519_validator.md @@ -9,15 +9,22 @@ This module implements the ed25519 validator scheme. - [Struct `Ed25519Validator`](#0x3_ed25519_validator_Ed25519Validator) - [Constants](#@Constants_0) - [Function `scheme`](#0x3_ed25519_validator_scheme) -- [Function `ed25519_public_key`](#0x3_ed25519_validator_ed25519_public_key) -- [Function `ed25519_signature`](#0x3_ed25519_validator_ed25519_signature) -- [Function `ed25519_authentication_key`](#0x3_ed25519_validator_ed25519_authentication_key) -- [Function `ed25519_public_key_to_address`](#0x3_ed25519_validator_ed25519_public_key_to_address) -- [Function `get_authentication_key`](#0x3_ed25519_validator_get_authentication_key) +- [Function `rotate_authentication_key_entry`](#0x3_ed25519_validator_rotate_authentication_key_entry) +- [Function `remove_authentication_key_entry`](#0x3_ed25519_validator_remove_authentication_key_entry) +- [Function `get_public_key_from_payload`](#0x3_ed25519_validator_get_public_key_from_payload) +- [Function `get_signature_from_payload`](#0x3_ed25519_validator_get_signature_from_payload) +- [Function `get_authentication_key_from_payload`](#0x3_ed25519_validator_get_authentication_key_from_payload) +- [Function `public_key_to_address`](#0x3_ed25519_validator_public_key_to_address) +- [Function `public_key_to_authentication_key`](#0x3_ed25519_validator_public_key_to_authentication_key) +- [Function `get_authentication_key_with_default`](#0x3_ed25519_validator_get_authentication_key_with_default) +- [Function `default_authentication_key`](#0x3_ed25519_validator_default_authentication_key) +- [Function `validate_signature`](#0x3_ed25519_validator_validate_signature) - [Function `validate`](#0x3_ed25519_validator_validate) -
use 0x1::option;
+
use 0x1::error;
+use 0x1::option;
+use 0x1::signer;
 use 0x1::vector;
 use 0x2::bcs;
 use 0x2::storage_context;
@@ -35,7 +42,7 @@ This module implements the ed25519 validator scheme.
 
 
 
-
struct Ed25519Validator has store
+
struct Ed25519Validator has drop, store
 
@@ -61,38 +68,57 @@ This module implements the ed25519 validator scheme. ## Constants - + -
const ED25519_PUBKEY_LENGTH: u64 = 32;
+
const EMalformedAuthenticationKey: u64 = 1002;
 
- + +error code -
const ED25519_SCHEME_LENGTH: u64 = 1;
+
const EMalformedAccount: u64 = 1001;
 
- + -
const ED25519_SIG_LENGTH: u64 = 64;
+
const SCHEME_ED25519: u64 = 0;
 
- + -
const SCHEME_ED25519: u64 = 0;
+
const V_ED25519_PUBKEY_LENGTH: u64 = 32;
+
+ + + + + + + +
const V_ED25519_SCHEME_LENGTH: u64 = 1;
+
+ + + + + + + +
const V_ED25519_SIG_LENGTH: u64 = 64;
 
@@ -113,7 +139,7 @@ This module implements the ed25519 validator scheme.
public fun scheme(): u64 {
-   SCHEME_ED25519
+    SCHEME_ED25519
 }
 
@@ -121,13 +147,13 @@ This module implements the ed25519 validator scheme. - + -## Function `ed25519_public_key` +## Function `rotate_authentication_key_entry` -
public fun ed25519_public_key(payload: &vector<u8>): vector<u8>
+
public entry fun rotate_authentication_key_entry<Ed25519Validator>(ctx: &mut storage_context::StorageContext, account: &signer, public_key: vector<u8>)
 
@@ -136,16 +162,45 @@ This module implements the ed25519 validator scheme. Implementation -
public fun ed25519_public_key(payload: &vector<u8>): vector<u8> {
-   let public_key = vector::empty<u8>();
-   let i = ED25519_SCHEME_LENGTH + ED25519_SIG_LENGTH;
-   while (i < ED25519_SCHEME_LENGTH + ED25519_SIG_LENGTH + ED25519_PUBKEY_LENGTH) {
-      let value = vector::borrow(payload, i);
-      vector::push_back(&mut public_key, *value);
-      i = i + 1;
-   };
+
public entry fun rotate_authentication_key_entry<Ed25519Validator>(
+    ctx: &mut StorageContext,
+    account: &signer,
+    public_key: vector<u8>
+) {
+    // compare newly passed public key with ed25519 public key length to ensure it's compatible
+    assert!(
+        vector::length(&public_key) == V_ED25519_PUBKEY_LENGTH,
+        error::invalid_argument(EMalformedAuthenticationKey)
+    );
+
+    // User can rotate the authentication key arbitrarily, so we do not need to check the new public key with the account address.
+    let authentication_key = public_key_to_authentication_key(public_key);
+    let account_addr = signer::address_of(account);
+    rotate_authentication_key(ctx, account_addr, authentication_key);
+}
+
+ + - public_key + + + + +## Function `remove_authentication_key_entry` + + + +
public entry fun remove_authentication_key_entry<Ed25519Validator>(ctx: &mut storage_context::StorageContext, account: &signer)
+
+ + + +
+Implementation + + +
public entry fun remove_authentication_key_entry<Ed25519Validator>(ctx: &mut StorageContext, account: &signer) {
+  account_authentication::remove_authentication_key<Ed25519Validator>(ctx, signer::address_of(account));
 }
 
@@ -153,13 +208,13 @@ This module implements the ed25519 validator scheme.
- + -## Function `ed25519_signature` +## Function `get_public_key_from_payload` -
public fun ed25519_signature(payload: &vector<u8>): vector<u8>
+
public fun get_public_key_from_payload(authenticator_payload: &vector<u8>): vector<u8>
 
@@ -168,16 +223,16 @@ This module implements the ed25519 validator scheme. Implementation -
public fun ed25519_signature(payload: &vector<u8>): vector<u8> {
-   let sign = vector::empty<u8>();
-   let i = ED25519_SCHEME_LENGTH;
-   while (i < ED25519_SIG_LENGTH + 1) {
-      let value = vector::borrow(payload, i);
-      vector::push_back(&mut sign, *value);
-      i = i + 1;
-   };
+
public fun get_public_key_from_payload(authenticator_payload: &vector<u8>): vector<u8> {
+    let public_key = vector::empty<u8>();
+    let i = V_ED25519_SCHEME_LENGTH + V_ED25519_SIG_LENGTH;
+    while (i < V_ED25519_SCHEME_LENGTH + V_ED25519_SIG_LENGTH + V_ED25519_PUBKEY_LENGTH) {
+        let value = vector::borrow(authenticator_payload, i);
+        vector::push_back(&mut public_key, *value);
+        i = i + 1;
+    };
 
-   sign
+    public_key
 }
 
@@ -185,14 +240,13 @@ This module implements the ed25519 validator scheme. - + -## Function `ed25519_authentication_key` +## Function `get_signature_from_payload` -Get the authentication key of the given authenticator. -
public fun ed25519_authentication_key(payload: &vector<u8>): vector<u8>
+
public fun get_signature_from_payload(authenticator_payload: &vector<u8>): vector<u8>
 
@@ -201,10 +255,16 @@ Get the authentication key of the given authenticator. Implementation -
public fun ed25519_authentication_key(payload: &vector<u8>): vector<u8> {
-   let public_key = ed25519_public_key(payload);
-   let addr = ed25519_public_key_to_address(public_key);
-   moveos_std::bcs::to_bytes(&addr)
+
public fun get_signature_from_payload(authenticator_payload: &vector<u8>): vector<u8> {
+    let sign = vector::empty<u8>();
+    let i = V_ED25519_SCHEME_LENGTH;
+    while (i < V_ED25519_SIG_LENGTH + 1) {
+        let value = vector::borrow(authenticator_payload, i);
+        vector::push_back(&mut sign, *value);
+        i = i + 1;
+    };
+
+    sign
 }
 
@@ -212,13 +272,14 @@ Get the authentication key of the given authenticator. - + -## Function `ed25519_public_key_to_address` +## Function `get_authentication_key_from_payload` +Get the authentication key of the given authenticator authenticator_payload. -
public fun ed25519_public_key_to_address(public_key: vector<u8>): address
+
public fun get_authentication_key_from_payload(authenticator_payload: &vector<u8>): vector<u8>
 
@@ -227,10 +288,10 @@ Get the authentication key of the given authenticator. Implementation -
public fun ed25519_public_key_to_address(public_key: vector<u8>): address {
-   let bytes = vector::singleton((SCHEME_ED25519 as u8));
-   vector::append(&mut bytes, public_key);
-   moveos_std::bcs::to_address(hash::blake2b256(&bytes))
+
public fun get_authentication_key_from_payload(authenticator_payload: &vector<u8>): vector<u8> {
+    let public_key = get_public_key_from_payload(authenticator_payload);
+    let addr = public_key_to_address(public_key);
+    moveos_std::bcs::to_bytes(&addr)
 }
 
@@ -238,13 +299,13 @@ Get the authentication key of the given authenticator. - + -## Function `get_authentication_key` +## Function `public_key_to_address` -
public fun get_authentication_key(ctx: &storage_context::StorageContext, addr: address): vector<u8>
+
public fun public_key_to_address(public_key: vector<u8>): address
 
@@ -253,15 +314,121 @@ Get the authentication key of the given authenticator. Implementation -
public fun get_authentication_key(ctx: &StorageContext, addr: address): vector<u8> {
+
public fun public_key_to_address(public_key: vector<u8>): address {
+    moveos_std::bcs::to_address(public_key_to_authentication_key(public_key))
+}
+
+ + + + + + + +## Function `public_key_to_authentication_key` + +Get the authentication key of the given public key. + + +
public fun public_key_to_authentication_key(public_key: vector<u8>): vector<u8>
+
+ - let auth_key_option = account_authentication::get_authentication_key<Ed25519Validator>(ctx, addr); - if(option::is_some(&auth_key_option)){ - option::extract(&mut auth_key_option) - }else{ - //if AuthenticationKey does not exist, return addr as authentication key - moveos_std::bcs::to_bytes(&addr) - } + +
+Implementation + + +
public fun public_key_to_authentication_key(public_key: vector<u8>): vector<u8> {
+    let bytes = vector::singleton((SCHEME_ED25519 as u8));
+    vector::append(&mut bytes, public_key);
+    hash::blake2b256(&bytes)
+}
+
+ + + +
+ + + +## Function `get_authentication_key_with_default` + +Get the authentication key of the given account, if it not exist, return the account address as authentication key. + + +
public fun get_authentication_key_with_default(ctx: &storage_context::StorageContext, addr: address): vector<u8>
+
+ + + +
+Implementation + + +
public fun get_authentication_key_with_default(ctx: &StorageContext, addr: address): vector<u8> {
+    let auth_key_option = account_authentication::get_authentication_key<Ed25519Validator>(ctx, addr);
+    if (option::is_some(&auth_key_option)) {
+        option::extract(&mut auth_key_option)
+    }else {
+        default_authentication_key(addr)
+    }
+}
+
+ + + +
+ + + +## Function `default_authentication_key` + + + +
public fun default_authentication_key(addr: address): vector<u8>
+
+ + + +
+Implementation + + +
public fun default_authentication_key(addr: address): vector<u8> {
+    moveos_std::bcs::to_bytes(&addr)
+}
+
+ + + +
+ + + +## Function `validate_signature` + +Only validate the authenticator's signature. + + +
public fun validate_signature(authenticator_payload: &vector<u8>, tx_hash: &vector<u8>)
+
+ + + +
+Implementation + + +
public fun validate_signature(authenticator_payload: &vector<u8>, tx_hash: &vector<u8>) {
+    assert!(
+        ed25519::verify(
+            &get_signature_from_payload(authenticator_payload),
+            &get_public_key_from_payload(authenticator_payload),
+            tx_hash
+        ),
+        auth_validator::error_invalid_authenticator()
+    );
 }
 
@@ -275,7 +442,7 @@ Get the authentication key of the given authenticator. -
public fun validate(ctx: &storage_context::StorageContext, payload: vector<u8>)
+
public fun validate(ctx: &storage_context::StorageContext, authenticator_payload: vector<u8>)
 
@@ -284,18 +451,16 @@ Get the authentication key of the given authenticator. Implementation -
public fun validate(ctx: &StorageContext, payload: vector<u8>){
-     let auth_key = ed25519_authentication_key(&payload);
-     let auth_key_in_account = get_authentication_key(ctx, storage_context::sender(ctx));
-     assert!(
-         auth_key_in_account == auth_key,
-         auth_validator::error_invalid_account_auth_key()
-     );
-     assert!(
-     ed25519::verify(&ed25519_signature(&payload),
-         &ed25519_public_key(&payload),
-         &storage_context::tx_hash(ctx)),
-    auth_validator::error_invalid_account_auth_key());
+
public fun validate(ctx: &StorageContext, authenticator_payload: vector<u8>) {
+    let tx_hash = storage_context::tx_hash(ctx);
+    validate_signature(&authenticator_payload, &tx_hash);
+
+    let auth_key = get_authentication_key_from_payload(&authenticator_payload);
+    let auth_key_in_account = get_authentication_key_with_default(ctx, storage_context::sender(ctx));
+    assert!(
+        auth_key_in_account == auth_key,
+        auth_validator::error_invalid_account_auth_key()
+    );
 }
 
diff --git a/crates/rooch-framework/doc/hash.md b/crates/rooch-framework/doc/hash.md index 13cb7aeabc..1423c558ea 100644 --- a/crates/rooch-framework/doc/hash.md +++ b/crates/rooch-framework/doc/hash.md @@ -11,6 +11,7 @@ Move standard library and wrap the functions at here. - [Function `sha3_256`](#0x3_hash_sha3_256) - [Function `blake2b256`](#0x3_hash_blake2b256) - [Function `keccak256`](#0x3_hash_keccak256) +- [Function `ripemd160`](#0x3_hash_ripemd160)
use 0x1::hash;
@@ -112,4 +113,28 @@ Hash the input bytes using keccak256 and returns 32 bytes.
 
 
 
+
+ + + +## Function `ripemd160` + +@param data: Arbitrary binary data to hash +Hash the input bytes using ripemd160 and returns 20 bytes. + + +
public fun ripemd160(data: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
native public fun ripemd160(data: &vector<u8>): vector<u8>;
+
+ + +
diff --git a/crates/rooch-framework/doc/multi_ed25519_validator.md b/crates/rooch-framework/doc/multi_ed25519_validator.md index df2ee36022..d3f4abbda4 100644 --- a/crates/rooch-framework/doc/multi_ed25519_validator.md +++ b/crates/rooch-framework/doc/multi_ed25519_validator.md @@ -24,7 +24,7 @@ This module implements the multi-ed25519 validator scheme. -
struct MultiEd25519Validator has store
+
struct MultiEd25519Validator has drop, store
 
diff --git a/crates/rooch-framework/doc/schnorr.md b/crates/rooch-framework/doc/schnorr.md new file mode 100644 index 0000000000..0adbe68007 --- /dev/null +++ b/crates/rooch-framework/doc/schnorr.md @@ -0,0 +1,91 @@ + + + +# Module `0x3::schnorr` + + + +- [Constants](#@Constants_0) +- [Function `verify`](#0x3_schnorr_verify) + + +
+ + + + + +## Constants + + + + +Error if the public key is invalid. + + +
const EInvalidPubKey: u64 = 1;
+
+ + + + + +Error if the signature is invalid. + + +
const EInvalidSignature: u64 = 0;
+
+ + + + + +Hash function name that are valid for verify. + + +
const KECCAK256: u8 = 0;
+
+ + + + + + + +
const SHA256: u8 = 1;
+
+ + + + + +## Function `verify` + +@param signature: A 64-bytes signature that is signed using Schnorr over Secpk256k1 key pairs. +@param public_key: A 32-bytes public key that is used to sign messages. +@param msg: The message that the signature is signed against. +@param hash: The hash function used to hash the message when signing. + +If the signature is valid to the pubkey and hashed message, return true. Else false. + + +
public fun verify(signature: &vector<u8>, public_key: &vector<u8>, msg: &vector<u8>, hash: u8): bool
+
+ + + +
+Implementation + + +
public native fun verify(
+    signature: &vector<u8>,
+    public_key: &vector<u8>,
+    msg: &vector<u8>,
+    hash: u8
+): bool;
+
+ + + +
diff --git a/crates/rooch-framework/doc/schnorr_validator.md b/crates/rooch-framework/doc/schnorr_validator.md new file mode 100644 index 0000000000..f9586b0877 --- /dev/null +++ b/crates/rooch-framework/doc/schnorr_validator.md @@ -0,0 +1,428 @@ + + + +# Module `0x3::schnorr_validator` + +This module implements the schnorr validator scheme. + + +- [Struct `SchnorrValidator`](#0x3_schnorr_validator_SchnorrValidator) +- [Constants](#@Constants_0) +- [Function `scheme`](#0x3_schnorr_validator_scheme) +- [Function `rotate_authentication_key_entry`](#0x3_schnorr_validator_rotate_authentication_key_entry) +- [Function `remove_authentication_key_entry`](#0x3_schnorr_validator_remove_authentication_key_entry) +- [Function `schnorr_public_key`](#0x3_schnorr_validator_schnorr_public_key) +- [Function `schnorr_signature`](#0x3_schnorr_validator_schnorr_signature) +- [Function `schnorr_authentication_key`](#0x3_schnorr_validator_schnorr_authentication_key) +- [Function `schnorr_public_key_to_address`](#0x3_schnorr_validator_schnorr_public_key_to_address) +- [Function `get_authentication_key`](#0x3_schnorr_validator_get_authentication_key) +- [Function `validate`](#0x3_schnorr_validator_validate) + + +
use 0x1::error;
+use 0x1::option;
+use 0x1::signer;
+use 0x1::vector;
+use 0x2::bcs;
+use 0x2::storage_context;
+use 0x3::account_authentication;
+use 0x3::auth_validator;
+use 0x3::hash;
+use 0x3::schnorr;
+
+ + + + + +## Struct `SchnorrValidator` + + + +
struct SchnorrValidator has drop, store
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const EMalformedAuthenticationKey: u64 = 1002;
+
+ + + + + +Hash function name that are valid for verify. + + +
const KECCAK256: u8 = 0;
+
+ + + + + + + +
const SHA256: u8 = 1;
+
+ + + + + +error code + + +
const EMalformedAccount: u64 = 1001;
+
+ + + + + + + +
const SCHEME_SCHNORR: u64 = 4;
+
+ + + + + + + +
const V_SCHNORR_HASH_LENGTH: u64 = 1;
+
+ + + + + + + +
const V_SCHNORR_PUBKEY_LENGTH: u64 = 32;
+
+ + + + + + + +
const V_SCHNORR_SCHEME_LENGTH: u64 = 1;
+
+ + + + + + + +
const V_SCHNORR_SIG_LENGTH: u64 = 64;
+
+ + + + + +## Function `scheme` + + + +
public fun scheme(): u64
+
+ + + +
+Implementation + + +
public fun scheme(): u64 {
+    SCHEME_SCHNORR
+}
+
+ + + +
+ + + +## Function `rotate_authentication_key_entry` + + + +
public entry fun rotate_authentication_key_entry<SchnorrValidator>(ctx: &mut storage_context::StorageContext, account: &signer, public_key: vector<u8>)
+
+ + + +
+Implementation + + +
public entry fun rotate_authentication_key_entry<SchnorrValidator>(
+    ctx: &mut StorageContext,
+    account: &signer,
+    public_key: vector<u8>
+) {
+    // compare newly passed public key with schnorr public key length to ensure it's compatible
+    assert!(
+        vector::length(&public_key) == V_SCHNORR_PUBKEY_LENGTH,
+        error::invalid_argument(EMalformedAuthenticationKey)
+    );
+
+    // ensure that the schnorr public key to address isn't matched with the ed25519 account address
+    let account_addr = signer::address_of(account);
+    let schnorr_addr = schnorr_public_key_to_address(public_key);
+    assert!(
+        account_addr != schnorr_addr,
+        error::invalid_argument(EMalformedAccount)
+    );
+
+    // serialize the address to an auth key and rotate it by calling rotate_authentication_key
+    let schnorr_authentication_key = moveos_std::bcs::to_bytes(&schnorr_addr);
+    account_authentication::rotate_authentication_key<SchnorrValidator>(ctx, account_addr, schnorr_authentication_key);
+}
+
+ + + +
+ + + +## Function `remove_authentication_key_entry` + + + +
public entry fun remove_authentication_key_entry<SchnorrValidator>(ctx: &mut storage_context::StorageContext, account: &signer)
+
+ + + +
+Implementation + + +
public entry fun remove_authentication_key_entry<SchnorrValidator>(ctx: &mut StorageContext, account: &signer) {
+    let account_addr = signer::address_of(account);
+    account_authentication::remove_authentication_key<SchnorrValidator>(ctx, account_addr);
+}
+
+ + + +
+ + + +## Function `schnorr_public_key` + + + +
public fun schnorr_public_key(authenticator_payload: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun schnorr_public_key(authenticator_payload: &vector<u8>): vector<u8> {
+    let public_key = vector::empty<u8>();
+    let i = V_SCHNORR_SCHEME_LENGTH + V_SCHNORR_SIG_LENGTH;
+    while (i < V_SCHNORR_SCHEME_LENGTH + V_SCHNORR_SIG_LENGTH + V_SCHNORR_PUBKEY_LENGTH) {
+        let value = vector::borrow(authenticator_payload, i);
+        vector::push_back(&mut public_key, *value);
+        i = i + 1;
+    };
+
+    public_key
+}
+
+ + + +
+ + + +## Function `schnorr_signature` + + + +
public fun schnorr_signature(authenticator_payload: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun schnorr_signature(authenticator_payload: &vector<u8>): vector<u8> {
+    let sign = vector::empty<u8>();
+    let i = V_SCHNORR_SCHEME_LENGTH;
+    while (i < V_SCHNORR_SIG_LENGTH + 1) {
+        let value = vector::borrow(authenticator_payload, i);
+        vector::push_back(&mut sign, *value);
+        i = i + 1;
+    };
+
+    sign
+}
+
+ + + +
+ + + +## Function `schnorr_authentication_key` + +Get the authentication key of the given authenticator. + + +
public fun schnorr_authentication_key(authenticator_payload: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
public fun schnorr_authentication_key(authenticator_payload: &vector<u8>): vector<u8> {
+    let public_key = schnorr_public_key(authenticator_payload);
+    let addr = schnorr_public_key_to_address(public_key);
+    moveos_std::bcs::to_bytes(&addr)
+}
+
+ + + +
+ + + +## Function `schnorr_public_key_to_address` + + + +
public fun schnorr_public_key_to_address(public_key: vector<u8>): address
+
+ + + +
+Implementation + + +
public fun schnorr_public_key_to_address(public_key: vector<u8>): address {
+    let bytes = vector::singleton((SCHEME_SCHNORR as u8));
+    vector::append(&mut bytes, public_key);
+    moveos_std::bcs::to_address(hash::blake2b256(&bytes))
+}
+
+ + + +
+ + + +## Function `get_authentication_key` + + + +
public fun get_authentication_key(ctx: &storage_context::StorageContext, addr: address): vector<u8>
+
+ + + +
+Implementation + + +
public fun get_authentication_key(ctx: &StorageContext, addr: address): vector<u8> {
+    let auth_key_option = account_authentication::get_authentication_key<SchnorrValidator>(ctx, addr);
+    if (option::is_some(&auth_key_option)) {
+        option::extract(&mut auth_key_option)
+    }else {
+        //if AuthenticationKey does not exist, return addr as authentication key
+        moveos_std::bcs::to_bytes(&addr)
+    }
+}
+
+ + + +
+ + + +## Function `validate` + + + +
public fun validate(ctx: &storage_context::StorageContext, authenticator_payload: vector<u8>)
+
+ + + +
+Implementation + + +
public fun validate(ctx: &StorageContext, authenticator_payload: vector<u8>) {
+    // TODO handle non-ed25519 auth key and address relationship
+    // let auth_key = schnorr_authentication_key(&authenticator_payload);
+    // let auth_key_in_account = get_authentication_key(ctx, storage_context::sender(ctx));
+    // assert!(
+    //    auth_key_in_account == auth_key,
+    //    auth_validator::error_invalid_account_auth_key()
+    // );
+    assert!(
+        schnorr::verify(
+            &schnorr_signature(&authenticator_payload),
+            &schnorr_public_key(&authenticator_payload),
+            &storage_context::tx_hash(ctx),
+            SHA256,
+        ),
+        auth_validator::error_invalid_account_auth_key()
+    );
+}
+
+ + + +
diff --git a/crates/rooch-framework/doc/session_key.md b/crates/rooch-framework/doc/session_key.md new file mode 100644 index 0000000000..4cd96a1b82 --- /dev/null +++ b/crates/rooch-framework/doc/session_key.md @@ -0,0 +1,436 @@ + + + +# Module `0x3::session_key` + + + +- [Struct `SessionScope`](#0x3_session_key_SessionScope) +- [Struct `SessionKey`](#0x3_session_key_SessionKey) +- [Resource `SessionKeys`](#0x3_session_key_SessionKeys) +- [Constants](#@Constants_0) +- [Function `is_expired`](#0x3_session_key_is_expired) +- [Function `exists_session_key`](#0x3_session_key_exists_session_key) +- [Function `get_session_key`](#0x3_session_key_get_session_key) +- [Function `create_session_key`](#0x3_session_key_create_session_key) +- [Function `create_session_key_entry`](#0x3_session_key_create_session_key_entry) +- [Function `validate`](#0x3_session_key_validate) +- [Function `active_session_key`](#0x3_session_key_active_session_key) + + +
use 0x1::ascii;
+use 0x1::error;
+use 0x1::option;
+use 0x1::signer;
+use 0x1::vector;
+use 0x2::account_storage;
+use 0x2::storage_context;
+use 0x2::table;
+use 0x2::tx_context;
+use 0x3::auth_validator;
+use 0x3::ed25519_validator;
+
+ + + + + +## Struct `SessionScope` + +The session's scope + + +
struct SessionScope has copy, drop, store
+
+ + + +
+Fields + + +
+
+module_address: address +
+
+ +
+
+module_name: ascii::String +
+
+ The scope module name, * means all modules in the module address +
+
+function_name: ascii::String +
+
+ The scope function name, * means all functions in the module +
+
+ + +
+ + + +## Struct `SessionKey` + + + +
struct SessionKey has copy, drop, store
+
+ + + +
+Fields + + +
+
+authentication_key: vector<u8> +
+
+ +
+
+scheme: u64 +
+
+ +
+
+scopes: vector<session_key::SessionScope> +
+
+ +
+
+expiration_time: u64 +
+
+ The session key's expiration time period, in seconds, 0 means never expired +
+
+last_active_time: u64 +
+
+ The session key's last active time +
+
+max_inactive_interval: u64 +
+
+ The session key's max inactive time period, in seconds +
+
+ + +
+ + + +## Resource `SessionKeys` + + + +
struct SessionKeys has key
+
+ + + +
+Fields + + +
+
+keys: table::Table<vector<u8>, session_key::SessionKey> +
+
+ +
+
+ + +
+ + + +## Constants + + + + + + +
const ESessionIsExpired: u64 = 4;
+
+ + + + + + + +
const ESessionKeyAlreadyExists: u64 = 2;
+
+ + + + + + + +
const ESessionKeyCreatePermissionDenied: u64 = 1;
+
+ + + + + + + +
const ESessionKeyIsInvalid: u64 = 3;
+
+ + + + + +## Function `is_expired` + + + +
public fun is_expired(_ctx: &storage_context::StorageContext, _session_key: &session_key::SessionKey): bool
+
+ + + +
+Implementation + + +
public fun is_expired(_ctx: &StorageContext, _session_key: &SessionKey) : bool {
+    //TODO check the session key is expired or not after the timestamp is supported
+    return false
+}
+
+ + + +
+ + + +## Function `exists_session_key` + + + +
public fun exists_session_key(ctx: &storage_context::StorageContext, account_address: address, authentication_key: vector<u8>): bool
+
+ + + +
+Implementation + + +
public fun exists_session_key(ctx: &StorageContext, account_address: address, authentication_key: vector<u8>) : bool {
+    option::is_some(&get_session_key(ctx, account_address, authentication_key))
+}
+
+ + + +
+ + + +## Function `get_session_key` + +Get the session key of the account_address by the authentication key + + +
public fun get_session_key(ctx: &storage_context::StorageContext, account_address: address, authentication_key: vector<u8>): option::Option<session_key::SessionKey>
+
+ + + +
+Implementation + + +
public fun get_session_key(ctx: &StorageContext, account_address: address, authentication_key: vector<u8>) : Option<SessionKey> {
+    if (!account_storage::global_exists<SessionKeys>(ctx, account_address)){
+        return option::none()
+    };
+    let session_keys = account_storage::global_borrow<SessionKeys>(ctx, account_address);
+    if (!table::contains(&session_keys.keys, authentication_key)){
+        return option::none()
+    }else{
+        option::some(*table::borrow(&session_keys.keys, authentication_key))
+    }
+}
+
+ + + +
+ + + +## Function `create_session_key` + + + +
public fun create_session_key(ctx: &mut storage_context::StorageContext, sender: &signer, authentication_key: vector<u8>, scheme: u64, scopes: vector<session_key::SessionScope>, expiration_time: u64, max_inactive_interval: u64)
+
+ + + +
+Implementation + + +
public fun create_session_key(ctx: &mut StorageContext, sender: &signer, authentication_key: vector<u8>, scheme: u64, scopes: vector<SessionScope>, expiration_time: u64, max_inactive_interval: u64) {
+    //Can not create new session key by the other session key
+    assert!(!auth_validator::is_validate_via_session_key(ctx), error::permission_denied(ESessionKeyCreatePermissionDenied));
+    let sender_addr = signer::address_of(sender);
+    assert!(!exists_session_key(ctx, sender_addr, authentication_key), error::already_exists(ESessionKeyAlreadyExists));
+
+    let session_key = SessionKey {
+        authentication_key: authentication_key,
+        scheme: scheme,
+        scopes: scopes,
+        expiration_time: expiration_time,
+        //TODO set the last active time to now
+        last_active_time: 0,
+        max_inactive_interval: max_inactive_interval,
+    };
+    if (!account_storage::global_exists<SessionKeys>(ctx, sender_addr)){
+        let keys = table::new<vector<u8>, SessionKey>(storage_context::tx_context_mut(ctx));
+        account_storage::global_move_to<SessionKeys>(ctx, sender, SessionKeys{keys});
+    };
+
+    let session_keys = account_storage::global_borrow_mut<SessionKeys>(ctx, sender_addr);
+    table::add(&mut session_keys.keys, authentication_key, session_key);
+}
+
+ + + +
+ + + +## Function `create_session_key_entry` + + + +
public entry fun create_session_key_entry(ctx: &mut storage_context::StorageContext, sender: &signer, authentication_key: vector<u8>, scheme: u64, scope_module_address: address, scope_module_name: ascii::String, scope_function_name: ascii::String, expiration_time: u64, max_inactive_interval: u64)
+
+ + + +
+Implementation + + +
public entry fun create_session_key_entry(ctx: &mut StorageContext, sender: &signer, authentication_key: vector<u8>, scheme: u64, scope_module_address: address, scope_module_name: std::ascii::String, scope_function_name: std::ascii::String,expiration_time: u64, max_inactive_interval: u64) {
+    create_session_key(ctx, sender, authentication_key, scheme, vector::singleton(SessionScope{
+        module_address: scope_module_address,
+        module_name: scope_module_name,
+        function_name: scope_function_name,
+    }), expiration_time, max_inactive_interval);
+}
+
+ + + +
+ + + +## Function `validate` + +Validate the current tx via the session key +If the authentication key is not a session key, return option::none +If the session key is expired or invalid, abort the tx, otherwise return option::some(authentication key) + + +
public(friend) fun validate(ctx: &storage_context::StorageContext, scheme: u64, authenticator_payload: vector<u8>): option::Option<vector<u8>>
+
+ + + +
+Implementation + + +
public(friend) fun validate(ctx: &StorageContext, scheme: u64, authenticator_payload: vector<u8>) : Option<vector<u8>> {
+    let sender_addr = storage_context::sender(ctx);
+    if (!account_storage::global_exists<SessionKeys>(ctx, sender_addr)){
+        return option::none()
+    };
+    let auth_key = if(scheme == ed25519_validator::scheme()){
+        ed25519_validator::get_authentication_key_from_payload(&authenticator_payload)
+    }else{
+        //TODO support other built-in validators
+        return option::none()
+    };
+
+    let session_key_option = get_session_key(ctx, sender_addr, auth_key);
+    if (option::is_none(&session_key_option)){
+        return option::none()
+    };
+    let session_key = option::extract(&mut session_key_option);
+    assert!(!is_expired(ctx, &session_key), error::permission_denied(ESessionIsExpired));
+    assert!(session_key.scheme == scheme, error::invalid_argument(ESessionKeyIsInvalid));
+    //TODO validate session scopes
+
+    if(scheme == ed25519_validator::scheme()){
+        ed25519_validator::validate_signature(&authenticator_payload, &storage_context::tx_hash(ctx));
+    }else{
+        //TODO support other built-in validators
+        abort 1
+    };
+    option::some(auth_key)
+}
+
+ + + +
+ + + +## Function `active_session_key` + + + +
public(friend) fun active_session_key(ctx: &mut storage_context::StorageContext, authentication_key: vector<u8>)
+
+ + + +
+Implementation + + +
public(friend) fun active_session_key(ctx: &mut StorageContext, authentication_key: vector<u8>) {
+    let sender_addr = storage_context::sender(ctx);
+    assert!(account_storage::global_exists<SessionKeys>(ctx, sender_addr), error::not_found(ESessionKeyIsInvalid));
+    let session_keys = account_storage::global_borrow_mut<SessionKeys>(ctx, sender_addr);
+    assert!(table::contains(&session_keys.keys, authentication_key), error::not_found(ESessionKeyIsInvalid));
+    let session_key = table::borrow_mut(&mut session_keys.keys, authentication_key);
+    //TODO set the last active time to now when the timestamp is supported
+    session_key.last_active_time = session_key.last_active_time + 1;
+}
+
+ + + +
diff --git a/crates/rooch-framework/doc/transaction_validator.md b/crates/rooch-framework/doc/transaction_validator.md index d09f0e6e2b..3e6ed7d1c5 100644 --- a/crates/rooch-framework/doc/transaction_validator.md +++ b/crates/rooch-framework/doc/transaction_validator.md @@ -15,8 +15,10 @@ use 0x3::account; use 0x3::account_authentication; use 0x3::address_mapping; +use 0x3::auth_validator; use 0x3::auth_validator_registry; use 0x3::builtin_validators; +use 0x3::session_key;
@@ -129,7 +131,7 @@ This function is for Rooch to validate the transaction sender's authenticator. If the authenticator is invaid, abort this function. -
public fun validate(ctx: &storage_context::StorageContext, tx_sequence_number: u64, scheme: u64, _authenticator_payload: vector<u8>): &auth_validator_registry::AuthValidator
+
public fun validate(ctx: &storage_context::StorageContext, tx_sequence_number: u64, scheme: u64, authenticator_payload: vector<u8>): auth_validator::TxValidateResult
 
@@ -138,7 +140,12 @@ If the authenticator is invaid, abort this function. Implementation -
public fun validate(ctx: &StorageContext, tx_sequence_number: u64, scheme: u64, _authenticator_payload: vector<u8>): &AuthValidator {
+
public fun validate(
+    ctx: &StorageContext,
+    tx_sequence_number: u64,
+    scheme: u64,
+    authenticator_payload: vector<u8>
+): TxValidateResult {
     // === validate the sequence number ===
 
     assert!(
@@ -161,14 +168,24 @@ If the authenticator is invaid, abort this function.
 
     // === validate the authenticator ===
 
-    let sender = storage_context::sender(ctx);
-    let auth_validator = auth_validator_registry::borrow_validator(ctx, scheme);
-    let validator_id = auth_validator_registry::validator_id(auth_validator);
-    // builtin scheme do not need to install
-    if(!rooch_framework::builtin_validators::is_builtin(scheme)){
-        assert!(account_authentication::is_auth_validator_installed(ctx, sender, validator_id), error::invalid_state(EValidateNotInstalledAuthValidator));
-    };
-    auth_validator
+    // if the authenticator authenticator_payload is session key, validate the session key
+    // otherwise return the authentication validator via the scheme
+    let session_key_option = session_key::validate(ctx, scheme, authenticator_payload);
+    if (option::is_some(&session_key_option)) {
+        auth_validator::new_tx_validate_result(scheme, option::none(), session_key_option)
+    }else {
+        let sender = storage_context::sender(ctx);
+        let auth_validator = auth_validator_registry::borrow_validator(ctx, scheme);
+        let validator_id = auth_validator::validator_id(auth_validator);
+        // builtin scheme do not need to install
+        if (!rooch_framework::builtin_validators::is_builtin_scheme(scheme)) {
+            assert!(
+                account_authentication::is_auth_validator_installed(ctx, sender, validator_id),
+                error::invalid_state(EValidateNotInstalledAuthValidator)
+            );
+        };
+        auth_validator::new_tx_validate_result(scheme, option::some(*auth_validator), option::none())
+    }
 }
 
diff --git a/crates/rooch-framework/error_description.errmap b/crates/rooch-framework/error_description.errmap new file mode 100644 index 0000000000..963c7c4edf Binary files /dev/null and b/crates/rooch-framework/error_description.errmap differ diff --git a/crates/rooch-framework/sources/account.move b/crates/rooch-framework/sources/account.move index 4152757684..eb0c3921c3 100644 --- a/crates/rooch-framework/sources/account.move +++ b/crates/rooch-framework/sources/account.move @@ -6,6 +6,7 @@ module rooch_framework::account{ use moveos_std::bcs; use moveos_std::storage_context::{Self, StorageContext}; use moveos_std::account_storage; + use rooch_framework::account_authentication; friend rooch_framework::transaction_validator; @@ -54,10 +55,14 @@ module rooch_framework::account{ /// Address to create is not a valid reserved address for Rooch framework const ENoValidFrameworkReservedAddress: u64 = 11; - + //TODO should we provide create account from arbitrary address? /// A entry function to create an account under `new_address` public entry fun create_account_entry(ctx: &mut StorageContext, new_address: address){ - Self::create_account(ctx, new_address); + // If account already exists, do nothing + // Because if the new address is the same as the sender, the account must already created in the `transaction_validator::pre_execute` function + if(!exists_at(ctx, new_address)){ + create_account(ctx, new_address); + }; } /// Publishes a new `Account` resource under `new_address`. A signer representing `new_address` @@ -70,7 +75,10 @@ module rooch_framework::account{ ); // there cannot be an Account resource under new_addr already. - assert!(!exists(new_address), error::already_exists(EAccountAlreadyExists)); + assert!( + !account_storage::global_exists(ctx, new_address), + error::already_exists(EAccountAlreadyExists) + ); create_account_unchecked(ctx, new_address) } @@ -84,7 +92,7 @@ module rooch_framework::account{ Account { sequence_number: 0, }); - + account_authentication::init_authentication_keys(ctx, &new_account); new_account } @@ -308,15 +316,6 @@ module rooch_framework::account{ storage_context::drop_test_context(ctx); } - #[test(sender=@0x42)] - #[expected_failure(abort_code = 0x1, location = moveos_std::account_storage)] - fun test_failure_entry_account_redundant_creation(sender: address){ - let ctx = storage_context::new_test_context(sender); - create_account_entry(&mut ctx, sender); - create_account_entry(&mut ctx, sender); - storage_context::drop_test_context(ctx); - } - #[test(sender=@0x42, resource_account=@0xbb6e573f7feb9d8474ac20813fc086cc3100b8b7d49c246b0f4aee8ea19eaef4)] #[expected_failure(abort_code = 0x30006, location = Self)] fun test_failure_create_resource_account_wrong_sequence_number(sender: address, resource_account: address){ diff --git a/crates/rooch-framework/sources/account_authentication.move b/crates/rooch-framework/sources/account_authentication.move index a1a51be714..f1311ceb22 100644 --- a/crates/rooch-framework/sources/account_authentication.move +++ b/crates/rooch-framework/sources/account_authentication.move @@ -7,8 +7,12 @@ module rooch_framework::account_authentication{ use std::signer; use std::vector; use moveos_std::account_storage; - use moveos_std::storage_context::StorageContext; + use moveos_std::storage_context::{Self, StorageContext}; + use moveos_std::type_table::{Self, TypeTable}; use rooch_framework::auth_validator_registry; + use rooch_framework::auth_validator; + + friend rooch_framework::account; /// max authentication key length const MAX_AUTHENTICATION_KEY_LENGTH: u64 = 256; @@ -16,50 +20,83 @@ module rooch_framework::account_authentication{ const EAuthValidatorAlreadyInstalled: u64 = 1; /// The provided authentication key has an invalid length - const EMalformedAuthenticationKey: u64 = 2; + const EMalformedAuthenticationKey: u64 = 2; + /// The authentication key has not been found for the specified validator + const EAuthenticationKeyNotFound: u64 = 3; /// A resource that holds the authentication key for this account. /// ValidatorType is a phantom type parameter that is used to distinguish between different auth validator types. - struct AuthenticationKey has key { + struct AuthenticationKey has key, drop { authentication_key: vector } + /// A resource that holds the authentication keys for this account. + struct AuthenticationKeys has key{ + authentication_keys: TypeTable, + } + + //TODO should we use the AuthenticationKeys to indecate the auth validator is installed for the account? /// A resource tha holds the auth validator ids for this account has installed. struct InstalledAuthValidator has key { validators: vector, } + public(friend) fun init_authentication_keys(ctx: &mut StorageContext, account: &signer) { + let authentication_keys = AuthenticationKeys { + authentication_keys: type_table::new(storage_context::tx_context_mut(ctx)), + }; + account_storage::global_move_to(ctx, account, authentication_keys); + } + public fun get_authentication_key(ctx: &StorageContext, account_addr: address): Option> { - if(!account_storage::global_exists>(ctx, account_addr)){ + if(!account_storage::global_exists(ctx, account_addr)){ option::none>() }else{ - option::some(account_storage::global_borrow>(ctx, account_addr).authentication_key) + let authentication_keys = account_storage::global_borrow(ctx, account_addr); + if(type_table::contains>(&authentication_keys.authentication_keys)){ + option::some(type_table::borrow>(&authentication_keys.authentication_keys).authentication_key) + }else{ + option::none>() + } } } #[private_generics(ValidatorType)] /// This function is used to rotate a resource account's authentication key, only the module which define the `ValidatorType` can call this function. - public fun rotate_authentication_key(ctx: &mut StorageContext, account: &signer, new_auth_key: vector) { - rotate_authentication_key_internal(ctx, account, new_auth_key); - } - - public(friend) fun rotate_authentication_key_internal(ctx: &mut StorageContext, account: &signer, new_auth_key: vector) { - let account_addr = signer::address_of(account); + public fun rotate_authentication_key(ctx: &mut StorageContext, account_addr: address, new_auth_key: vector) { assert!( vector::length(&new_auth_key) <= MAX_AUTHENTICATION_KEY_LENGTH, error::invalid_argument(EMalformedAuthenticationKey) ); - - if(account_storage::global_exists>(ctx, account_addr)){ - let authentication_key = account_storage::global_borrow_mut>(ctx, account_addr); + //We need to ensure the AuthenticationKeys resource exists before we can rotate the authentication key. + let authentication_keys = account_storage::global_borrow_mut(ctx, account_addr); + if(type_table::contains>(&authentication_keys.authentication_keys)){ + let authentication_key = type_table::borrow_mut>(&mut authentication_keys.authentication_keys); authentication_key.authentication_key = new_auth_key; }else{ let authentication_key = AuthenticationKey { authentication_key: new_auth_key, }; - account_storage::global_move_to(ctx, account, authentication_key); - } + type_table::add(&mut authentication_keys.authentication_keys, authentication_key); + }; + } + + #[private_generics(ValidatorType)] + /// This function is used to remove a resource account's authentication key, only the module which define the `ValidatorType` can call this function. + public fun remove_authentication_key(ctx: &mut StorageContext, account_addr: address): AuthenticationKey { + assert!( + account_storage::global_exists(ctx, account_addr), + error::not_found(EAuthenticationKeyNotFound) + ); + let authentication_keys = account_storage::global_borrow_mut(ctx, account_addr); + assert!( + type_table::contains>(&authentication_keys.authentication_keys), + error::not_found(EAuthenticationKeyNotFound) + ); + + let removed_authentication_key = type_table::remove>(&mut authentication_keys.authentication_keys); + removed_authentication_key } /// Return the authentication validator is installed for the account at `account_addr`. @@ -72,9 +109,10 @@ module rooch_framework::account_authentication{ } } + //TODO should we init the AuthenticationKey when install auth validator? public fun install_auth_validator(ctx: &mut StorageContext, account_signer: &signer) { let validator = auth_validator_registry::borrow_validator_by_type(ctx); - let validator_id = auth_validator_registry::validator_id(validator); + let validator_id = auth_validator::validator_id(validator); let account_addr = signer::address_of(account_signer); assert!( @@ -101,12 +139,27 @@ module rooch_framework::account_authentication{ } #[test(sender=@0x42)] - fun test_rotate_authentication_key_internal(sender: address){ + fun test_rotate_authentication_key(sender: signer){ let ctx = moveos_std::storage_context::new_test_context(@std); - let sender_signer = rooch_framework::account::create_signer_for_test(sender); - rotate_authentication_key_internal(&mut ctx, &sender_signer, x"0123"); + init_authentication_keys(&mut ctx, &sender); + let sender_addr = signer::address_of(&sender); + let authentication_key_option = get_authentication_key(&ctx, sender_addr); + assert!(option::is_none(&authentication_key_option), 1000); + rotate_authentication_key(&mut ctx, sender_addr, x"0123"); + let authentication_key_option = get_authentication_key(&ctx, sender_addr); + assert!(option::is_some(&authentication_key_option), 1001); moveos_std::storage_context::drop_test_context(ctx); } - + #[test(sender=@0x42)] + fun test_remove_authentication_key(sender: signer){ + let ctx = moveos_std::storage_context::new_test_context(@std); + init_authentication_keys(&mut ctx, &sender); + let sender_addr = signer::address_of(&sender); + let authentication_key = x"1234"; + rotate_authentication_key(&mut ctx, sender_addr, authentication_key); + let removed_authentication_key = remove_authentication_key(&mut ctx, sender_addr); + assert!(removed_authentication_key.authentication_key == authentication_key, EMalformedAuthenticationKey); + moveos_std::storage_context::drop_test_context(ctx); + } } \ No newline at end of file diff --git a/crates/rooch-framework/sources/address_mapping.move b/crates/rooch-framework/sources/address_mapping.move index 0444bc9f4a..255001b042 100644 --- a/crates/rooch-framework/sources/address_mapping.move +++ b/crates/rooch-framework/sources/address_mapping.move @@ -48,13 +48,23 @@ module rooch_framework::address_mapping{ let addr = table::borrow(&am.mapping, maddress); option::some(*addr) }else{ - default_rooch_address(maddress) + option::none() + } + } + + /// Resolve a multi-chain address to a rooch address, if not exists, generate a new rooch address + public fun resolve_or_generate(ctx: &StorageContext, maddress: MultiChainAddress): address { + let addr = resolve(ctx, maddress); + if(option::is_none(&addr)){ + generate_rooch_address(maddress) + }else{ + option::extract(&mut addr) } } - fun default_rooch_address(maddress: MultiChainAddress): Option
{ + fun generate_rooch_address(maddress: MultiChainAddress): address { let hash = blake2b256(&maddress.raw_address); - option::some(moveos_std::bcs::to_address(hash)) + moveos_std::bcs::to_address(hash) } /// Check if a multi-chain address is bound to a rooch address diff --git a/crates/rooch-framework/sources/auth_validator/auth_validator.move b/crates/rooch-framework/sources/auth_validator/auth_validator.move index 6399491929..b9c8ed1beb 100644 --- a/crates/rooch-framework/sources/auth_validator/auth_validator.move +++ b/crates/rooch-framework/sources/auth_validator/auth_validator.move @@ -1,21 +1,119 @@ /// This module contains the error code for auth_validator module /// The auth_validator implementation should contain the following functions -/// public fun validate(ctx: &StorageContext, payload: vector) -module rooch_framework::auth_validator{ +/// public fun validate(ctx: &StorageContext, authenticator_payload: vector) +/// fun pre_execute(ctx: &mut StorageContext) +/// fun post_execute(ctx: &mut StorageContext) +module rooch_framework::auth_validator { use std::error; - + use std::option::{Self, Option}; + use moveos_std::storage_context::{Self, StorageContext}; + + friend rooch_framework::auth_validator_registry; + friend rooch_framework::transaction_validator; + + /// The function must be executed after the transaction is validated + const EMustExecuteAfterValidate: u64 = 1; + /// The AuthKey in transaction's authenticator do not match with the sender's account auth key const EValidateInvalidAccountAuthKey: u64 = 1001; /// InvalidAuthenticator, include invalid signature const EValidateInvalidAuthenticator: u64 = 1002; - public fun error_invalid_account_auth_key(): u64 { - error::invalid_argument(EValidateInvalidAccountAuthKey) + error::invalid_argument(EValidateInvalidAccountAuthKey) } public fun error_invalid_authenticator(): u64 { - error::invalid_argument(EValidateInvalidAuthenticator) + error::invalid_argument(EValidateInvalidAuthenticator) + } + + /// The Authentication Validator + struct AuthValidator has store, copy, drop { + id: u64, + module_address: address, + module_name: std::ascii::String, + } + + public(friend) fun new_auth_validator( + id: u64, + module_address: address, + module_name: std::ascii::String + ): AuthValidator { + AuthValidator { + id: id, + module_address: module_address, + module_name: module_name, + } + } + + public fun validator_id(validator: &AuthValidator): u64 { + validator.id + } + + public fun validator_module_address(validator: &AuthValidator): address { + validator.module_address + } + + public fun validator_module_name(validator: &AuthValidator): std::ascii::String { + validator.module_name } + /// The Transaction Validate Result + /// this result will be stored in the TxContext + struct TxValidateResult has copy, store, drop { + /// The auth validator's scheme that validate the transaction + scheme: u64, + auth_validator: Option, + session_key: Option>, + } + + public(friend) fun new_tx_validate_result( + scheme: u64, + auth_validator: Option, + session_key: Option> + ): TxValidateResult { + TxValidateResult { + scheme: scheme, + auth_validator: auth_validator, + session_key: session_key, + } + } + + /// Get the TxValidateResult from the TxContext, Only can be called after the transaction is validated + public fun get_validate_result_from_tx_ctx(ctx: &StorageContext): TxValidateResult { + let validate_result_opt = storage_context::get(ctx); + assert!(option::is_some(&validate_result_opt), error::invalid_state(EMustExecuteAfterValidate)); + option::extract(&mut validate_result_opt) + } + + /// Get the auth validator's scheme from the TxValidateResult in the TxContext + public fun get_validator_scheme_from_tx_ctx(ctx: &StorageContext): u64 { + let validate_result = get_validate_result_from_tx_ctx(ctx); + validate_result.scheme + } + + /// Get the session key from the TxValidateResult in the TxContext + /// If the TxValidateResult is None or SessionKey is None, return None + public fun get_session_key_from_tx_ctx_option(ctx: &StorageContext): Option> { + let validate_result_opt = storage_context::get(ctx); + if (option::is_some(&validate_result_opt)) { + let validate_result = option::extract(&mut validate_result_opt); + validate_result.session_key + }else { + option::none>() + } + } + + /// The current tx is validate via the session key or not + public fun is_validate_via_session_key(ctx: &StorageContext): bool { + option::is_some(&get_session_key_from_tx_ctx_option(ctx)) + } + + /// Get the session key from the TxValidateResult in the TxContext + /// Only can be called after the transaction is validated + public fun get_session_key_from_tx_ctx(ctx: &StorageContext): vector { + let sesion_key_option = get_session_key_from_tx_ctx_option(ctx); + assert!(option::is_some(&sesion_key_option), error::invalid_state(EMustExecuteAfterValidate)); + option::extract(&mut sesion_key_option) + } } \ No newline at end of file diff --git a/crates/rooch-framework/sources/auth_validator/auth_validator_registry.move b/crates/rooch-framework/sources/auth_validator/auth_validator_registry.move index d6d0908aff..458c373ad6 100644 --- a/crates/rooch-framework/sources/auth_validator/auth_validator_registry.move +++ b/crates/rooch-framework/sources/auth_validator/auth_validator_registry.move @@ -6,6 +6,7 @@ module rooch_framework::auth_validator_registry{ use moveos_std::type_table::{Self, TypeTable}; use moveos_std::account_storage; use moveos_std::storage_context::{Self, StorageContext}; + use rooch_framework::auth_validator::{Self, AuthValidator}; friend rooch_framework::genesis; friend rooch_framework::builtin_validators; @@ -13,11 +14,7 @@ module rooch_framework::auth_validator_registry{ const EValidatorUnregistered: u64 = 1; const EValidatorAlreadyRegistered: u64 = 2; - struct AuthValidator has store { - id: u64, - module_address: address, - module_name: std::ascii::String, - } + struct AuthValidatorWithType has key { id: u64, @@ -61,11 +58,11 @@ module rooch_framework::auth_validator_registry{ }; type_table::add(&mut registry.validators_with_type, validator_with_type); - let validator = AuthValidator { + let validator = auth_validator::new_auth_validator( id, - module_address: module_address, - module_name: module_name, - }; + module_address, + module_name, + ); table::add(&mut registry.validators, id, validator); registry.validator_num = registry.validator_num + 1; @@ -85,17 +82,6 @@ module rooch_framework::auth_validator_registry{ table::borrow(®istry.validators, validator_with_type.id) } - public fun validator_id(validator: &AuthValidator): u64 { - validator.id - } - - public fun validator_module_address(validator: &AuthValidator): address { - validator.module_address - } - - public fun validator_module_name(validator: &AuthValidator): std::ascii::String { - validator.module_name - } #[test_only] struct TestAuthValidator has store{ @@ -106,8 +92,10 @@ module rooch_framework::auth_validator_registry{ genesis_init(&mut ctx, &sender); register(&mut ctx); let validator = borrow_validator_by_type(&ctx); - let validator2 = borrow_validator(&ctx, validator.id); - assert!(validator.id == validator2.id, 1000); + let validator_id = auth_validator::validator_id(validator); + let validator2 = borrow_validator(&ctx, validator_id); + let validator2_id = auth_validator::validator_id(validator2); + assert!(validator_id == validator2_id, 1000); storage_context::drop_test_context(ctx); } } \ No newline at end of file diff --git a/crates/rooch-framework/sources/auth_validator/builtin_validators.move b/crates/rooch-framework/sources/auth_validator/builtin_validators.move index ee00c41410..d12afe214b 100644 --- a/crates/rooch-framework/sources/auth_validator/builtin_validators.move +++ b/crates/rooch-framework/sources/auth_validator/builtin_validators.move @@ -4,7 +4,9 @@ module rooch_framework::builtin_validators{ use rooch_framework::auth_validator_registry; use rooch_framework::ed25519_validator; use rooch_framework::multi_ed25519_validator; - use rooch_framework::ecdsa_validator; + use rooch_framework::ecdsa_k1_validator; + use rooch_framework::ecdsa_k1_recoverable_validator; + use rooch_framework::schnorr_validator; friend rooch_framework::genesis; @@ -18,11 +20,17 @@ module rooch_framework::builtin_validators{ let id = auth_validator_registry::register_internal(ctx); assert!(id == multi_ed25519_validator::scheme(), std::error::internal(E_GENESIS_INIT)); //SCHEME_ECDSA: u64 = 2; - let id = auth_validator_registry::register_internal(ctx); - assert!(id == ecdsa_validator::scheme(), std::error::internal(E_GENESIS_INIT)); + let id = auth_validator_registry::register_internal(ctx); + assert!(id == ecdsa_k1_validator::scheme(), std::error::internal(E_GENESIS_INIT)); + //SCHEME_ECDSA_RECOVERABLE: u64 = 3; + let id = auth_validator_registry::register_internal(ctx); + assert!(id == ecdsa_k1_recoverable_validator::scheme(), std::error::internal(E_GENESIS_INIT)); + //SCHEME_SCHNORR: u64 = 4; + let id = auth_validator_registry::register_internal(ctx); + assert!(id == schnorr_validator::scheme(), std::error::internal(E_GENESIS_INIT)); } - public fun is_builtin(scheme: u64): bool { - scheme == ed25519_validator::scheme() || scheme == multi_ed25519_validator::scheme() || scheme == ecdsa_validator::scheme() + public fun is_builtin_scheme(scheme: u64): bool { + scheme == ed25519_validator::scheme() || scheme == multi_ed25519_validator::scheme() || scheme == ecdsa_k1_validator::scheme() || scheme == ecdsa_k1_recoverable_validator::scheme() || scheme == schnorr_validator::scheme() } } \ No newline at end of file diff --git a/crates/rooch-framework/sources/auth_validator/ecdsa_k1_recoverable_validator.move b/crates/rooch-framework/sources/auth_validator/ecdsa_k1_recoverable_validator.move new file mode 100644 index 0000000000..e4b48bfaa3 --- /dev/null +++ b/crates/rooch-framework/sources/auth_validator/ecdsa_k1_recoverable_validator.move @@ -0,0 +1,146 @@ +/// This module implements the ECDSA Recoverable over Secpk256k1 validator scheme. +module rooch_framework::ecdsa_k1_recoverable_validator { + + use std::error; + use std::vector; + use std::option; + use std::signer; + use moveos_std::storage_context::{Self, StorageContext}; + use rooch_framework::account_authentication; + use rooch_framework::hash; + use rooch_framework::ecdsa_k1_recoverable; + use rooch_framework::auth_validator; + + const SCHEME_ECDSA_RECOVERABLE: u64 = 3; + + const V_ECDSA_RECOVERABLE_SCHEME_LENGTH: u64 = 1; + const V_ECDSA_RECOVERABLE_PUBKEY_LENGTH: u64 = 33; + const V_ECDSA_RECOVERABLE_SIG_LENGTH: u64 = 65; + const V_ECDSA_RECOVERABLE_HASH_LENGTH: u64 = 1; + /// Hash function name that are valid for ecrecover and verify. + const KECCAK256: u8 = 0; + const SHA256: u8 = 1; + /// error code + const EMalformedAccount: u64 = 1001; + const EMalformedAuthenticationKey: u64 = 1002; + + struct EcdsaK1RecoverableValidator has store, drop {} + + public fun scheme(): u64 { + SCHEME_ECDSA_RECOVERABLE + } + + public entry fun rotate_authentication_key_entry( + ctx: &mut StorageContext, + account: &signer, + public_key: vector + ) { + // compare newly passed public key with ecdsa recoverable public key length to ensure it's compatible + assert!( + vector::length(&public_key) == V_ECDSA_RECOVERABLE_PUBKEY_LENGTH, + error::invalid_argument(EMalformedAuthenticationKey) + ); + + // ensure that the ecdsa recoverable public key to address isn't matched with the ed25519 account address + let account_addr = signer::address_of(account); + let ecdsa_recoverable_addr = ecdsa_k1_recoverable_public_key_to_address(public_key); + assert!( + account_addr != ecdsa_recoverable_addr, + error::invalid_argument(EMalformedAccount) + ); + + // serialize the address to an auth key and rotate it by calling rotate_authentication_key + let ecdsa_k1_recoverable_authentication_key = moveos_std::bcs::to_bytes(&ecdsa_recoverable_addr); + account_authentication::rotate_authentication_key( + ctx, + account_addr, + ecdsa_k1_recoverable_authentication_key + ); + } + + public entry fun remove_authentication_key_entry(ctx: &mut StorageContext, account: &signer) { + let account_addr = signer::address_of(account); + account_authentication::remove_authentication_key(ctx, account_addr); + } + + public fun ecdsa_k1_recoverable_public_key(authenticator_payload: &vector): vector { + let public_key = vector::empty(); + let i = V_ECDSA_RECOVERABLE_SCHEME_LENGTH + V_ECDSA_RECOVERABLE_SIG_LENGTH; + while (i < V_ECDSA_RECOVERABLE_SCHEME_LENGTH + V_ECDSA_RECOVERABLE_SIG_LENGTH + V_ECDSA_RECOVERABLE_PUBKEY_LENGTH) { + let value = vector::borrow(authenticator_payload, i); + vector::push_back(&mut public_key, *value); + i = i + 1; + }; + + public_key + } + + public fun ecdsa_k1_recoverable_signature(authenticator_payload: &vector): vector { + let sign = vector::empty(); + let i = V_ECDSA_RECOVERABLE_SCHEME_LENGTH; + while (i < V_ECDSA_RECOVERABLE_SIG_LENGTH + 1) { + let value = vector::borrow(authenticator_payload, i); + vector::push_back(&mut sign, *value); + i = i + 1; + }; + + sign + } + + /// Get the authentication key of the given authenticator. + public fun ecdsa_k1_recoverable_authentication_key(authenticator_payload: &vector): vector { + let public_key = ecdsa_k1_recoverable_public_key(authenticator_payload); + let addr = ecdsa_k1_recoverable_public_key_to_address(public_key); + moveos_std::bcs::to_bytes(&addr) + } + + public fun ecdsa_k1_recoverable_public_key_to_address(public_key: vector): address { + let bytes = vector::singleton((SCHEME_ECDSA_RECOVERABLE as u8)); + vector::append(&mut bytes, public_key); + moveos_std::bcs::to_address(hash::blake2b256(&bytes)) + } + + public fun get_authentication_key(ctx: &StorageContext, addr: address): vector { + let auth_key_option = account_authentication::get_authentication_key(ctx, addr); + if (option::is_some(&auth_key_option)) { + option::extract(&mut auth_key_option) + }else { + //if AuthenticationKey does not exist, return addr as authentication key + moveos_std::bcs::to_bytes(&addr) + } + } + + public fun validate(ctx: &StorageContext, authenticator_payload: vector) { + // TODO handle non-ed25519 auth key and address relationship + // let auth_key = ecdsa_k1_recoverable_authentication_key(&authenticator_payload); + // let auth_key_in_account = get_authentication_key(ctx, storage_context::sender(ctx)); + // assert!( + // auth_key_in_account == auth_key, + // auth_validator::error_invalid_account_auth_key() + // ); + assert!( + ecdsa_k1_recoverable::verify( + &ecdsa_k1_recoverable_signature(&authenticator_payload), + &storage_context::tx_hash(ctx), + SHA256, // KECCAK256:0, SHA256:1, TODO: The hash type may need to be passed through the authenticator + ), + auth_validator::error_invalid_authenticator() + ); + } + + fun pre_execute( + _ctx: &mut StorageContext, + ) {} + + fun post_execute( + _ctx: &mut StorageContext, + ) {} + + // this test ensures that the ecdsa_k1_recoverable_public_key_to_address function is compatible with the one in the rust code + #[test] + fun test_ecdsa_k1_recoverable_public_key_to_address() { + let public_key = x"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"; + let addr = ecdsa_k1_recoverable_public_key_to_address(public_key); + assert!(addr == @0x8c891976da9498ec1d3ff778a5d6c40c217d63cc8c48539c959f8b683eedf5a4, 1000); + } +} \ No newline at end of file diff --git a/crates/rooch-framework/sources/auth_validator/ecdsa_k1_validator.move b/crates/rooch-framework/sources/auth_validator/ecdsa_k1_validator.move new file mode 100644 index 0000000000..c8f74d0d58 --- /dev/null +++ b/crates/rooch-framework/sources/auth_validator/ecdsa_k1_validator.move @@ -0,0 +1,143 @@ +/// This module implements the ECDSA over Secpk256k1 validator scheme. +module rooch_framework::ecdsa_k1_validator { + + use std::error; + use std::vector; + use std::option; + use std::signer; + use moveos_std::storage_context::{Self, StorageContext}; + use rooch_framework::account_authentication; + use rooch_framework::hash; + use rooch_framework::ecdsa_k1; + use rooch_framework::auth_validator; + + const SCHEME_ECDSA: u64 = 2; + + const V_ECDSA_SCHEME_LENGTH: u64 = 1; + const V_ECDSA_PUBKEY_LENGTH: u64 = 33; + const V_ECDSA_SIG_LENGTH: u64 = 64; + const V_ECDSA_HASH_LENGTH: u64 = 1; + /// Hash function name that are valid for ecrecover and verify. + const KECCAK256: u8 = 0; + const SHA256: u8 = 1; + /// error code + const EMalformedAccount: u64 = 1001; + const EMalformedAuthenticationKey: u64 = 1002; + + struct EcdsaK1Validator has store, drop {} + + public fun scheme(): u64 { + SCHEME_ECDSA + } + + public entry fun rotate_authentication_key_entry( + ctx: &mut StorageContext, + account: &signer, + public_key: vector + ) { + // compare newly passed public key with ecdsa public key length to ensure it's compatible + assert!( + vector::length(&public_key) == V_ECDSA_PUBKEY_LENGTH, + error::invalid_argument(EMalformedAuthenticationKey) + ); + + // ensure that the ecdsa public key to address isn't matched with the ed25519 account address + let account_addr = signer::address_of(account); + let ecdsa_addr = ecdsa_k1_public_key_to_address(public_key); + assert!( + account_addr != ecdsa_addr, + error::invalid_argument(EMalformedAccount) + ); + + // serialize the address to an auth key and rotate it by calling rotate_authentication_key + let ecdsa_k1_authentication_key = moveos_std::bcs::to_bytes(&ecdsa_addr); + account_authentication::rotate_authentication_key(ctx, account_addr, ecdsa_k1_authentication_key); + } + + public entry fun remove_authentication_key_entry(ctx: &mut StorageContext, account: &signer) { + let account_addr = signer::address_of(account); + account_authentication::remove_authentication_key(ctx, account_addr); + } + + public fun ecdsa_k1_public_key(authenticator_payload: &vector): vector { + let public_key = vector::empty(); + let i = V_ECDSA_SCHEME_LENGTH + V_ECDSA_SIG_LENGTH; + while (i < V_ECDSA_SCHEME_LENGTH + V_ECDSA_SIG_LENGTH + V_ECDSA_PUBKEY_LENGTH) { + let value = vector::borrow(authenticator_payload, i); + vector::push_back(&mut public_key, *value); + i = i + 1; + }; + + public_key + } + + public fun ecdsa_k1_signature(authenticator_payload: &vector): vector { + let sign = vector::empty(); + let i = V_ECDSA_SCHEME_LENGTH; + while (i < V_ECDSA_SIG_LENGTH + 1) { + let value = vector::borrow(authenticator_payload, i); + vector::push_back(&mut sign, *value); + i = i + 1; + }; + + sign + } + + /// Get the authentication key of the given authenticator. + public fun ecdsa_k1_authentication_key(authenticator_payload: &vector): vector { + let public_key = ecdsa_k1_public_key(authenticator_payload); + let addr = ecdsa_k1_public_key_to_address(public_key); + moveos_std::bcs::to_bytes(&addr) + } + + public fun ecdsa_k1_public_key_to_address(public_key: vector): address { + let bytes = vector::singleton((SCHEME_ECDSA as u8)); + vector::append(&mut bytes, public_key); + moveos_std::bcs::to_address(hash::blake2b256(&bytes)) + } + + public fun get_authentication_key(ctx: &StorageContext, addr: address): vector { + let auth_key_option = account_authentication::get_authentication_key(ctx, addr); + if (option::is_some(&auth_key_option)) { + option::extract(&mut auth_key_option) + }else { + //if AuthenticationKey does not exist, return addr as authentication key + moveos_std::bcs::to_bytes(&addr) + } + } + + public fun validate(ctx: &StorageContext, authenticator_payload: vector) { + // TODO handle non-ed25519 auth key and address relationship + // let auth_key = ecdsa_k1_authentication_key(&authenticator_payload); + // let auth_key_in_account = get_authentication_key(ctx, storage_context::sender(ctx)); + // assert!( + // auth_key_in_account == auth_key, + // auth_validator::error_invalid_account_auth_key() + // ); + assert!( + ecdsa_k1::verify( + &ecdsa_k1_signature(&authenticator_payload), + &ecdsa_k1_public_key(&authenticator_payload), + &storage_context::tx_hash(ctx), + SHA256, // KECCAK256:0, SHA256:1, TODO: The hash type may need to be passed through the authenticator + ), + auth_validator::error_invalid_authenticator() + ); + } + + fun pre_execute( + _ctx: &mut StorageContext, + ) {} + + fun post_execute( + _ctx: &mut StorageContext, + ) {} + + // this test ensures that the ecdsa_k1_public_key_to_address function is compatible with the one in the rust code + #[test] + fun test_ecdsa_k1_public_key_to_address() { + let public_key = x"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"; + let addr = ecdsa_k1_public_key_to_address(public_key); + assert!(addr == @0x92718e81a52369b4bc3169161737318ddf022945391a69263e8d4289c79a0c67, 1000); + } +} \ No newline at end of file diff --git a/crates/rooch-framework/sources/auth_validator/ecdsa_validator.move b/crates/rooch-framework/sources/auth_validator/ecdsa_validator.move deleted file mode 100644 index 423c347ead..0000000000 --- a/crates/rooch-framework/sources/auth_validator/ecdsa_validator.move +++ /dev/null @@ -1,28 +0,0 @@ -/// This module implements the ecdsa validator scheme. -module rooch_framework::ecdsa_validator { - - use moveos_std::storage_context::{Self, StorageContext}; - use rooch_framework::ecdsa_k1; - use rooch_framework::auth_validator; - - const SCHEME_ECDSA: u64 = 2; - - struct EcdsaValidator has store{ - } - - public fun scheme(): u64 { - SCHEME_ECDSA - } - - public fun validate(ctx: &StorageContext, payload: vector){ - //FIXME check the address and public key relationship - assert!( - ecdsa_k1::verify( - &payload, - &storage_context::tx_hash(ctx), - 0 // KECCAK256:0, SHA256:1, TODO: The hash type may need to be passed through the authenticator - ), - auth_validator::error_invalid_authenticator()); - } - -} \ No newline at end of file diff --git a/crates/rooch-framework/sources/auth_validator/ed25519_validator.move b/crates/rooch-framework/sources/auth_validator/ed25519_validator.move index d863bdc16e..c2d57b5940 100644 --- a/crates/rooch-framework/sources/auth_validator/ed25519_validator.move +++ b/crates/rooch-framework/sources/auth_validator/ed25519_validator.move @@ -1,95 +1,158 @@ /// This module implements the ed25519 validator scheme. module rooch_framework::ed25519_validator { - use std::vector; - use std::option; - use moveos_std::storage_context::{Self, StorageContext}; - use rooch_framework::hash; - use rooch_framework::account_authentication; - use rooch_framework::ed25519; - use rooch_framework::auth_validator; - - const SCHEME_ED25519: u64 = 0; - - const ED25519_SCHEME_LENGTH: u64 = 1; - const ED25519_PUBKEY_LENGTH: u64 = 32; - const ED25519_SIG_LENGTH: u64 = 64; - - - struct Ed25519Validator has store{ - } - - public fun scheme(): u64 { - SCHEME_ED25519 - } - - public fun ed25519_public_key(payload: &vector): vector { - let public_key = vector::empty(); - let i = ED25519_SCHEME_LENGTH + ED25519_SIG_LENGTH; - while (i < ED25519_SCHEME_LENGTH + ED25519_SIG_LENGTH + ED25519_PUBKEY_LENGTH) { - let value = vector::borrow(payload, i); - vector::push_back(&mut public_key, *value); - i = i + 1; - }; - - public_key - } - - public fun ed25519_signature(payload: &vector): vector { - let sign = vector::empty(); - let i = ED25519_SCHEME_LENGTH; - while (i < ED25519_SIG_LENGTH + 1) { - let value = vector::borrow(payload, i); - vector::push_back(&mut sign, *value); - i = i + 1; - }; - - sign - } - - /// Get the authentication key of the given authenticator. - public fun ed25519_authentication_key(payload: &vector): vector { - let public_key = ed25519_public_key(payload); - let addr = ed25519_public_key_to_address(public_key); - moveos_std::bcs::to_bytes(&addr) - } - - public fun ed25519_public_key_to_address(public_key: vector): address { - let bytes = vector::singleton((SCHEME_ED25519 as u8)); - vector::append(&mut bytes, public_key); - moveos_std::bcs::to_address(hash::blake2b256(&bytes)) - } - - public fun get_authentication_key(ctx: &StorageContext, addr: address): vector { - - let auth_key_option = account_authentication::get_authentication_key(ctx, addr); - if(option::is_some(&auth_key_option)){ - option::extract(&mut auth_key_option) - }else{ - //if AuthenticationKey does not exist, return addr as authentication key + use std::error; + use std::vector; + use std::option; + use std::signer; + use moveos_std::storage_context::{Self, StorageContext}; + use rooch_framework::hash; + use rooch_framework::account_authentication; + use rooch_framework::ed25519; + use rooch_framework::auth_validator; + + const SCHEME_ED25519: u64 = 0; + + const V_ED25519_SCHEME_LENGTH: u64 = 1; + const V_ED25519_PUBKEY_LENGTH: u64 = 32; + const V_ED25519_SIG_LENGTH: u64 = 64; + /// error code + const EMalformedAccount: u64 = 1001; + const EMalformedAuthenticationKey: u64 = 1002; + + struct Ed25519Validator has store, drop {} + + public fun scheme(): u64 { + SCHEME_ED25519 + } + + public entry fun rotate_authentication_key_entry( + ctx: &mut StorageContext, + account: &signer, + public_key: vector + ) { + // compare newly passed public key with ed25519 public key length to ensure it's compatible + assert!( + vector::length(&public_key) == V_ED25519_PUBKEY_LENGTH, + error::invalid_argument(EMalformedAuthenticationKey) + ); + + // User can rotate the authentication key arbitrarily, so we do not need to check the new public key with the account address. + let authentication_key = public_key_to_authentication_key(public_key); + let account_addr = signer::address_of(account); + rotate_authentication_key(ctx, account_addr, authentication_key); + } + + fun rotate_authentication_key(ctx: &mut StorageContext, account_addr: address, authentication_key: vector) { + account_authentication::rotate_authentication_key(ctx, account_addr, authentication_key); + } + + public entry fun remove_authentication_key_entry(ctx: &mut StorageContext, account: &signer) { + account_authentication::remove_authentication_key(ctx, signer::address_of(account)); + } + + public fun get_public_key_from_payload(authenticator_payload: &vector): vector { + let public_key = vector::empty(); + let i = V_ED25519_SCHEME_LENGTH + V_ED25519_SIG_LENGTH; + while (i < V_ED25519_SCHEME_LENGTH + V_ED25519_SIG_LENGTH + V_ED25519_PUBKEY_LENGTH) { + let value = vector::borrow(authenticator_payload, i); + vector::push_back(&mut public_key, *value); + i = i + 1; + }; + + public_key + } + + public fun get_signature_from_payload(authenticator_payload: &vector): vector { + let sign = vector::empty(); + let i = V_ED25519_SCHEME_LENGTH; + while (i < V_ED25519_SIG_LENGTH + 1) { + let value = vector::borrow(authenticator_payload, i); + vector::push_back(&mut sign, *value); + i = i + 1; + }; + + sign + } + + /// Get the authentication key of the given authenticator authenticator_payload. + public fun get_authentication_key_from_payload(authenticator_payload: &vector): vector { + let public_key = get_public_key_from_payload(authenticator_payload); + let addr = public_key_to_address(public_key); moveos_std::bcs::to_bytes(&addr) - } - } + } - public fun validate(ctx: &StorageContext, payload: vector){ - let auth_key = ed25519_authentication_key(&payload); - let auth_key_in_account = get_authentication_key(ctx, storage_context::sender(ctx)); + public fun public_key_to_address(public_key: vector): address { + moveos_std::bcs::to_address(public_key_to_authentication_key(public_key)) + } + + /// Get the authentication key of the given public key. + public fun public_key_to_authentication_key(public_key: vector): vector { + let bytes = vector::singleton((SCHEME_ED25519 as u8)); + vector::append(&mut bytes, public_key); + hash::blake2b256(&bytes) + } + + /// Get the authentication key of the given account, if it not exist, return the account address as authentication key. + public fun get_authentication_key_with_default(ctx: &StorageContext, addr: address): vector { + let auth_key_option = account_authentication::get_authentication_key(ctx, addr); + if (option::is_some(&auth_key_option)) { + option::extract(&mut auth_key_option) + }else { + default_authentication_key(addr) + } + } + + public fun default_authentication_key(addr: address): vector { + moveos_std::bcs::to_bytes(&addr) + } + + /// Only validate the authenticator's signature. + public fun validate_signature(authenticator_payload: &vector, tx_hash: &vector) { + assert!( + ed25519::verify( + &get_signature_from_payload(authenticator_payload), + &get_public_key_from_payload(authenticator_payload), + tx_hash + ), + auth_validator::error_invalid_authenticator() + ); + } + + public fun validate(ctx: &StorageContext, authenticator_payload: vector) { + let tx_hash = storage_context::tx_hash(ctx); + validate_signature(&authenticator_payload, &tx_hash); + + let auth_key = get_authentication_key_from_payload(&authenticator_payload); + let auth_key_in_account = get_authentication_key_with_default(ctx, storage_context::sender(ctx)); assert!( auth_key_in_account == auth_key, auth_validator::error_invalid_account_auth_key() ); - assert!( - ed25519::verify(&ed25519_signature(&payload), - &ed25519_public_key(&payload), - &storage_context::tx_hash(ctx)), - auth_validator::error_invalid_account_auth_key()); - } - - // this test ensures that the ed25519_public_key_to_address function is compatible with the one in the rust code - #[test] - fun test_ed25519_public_key_to_address(){ - let public_key = x"3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29"; - let addr = ed25519_public_key_to_address(public_key); - assert!(addr == @0x7a1378aafadef8ce743b72e8b248295c8f61c102c94040161146ea4d51a182b6, 1000) - } + } + + + fun pre_execute( + _ctx: &mut StorageContext, + ) {} + + fun post_execute( + ctx: &mut StorageContext, + ) { + let account_addr = storage_context::sender(ctx); + let auth_key_option = account_authentication::get_authentication_key(ctx, account_addr); + // If the account does not have an authentication key, set the account address as the authentication key after the first transaction is executed. + if (option::is_none(&auth_key_option)) { + let authentication_key = default_authentication_key(account_addr); + rotate_authentication_key(ctx, account_addr, authentication_key); + } + } + + // this test ensures that the ed25519 public_key_to_address function is compatible with the one in the rust code + #[test] + fun test_public_key_to_address() { + let public_key = x"3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29"; + let addr = public_key_to_address(public_key); + assert!(addr == @0x7a1378aafadef8ce743b72e8b248295c8f61c102c94040161146ea4d51a182b6, 1000) + } } \ No newline at end of file diff --git a/crates/rooch-framework/sources/auth_validator/multi_ed25519_validator.move b/crates/rooch-framework/sources/auth_validator/multi_ed25519_validator.move index 47127e6e43..ba9e9a0a82 100644 --- a/crates/rooch-framework/sources/auth_validator/multi_ed25519_validator.move +++ b/crates/rooch-framework/sources/auth_validator/multi_ed25519_validator.move @@ -5,7 +5,7 @@ module rooch_framework::multi_ed25519_validator { const SCHEME_MULTIED25519: u64 = 1; - struct MultiEd25519Validator has store{ + struct MultiEd25519Validator has store, drop { } public fun scheme(): u64 { @@ -17,4 +17,13 @@ module rooch_framework::multi_ed25519_validator { abort std::error::not_implemented(1) } + fun pre_execute( + _ctx: &mut StorageContext, + ) { + } + + fun post_execute( + _ctx: &mut StorageContext, + ) { + } } \ No newline at end of file diff --git a/crates/rooch-framework/sources/auth_validator/schnorr_validator.move b/crates/rooch-framework/sources/auth_validator/schnorr_validator.move new file mode 100644 index 0000000000..98b2152546 --- /dev/null +++ b/crates/rooch-framework/sources/auth_validator/schnorr_validator.move @@ -0,0 +1,142 @@ +/// This module implements the schnorr validator scheme. +module rooch_framework::schnorr_validator { + + use std::error; + use std::vector; + use std::option; + use std::signer; + use moveos_std::storage_context::{Self, StorageContext}; + use rooch_framework::account_authentication; + use rooch_framework::hash; + use rooch_framework::schnorr; + use rooch_framework::auth_validator; + + const SCHEME_SCHNORR: u64 = 4; + const V_SCHNORR_SCHEME_LENGTH: u64 = 1; + const V_SCHNORR_PUBKEY_LENGTH: u64 = 32; + const V_SCHNORR_SIG_LENGTH: u64 = 64; + const V_SCHNORR_HASH_LENGTH: u64 = 1; + /// Hash function name that are valid for verify. + const KECCAK256: u8 = 0; + const SHA256: u8 = 1; + /// error code + const EMalformedAccount: u64 = 1001; + const EMalformedAuthenticationKey: u64 = 1002; + + struct SchnorrValidator has store, drop {} + + public fun scheme(): u64 { + SCHEME_SCHNORR + } + + public entry fun rotate_authentication_key_entry( + ctx: &mut StorageContext, + account: &signer, + public_key: vector + ) { + // compare newly passed public key with schnorr public key length to ensure it's compatible + assert!( + vector::length(&public_key) == V_SCHNORR_PUBKEY_LENGTH, + error::invalid_argument(EMalformedAuthenticationKey) + ); + + // ensure that the schnorr public key to address isn't matched with the ed25519 account address + let account_addr = signer::address_of(account); + let schnorr_addr = schnorr_public_key_to_address(public_key); + assert!( + account_addr != schnorr_addr, + error::invalid_argument(EMalformedAccount) + ); + + // serialize the address to an auth key and rotate it by calling rotate_authentication_key + let schnorr_authentication_key = moveos_std::bcs::to_bytes(&schnorr_addr); + account_authentication::rotate_authentication_key(ctx, account_addr, schnorr_authentication_key); + } + + public entry fun remove_authentication_key_entry(ctx: &mut StorageContext, account: &signer) { + let account_addr = signer::address_of(account); + account_authentication::remove_authentication_key(ctx, account_addr); + } + + public fun schnorr_public_key(authenticator_payload: &vector): vector { + let public_key = vector::empty(); + let i = V_SCHNORR_SCHEME_LENGTH + V_SCHNORR_SIG_LENGTH; + while (i < V_SCHNORR_SCHEME_LENGTH + V_SCHNORR_SIG_LENGTH + V_SCHNORR_PUBKEY_LENGTH) { + let value = vector::borrow(authenticator_payload, i); + vector::push_back(&mut public_key, *value); + i = i + 1; + }; + + public_key + } + + public fun schnorr_signature(authenticator_payload: &vector): vector { + let sign = vector::empty(); + let i = V_SCHNORR_SCHEME_LENGTH; + while (i < V_SCHNORR_SIG_LENGTH + 1) { + let value = vector::borrow(authenticator_payload, i); + vector::push_back(&mut sign, *value); + i = i + 1; + }; + + sign + } + + /// Get the authentication key of the given authenticator. + public fun schnorr_authentication_key(authenticator_payload: &vector): vector { + let public_key = schnorr_public_key(authenticator_payload); + let addr = schnorr_public_key_to_address(public_key); + moveos_std::bcs::to_bytes(&addr) + } + + public fun schnorr_public_key_to_address(public_key: vector): address { + let bytes = vector::singleton((SCHEME_SCHNORR as u8)); + vector::append(&mut bytes, public_key); + moveos_std::bcs::to_address(hash::blake2b256(&bytes)) + } + + public fun get_authentication_key(ctx: &StorageContext, addr: address): vector { + let auth_key_option = account_authentication::get_authentication_key(ctx, addr); + if (option::is_some(&auth_key_option)) { + option::extract(&mut auth_key_option) + }else { + //if AuthenticationKey does not exist, return addr as authentication key + moveos_std::bcs::to_bytes(&addr) + } + } + + public fun validate(ctx: &StorageContext, authenticator_payload: vector) { + // TODO handle non-ed25519 auth key and address relationship + // let auth_key = schnorr_authentication_key(&authenticator_payload); + // let auth_key_in_account = get_authentication_key(ctx, storage_context::sender(ctx)); + // assert!( + // auth_key_in_account == auth_key, + // auth_validator::error_invalid_account_auth_key() + // ); + assert!( + schnorr::verify( + &schnorr_signature(&authenticator_payload), + &schnorr_public_key(&authenticator_payload), + &storage_context::tx_hash(ctx), + SHA256, + ), + auth_validator::error_invalid_account_auth_key() + ); + } + + fun pre_execute( + _ctx: &mut StorageContext, + ) {} + + fun post_execute( + _ctx: &mut StorageContext, + ) {} + + // this test ensures that the schnorr_public_key_to_address function is compatible with the one in the rust code + #[test] + fun test_schnorr_public_key_to_address() { + let public_key = x"1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"; + let addr = schnorr_public_key_to_address(public_key); + assert!(addr == @0xa519b36bbecc294726bbfd962ab46ca4e09baacca7cd90d5d2da2331afb363e6, 1000); + } +} \ No newline at end of file diff --git a/crates/rooch-framework/sources/crypto/ecdsa_k1.move b/crates/rooch-framework/sources/crypto/ecdsa_k1.move index e0692e9eb5..aa0330fb0c 100644 --- a/crates/rooch-framework/sources/crypto/ecdsa_k1.move +++ b/crates/rooch-framework/sources/crypto/ecdsa_k1.move @@ -1,138 +1,53 @@ module rooch_framework::ecdsa_k1 { - /// Error if the public key cannot be recovered from the signature. - const EFailToRecoverPubKey: u64 = 0; - /// Error if the signature is invalid. - const EInvalidSignature: u64 = 1; + const EInvalidSignature: u64 = 0; + + /// Error if the public key is invalid. + const EInvalidPubKey: u64 = 1; /// Hash function name that are valid for ecrecover and verify. const KECCAK256: u8 = 0; const SHA256: u8 = 1; - /// @param signature: A 65-bytes signature in form (r, s, v) that is signed using - /// The accepted v values are {0, 1, 2, 3}. - /// @param msg: The message that the signature is signed against, this is raw message without hashing. - /// @param hash: The hash function used to hash the message when signing. - /// - /// If the signature is valid, return the corresponding recovered Secpk256k1 public - /// key, otherwise throw error. This is similar to ecrecover in Ethereum, can only be - /// applied to Ecdsa signatures. - public native fun ecrecover(signature: &vector, msg: &vector, hash: u8): vector; - - /// @param pubkey: A 33-bytes compressed public key, a prefix either 0x02 or 0x03 and a 256-bit integer. - /// - /// If the compressed public key is valid, return the 65-bytes uncompressed public key, - /// otherwise throw error. - public native fun decompress_pubkey(pubkey: &vector): vector; - - /// @param signature: A 65-bytes signature in form (r, s, v) that is signed using + /// @param signature: A 64-bytes signature in form (r, s) that is signed using /// Ecdsa. This is an non-recoverable signature without recovery id. + /// @param public_key: A 33-bytes public key that is used to sign messages. /// @param msg: The message that the signature is signed against. /// @param hash: The hash function used to hash the message when signing. /// /// If the signature is valid to the pubkey and hashed message, return true. Else false. public native fun verify( signature: &vector, + public_key: &vector, msg: &vector, hash: u8 ): bool; #[test] - fun test_ecrecover_pubkey() { - // test case generated against https://github.com/MystenLabs/fastcrypto/blob/f9e64dc028040f863a53a6a88072bda71abd9946/fastcrypto/src/tests/secp256k1_recoverable_tests.rs - let msg = b"Hello, world!"; - - // recover with keccak256 hash - let sig = x"7e4237ebfbc36613e166bfc5f6229360a9c1949242da97ca04867e4de57b2df30c8340bcb320328cf46d71bda51fcb519e3ce53b348eec62de852e350edbd88600"; - let pubkey_bytes = x"02337cca2171fdbfcfd657fa59881f46269f1e590b5ffab6023686c7ad2ecc2c1c"; - let pubkey = ecrecover(&sig, &msg, 0); - assert!(pubkey == pubkey_bytes, 0); - - // recover with sha256 hash - let sig = x"e5847245b38548547f613aaea3421ad47f5b95a222366fb9f9b8c57568feb19c7077fc31e7d83e00acc1347d08c3e1ad50a4eeb6ab044f25c861ddc7be5b8f9f01"; - let pubkey_bytes = x"02337cca2171fdbfcfd657fa59881f46269f1e590b5ffab6023686c7ad2ecc2c1c"; - let pubkey = ecrecover(&sig, &msg, 1); - assert!(pubkey == pubkey_bytes, 0); - } - - // TODO: test - - #[test] - #[expected_failure(abort_code = EFailToRecoverPubKey)] - fun test_ecrecover_pubkey_fail_to_recover() { - let msg = x"00"; - let sig = x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - ecrecover(&sig, &msg, 1); + fun test_verify_success() { + let msg = x"00010203"; + let pubkey = x"033e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a62"; + let sig = x"416a21d50b3c838328d4f03213f8ef0c3776389a972ba1ecd37b56243734eba208ea6aaa6fc076ad7accd71d355f693a6fe54fe69b3c168eace9803827bc9046"; + let result = verify(&sig, &pubkey, &msg, SHA256); + assert!(result, 0); } #[test] #[expected_failure(abort_code = EInvalidSignature)] - fun test_ecrecover_pubkey_invalid_sig() { - let msg = b"Hello, world!"; - // incorrect length sig - let sig = x"7e4237ebfbc36613e166bfc5f6229360a9c1949242da97ca04867e4de57b2df30c8340bcb320328cf46d71bda51fcb519e3ce53b348eec62de852e350edbd886"; - ecrecover(&sig, &msg, 1); - } - - #[test] - fun test_verify_fails_with_recoverable_sig() { - + fun test_verify_fails_invalid_sig() { + let msg = x"00010203"; + let pubkey = x"033e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a62"; + let sig = x""; + verify(&sig, &pubkey, &msg, SHA256); } #[test] - fun test_verify_success_with_nonrecoverable_sig() { - - } - - #[test] - fun test_ecdsa_invalid() { - - } - - #[test] - fun test_ecrecover_eth_address() { - - } - - // Helper Move function to recover signature directly to an ETH address. - fun ecrecover_eth_address(sig: vector, msg: vector): vector { - use std::vector; - use rooch_framework::hash; - - // Normalize the last byte of the signature to be 0 or 1. - let v = vector::borrow_mut(&mut sig, 64); - if (*v == 27) { - *v = 0; - } else if (*v == 28) { - *v = 1; - } else if (*v > 35) { - *v = (*v - 1) % 2; - }; - - let pubkey = ecrecover(&sig, &msg, 0); - - let uncompressed = decompress_pubkey(&pubkey); - - // Take the last 64 bytes of the uncompressed pubkey. - let uncompressed_64 = vector::empty(); - let i = 1; - while (i < 65) { - let value = vector::borrow(&uncompressed, i); - vector::push_back(&mut uncompressed_64, *value); - i = i + 1; - }; - - // Take the last 20 bytes of the hash of the 64-bytes uncompressed pubkey. - let hashed = hash::keccak256(&uncompressed_64); - let addr = vector::empty(); - let i = 12; - while (i < 32) { - let value = vector::borrow(&hashed, i); - vector::push_back(&mut addr, *value); - i = i + 1; - }; - - addr + #[expected_failure(abort_code = EInvalidPubKey)] + fun test_verify_fails_invalid_pubkey() { + let msg = x"00010203"; + let pubkey = x""; + let sig = x"416a21d50b3c838328d4f03213f8ef0c3776389a972ba1ecd37b56243734eba208ea6aaa6fc076ad7accd71d355f693a6fe54fe69b3c168eace9803827bc9046"; + verify(&sig, &pubkey, &msg, SHA256); } } diff --git a/crates/rooch-framework/sources/crypto/ecdsa_k1_recoverable.move b/crates/rooch-framework/sources/crypto/ecdsa_k1_recoverable.move new file mode 100644 index 0000000000..bdf2d88095 --- /dev/null +++ b/crates/rooch-framework/sources/crypto/ecdsa_k1_recoverable.move @@ -0,0 +1,160 @@ +module rooch_framework::ecdsa_k1_recoverable { + + /// Error if the public key cannot be recovered from the signature. + const EFailToRecoverPubKey: u64 = 0; + + /// Error if the signature is invalid. + const EInvalidSignature: u64 = 1; + + /// Error if the public key is invalid. + const EInvalidPubKey: u64 = 2; + + /// Hash function name that are valid for ecrecover and verify. + const KECCAK256: u8 = 0; + const SHA256: u8 = 1; + + /// @param signature: A 65-bytes signature in form (r, s, v) that is signed using + /// The accepted v values are {0, 1, 2, 3}. + /// @param msg: The message that the signature is signed against, this is raw message without hashing. + /// @param hash: The hash function used to hash the message when signing. + /// + /// If the signature is valid, return the corresponding recovered Secpk256k1 public + /// key, otherwise throw error. This is similar to ecrecover in Ethereum, can only be + /// applied to Ecdsa signatures. + public native fun ecrecover(signature: &vector, msg: &vector, hash: u8): vector; + + /// @param pubkey: A 33-bytes compressed public key, a prefix either 0x02 or 0x03 and a 256-bit integer. + /// + /// If the compressed public key is valid, return the 65-bytes uncompressed public key, + /// otherwise throw error. + public native fun decompress_pubkey(pubkey: &vector): vector; + + /// @param signature: A 65-bytes signature in form (r, s, v) that is signed using + /// Ecdsa. This is a recoverable signature with a recovery id. + /// @param msg: The message that the signature is signed against. + /// @param hash: The hash function used to hash the message when signing. + /// + /// If the signature is valid to the pubkey and hashed message, return true. Else false. + public native fun verify( + signature: &vector, + msg: &vector, + hash: u8 + ): bool; + + #[test] + fun test_ecrecover_pubkey() { + // test case generated against https://github.com/MystenLabs/fastcrypto/blob/f9e64dc028040f863a53a6a88072bda71abd9946/fastcrypto/src/tests/secp256k1_recoverable_tests.rs + let msg = b"Hello, world!"; + + // recover with keccak256 hash + let sig = x"7e4237ebfbc36613e166bfc5f6229360a9c1949242da97ca04867e4de57b2df30c8340bcb320328cf46d71bda51fcb519e3ce53b348eec62de852e350edbd88600"; + let pubkey_bytes = x"02337cca2171fdbfcfd657fa59881f46269f1e590b5ffab6023686c7ad2ecc2c1c"; + let pubkey = ecrecover(&sig, &msg, KECCAK256); + assert!(pubkey == pubkey_bytes, 0); + + // recover with sha256 hash + let sig = x"e5847245b38548547f613aaea3421ad47f5b95a222366fb9f9b8c57568feb19c7077fc31e7d83e00acc1347d08c3e1ad50a4eeb6ab044f25c861ddc7be5b8f9f01"; + let pubkey_bytes = x"02337cca2171fdbfcfd657fa59881f46269f1e590b5ffab6023686c7ad2ecc2c1c"; + let pubkey = ecrecover(&sig, &msg, SHA256); + assert!(pubkey == pubkey_bytes, 0); + } + + #[test] + #[expected_failure(abort_code = EFailToRecoverPubKey)] + fun test_ecrecover_pubkey_fail_to_recover() { + let msg = x"00"; + let sig = x"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + ecrecover(&sig, &msg, SHA256); + } + + #[test] + #[expected_failure(abort_code = EInvalidSignature)] + fun test_ecrecover_pubkey_invalid_sig() { + let msg = b"Hello, world!"; + // incorrect length sig + let sig = x"7e4237ebfbc36613e166bfc5f6229360a9c1949242da97ca04867e4de57b2df30c8340bcb320328cf46d71bda51fcb519e3ce53b348eec62de852e350edbd886"; + ecrecover(&sig, &msg, SHA256); + } + + #[test] + #[expected_failure(abort_code = EInvalidSignature)] + fun test_verify_fails_invalid_sig() { + let msg = b"Hello, world!"; + let sig = x""; + verify(&sig, &msg, KECCAK256); + } + + #[test] + fun test_verify_success() { + let msg = b"Hello, world!"; + let sig = x"7e4237ebfbc36613e166bfc5f6229360a9c1949242da97ca04867e4de57b2df30c8340bcb320328cf46d71bda51fcb519e3ce53b348eec62de852e350edbd88600"; + let result = verify(&sig, &msg, KECCAK256); + assert!(result, 0) + } + + #[test] + fun test_decompress_pubkey() { + let pubkey = x"033e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a62"; + assert!(std::vector::length(&pubkey) == 33, 0); + let pubkey_decompressed = decompress_pubkey(&pubkey); + assert!(std::vector::length(&pubkey_decompressed) == 65, 0); + } + + #[test] + #[expected_failure(abort_code = EInvalidPubKey)] + fun test_decompress_pubkey_invalid_pubkey() { + let pubkey = x"013e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a62"; + decompress_pubkey(&pubkey); + } + + #[test] + fun test_ecrecover_eth_address() { + // recover with keccak256 hash from ecrecover_eth_address function + let msg = b"Hello, world!"; + let sig = x"e5847245b38548547f613aaea3421ad47f5b95a222366fb9f9b8c57568feb19c7077fc31e7d83e00acc1347d08c3e1ad50a4eeb6ab044f25c861ddc7be5b8f9f01"; + let eth_address = x"4259abf3f34ab0e5a399494cb1e9a7f8465ae4d6"; + let addr = ecrecover_eth_address(sig, msg); + assert!(addr == eth_address, 0); + } + + // Helper Move function to recover signature directly to an ETH address. + fun ecrecover_eth_address(sig: vector, msg: vector): vector { + use std::vector; + use rooch_framework::hash; + + // Normalize the last byte of the signature to be 0 or 1. + let v = vector::borrow_mut(&mut sig, 64); + if (*v == 27) { + *v = 0; + } else if (*v == 28) { + *v = 1; + } else if (*v > 35) { + *v = (*v - 1) % 2; + }; + + let pubkey = ecrecover(&sig, &msg, 0); + + let uncompressed = decompress_pubkey(&pubkey); + + // Take the last 64 bytes of the uncompressed pubkey. + let uncompressed_64 = vector::empty(); + let i = 1; + while (i < 65) { + let value = vector::borrow(&uncompressed, i); + vector::push_back(&mut uncompressed_64, *value); + i = i + 1; + }; + + // Take the last 20 bytes of the hash of the 64-bytes uncompressed pubkey. + let hashed = hash::keccak256(&uncompressed_64); + let addr = vector::empty(); + let i = 12; + while (i < 32) { + let value = vector::borrow(&hashed, i); + vector::push_back(&mut addr, *value); + i = i + 1; + }; + + addr + } +} diff --git a/crates/rooch-framework/sources/crypto/hash.move b/crates/rooch-framework/sources/crypto/hash.move index f8a8f037d4..b90e940800 100644 --- a/crates/rooch-framework/sources/crypto/hash.move +++ b/crates/rooch-framework/sources/crypto/hash.move @@ -18,6 +18,10 @@ module rooch_framework::hash { /// Hash the input bytes using keccak256 and returns 32 bytes. native public fun keccak256(data: &vector): vector; + /// @param data: Arbitrary binary data to hash + /// Hash the input bytes using ripemd160 and returns 20 bytes. + native public fun ripemd160(data: &vector): vector; + #[test] fun test_keccak256_hash() { let msg = b"hello world!"; @@ -43,4 +47,17 @@ module rooch_framework::hash { let long_msg = b"57caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd6"; let _ = blake2b256(&long_msg); } + + #[test] + fun test_ripemd160_hash() { + let msg = b"Hello, World!"; + let hashed_msg_bytes = x"527a6a4b9a6da75607546842e0e00105350b1aaf"; + let hashed_msg = ripemd160(&msg); + assert!(hashed_msg == hashed_msg_bytes, 0); + + let empty_msg = b""; + let _ = ripemd160(&empty_msg); + let long_msg = b"57caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd657caa176af1ac0433c5df30e8dabcd2ec1af1e92a26eced5f719b88458777cd6"; + let _ = ripemd160(&long_msg); + } } diff --git a/crates/rooch-framework/sources/crypto/schnorr.move b/crates/rooch-framework/sources/crypto/schnorr.move new file mode 100644 index 0000000000..7158546c8e --- /dev/null +++ b/crates/rooch-framework/sources/crypto/schnorr.move @@ -0,0 +1,72 @@ +module rooch_framework::schnorr { + + /// Error if the signature is invalid. + const EInvalidSignature: u64 = 0; + + /// Error if the public key is invalid. + const EInvalidPubKey: u64 = 1; + + /// Hash function name that are valid for verify. + const KECCAK256: u8 = 0; + const SHA256: u8 = 1; + + /// @param signature: A 64-bytes signature that is signed using Schnorr over Secpk256k1 key pairs. + /// @param public_key: A 32-bytes public key that is used to sign messages. + /// @param msg: The message that the signature is signed against. + /// @param hash: The hash function used to hash the message when signing. + /// + /// If the signature is valid to the pubkey and hashed message, return true. Else false. + public native fun verify( + signature: &vector, + public_key: &vector, + msg: &vector, + hash: u8 + ): bool; + + #[test] + public fun test_schnorr_invalid_sig() { + let msg = x"00010203"; + let pk = x"3e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a62"; + let invalid_sig = x"235c98d74dd7926570a757550d282b0a4bde6c53772c62348a04085201811f7f99240b073efa9822b224ee906b8d816977106a72ca01ed6835fd04c9b7112400"; + + let verify = verify(&invalid_sig, &pk, &msg, SHA256); + assert!(!verify, EInvalidSignature); + + let pk = x"3e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a62"; + let sig = x"bf4395f2f5a75dcfc82f7f4dd9ff032c450b5caed39bdd7b09df4cfa1b15ecd0c9f1d124916903b5291623bd06f2bc005ad8e92c74ec6d962f2d41f3ea2600e8"; + let other_msg = x"00010203"; + + let verify = verify(&sig, &pk, &other_msg, KECCAK256); + assert!(!verify, EInvalidSignature); + } + + #[test] + public fun test_schnorr_valid_sig() { + let msg = x"00010203"; + let pk = x"3e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a62"; + let sig = x"bf4395f2f5a75dcfc82f7f4dd9ff032c450b5caed39bdd7b09df4cfa1b15ecd0c9f1d124916903b5291623bd06f2bc005ad8e92c74ec6d962f2d41f3ea2600e7"; + + let verify = verify(&sig, &pk, &msg, SHA256); + assert!(verify, EInvalidSignature); + } + + #[test] + public fun test_schnorr_invalid_pubkey() { + let msg = x"00010203"; + let pk = x"3e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a63"; + let sig = x"bf4395f2f5a75dcfc82f7f4dd9ff032c450b5caed39bdd7b09df4cfa1b15ecd0c9f1d124916903b5291623bd06f2bc005ad8e92c74ec6d962f2d41f3ea2600e7"; + + let verify = verify(&sig, &pk, &msg, SHA256); + assert!(!verify, EInvalidPubKey) + } + + #[test] + public fun test_schnorr_valid_pubkey() { + let msg = x"00010203"; + let pk = x"3e99a541db69bd32040dfe5037fbf5210dafa8151a71e21c5204b05d95ce0a62"; + let sig = x"1312289adb61ab33b1132f5ecd1e4d5f791f4618f5e17de2284b286c534a4fb8a7ee8a141f9a98eab92488796007e53cb71a3a3a4d738cf2a818acb48178153a"; + + let verify = verify(&sig, &pk, &msg, KECCAK256); + assert!(verify, EInvalidPubKey) + } +} \ No newline at end of file diff --git a/crates/rooch-framework/sources/session_key.move b/crates/rooch-framework/sources/session_key.move new file mode 100644 index 0000000000..267943054c --- /dev/null +++ b/crates/rooch-framework/sources/session_key.move @@ -0,0 +1,144 @@ +module rooch_framework::session_key { + use std::vector; + use std::option::{Self, Option}; + use std::error; + use std::signer; + use moveos_std::storage_context::{Self, StorageContext}; + use moveos_std::account_storage; + use moveos_std::table::{Self, Table}; + use rooch_framework::auth_validator; + use rooch_framework::ed25519_validator; + // use rooch_framework::multi_ed25519_validator; + // use rooch_framework::ecdsa_validator; + // use rooch_framework::schnorr_validator; + + friend rooch_framework::transaction_validator; + + const ESessionKeyCreatePermissionDenied: u64 = 1; + const ESessionKeyAlreadyExists: u64 = 2; + const ESessionKeyIsInvalid: u64 = 3; + const ESessionIsExpired: u64 = 4; + + /// The session's scope + struct SessionScope has store,copy,drop { + //TODO should we allow the scope module address is `*`? + module_address: address, + /// The scope module name, `*` means all modules in the module address + module_name: std::ascii::String, + /// The scope function name, `*` means all functions in the module + function_name: std::ascii::String, + } + + struct SessionKey has store,copy,drop { + authentication_key: vector, + scheme: u64, + scopes: vector, + /// The session key's expiration time period, in seconds, 0 means never expired + expiration_time: u64, + /// The session key's last active time + last_active_time: u64, + /// The session key's max inactive time period, in seconds + max_inactive_interval: u64, + } + + struct SessionKeys has key { + keys: Table, SessionKey>, + } + + public fun is_expired(_ctx: &StorageContext, _session_key: &SessionKey) : bool { + //TODO check the session key is expired or not after the timestamp is supported + return false + } + + public fun exists_session_key(ctx: &StorageContext, account_address: address, authentication_key: vector) : bool { + option::is_some(&get_session_key(ctx, account_address, authentication_key)) + } + + /// Get the session key of the account_address by the authentication key + public fun get_session_key(ctx: &StorageContext, account_address: address, authentication_key: vector) : Option { + if (!account_storage::global_exists(ctx, account_address)){ + return option::none() + }; + let session_keys = account_storage::global_borrow(ctx, account_address); + if (!table::contains(&session_keys.keys, authentication_key)){ + return option::none() + }else{ + option::some(*table::borrow(&session_keys.keys, authentication_key)) + } + } + + public fun create_session_key(ctx: &mut StorageContext, sender: &signer, authentication_key: vector, scheme: u64, scopes: vector, expiration_time: u64, max_inactive_interval: u64) { + //Can not create new session key by the other session key + assert!(!auth_validator::is_validate_via_session_key(ctx), error::permission_denied(ESessionKeyCreatePermissionDenied)); + let sender_addr = signer::address_of(sender); + assert!(!exists_session_key(ctx, sender_addr, authentication_key), error::already_exists(ESessionKeyAlreadyExists)); + + let session_key = SessionKey { + authentication_key: authentication_key, + scheme: scheme, + scopes: scopes, + expiration_time: expiration_time, + //TODO set the last active time to now + last_active_time: 0, + max_inactive_interval: max_inactive_interval, + }; + if (!account_storage::global_exists(ctx, sender_addr)){ + let keys = table::new, SessionKey>(storage_context::tx_context_mut(ctx)); + account_storage::global_move_to(ctx, sender, SessionKeys{keys}); + }; + + let session_keys = account_storage::global_borrow_mut(ctx, sender_addr); + table::add(&mut session_keys.keys, authentication_key, session_key); + } + + public entry fun create_session_key_entry(ctx: &mut StorageContext, sender: &signer, authentication_key: vector, scheme: u64, scope_module_address: address, scope_module_name: std::ascii::String, scope_function_name: std::ascii::String,expiration_time: u64, max_inactive_interval: u64) { + create_session_key(ctx, sender, authentication_key, scheme, vector::singleton(SessionScope{ + module_address: scope_module_address, + module_name: scope_module_name, + function_name: scope_function_name, + }), expiration_time, max_inactive_interval); + } + + /// Validate the current tx via the session key + /// If the authentication key is not a session key, return option::none + /// If the session key is expired or invalid, abort the tx, otherwise return option::some(authentication key) + public(friend) fun validate(ctx: &StorageContext, scheme: u64, authenticator_payload: vector) : Option> { + let sender_addr = storage_context::sender(ctx); + if (!account_storage::global_exists(ctx, sender_addr)){ + return option::none() + }; + let auth_key = if(scheme == ed25519_validator::scheme()){ + ed25519_validator::get_authentication_key_from_payload(&authenticator_payload) + }else{ + //TODO support other built-in validators + return option::none() + }; + + let session_key_option = get_session_key(ctx, sender_addr, auth_key); + if (option::is_none(&session_key_option)){ + return option::none() + }; + let session_key = option::extract(&mut session_key_option); + assert!(!is_expired(ctx, &session_key), error::permission_denied(ESessionIsExpired)); + assert!(session_key.scheme == scheme, error::invalid_argument(ESessionKeyIsInvalid)); + //TODO validate session scopes + + if(scheme == ed25519_validator::scheme()){ + ed25519_validator::validate_signature(&authenticator_payload, &storage_context::tx_hash(ctx)); + }else{ + //TODO support other built-in validators + abort 1 + }; + option::some(auth_key) + } + + public(friend) fun active_session_key(ctx: &mut StorageContext, authentication_key: vector) { + let sender_addr = storage_context::sender(ctx); + assert!(account_storage::global_exists(ctx, sender_addr), error::not_found(ESessionKeyIsInvalid)); + let session_keys = account_storage::global_borrow_mut(ctx, sender_addr); + assert!(table::contains(&session_keys.keys, authentication_key), error::not_found(ESessionKeyIsInvalid)); + let session_key = table::borrow_mut(&mut session_keys.keys, authentication_key); + //TODO set the last active time to now when the timestamp is supported + session_key.last_active_time = session_key.last_active_time + 1; + } +} \ No newline at end of file diff --git a/crates/rooch-framework/sources/transaction_validator.move b/crates/rooch-framework/sources/transaction_validator.move index 69915f4071..e2dcb82c4f 100644 --- a/crates/rooch-framework/sources/transaction_validator.move +++ b/crates/rooch-framework/sources/transaction_validator.move @@ -1,11 +1,13 @@ module rooch_framework::transaction_validator { use std::error; use std::option; - use moveos_std::storage_context::{Self,StorageContext}; + use moveos_std::storage_context::{Self, StorageContext}; use rooch_framework::account; - use rooch_framework::address_mapping::{Self,MultiChainAddress}; + use rooch_framework::address_mapping::{Self, MultiChainAddress}; use rooch_framework::account_authentication; - use rooch_framework::auth_validator_registry::{Self, AuthValidator}; + use rooch_framework::auth_validator::{Self, TxValidateResult}; + use rooch_framework::auth_validator_registry; + use rooch_framework::session_key; const MAX_U64: u128 = 18446744073709551615; @@ -13,11 +15,10 @@ module rooch_framework::transaction_validator { /// Transaction exceeded its allocated max gas const EOUT_OF_GAS: u64 = 6; + //TODO Migrate the error code to the auth_validator module /// Validate errors. These are separated out from the other errors in this /// module since they are mapped separately to major VM statuses, and are /// important to the semantics of the system. - - const EValidateSequenceNuberTooOld: u64 = 1001; const EValidateSequenceNumberTooNew: u64 = 1002; const EValidateAccountDoesNotExist: u64 = 1003; @@ -29,14 +30,18 @@ module rooch_framework::transaction_validator { /// The authenticator's scheme is not installed to the sender's account const EValidateNotInstalledAuthValidator: u64 = 1010; - + #[view] /// This function is for Rooch to validate the transaction sender's authenticator. /// If the authenticator is invaid, abort this function. - //TODO should we need the authenticator_payload? - public fun validate(ctx: &StorageContext, tx_sequence_number: u64, scheme: u64, _authenticator_payload: vector): &AuthValidator { + public fun validate( + ctx: &StorageContext, + tx_sequence_number: u64, + scheme: u64, + authenticator_payload: vector + ): TxValidateResult { // === validate the sequence number === - + assert!( (tx_sequence_number as u128) < MAX_U64, error::out_of_range(EValidateSequenceNumberTooBig) @@ -57,14 +62,24 @@ module rooch_framework::transaction_validator { // === validate the authenticator === - let sender = storage_context::sender(ctx); - let auth_validator = auth_validator_registry::borrow_validator(ctx, scheme); - let validator_id = auth_validator_registry::validator_id(auth_validator); - // builtin scheme do not need to install - if(!rooch_framework::builtin_validators::is_builtin(scheme)){ - assert!(account_authentication::is_auth_validator_installed(ctx, sender, validator_id), error::invalid_state(EValidateNotInstalledAuthValidator)); - }; - auth_validator + // if the authenticator authenticator_payload is session key, validate the session key + // otherwise return the authentication validator via the scheme + let session_key_option = session_key::validate(ctx, scheme, authenticator_payload); + if (option::is_some(&session_key_option)) { + auth_validator::new_tx_validate_result(scheme, option::none(), session_key_option) + }else { + let sender = storage_context::sender(ctx); + let auth_validator = auth_validator_registry::borrow_validator(ctx, scheme); + let validator_id = auth_validator::validator_id(auth_validator); + // builtin scheme do not need to install + if (!rooch_framework::builtin_validators::is_builtin_scheme(scheme)) { + assert!( + account_authentication::is_auth_validator_installed(ctx, sender, validator_id), + error::invalid_state(EValidateNotInstalledAuthValidator) + ); + }; + auth_validator::new_tx_validate_result(scheme, option::some(*auth_validator), option::none()) + } } /// Transaction pre_execute function. @@ -72,19 +87,19 @@ module rooch_framework::transaction_validator { /// This function is for Rooch to auto create account and address maping. fun pre_execute( ctx: &mut StorageContext, - ) { + ) { let sender = storage_context::sender(ctx); //Auto create account if not exist if (!account::exists_at(ctx, sender)) { - account::create_account(ctx, sender); + account::create_account(ctx, sender); }; // the transaction validator will put the multi chain address into the context let multichain_address = storage_context::get(ctx); - if (option::is_some(&multichain_address)){ + if (option::is_some(&multichain_address)) { let multichain_address = option::extract(&mut multichain_address); //Auto create address mapping if not exist if (!address_mapping::exists_mapping(ctx, multichain_address)) { - address_mapping::bind_no_check(ctx, sender, multichain_address); + address_mapping::bind_no_check(ctx, sender, multichain_address); }; } } @@ -94,8 +109,17 @@ module rooch_framework::transaction_validator { /// This function is for Rooch to update the sender's sequence number and pay the gas fee. fun post_execute( ctx: &mut StorageContext, - ) { + ) { //TODO handle transaction gas fee + + // Active the session key + + let session_key_opt = auth_validator::get_session_key_from_tx_ctx_option(ctx); + if (option::is_some(&session_key_opt)) { + let session_key = option::extract(&mut session_key_opt); + session_key::active_session_key(ctx, session_key); + }; + // Increment sequence number account::increment_sequence_number(ctx); } diff --git a/crates/rooch-framework/src/bindings/address_mapping.rs b/crates/rooch-framework/src/bindings/address_mapping.rs index 321fd159af..fdbaf2fa6a 100644 --- a/crates/rooch-framework/src/bindings/address_mapping.rs +++ b/crates/rooch-framework/src/bindings/address_mapping.rs @@ -20,6 +20,7 @@ pub struct AddressMapping<'a> { impl<'a> AddressMapping<'a> { const RESOLVE_FUNCTION_NAME: &'static IdentStr = ident_str!("resolve"); + const RESOLVE_OR_GENERATE_FUNCTION_NAME: &'static IdentStr = ident_str!("resolve_or_generate"); pub fn resolve(&self, multichain_address: MultiChainAddress) -> Result> { if multichain_address.is_rooch_address() { @@ -40,6 +41,27 @@ impl<'a> AddressMapping<'a> { }) } } + + pub fn resovle_or_generate( + &self, + multichain_address: MultiChainAddress, + ) -> Result { + if multichain_address.is_rooch_address() { + let rooch_address: RoochAddress = multichain_address.try_into()?; + Ok(rooch_address.into()) + } else { + let ctx = TxContext::zero(); + let call = FunctionCall::new( + Self::function_id(Self::RESOLVE_OR_GENERATE_FUNCTION_NAME), + vec![], + vec![multichain_address.to_bytes()], + ); + self.caller.call_function(&ctx, call).map(|values| { + let value = values.get(0).expect("Expected return value"); + AccountAddress::from_bytes(&value.value).expect("Expected return address") + }) + } + } } impl<'a> ModuleBundle<'a> for AddressMapping<'a> { diff --git a/crates/rooch-framework/src/bindings/auth_validator.rs b/crates/rooch-framework/src/bindings/auth_validator.rs new file mode 100644 index 0000000000..f9df014bee --- /dev/null +++ b/crates/rooch-framework/src/bindings/auth_validator.rs @@ -0,0 +1,64 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use move_core_types::value::MoveValue; +use moveos_types::{ + module_binding::MoveFunctionCaller, move_types::FunctionId, transaction::FunctionCall, + tx_context::TxContext, +}; +use rooch_types::framework::auth_validator::AuthValidator; + +use super::transaction_validator::TransactionValidator; + +/// Rust bindings for developer custom auth validator module +/// Because the module is not in RoochFramework, we need to dynamically determine the module id base on the AuthValidator struct +pub struct AuthValidatorCaller<'a> { + caller: &'a dyn MoveFunctionCaller, + auth_validator: AuthValidator, +} + +impl<'a> AuthValidatorCaller<'a> { + pub fn new(caller: &'a dyn MoveFunctionCaller, auth_validator: AuthValidator) -> Self { + Self { + caller, + auth_validator, + } + } + + pub fn validate(&self, ctx: &TxContext, payload: Vec) -> Result<()> { + let auth_validator_call = FunctionCall::new( + self.auth_validator.validator_function_id(), + vec![], + vec![MoveValue::vector_u8(payload).simple_serialize().unwrap()], + ); + self.caller + .call_function(ctx, auth_validator_call) + .map(|values| { + debug_assert!(values.is_empty(), "should not have return values"); + })?; + Ok(()) + } + + pub fn pre_execute_function_id(&self) -> FunctionId { + FunctionId::new( + self.auth_validator.validator_module_id(), + TransactionValidator::PRE_EXECUTE_FUNCTION_NAME.to_owned(), + ) + } + + pub fn pre_execute_function_call(&self) -> FunctionCall { + FunctionCall::new(self.pre_execute_function_id(), vec![], vec![]) + } + + pub fn post_execute_function_id(&self) -> FunctionId { + FunctionId::new( + self.auth_validator.validator_module_id(), + TransactionValidator::POST_EXECUTE_FUNCTION_NAME.to_owned(), + ) + } + + pub fn post_execute_function_call(&self) -> FunctionCall { + FunctionCall::new(self.post_execute_function_id(), vec![], vec![]) + } +} diff --git a/crates/rooch-framework/src/bindings/ecdsa_k1_recoverable_validator.rs b/crates/rooch-framework/src/bindings/ecdsa_k1_recoverable_validator.rs new file mode 100644 index 0000000000..4936fa2e7b --- /dev/null +++ b/crates/rooch-framework/src/bindings/ecdsa_k1_recoverable_validator.rs @@ -0,0 +1,48 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use crate::ROOCH_FRAMEWORK_ADDRESS; +use anyhow::Result; +use move_core_types::{ + account_address::AccountAddress, ident_str, identifier::IdentStr, value::MoveValue, +}; +use moveos_types::{ + module_binding::{ModuleBundle, MoveFunctionCaller}, + transaction::FunctionCall, + tx_context::TxContext, +}; + +/// Rust bindings for RoochFramework ecdsa_k1_recoverable_validator module +pub struct EcdsaK1RecoverableValidator<'a> { + caller: &'a dyn MoveFunctionCaller, +} + +impl<'a> EcdsaK1RecoverableValidator<'a> { + const VALIDATE_FUNCTION_NAME: &'static IdentStr = ident_str!("validate"); + + pub fn validate(&self, ctx: &TxContext, payload: Vec) -> Result<()> { + let auth_validator_call = FunctionCall::new( + Self::function_id(Self::VALIDATE_FUNCTION_NAME), + vec![], + vec![MoveValue::vector_u8(payload).simple_serialize().unwrap()], + ); + self.caller + .call_function(ctx, auth_validator_call) + .map(|values| { + debug_assert!(values.is_empty(), "should not have return values"); + })?; + Ok(()) + } +} + +impl<'a> ModuleBundle<'a> for EcdsaK1RecoverableValidator<'a> { + const MODULE_NAME: &'static IdentStr = ident_str!("ecdsa_k1_recoverable_validator"); + const MODULE_ADDRESS: AccountAddress = ROOCH_FRAMEWORK_ADDRESS; + + fn new(caller: &'a impl MoveFunctionCaller) -> Self + where + Self: Sized, + { + Self { caller } + } +} diff --git a/crates/rooch-framework/src/bindings/ecdsa_k1_validator.rs b/crates/rooch-framework/src/bindings/ecdsa_k1_validator.rs new file mode 100644 index 0000000000..6143e77dcb --- /dev/null +++ b/crates/rooch-framework/src/bindings/ecdsa_k1_validator.rs @@ -0,0 +1,48 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use crate::ROOCH_FRAMEWORK_ADDRESS; +use anyhow::Result; +use move_core_types::{ + account_address::AccountAddress, ident_str, identifier::IdentStr, value::MoveValue, +}; +use moveos_types::{ + module_binding::{ModuleBundle, MoveFunctionCaller}, + transaction::FunctionCall, + tx_context::TxContext, +}; + +/// Rust bindings for RoochFramework ecdsa_k1_validator module +pub struct EcdsaK1Validator<'a> { + caller: &'a dyn MoveFunctionCaller, +} + +impl<'a> EcdsaK1Validator<'a> { + const VALIDATE_FUNCTION_NAME: &'static IdentStr = ident_str!("validate"); + + pub fn validate(&self, ctx: &TxContext, payload: Vec) -> Result<()> { + let auth_validator_call = FunctionCall::new( + Self::function_id(Self::VALIDATE_FUNCTION_NAME), + vec![], + vec![MoveValue::vector_u8(payload).simple_serialize().unwrap()], + ); + self.caller + .call_function(ctx, auth_validator_call) + .map(|values| { + debug_assert!(values.is_empty(), "should not have return values"); + })?; + Ok(()) + } +} + +impl<'a> ModuleBundle<'a> for EcdsaK1Validator<'a> { + const MODULE_NAME: &'static IdentStr = ident_str!("ecdsa_k1_validator"); + const MODULE_ADDRESS: AccountAddress = ROOCH_FRAMEWORK_ADDRESS; + + fn new(caller: &'a impl MoveFunctionCaller) -> Self + where + Self: Sized, + { + Self { caller } + } +} diff --git a/crates/rooch-framework/src/bindings/empty.rs b/crates/rooch-framework/src/bindings/empty.rs index 075f92663c..3697bf5746 100644 --- a/crates/rooch-framework/src/bindings/empty.rs +++ b/crates/rooch-framework/src/bindings/empty.rs @@ -17,7 +17,7 @@ pub struct Empty<'a> { } impl<'a> Empty<'a> { - const EMPTY_FUNCTION_NAME: &'static IdentStr = ident_str!("empty"); + pub const EMPTY_FUNCTION_NAME: &'static IdentStr = ident_str!("empty"); pub fn empty(&self, ctx: &TxContext) -> Result<()> { let empty_call = diff --git a/crates/rooch-framework/src/bindings/mod.rs b/crates/rooch-framework/src/bindings/mod.rs index 5d4641cb10..50291da20a 100644 --- a/crates/rooch-framework/src/bindings/mod.rs +++ b/crates/rooch-framework/src/bindings/mod.rs @@ -2,6 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 pub mod address_mapping; +pub mod auth_validator; +pub mod ecdsa_k1_recoverable_validator; +pub mod ecdsa_k1_validator; pub mod ed25519_validator; pub mod empty; +pub mod schnorr_validator; +pub mod session_key; pub mod transaction_validator; diff --git a/crates/rooch-framework/src/bindings/schnorr_validator.rs b/crates/rooch-framework/src/bindings/schnorr_validator.rs new file mode 100644 index 0000000000..46d398850b --- /dev/null +++ b/crates/rooch-framework/src/bindings/schnorr_validator.rs @@ -0,0 +1,48 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use crate::ROOCH_FRAMEWORK_ADDRESS; +use anyhow::Result; +use move_core_types::{ + account_address::AccountAddress, ident_str, identifier::IdentStr, value::MoveValue, +}; +use moveos_types::{ + module_binding::{ModuleBundle, MoveFunctionCaller}, + transaction::FunctionCall, + tx_context::TxContext, +}; + +/// Rust bindings for RoochFramework schnorr_validator module +pub struct SchnorrValidator<'a> { + caller: &'a dyn MoveFunctionCaller, +} + +impl<'a> SchnorrValidator<'a> { + const VALIDATE_FUNCTION_NAME: &'static IdentStr = ident_str!("validate"); + + pub fn validate(&self, ctx: &TxContext, payload: Vec) -> Result<()> { + let auth_validator_call = FunctionCall::new( + Self::function_id(Self::VALIDATE_FUNCTION_NAME), + vec![], + vec![MoveValue::vector_u8(payload).simple_serialize().unwrap()], + ); + self.caller + .call_function(ctx, auth_validator_call) + .map(|values| { + debug_assert!(values.is_empty(), "should not have return values"); + })?; + Ok(()) + } +} + +impl<'a> ModuleBundle<'a> for SchnorrValidator<'a> { + const MODULE_NAME: &'static IdentStr = ident_str!("schnorr_validator"); + const MODULE_ADDRESS: AccountAddress = ROOCH_FRAMEWORK_ADDRESS; + + fn new(caller: &'a impl MoveFunctionCaller) -> Self + where + Self: Sized, + { + Self { caller } + } +} diff --git a/crates/rooch-framework/src/bindings/session_key.rs b/crates/rooch-framework/src/bindings/session_key.rs new file mode 100644 index 0000000000..6419b05148 --- /dev/null +++ b/crates/rooch-framework/src/bindings/session_key.rs @@ -0,0 +1,89 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use crate::ROOCH_FRAMEWORK_ADDRESS; +use anyhow::Result; +use move_core_types::{ + account_address::AccountAddress, ident_str, identifier::IdentStr, value::MoveValue, +}; +use moveos_types::{ + module_binding::{ModuleBundle, MoveFunctionCaller}, + move_option::MoveOption, + state::MoveState, + transaction::{FunctionCall, MoveAction}, + tx_context::TxContext, +}; +use rooch_types::{ + crypto::BuiltinScheme, + framework::session_key::{SessionKey, SessionScope}, +}; + +/// Rust bindings for RoochFramework session_key module +pub struct SessionKeyModule<'a> { + caller: &'a dyn MoveFunctionCaller, +} + +impl<'a> SessionKeyModule<'a> { + pub const GET_SESSION_KEY_FUNCTION_NAME: &'static IdentStr = ident_str!("get_session_key"); + pub const CREATE_SESSION_KEY_ENTRY_FUNCTION_NAME: &'static IdentStr = + ident_str!("create_session_key_entry"); + + pub fn get_session_key( + &self, + account_address: AccountAddress, + auth_key: Vec, + ) -> Result> { + let call = FunctionCall::new( + Self::function_id(Self::GET_SESSION_KEY_FUNCTION_NAME), + vec![], + vec![ + MoveValue::Address(account_address) + .simple_serialize() + .unwrap(), + MoveValue::vector_u8(auth_key).simple_serialize().unwrap(), + ], + ); + let ctx = TxContext::new_readonly_ctx(account_address); + let session_key = self.caller.call_function(&ctx, call).map(|mut values| { + let value = values.pop().expect("should have one return value"); + bcs::from_bytes::>(&value.value) + .expect("should be a valid MoveOption") + .into() + })?; + Ok(session_key) + } + + pub fn create_session_key_action( + authentication_key: Vec, + scheme: BuiltinScheme, + scope: SessionScope, + expiration_time: u64, + max_inactive_interval: u64, + ) -> MoveAction { + Self::create_move_action( + Self::CREATE_SESSION_KEY_ENTRY_FUNCTION_NAME, + vec![], + vec![ + MoveValue::vector_u8(authentication_key), + MoveValue::U64(scheme.flag() as u64), + scope.module_address.to_move_value(), + scope.module_name.to_move_value(), + scope.function_name.to_move_value(), + MoveValue::U64(expiration_time), + MoveValue::U64(max_inactive_interval), + ], + ) + } +} + +impl<'a> ModuleBundle<'a> for SessionKeyModule<'a> { + const MODULE_NAME: &'static IdentStr = ident_str!("session_key"); + const MODULE_ADDRESS: AccountAddress = ROOCH_FRAMEWORK_ADDRESS; + + fn new(caller: &'a impl MoveFunctionCaller) -> Self + where + Self: Sized, + { + Self { caller } + } +} diff --git a/crates/rooch-framework/src/bindings/transaction_validator.rs b/crates/rooch-framework/src/bindings/transaction_validator.rs index d3b7e41e0e..e10e306f8f 100644 --- a/crates/rooch-framework/src/bindings/transaction_validator.rs +++ b/crates/rooch-framework/src/bindings/transaction_validator.rs @@ -12,7 +12,7 @@ use moveos_types::{ transaction::FunctionCall, tx_context::TxContext, }; -use rooch_types::framework::auth_validator_registry::AuthValidator; +use rooch_types::framework::auth_validator::TxValidateResult; use rooch_types::transaction::AuthenticatorInfo; /// Rust bindings for RoochFramework transaction_validator module @@ -21,11 +21,11 @@ pub struct TransactionValidator<'a> { } impl<'a> TransactionValidator<'a> { - const VALIDATE_FUNCTION_NAME: &'static IdentStr = ident_str!("validate"); - const PRE_EXECUTE_FUNCTION_NAME: &IdentStr = ident_str!("pre_execute"); - const POST_EXECUTE_FUNCTION_NAME: &IdentStr = ident_str!("post_execute"); + pub const VALIDATE_FUNCTION_NAME: &'static IdentStr = ident_str!("validate"); + pub const PRE_EXECUTE_FUNCTION_NAME: &IdentStr = ident_str!("pre_execute"); + pub const POST_EXECUTE_FUNCTION_NAME: &IdentStr = ident_str!("post_execute"); - pub fn validate(&self, ctx: &TxContext, auth: AuthenticatorInfo) -> Result<()> { + pub fn validate(&self, ctx: &TxContext, auth: AuthenticatorInfo) -> Result { let tx_validator_call = FunctionCall::new( Self::function_id(Self::VALIDATE_FUNCTION_NAME), vec![], @@ -36,7 +36,7 @@ impl<'a> TransactionValidator<'a> { MoveValue::U64(auth.authenticator.scheme) .simple_serialize() .unwrap(), - MoveValue::vector_u8(auth.authenticator.payload.clone()) + MoveValue::vector_u8(auth.authenticator.payload) .simple_serialize() .unwrap(), ], @@ -46,31 +46,27 @@ impl<'a> TransactionValidator<'a> { .call_function(ctx, tx_validator_call) .map(|mut values| { let value = values.pop().expect("should have one return value"); - bcs::from_bytes::(&value.value) - .expect("should be a valid auth validator") + bcs::from_bytes::(&value.value) + .expect("should be a valid TxValidateResult") })?; - let auth_validator_call = FunctionCall::new( - auth_validator.validator_function_id(), - vec![], - vec![MoveValue::vector_u8(auth.authenticator.payload) - .simple_serialize() - .unwrap()], - ); - self.caller - .call_function(ctx, auth_validator_call) - .map(|values| { - debug_assert!(values.is_empty(), "should not have return values"); - })?; - Ok(()) + Ok(auth_validator) } pub fn pre_execute_function_id() -> FunctionId { Self::function_id(Self::PRE_EXECUTE_FUNCTION_NAME) } + pub fn pre_execute_function_call() -> FunctionCall { + FunctionCall::new(Self::pre_execute_function_id(), vec![], vec![]) + } + pub fn post_execute_function_id() -> FunctionId { Self::function_id(Self::POST_EXECUTE_FUNCTION_NAME) } + + pub fn post_execute_function_call() -> FunctionCall { + FunctionCall::new(Self::post_execute_function_id(), vec![], vec![]) + } } impl<'a> ModuleBundle<'a> for TransactionValidator<'a> { diff --git a/crates/rooch-framework/src/lib.rs b/crates/rooch-framework/src/lib.rs index 72b5a6d4f7..35d42ba6e4 100644 --- a/crates/rooch-framework/src/lib.rs +++ b/crates/rooch-framework/src/lib.rs @@ -5,3 +5,9 @@ pub mod bindings; pub mod natives; pub use rooch_types::addresses::*; + +const ROOCH_FRAMEWORK_ERROR_DESCRIPTIONS: &[u8] = include_bytes!("../error_description.errmap"); + +pub fn rooch_framework_error_descriptions() -> &'static [u8] { + ROOCH_FRAMEWORK_ERROR_DESCRIPTIONS +} diff --git a/crates/rooch-framework/src/natives/mod.rs b/crates/rooch-framework/src/natives/mod.rs index 03119466bb..2a85193ae8 100644 --- a/crates/rooch-framework/src/natives/mod.rs +++ b/crates/rooch-framework/src/natives/mod.rs @@ -16,6 +16,8 @@ pub struct GasParameters { hash: rooch_framework::crypto::hash::GasParameters, ed25519: rooch_framework::crypto::ed25519::GasParameters, ecdsa_k1: rooch_framework::crypto::ecdsa_k1::GasParameters, + ecdsa_k1_recoverable: rooch_framework::crypto::ecdsa_k1_recoverable::GasParameters, + schnorr: rooch_framework::crypto::schnorr::GasParameters, } impl GasParameters { @@ -26,6 +28,9 @@ impl GasParameters { hash: rooch_framework::crypto::hash::GasParameters::zeros(), ed25519: rooch_framework::crypto::ed25519::GasParameters::zeros(), ecdsa_k1: rooch_framework::crypto::ecdsa_k1::GasParameters::zeros(), + ecdsa_k1_recoverable: + rooch_framework::crypto::ecdsa_k1_recoverable::GasParameters::zeros(), + schnorr: rooch_framework::crypto::schnorr::GasParameters::zeros(), } } } @@ -60,6 +65,14 @@ pub fn all_natives(gas_params: GasParameters) -> NativeFunctionTable { "ecdsa_k1", rooch_framework::crypto::ecdsa_k1::make_all(gas_params.ecdsa_k1) ); + add_natives!( + "ecdsa_k1_recoverable", + rooch_framework::crypto::ecdsa_k1_recoverable::make_all(gas_params.ecdsa_k1_recoverable) + ); + add_natives!( + "schnorr", + rooch_framework::crypto::schnorr::make_all(gas_params.schnorr) + ); let rooch_native_fun_table = make_table_from_iter(ROOCH_FRAMEWORK_ADDRESS, natives); native_fun_table.extend(rooch_native_fun_table); diff --git a/crates/rooch-framework/src/natives/rooch_framework/crypto/ecdsa_k1.rs b/crates/rooch-framework/src/natives/rooch_framework/crypto/ecdsa_k1.rs index 2edb397aa3..a602fee964 100644 --- a/crates/rooch-framework/src/natives/rooch_framework/crypto/ecdsa_k1.rs +++ b/crates/rooch-framework/src/natives/rooch_framework/crypto/ecdsa_k1.rs @@ -3,12 +3,9 @@ use crate::natives::helpers::{make_module_natives, make_native}; use fastcrypto::{ - error::FastCryptoError, hash::{Keccak256, Sha256}, - secp256k1::{ - recoverable::Secp256k1RecoverableSignature, Secp256k1PublicKey, Secp256k1Signature, - }, - traits::{RecoverableSignature, ToFromBytes}, + secp256k1::{Secp256k1PublicKey, Secp256k1Signature}, + traits::ToFromBytes, }; use move_binary_format::errors::PartialVMResult; use move_vm_runtime::native_functions::{NativeContext, NativeFunction}; @@ -21,79 +18,12 @@ use move_vm_types::{ use smallvec::smallvec; use std::collections::VecDeque; -pub const FAIL_TO_RECOVER_PUBKEY: u64 = 0; -pub const INVALID_SIGNATURE: u64 = 1; -pub const INVALID_PUBKEY: u64 = 2; +pub const INVALID_SIGNATURE: u64 = 0; +pub const INVALID_PUBKEY: u64 = 1; pub const KECCAK256: u8 = 0; pub const SHA256: u8 = 1; -pub fn native_ecrecover( - _gas_params: &FromBytesGasParameters, - _context: &mut NativeContext, - ty_args: Vec, - mut args: VecDeque, -) -> PartialVMResult { - debug_assert!(ty_args.is_empty()); - debug_assert!(args.len() == 3); - - let hash = pop_arg!(args, u8); - let msg = pop_arg!(args, VectorRef); - let signature = pop_arg!(args, VectorRef); - - let msg_ref = msg.as_bytes_ref(); - let signature_ref = signature.as_bytes_ref(); - - // TODO(Gas): Charge the arg size dependent costs - - let cost = 0.into(); - - let Ok(sig) = ::from_bytes(&signature_ref) else { - return Ok(NativeResult::err(cost, INVALID_SIGNATURE)); - }; - - let pk = match hash { - KECCAK256 => sig.recover_with_hash::(&msg_ref), - SHA256 => sig.recover_with_hash::(&msg_ref), - _ => Err(FastCryptoError::InvalidInput), // We should never reach here - }; - - match pk { - Ok(pk) => Ok(NativeResult::ok( - cost, - smallvec![Value::vector_u8(pk.as_bytes().to_vec())], - )), - Err(_) => Ok(NativeResult::err(cost, FAIL_TO_RECOVER_PUBKEY)), - } -} - -pub fn native_decompress_pubkey( - _gas_params: &FromBytesGasParameters, - _context: &mut NativeContext, - ty_args: Vec, - mut args: VecDeque, -) -> PartialVMResult { - debug_assert!(ty_args.is_empty()); - debug_assert!(args.len() == 1); - - let pubkey = pop_arg!(args, VectorRef); - let pubkey_ref = pubkey.as_bytes_ref(); - - // TODO(Gas): Charge the arg size dependent costs - let cost = 0.into(); - - match Secp256k1PublicKey::from_bytes(&pubkey_ref) { - Ok(pubkey) => { - let uncompressed = &pubkey.pubkey.serialize_uncompressed(); - Ok(NativeResult::ok( - cost, - smallvec![Value::vector_u8(uncompressed.to_vec())], - )) - } - Err(_) => Ok(NativeResult::err(cost, INVALID_PUBKEY)), - } -} - pub fn native_verify( _gas_params: &FromBytesGasParameters, _context: &mut NativeContext, @@ -101,46 +31,37 @@ pub fn native_verify( mut args: VecDeque, ) -> PartialVMResult { debug_assert!(ty_args.is_empty()); - debug_assert!(args.len() == 3); + debug_assert!(args.len() == 4); let hash = pop_arg!(args, u8); let msg = pop_arg!(args, VectorRef); + let public_key_bytes = pop_arg!(args, VectorRef); let signature_bytes = pop_arg!(args, VectorRef); let msg_ref = msg.as_bytes_ref(); + let public_key_bytes_ref = public_key_bytes.as_bytes_ref(); let signature_bytes_ref = signature_bytes.as_bytes_ref(); // TODO(Gas): Charge the arg size dependent costs let cost = 0.into(); - let Ok(sig) = ::from_bytes(&signature_bytes_ref) else { + let Ok(sig) = ::from_bytes(&signature_bytes_ref) else { return Ok(NativeResult::err(cost, INVALID_SIGNATURE)); }; - let pk = match hash { - KECCAK256 => match sig.recover_with_hash::(&msg_ref) { - Ok(pk) => pk, - Err(_) => { - return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)])); - } - }, - SHA256 => match sig.recover_with_hash::(&msg_ref) { - Ok(pk) => pk, - Err(_) => { - return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)])); - } - }, - _ => { - return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)])); - } + let Ok(public_key) = ::from_bytes(&public_key_bytes_ref) else { + return Ok(NativeResult::err(cost, INVALID_PUBKEY)); }; - let sign = Secp256k1Signature::from(&sig); let result = match hash { - KECCAK256 => pk.verify_with_hash::(&msg_ref, &sign).is_ok(), - SHA256 => pk.verify_with_hash::(&msg_ref, &sign).is_ok(), + KECCAK256 => public_key + .verify_with_hash::(&msg_ref, &sig) + .is_ok(), + SHA256 => public_key + .verify_with_hash::(&msg_ref, &sig) + .is_ok(), _ => false, }; @@ -162,8 +83,6 @@ impl FromBytesGasParameters { #[derive(Debug, Clone)] pub struct GasParameters { - pub ecrecover: FromBytesGasParameters, - pub decompress_pubkey: FromBytesGasParameters, pub verify: FromBytesGasParameters, } @@ -171,24 +90,12 @@ impl GasParameters { pub fn zeros() -> Self { Self { verify: FromBytesGasParameters::zeros(), - decompress_pubkey: FromBytesGasParameters::zeros(), - ecrecover: FromBytesGasParameters::zeros(), } } } pub fn make_all(gas_params: GasParameters) -> impl Iterator { - let natives = [ - ( - "ecrecover", - make_native(gas_params.ecrecover, native_ecrecover), - ), - ( - "decompress_pubkey", - make_native(gas_params.decompress_pubkey, native_decompress_pubkey), - ), - ("verify", make_native(gas_params.verify, native_verify)), - ]; + let natives = [("verify", make_native(gas_params.verify, native_verify))]; make_module_natives(natives) } diff --git a/crates/rooch-framework/src/natives/rooch_framework/crypto/ecdsa_k1_recoverable.rs b/crates/rooch-framework/src/natives/rooch_framework/crypto/ecdsa_k1_recoverable.rs new file mode 100644 index 0000000000..3206371318 --- /dev/null +++ b/crates/rooch-framework/src/natives/rooch_framework/crypto/ecdsa_k1_recoverable.rs @@ -0,0 +1,195 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use crate::natives::helpers::{make_module_natives, make_native}; +use fastcrypto::{ + error::FastCryptoError, + hash::{Keccak256, Sha256}, + secp256k1::{recoverable::Secp256k1RecoverableSignature, Secp256k1PublicKey}, + traits::{RecoverableSignature, ToFromBytes}, +}; +use move_binary_format::errors::PartialVMResult; +use move_vm_runtime::native_functions::{NativeContext, NativeFunction}; +use move_vm_types::{ + loaded_data::runtime_types::Type, + natives::function::NativeResult, + pop_arg, + values::{Value, VectorRef}, +}; +use smallvec::smallvec; +use std::collections::VecDeque; + +pub const FAIL_TO_RECOVER_PUBKEY: u64 = 0; +pub const INVALID_SIGNATURE: u64 = 1; +pub const INVALID_PUBKEY: u64 = 2; + +pub const KECCAK256: u8 = 0; +pub const SHA256: u8 = 1; + +pub fn native_ecrecover( + _gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 3); + + let hash = pop_arg!(args, u8); + let msg = pop_arg!(args, VectorRef); + let signature = pop_arg!(args, VectorRef); + + let msg_ref = msg.as_bytes_ref(); + let signature_ref = signature.as_bytes_ref(); + + // TODO(Gas): Charge the arg size dependent costs + + let cost = 0.into(); + + let Ok(sig) = ::from_bytes(&signature_ref) else { + return Ok(NativeResult::err(cost, INVALID_SIGNATURE)); + }; + + let pk = match hash { + KECCAK256 => sig.recover_with_hash::(&msg_ref), + SHA256 => sig.recover_with_hash::(&msg_ref), + _ => Err(FastCryptoError::InvalidInput), // We should never reach here + }; + + match pk { + Ok(pk) => Ok(NativeResult::ok( + cost, + smallvec![Value::vector_u8(pk.as_bytes().to_vec())], + )), + Err(_) => Ok(NativeResult::err(cost, FAIL_TO_RECOVER_PUBKEY)), + } +} + +pub fn native_decompress_pubkey( + _gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 1); + + let pubkey = pop_arg!(args, VectorRef); + let pubkey_ref = pubkey.as_bytes_ref(); + + // TODO(Gas): Charge the arg size dependent costs + let cost = 0.into(); + + match Secp256k1PublicKey::from_bytes(&pubkey_ref) { + Ok(pubkey) => { + let uncompressed = &pubkey.pubkey.serialize_uncompressed(); + Ok(NativeResult::ok( + cost, + smallvec![Value::vector_u8(uncompressed.to_vec())], + )) + } + Err(_) => Ok(NativeResult::err(cost, INVALID_PUBKEY)), + } +} + +pub fn native_verify( + _gas_params: &FromBytesGasParameters, + _context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 3); + + let hash = pop_arg!(args, u8); + + let msg = pop_arg!(args, VectorRef); + let signature_bytes = pop_arg!(args, VectorRef); + + let msg_ref = msg.as_bytes_ref(); + let signature_bytes_ref = signature_bytes.as_bytes_ref(); + + // TODO(Gas): Charge the arg size dependent costs + + let cost = 0.into(); + + let Ok(sig) = ::from_bytes(&signature_bytes_ref) else { + return Ok(NativeResult::err(cost, INVALID_SIGNATURE)); + }; + + let pk = match hash { + KECCAK256 => match sig.recover_with_hash::(&msg_ref) { + Ok(pk) => pk, + Err(_) => { + return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)])); + } + }, + SHA256 => match sig.recover_with_hash::(&msg_ref) { + Ok(pk) => pk, + Err(_) => { + return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)])); + } + }, + _ => { + return Ok(NativeResult::ok(cost, smallvec![Value::bool(false)])); + } + }; + + let result = match hash { + KECCAK256 => pk + .verify_recoverable_with_hash::(&msg_ref, &sig) + .is_ok(), + SHA256 => pk + .verify_recoverable_with_hash::(&msg_ref, &sig) + .is_ok(), + _ => false, + }; + + Ok(NativeResult::ok(cost, smallvec![Value::bool(result)])) +} + +#[derive(Debug, Clone)] +pub struct FromBytesGasParameters {} + +impl FromBytesGasParameters { + pub fn zeros() -> Self { + Self {} + } +} + +/*************************************************************************************************** + * module + **************************************************************************************************/ + +#[derive(Debug, Clone)] +pub struct GasParameters { + pub verify: FromBytesGasParameters, + pub ecrecover: FromBytesGasParameters, + pub decompress_pubkey: FromBytesGasParameters, +} + +impl GasParameters { + pub fn zeros() -> Self { + Self { + verify: FromBytesGasParameters::zeros(), + ecrecover: FromBytesGasParameters::zeros(), + decompress_pubkey: FromBytesGasParameters::zeros(), + } + } +} + +pub fn make_all(gas_params: GasParameters) -> impl Iterator { + let natives = [ + ( + "decompress_pubkey", + make_native(gas_params.decompress_pubkey, native_decompress_pubkey), + ), + ( + "ecrecover", + make_native(gas_params.ecrecover, native_ecrecover), + ), + ("verify", make_native(gas_params.verify, native_verify)), + ]; + + make_module_natives(natives) +} diff --git a/crates/rooch-framework/src/natives/rooch_framework/crypto/hash.rs b/crates/rooch-framework/src/natives/rooch_framework/crypto/hash.rs index edb786b8d8..dd2cf76dc6 100644 --- a/crates/rooch-framework/src/natives/rooch_framework/crypto/hash.rs +++ b/crates/rooch-framework/src/natives/rooch_framework/crypto/hash.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::natives::helpers::{make_module_natives, make_native}; -use fastcrypto::hash::{Blake2b256, HashFunction, Keccak256}; +use fastcrypto::hash::{Blake2b256, HashFunction, Keccak256, Ripemd160}; use move_binary_format::errors::PartialVMResult; use move_vm_runtime::native_functions::{NativeContext, NativeFunction}; use move_vm_types::{ @@ -66,6 +66,22 @@ pub fn native_blake2b256( hash::(context, ty_args, args) } +/*************************************************************************************************** + * native fun ripemd160 + * Implementation of the Move native function `hash::ripemd160(data: &vector): vector` + * gas cost: hash_ripemd160_cost_base | base cost for function call and fixed opers + * + hash_ripemd160_data_cost_per_byte * msg.len() | cost depends on length of message + * + hash_ripemd160_data_cost_per_block * num_blocks | cost depends on number of blocks in message + **************************************************************************************************/ +pub fn native_ripemd160( + _gas_params: &FromBytesGasParameters, + context: &mut NativeContext, + ty_args: Vec, + args: VecDeque, +) -> PartialVMResult { + hash::(context, ty_args, args) +} + #[derive(Debug, Clone)] pub struct FromBytesGasParameters {} @@ -83,6 +99,7 @@ impl FromBytesGasParameters { pub struct GasParameters { pub keccak256: FromBytesGasParameters, pub blake2b256: FromBytesGasParameters, + pub ripemd160: FromBytesGasParameters, } impl GasParameters { @@ -90,6 +107,7 @@ impl GasParameters { Self { keccak256: FromBytesGasParameters::zeros(), blake2b256: FromBytesGasParameters::zeros(), + ripemd160: FromBytesGasParameters::zeros(), } } } @@ -104,6 +122,10 @@ pub fn make_all(gas_params: GasParameters) -> impl Iterator, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 4); + + let hash = pop_arg!(args, u8); + let msg = pop_arg!(args, VectorRef); + let public_key_bytes = pop_arg!(args, VectorRef); + let signature_bytes = pop_arg!(args, VectorRef); + + let msg_ref = msg.as_bytes_ref(); + let signature_bytes_ref = signature_bytes.as_bytes_ref(); + let public_key_bytes_ref = public_key_bytes.as_bytes_ref(); + + // TODO(Gas): Charge the arg size dependent costs + + let cost = 0.into(); + + let Ok(sig) = Signature::from_slice(&signature_bytes_ref) else { + return Ok(NativeResult::err(cost, INVALID_SIGNATURE)); + }; + + let Ok(public_key) = ::from_bytes(&public_key_bytes_ref) else { + return Ok(NativeResult::err(cost, INVALID_PUBKEY)); + }; + + let sign = SchnorrSignature::from(&sig); + let result = match hash { + KECCAK256 => public_key + .verify_with_hash::(&msg_ref, &sign) + .is_ok(), + SHA256 => public_key + .verify_with_hash::(&msg_ref, &sign) + .is_ok(), + _ => false, + }; + + Ok(NativeResult::ok(cost, smallvec![Value::bool(result)])) +} + +#[derive(Debug, Clone)] +pub struct FromBytesGasParameters {} + +impl FromBytesGasParameters { + pub fn zeros() -> Self { + Self {} + } +} + +/*************************************************************************************************** + * module + **************************************************************************************************/ + +#[derive(Debug, Clone)] +pub struct GasParameters { + pub verify: FromBytesGasParameters, +} + +impl GasParameters { + pub fn zeros() -> Self { + Self { + verify: FromBytesGasParameters::zeros(), + } + } +} + +pub fn make_all(gas_params: GasParameters) -> impl Iterator { + let natives = [("verify", make_native(gas_params.verify, native_verify))]; + + make_module_natives(natives) +} diff --git a/crates/rooch-genesis/genesis/genesis b/crates/rooch-genesis/genesis/genesis index 19edd43e6c..bbfbc3df6e 100644 Binary files a/crates/rooch-genesis/genesis/genesis and b/crates/rooch-genesis/genesis/genesis differ diff --git a/crates/rooch-genesis/src/lib.rs b/crates/rooch-genesis/src/lib.rs index 60d770f961..ca764c02ef 100644 --- a/crates/rooch-genesis/src/lib.rs +++ b/crates/rooch-genesis/src/lib.rs @@ -9,7 +9,6 @@ use moveos::moveos::MoveOSConfig; use moveos_stdlib_builder::BuildOptions; use moveos_types::transaction::{MoveAction, MoveOSTransaction}; use once_cell::sync::Lazy; -use rooch_framework::bindings::transaction_validator; use serde::{Deserialize, Serialize}; use std::{ fs::File, @@ -52,22 +51,10 @@ impl RoochGenesis { pub fn build_with_option(option: BuildOption) -> Result { let config = MoveOSConfig { vm_config: VMConfig::default(), - pre_execute_function: Some( - transaction_validator::TransactionValidator::pre_execute_function_id(), - ), - post_execute_function: Some( - transaction_validator::TransactionValidator::post_execute_function_id(), - ), }; let config_for_test = MoveOSConfig { vm_config: VMConfig::default(), - pre_execute_function: Some( - transaction_validator::TransactionValidator::pre_execute_function_id(), - ), - post_execute_function: Some( - transaction_validator::TransactionValidator::post_execute_function_id(), - ), }; let gas_params = rooch_framework::natives::GasParameters::zeros(); @@ -180,6 +167,7 @@ where mod tests { use crate::GenesisPackage; use moveos::moveos::MoveOS; + use moveos_store::MoveOSStore; use rooch_framework::natives::all_natives; #[test] @@ -202,9 +190,14 @@ mod tests { #[test] fn test_genesis_init() { let genesis = super::RoochGenesis::build().expect("build rooch framework failed"); - let db = moveos_store::MoveOSDB::new_with_memory_store(); - let mut moveos = MoveOS::new(db, all_natives(genesis.gas_params), genesis.config) - .expect("init moveos failed"); + // let db = moveos_store::MoveOSStore::new_with_memory_store(); + let moveos_store = MoveOSStore::mock_moveos_store().unwrap(); + let mut moveos = MoveOS::new( + moveos_store, + all_natives(genesis.gas_params), + genesis.config, + ) + .expect("init moveos failed"); moveos .init_genesis(genesis.genesis_package.genesis_txs) .expect("init genesis failed"); diff --git a/crates/rooch-integration-test-runner/src/lib.rs b/crates/rooch-integration-test-runner/src/lib.rs index 327ba9172d..3ab4307064 100644 --- a/crates/rooch-integration-test-runner/src/lib.rs +++ b/crates/rooch-integration-test-runner/src/lib.rs @@ -4,24 +4,28 @@ use clap::Parser; use codespan_reporting::diagnostic::Severity; use codespan_reporting::term::termcolor::Buffer; -use move_command_line_common::address::NumericalAddress; -use move_command_line_common::files::verify_and_create_named_address_mapping; -use move_command_line_common::{address::ParsedAddress, values::ParsableValue}; +use move_command_line_common::{ + address::{NumericalAddress, ParsedAddress}, + files::verify_and_create_named_address_mapping, + values::ParsableValue, +}; use move_compiler::compiled_unit::CompiledUnitEnum; use move_compiler::FullyCompiledProgram; use move_core_types::effects::{ChangeSet, Op}; +use move_core_types::language_storage::TypeTag; use move_transactional_test_runner::{ tasks::{InitCommand, SyntaxChoice}, vm_test_harness::view_resource_in_move_storage, }; use move_vm_runtime::session::SerializedReturnValues; use moveos::moveos::MoveOS; -use moveos::moveos_test_runner::MoveOSTestAdapter; -use moveos::moveos_test_runner::{CompiledState, TaskInput}; +use moveos::moveos_test_runner::{CompiledState, MoveOSTestAdapter, TaskInput}; +use moveos_store::MoveOSStore; +use moveos_types::move_module::MoveModule; use moveos_types::move_types::FunctionId; -use moveos_types::object::ObjectID; -use moveos_types::state::StateChangeSet; -use moveos_types::state_resolver::AnnotatedStateReader; +use moveos_types::object::{NamedTableID, ObjectID}; +use moveos_types::state::{MoveStructType, State, StateChangeSet}; +use moveos_types::state_resolver::{module_name_to_key, AnnotatedStateReader}; use moveos_types::transaction::{MoveAction, MoveOSTransaction, TransactionOutput}; use moveos_verifier::build::build_model; use moveos_verifier::metadata::run_extended_checks; @@ -86,11 +90,15 @@ impl<'a> MoveOSTestAdapter<'a> for MoveOSTestRunner<'a> { None => BTreeMap::new(), }; - let db = moveos_store::MoveOSDB::new_with_memory_store(); + let moveos_store = MoveOSStore::mock_moveos_store().unwrap(); let genesis: &RoochGenesis = &rooch_genesis::ROOCH_GENESIS; - let mut moveos = - MoveOS::new(db, genesis.all_natives(), genesis.config_for_test.clone()).unwrap(); + let mut moveos = MoveOS::new( + moveos_store, + genesis.all_natives(), + genesis.config_for_test.clone(), + ) + .unwrap(); moveos.init_genesis(genesis.genesis_txs()).unwrap(); @@ -111,8 +119,9 @@ impl<'a> MoveOSTestAdapter<'a> for MoveOSTestRunner<'a> { } // Apply new modules and add precompiled address mapping - let mut change_set = ChangeSet::new(); - let table_change_set = StateChangeSet::default(); + let mut table_change_set = StateChangeSet::default(); + // let mut mutated_accounts = BTreeSet::new(); + let module_value_type = TypeTag::Struct(Box::new(MoveModule::struct_tag())); if let Some(pre_compiled_lib) = pre_compiled_deps { for c in &pre_compiled_lib.compiled { if let CompiledUnitEnum::Module(m) = c { @@ -137,11 +146,24 @@ impl<'a> MoveOSTestAdapter<'a> for MoveOSTestRunner<'a> { let (_, module_id) = m.module_id(); let mut bytes = vec![]; m.named_module.module.serialize(&mut bytes).unwrap(); - let op = Op::New(bytes); - change_set.add_module_op(module_id, op).unwrap(); + + let handle = NamedTableID::Module(*module_id.address()).to_object_id(); + table_change_set.add_op( + handle, + module_name_to_key(module_id.name()), + Op::New(State { + value_type: module_value_type.clone(), + value: bytes, + }), + ); + moveos + .state() + .create_account_storage(*module_id.address()) + .unwrap(); } } } + let change_set = ChangeSet::new(); moveos .state() .apply_change_set(change_set, table_change_set) @@ -186,7 +208,7 @@ impl<'a> MoveOSTestAdapter<'a> for MoveOSTestRunner<'a> { MoveAction::new_module_bundle(vec![module_bytes]), ); let verified_tx = self.moveos.verify(tx)?; - let (_state_root, output) = self.moveos.execute(verified_tx)?; + let (_state_root, output) = self.moveos.execute_and_apply(verified_tx)?; Ok((Some(tx_output_to_str(output)), module)) } @@ -222,7 +244,7 @@ impl<'a> MoveOSTestAdapter<'a> for MoveOSTestRunner<'a> { MoveAction::new_script_call(script_bytes, type_args, args), ); let verified_tx = self.moveos.verify(tx)?; - let (_state_root, output) = self.moveos.execute(verified_tx)?; + let (_state_root, output) = self.moveos.execute_and_apply(verified_tx)?; //TODO return values let value = SerializedReturnValues { mutable_reference_outputs: vec![], @@ -261,7 +283,7 @@ impl<'a> MoveOSTestAdapter<'a> for MoveOSTestRunner<'a> { MoveAction::new_function_call(function_id, type_args, args), ); let verified_tx = self.moveos.verify(tx)?; - let (_state_root, output) = self.moveos.execute(verified_tx)?; + let (_state_root, output) = self.moveos.execute_and_apply(verified_tx)?; debug_assert!(output.status == move_core_types::vm_status::KeptVMStatus::Executed); //TODO return values let value = SerializedReturnValues { diff --git a/crates/rooch-key/src/key_derive.rs b/crates/rooch-key/src/key_derive.rs index ef68cd6624..8a17315fe9 100644 --- a/crates/rooch-key/src/key_derive.rs +++ b/crates/rooch-key/src/key_derive.rs @@ -2,9 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::anyhow; +use bip32::XPrv; use bip32::{ChildNumber, DerivationPath}; use bip39::{Language, Mnemonic, MnemonicType, Seed}; use fastcrypto::ed25519::Ed25519KeyPair; +use fastcrypto::secp256k1::recoverable::{ + Secp256k1RecoverableKeyPair, Secp256k1RecoverablePrivateKey, +}; +use fastcrypto::secp256k1::schnorr::{SchnorrKeyPair, SchnorrPrivateKey}; +use fastcrypto::secp256k1::{Secp256k1KeyPair, Secp256k1PrivateKey}; use fastcrypto::{ ed25519::Ed25519PrivateKey, traits::{KeyPair, ToFromBytes}, @@ -15,68 +21,206 @@ use rooch_types::error::RoochError; use slip10_ed25519::derive_ed25519_private_key; use std::string::String; -pub const DERIVATION_PATH_COIN_TYPE: u32 = 784; +// Coin type +pub const DERIVATION_PATH_COIN_TYPE_BTC: u32 = 0; +pub const DERIVATION_PATH_COIN_TYPE_ETH: u32 = 60; +pub const DERIVATION_PATH_COIN_TYPE_SUI: u32 = 784; +pub const DERIVATION_PATH_COIN_TYPE_LBTC: u32 = 998; +pub const DERIVATION_PATH_COIN_TYPE_NOSTR: u32 = 1237; +// Purpose +/// Ed25519 follows SLIP-0010 using hardened path: m/44'/784'/0'/0'/{index}' +/// Note that the purpose node is used to distinguish signature schemes. pub const DERVIATION_PATH_PURPOSE_ED25519: u32 = 44; +/// BIP39 is used to generate mnemonic seed words and derive a binary seed from them. +/// BIP32 is used to derive the path m/44'/1237'/'/0/0 (according to the Nostr entry on SLIP44). +/// A basic client can simply use an account of 0 to derive a single key. For more advanced use-cases you can increment account, allowing generation of practically infinite keys from the 5-level path with hardened derivation. +/// Other types of clients can still get fancy and use other derivation paths for their own other purposes. +pub const DERVIATION_PATH_PURPOSE_SCHNORR: u32 = 44; pub const DERVIATION_PATH_PURPOSE_ECDSA: u32 = 54; pub const DERVIATION_PATH_PURPOSE_SECP256R1: u32 = 74; -/// Ed25519 follows SLIP-0010 using hardened path: m/44'/784'/0'/0'/{index}' -/// Note that the purpose node is used to distinguish signature schemes. pub fn derive_key_pair_from_path( seed: &[u8], derivation_path: Option, - key_scheme: &BuiltinScheme, + crypto_scheme: &BuiltinScheme, ) -> Result<(RoochAddress, RoochKeyPair), RoochError> { - // TODO:: Other scheme - let path = validate_path(key_scheme, derivation_path)?; - let indexes = path.into_iter().map(|i| i.into()).collect::>(); - let derived = derive_ed25519_private_key(seed, &indexes); - let sk = Ed25519PrivateKey::from_bytes(&derived) - .map_err(|e| RoochError::SignatureKeyGenError(e.to_string()))?; - let kp: Ed25519KeyPair = sk.into(); - Ok((kp.public().into(), RoochKeyPair::Ed25519(kp))) + let path = validate_path(crypto_scheme, derivation_path)?; + match crypto_scheme { + BuiltinScheme::Ed25519 => { + let indexes = path.into_iter().map(|i| i.into()).collect::>(); + let derived = derive_ed25519_private_key(seed, &indexes); + let sk = Ed25519PrivateKey::from_bytes(&derived) + .map_err(|e| RoochError::SignatureKeyGenError(e.to_string()))?; + let kp: Ed25519KeyPair = sk.into(); + Ok((kp.public().into(), RoochKeyPair::Ed25519(kp))) + } + BuiltinScheme::MultiEd25519 => { + todo!() + } + BuiltinScheme::Ecdsa => { + let child_xprv = XPrv::derive_from_path(seed, &path) + .map_err(|e| RoochError::SignatureKeyGenError(e.to_string()))?; + let kp = Secp256k1KeyPair::from( + Secp256k1PrivateKey::from_bytes(child_xprv.private_key().to_bytes().as_slice()) + .map_err(|e| RoochError::SignatureKeyGenError(e.to_string()))?, + ); + Ok((kp.public().into(), RoochKeyPair::Ecdsa(kp))) + } + BuiltinScheme::EcdsaRecoverable => { + let child_xprv = XPrv::derive_from_path(seed, &path) + .map_err(|e| RoochError::SignatureKeyGenError(e.to_string()))?; + let kp = Secp256k1RecoverableKeyPair::from( + Secp256k1RecoverablePrivateKey::from_bytes( + child_xprv.private_key().to_bytes().as_slice(), + ) + .map_err(|e| RoochError::SignatureKeyGenError(e.to_string()))?, + ); + Ok((kp.public().into(), RoochKeyPair::EcdsaRecoverable(kp))) + } + BuiltinScheme::Schnorr => { + let child_xprv = XPrv::derive_from_path(seed, &path) + .map_err(|e| RoochError::SignatureKeyGenError(e.to_string()))?; + let kp = SchnorrKeyPair::from( + SchnorrPrivateKey::from_bytes(child_xprv.private_key().to_bytes().as_slice()) + .map_err(|e| RoochError::SignatureKeyGenError(e.to_string()))?, + ); + Ok((kp.public().into(), RoochKeyPair::Schnorr(kp))) + } + } } pub fn validate_path( - _: &BuiltinScheme, + crypto_scheme: &BuiltinScheme, path: Option, ) -> Result { // The derivation path must be hardened at all levels with purpose = 44, coin_type = 784 - match path { - Some(p) => { - // The derivation path must be hardened at all levels with purpose = 44, coin_type = 784 - if let &[purpose, coin_type, account, change, address] = p.as_ref() { - if Some(purpose) == ChildNumber::new(DERVIATION_PATH_PURPOSE_ED25519, true).ok() - && Some(coin_type) == ChildNumber::new(DERIVATION_PATH_COIN_TYPE, true).ok() - && account.is_hardened() - && change.is_hardened() - && address.is_hardened() - { - Ok(p) - } else { - Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + match crypto_scheme { + BuiltinScheme::Ed25519 => { + match path { + Some(p) => { + // The derivation path must be hardened at all levels with purpose = 44, coin_type = 784 + if let &[purpose, coin_type, account, change, address] = p.as_ref() { + if Some(purpose) + == ChildNumber::new(DERVIATION_PATH_PURPOSE_ED25519, true).ok() + && Some(coin_type) + == ChildNumber::new(DERIVATION_PATH_COIN_TYPE_SUI, true).ok() + && account.is_hardened() + && change.is_hardened() + && address.is_hardened() + { + Ok(p) + } else { + Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + } + } else { + Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + } + } + None => Ok(format!( + "m/{DERVIATION_PATH_PURPOSE_ED25519}'/{DERIVATION_PATH_COIN_TYPE_SUI}'/0'/0'/0'" + ) + .parse() + .map_err(|_| RoochError::SignatureKeyGenError("Cannot parse path".to_owned()))?), + } + } + BuiltinScheme::MultiEd25519 => { + todo!() + } + BuiltinScheme::Ecdsa => { + match path { + Some(p) => { + // The derivation path must be hardened at all levels with purpose = 44, coin_type = 784 + if let &[purpose, coin_type, account, change, address] = p.as_ref() { + if Some(purpose) + == ChildNumber::new(DERVIATION_PATH_PURPOSE_ECDSA, true).ok() + && Some(coin_type) + == ChildNumber::new(DERIVATION_PATH_COIN_TYPE_SUI, true).ok() + && account.is_hardened() + && change.is_hardened() + && address.is_hardened() + { + Ok(p) + } else { + Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + } + } else { + Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + } + } + None => Ok(format!( + "m/{DERVIATION_PATH_PURPOSE_ECDSA}'/{DERIVATION_PATH_COIN_TYPE_SUI}'/0'/0'/0'" + ) + .parse() + .map_err(|_| RoochError::SignatureKeyGenError("Cannot parse path".to_owned()))?), + } + } + BuiltinScheme::EcdsaRecoverable => { + match path { + Some(p) => { + // The derivation path must be hardened at all levels with purpose = 44, coin_type = 784 + if let &[purpose, coin_type, account, change, address] = p.as_ref() { + if Some(purpose) + == ChildNumber::new(DERVIATION_PATH_PURPOSE_ECDSA, true).ok() + && Some(coin_type) + == ChildNumber::new(DERIVATION_PATH_COIN_TYPE_SUI, true).ok() + && account.is_hardened() + && change.is_hardened() + && address.is_hardened() + { + Ok(p) + } else { + Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + } + } else { + Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + } + } + None => Ok(format!( + "m/{DERVIATION_PATH_PURPOSE_ECDSA}'/{DERIVATION_PATH_COIN_TYPE_SUI}'/0'/0'/0'" + ) + .parse() + .map_err(|_| RoochError::SignatureKeyGenError("Cannot parse path".to_owned()))?), + } + } + BuiltinScheme::Schnorr => { + match path { + Some(p) => { + // The derivation path must be hardened at all levels with purpose = 44, coin_type = 784 + if let &[purpose, coin_type, account, change, address] = p.as_ref() { + if Some(purpose) == ChildNumber::new(DERVIATION_PATH_PURPOSE_SCHNORR, true).ok() + && Some(coin_type) == ChildNumber::new(DERIVATION_PATH_COIN_TYPE_NOSTR, true).ok() + && account.is_hardened() + && change.is_hardened() + && address.is_hardened() + { + Ok(p) + } else { + Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + } + } else { + Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + } } - } else { - Err(RoochError::SignatureKeyGenError("Invalid path".to_owned())) + None => Ok(format!( + // for a signle key + "m/{DERVIATION_PATH_PURPOSE_SCHNORR}'/{DERIVATION_PATH_COIN_TYPE_NOSTR}'/0'/0'/0'" + ) + .parse() + .map_err(|_| RoochError::SignatureKeyGenError("Cannot parse path".to_owned()))?), } } - None => Ok(format!( - "m/{DERVIATION_PATH_PURPOSE_ED25519}'/{DERIVATION_PATH_COIN_TYPE}'/0'/0'/0'" - ) - .parse() - .map_err(|_| RoochError::SignatureKeyGenError("Cannot parse path".to_owned()))?), } } pub fn generate_new_key( - key_scheme: BuiltinScheme, + crypto_scheme: BuiltinScheme, derivation_path: Option, word_length: Option, ) -> Result<(RoochAddress, RoochKeyPair, BuiltinScheme, String), anyhow::Error> { let mnemonic = Mnemonic::new(parse_word_length(word_length)?, Language::English); let seed = Seed::new(&mnemonic, ""); - match derive_key_pair_from_path(seed.as_bytes(), derivation_path, &key_scheme) { - Ok((address, kp)) => Ok((address, kp, key_scheme, mnemonic.phrase().to_string())), + match derive_key_pair_from_path(seed.as_bytes(), derivation_path, &crypto_scheme) { + Ok((address, kp)) => Ok((address, kp, crypto_scheme, mnemonic.phrase().to_string())), Err(e) => Err(anyhow!("Failed to generate keypair: {:?}", e)), } } diff --git a/crates/rooch-key/src/keystore.rs b/crates/rooch-key/src/keystore.rs index af3367cc32..399ccff828 100644 --- a/crates/rooch-key/src/keystore.rs +++ b/crates/rooch-key/src/keystore.rs @@ -36,63 +36,119 @@ pub enum Keystore { #[enum_dispatch] pub trait AccountKeystore: Send + Sync { - fn add_key(&mut self, keypair: RoochKeyPair) -> Result<(), anyhow::Error>; - fn keys(&self) -> Vec; - fn get_key(&self, address: &RoochAddress) -> Result<&RoochKeyPair, anyhow::Error>; + fn add_key_pair_by_scheme( + &mut self, + keypair: RoochKeyPair, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error>; + fn get_address_public_keys(&self) -> Vec<(RoochAddress, PublicKey)>; + fn get_public_key_by_scheme(&self, scheme: BuiltinScheme) -> Result; + fn get_key_pairs(&self, address: &RoochAddress) -> Result, anyhow::Error>; + fn get_key_pair_by_scheme( + &self, + address: &RoochAddress, + scheme: BuiltinScheme, + ) -> Result<&RoochKeyPair, signature::Error>; + fn update_key_pair_by_scheme( + &mut self, + address: &RoochAddress, + keypair: RoochKeyPair, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error>; + fn nullify_key_pair_by_scheme( + &mut self, + address: &RoochAddress, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error>; fn sign_hashed( &self, address: &RoochAddress, msg: &[u8], + scheme: BuiltinScheme, ) -> Result; fn sign_transaction( &self, address: &RoochAddress, msg: RoochTransactionData, + scheme: BuiltinScheme, ) -> Result; fn sign_secure( &self, address: &RoochAddress, msg: &T, + scheme: BuiltinScheme, ) -> Result where T: Serialize; fn addresses(&self) -> Vec { - self.keys().iter().map(|k| k.into()).collect() + self.get_address_public_keys() + .iter() + .map(|(address, _public_key)| *address) + .collect() } fn generate_and_add_new_key( &mut self, - key_scheme: BuiltinScheme, + crypto_scheme: BuiltinScheme, derivation_path: Option, word_length: Option, ) -> Result<(RoochAddress, String, BuiltinScheme), anyhow::Error> { let (address, kp, scheme, phrase) = - generate_new_key(key_scheme, derivation_path, word_length)?; - self.add_key(kp)?; + generate_new_key(crypto_scheme, derivation_path, word_length)?; + self.add_key_pair_by_scheme(kp, scheme)?; Ok((address, phrase, scheme)) } fn import_from_mnemonic( &mut self, phrase: &str, - key_scheme: BuiltinScheme, + crypto_scheme: BuiltinScheme, derivation_path: Option, ) -> Result { let mnemonic = Mnemonic::from_phrase(phrase, Language::English) .map_err(|e| anyhow::anyhow!("Invalid mnemonic phrase: {:?}", e))?; let seed = Seed::new(&mnemonic, ""); - match derive_key_pair_from_path(seed.as_bytes(), derivation_path, &key_scheme) { + match derive_key_pair_from_path(seed.as_bytes(), derivation_path, &crypto_scheme) { Ok((address, kp)) => { - self.add_key(kp)?; + self.add_key_pair_by_scheme(kp, crypto_scheme)?; Ok(address) } Err(e) => Err(anyhow!("error getting keypair {:?}", e)), } } + + fn update_address_with_key_pair_from_scheme( + &mut self, + address: &RoochAddress, + phrase: String, + crypto_scheme: BuiltinScheme, + derivation_path: Option, + ) -> Result { + let mnemonic = Mnemonic::from_phrase(phrase.as_str(), Language::English) + .map_err(|e| anyhow::anyhow!("Invalid mnemonic phrase: {:?}", e))?; + let seed = Seed::new(&mnemonic, ""); + match derive_key_pair_from_path(seed.as_bytes(), derivation_path, &crypto_scheme) { + Ok((_, kp)) => { + let public_key = kp.public(); + self.update_key_pair_by_scheme(address, kp, crypto_scheme)?; + Ok(public_key) + } + Err(e) => Err(anyhow!("error getting keypair {:?}", e)), + } + } + + fn nullify_address_with_key_pair_from_scheme( + &mut self, + address: &RoochAddress, + crypto_scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + self.nullify_key_pair_by_scheme(address, crypto_scheme)?; + Ok(()) + } } impl Display for Keystore { @@ -112,9 +168,164 @@ impl Display for Keystore { } } +#[derive(Default, Serialize, Deserialize)] +pub(crate) struct BaseKeyStore { + keys: BTreeMap>, +} + +impl AccountKeystore for BaseKeyStore { + fn get_key_pair_by_scheme( + &self, + address: &RoochAddress, + scheme: BuiltinScheme, + ) -> Result<&RoochKeyPair, signature::Error> { + if let Some(inner_map) = self.keys.get(address) { + if let Some(keypair) = inner_map.get(&scheme) { + Ok(keypair) + } else { + Err(signature::Error::from_source(format!( + "Scheme not found for address: [{:?}]", + scheme + ))) + } + } else { + Err(signature::Error::from_source(format!( + "Cannot find key for address: [{:?}]", + address + ))) + } + } + + fn sign_hashed( + &self, + address: &RoochAddress, + msg: &[u8], + scheme: BuiltinScheme, + ) -> Result { + Ok(Signature::new_hashed( + msg, + self.get_key_pair_by_scheme(address, scheme)?, + )) + } + + fn sign_secure( + &self, + address: &RoochAddress, + msg: &T, + scheme: BuiltinScheme, + ) -> Result + where + T: Serialize, + { + Ok(Signature::new_secure( + msg, + self.get_key_pair_by_scheme(address, scheme)?, + )) + } + + fn sign_transaction( + &self, + address: &RoochAddress, + msg: RoochTransactionData, + scheme: BuiltinScheme, + ) -> Result { + let kp = self + .get_key_pair_by_scheme(address, scheme) + .ok() + .ok_or_else(|| { + signature::Error::from_source(format!("Cannot find key for address: [{address}]")) + })?; + + let signature = Signature::new_hashed(msg.hash().as_bytes(), kp); + + let auth = match kp.public().scheme() { + BuiltinScheme::Ed25519 => authenticator::Authenticator::ed25519(signature), + BuiltinScheme::Ecdsa => authenticator::Authenticator::ecdsa(signature), + BuiltinScheme::EcdsaRecoverable => { + authenticator::Authenticator::ecdsa_recoverable(signature) + } + BuiltinScheme::MultiEd25519 => todo!(), + BuiltinScheme::Schnorr => authenticator::Authenticator::schnorr(signature), + }; + + Ok(RoochTransaction::new(msg, auth)) + } + + fn add_key_pair_by_scheme( + &mut self, + keypair: RoochKeyPair, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + let address: RoochAddress = (&keypair.public()).into(); + self.keys + .entry(address) + .or_insert_with(BTreeMap::new) + .insert(scheme, keypair); + Ok(()) + } + + fn get_public_key_by_scheme(&self, scheme: BuiltinScheme) -> Result { + for inner_map in self.keys.values() { + if let Some(keypair) = inner_map.get(&scheme) { + return Ok(keypair.public()); + } + } + Err(anyhow!("Cannot find key for scheme: [{:?}]", scheme)) + } + + fn get_address_public_keys(&self) -> Vec<(RoochAddress, PublicKey)> { + let mut result = Vec::new(); + for (address, inner_map) in &self.keys { + for keypair in inner_map.values() { + let public_key = keypair.public(); + result.push((*address, public_key)); + } + } + result + } + + fn get_key_pairs(&self, address: &RoochAddress) -> Result, anyhow::Error> { + match self.keys.get(address) { + Some(key_map) => { + // Collect references to RoochKeyPair objects from the inner map into a Vec. + let key_pairs: Vec<&RoochKeyPair> = key_map.values().collect(); + Ok(key_pairs) + } + None => Err(anyhow!("Cannot find key for address: [{address}]")), + } + } + + fn update_key_pair_by_scheme( + &mut self, + address: &RoochAddress, + keypair: RoochKeyPair, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + // First, get the inner map associated with the address + let inner_map = self.keys.entry(*address).or_insert_with(BTreeMap::new); + + // Insert or update the keypair for the specified scheme in the inner map + inner_map.insert(scheme, keypair); + Ok(()) + } + + fn nullify_key_pair_by_scheme( + &mut self, + address: &RoochAddress, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + // First, get the inner map associated with the address + let inner_map = self.keys.entry(*address).or_insert_with(BTreeMap::new); + + // Remove or nullify the keypair for the specified scheme in the inner map + inner_map.remove(&scheme); + Ok(()) + } +} + #[derive(Default)] pub struct FileBasedKeystore { - keys: BTreeMap, + keystore: BaseKeyStore, path: Option, } @@ -145,98 +356,138 @@ impl<'de> Deserialize<'de> for FileBasedKeystore { } impl AccountKeystore for FileBasedKeystore { + fn get_key_pair_by_scheme( + &self, + address: &RoochAddress, + scheme: BuiltinScheme, + ) -> Result<&RoochKeyPair, signature::Error> { + self.keystore.get_key_pair_by_scheme(address, scheme) + } + fn sign_hashed( &self, address: &RoochAddress, msg: &[u8], + scheme: BuiltinScheme, ) -> Result { - Ok(Signature::new_hashed( - msg, - self.keys.get(address).ok_or_else(|| { - signature::Error::from_source(format!("Cannot find key for address: [{address}]")) - })?, - )) + self.keystore.sign_hashed(address, msg, scheme) } - fn sign_secure(&self, address: &RoochAddress, msg: &T) -> Result + fn sign_secure( + &self, + address: &RoochAddress, + msg: &T, + scheme: BuiltinScheme, + ) -> Result where T: Serialize, { - Ok(Signature::new_secure( - msg, - self.keys.get(address).ok_or_else(|| { - signature::Error::from_source(format!("Cannot find key for address: [{address}]")) - })?, - )) + self.keystore.sign_secure(address, msg, scheme) } fn sign_transaction( &self, address: &RoochAddress, msg: RoochTransactionData, + scheme: BuiltinScheme, ) -> Result { - let pk = self.get_key(address).ok().ok_or_else(|| { - signature::Error::from_source(format!("Cannot find key for address: [{address}]")) - })?; + self.keystore.sign_transaction(address, msg, scheme) + } - let signature = Signature::new_hashed(msg.hash().as_bytes(), pk); + fn add_key_pair_by_scheme( + &mut self, + keypair: RoochKeyPair, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + self.keystore.add_key_pair_by_scheme(keypair, scheme)?; + //TODO should check test env at here? + if std::env::var_os("TEST_ENV").is_none() { + self.save()?; + } + Ok(()) + } - let auth = match pk.public().scheme() { - BuiltinScheme::Ed25519 => authenticator::Authenticator::ed25519(signature), - BuiltinScheme::Ecdsa => todo!(), - BuiltinScheme::MultiEd25519 => todo!(), - }; + fn get_public_key_by_scheme(&self, scheme: BuiltinScheme) -> Result { + self.keystore.get_public_key_by_scheme(scheme) + } - Ok(RoochTransaction::new(msg, auth)) + fn get_address_public_keys(&self) -> Vec<(RoochAddress, PublicKey)> { + self.keystore.get_address_public_keys() } - fn add_key(&mut self, keypair: RoochKeyPair) -> Result<(), anyhow::Error> { - match std::env::var_os("TEST_ENV") { - Some(_) => {} - None => { - let address: RoochAddress = (&keypair.public()).into(); - self.keys.insert(address, keypair); - self.save()?; - } - } - Ok(()) + fn get_key_pairs(&self, address: &RoochAddress) -> Result, anyhow::Error> { + self.keystore.get_key_pairs(address) } - fn keys(&self) -> Vec { - self.keys.values().map(|key| key.public()).collect() + fn update_key_pair_by_scheme( + &mut self, + address: &RoochAddress, + keypair: RoochKeyPair, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + self.keystore + .update_key_pair_by_scheme(address, keypair, scheme)?; + //TODO should check test env at here? + if std::env::var_os("TEST_ENV").is_none() { + self.save()?; + } + Ok(()) } - fn get_key(&self, address: &RoochAddress) -> Result<&RoochKeyPair, anyhow::Error> { - match self.keys.get(address) { - Some(key) => Ok(key), - None => Err(anyhow!("Cannot find key for address: [{address}]")), + fn nullify_key_pair_by_scheme( + &mut self, + address: &RoochAddress, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + self.keystore.nullify_key_pair_by_scheme(address, scheme)?; + //TODO should check test env at here? + if std::env::var_os("TEST_ENV").is_none() { + self.save()?; } + Ok(()) } } impl FileBasedKeystore { pub fn new(path: &PathBuf) -> Result { - let keys = if path.exists() { + let keys: BTreeMap> = if path.exists() { let reader = BufReader::new( File::open(path) - .map_err(|e| anyhow!("Can't open FileBasedKeystore from {:?}: {e}", path))?, + .map_err(|e| anyhow!("Can't open FileBasedKeystore from {:?}: {}", path, e))?, ); - let kp_strings: Vec = serde_json::from_reader(reader) - .map_err(|e| anyhow!("Can't deserialize FileBasedKeystore from {:?}: {e}", path))?; - kp_strings - .iter() - .map(|kpstr| { - let key = RoochKeyPair::decode_base64(kpstr); - key.map(|k| (Into::::into(&k.public()), k)) + let kp_strings: BTreeMap> = + serde_json::from_reader(reader).map_err(|e| { + anyhow!("Can't deserialize FileBasedKeystore from {:?}: {}", path, e) + })?; + + let keys: Result<_, anyhow::Error> = kp_strings + .into_iter() + .map(|(address, inner_map)| { + let inner_map_decoded: Result<_, anyhow::Error> = inner_map + .into_iter() + .map(|(scheme, key_str)| { + let keypair = match RoochKeyPair::decode_base64(&key_str) { + Ok(kp) => kp, + Err(err) => { + return Err(anyhow::anyhow!( + "Failed to decode base64: {}", + err + )); + } + }; + Ok((scheme, keypair)) + }) + .collect(); + inner_map_decoded.map(|inner_map| (address, inner_map)) }) - .collect::, _>>() - .map_err(|e| anyhow::anyhow!("Invalid Keypair file {:#?} {:?}", e, path))? + .collect(); + keys.map_err(|e| anyhow!("Invalid Keypair file {:#?} {:?}", e, path))? } else { BTreeMap::new() }; Ok(Self { - keys, + keystore: BaseKeyStore { keys }, path: Some(path.to_path_buf()), }) } @@ -247,101 +498,187 @@ impl FileBasedKeystore { pub fn save(&self) -> Result<(), anyhow::Error> { if let Some(path) = &self.path { - let store = serde_json::to_string_pretty( - &self - .keys - .values() - .map(EncodeDecodeBase64::encode_base64) - .collect::>(), - ) - .unwrap(); - fs::write(path, store)? + let store = serde_json::to_string_pretty(&self.encode_keys())?; + fs::write(path, store)?; } Ok(()) } + fn encode_keys(&self) -> BTreeMap> { + self.keystore + .keys + .iter() + .map(|(address, inner_map)| { + let inner_map_encoded = inner_map + .iter() + .map(|(scheme, keypair)| (*scheme, EncodeDecodeBase64::encode_base64(keypair))) + .collect(); + (*address, inner_map_encoded) + }) + .collect() + } + pub fn key_pairs(&self) -> Vec<&RoochKeyPair> { - self.keys.values().collect() + self.keystore + .keys + .values() + .flat_map(|inner_map| inner_map.values()) + .collect() } } #[derive(Default, Serialize, Deserialize)] pub struct InMemKeystore { - keys: BTreeMap, + keystore: BaseKeyStore, } impl AccountKeystore for InMemKeystore { - fn sign_hashed( + fn sign_secure( &self, address: &RoochAddress, - msg: &[u8], - ) -> Result { - Ok(Signature::new_hashed( - msg, - self.keys.get(address).ok_or_else(|| { - signature::Error::from_source(format!("Cannot find key for address: [{address}]")) - })?, - )) + msg: &T, + scheme: BuiltinScheme, + ) -> Result + where + T: Serialize, + { + self.keystore.sign_secure(address, msg, scheme) } fn sign_transaction( &self, address: &RoochAddress, msg: RoochTransactionData, + scheme: BuiltinScheme, ) -> Result { - let pk = self.get_key(address).ok().ok_or_else(|| { - signature::Error::from_source(format!("Cannot find key for address: [{address}]")) - })?; + self.keystore.sign_transaction(address, msg, scheme) + } - let signature = Signature::new_hashed(msg.hash().as_bytes(), pk); + fn add_key_pair_by_scheme( + &mut self, + keypair: RoochKeyPair, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + self.keystore.add_key_pair_by_scheme(keypair, scheme) + } - let auth = match pk.public().scheme() { - BuiltinScheme::Ed25519 => authenticator::Authenticator::ed25519(signature), - BuiltinScheme::Ecdsa => todo!(), - BuiltinScheme::MultiEd25519 => todo!(), - }; + fn get_public_key_by_scheme(&self, scheme: BuiltinScheme) -> Result { + self.keystore.get_public_key_by_scheme(scheme) + } - Ok(RoochTransaction::new(msg, auth)) + fn get_address_public_keys(&self) -> Vec<(RoochAddress, PublicKey)> { + self.keystore.get_address_public_keys() } - fn sign_secure(&self, address: &RoochAddress, msg: &T) -> Result - where - T: Serialize, - { - Ok(Signature::new_secure( - msg, - self.keys.get(address).ok_or_else(|| { - signature::Error::from_source(format!("Cannot find key for address: [{address}]")) - })?, - )) + fn get_key_pairs(&self, address: &RoochAddress) -> Result, anyhow::Error> { + self.keystore.get_key_pairs(address) } - fn add_key(&mut self, keypair: RoochKeyPair) -> Result<(), anyhow::Error> { - let address: RoochAddress = (&keypair.public()).into(); - self.keys.insert(address, keypair); - Ok(()) + fn update_key_pair_by_scheme( + &mut self, + address: &RoochAddress, + keypair: RoochKeyPair, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + self.keystore + .update_key_pair_by_scheme(address, keypair, scheme) } - fn keys(&self) -> Vec { - self.keys.values().map(|key| key.public()).collect() + fn nullify_key_pair_by_scheme( + &mut self, + address: &RoochAddress, + scheme: BuiltinScheme, + ) -> Result<(), anyhow::Error> { + self.keystore.nullify_key_pair_by_scheme(address, scheme) } - fn get_key(&self, address: &RoochAddress) -> Result<&RoochKeyPair, anyhow::Error> { - match self.keys.get(address) { - Some(key) => Ok(key), - None => Err(anyhow!("Cannot find key for address: [{address}]")), - } + fn get_key_pair_by_scheme( + &self, + address: &RoochAddress, + scheme: BuiltinScheme, + ) -> Result<&RoochKeyPair, signature::Error> { + self.keystore.get_key_pair_by_scheme(address, scheme) + } + + fn sign_hashed( + &self, + address: &RoochAddress, + msg: &[u8], + scheme: BuiltinScheme, + ) -> Result { + self.keystore.sign_hashed(address, msg, scheme) } } impl InMemKeystore { - pub fn new_insecure_for_tests(initial_key_number: usize) -> Self { + pub fn new_ed25519_insecure_for_tests(initial_key_number: usize) -> Self { let mut rng = StdRng::from_seed([0; 32]); let keys = (0..initial_key_number) .map(|_| get_key_pair_from_rng(&mut rng)) - .map(|(ad, k)| (ad, RoochKeyPair::Ed25519(k))) - .collect::>(); + .map(|(ad, k)| { + ( + ad, + BTreeMap::from_iter(vec![(BuiltinScheme::Ed25519, RoochKeyPair::Ed25519(k))]), + ) + }) + .collect::>>(); + + Self { + keystore: BaseKeyStore { keys }, + } + } - Self { keys } + pub fn new_ecdsa_insecure_for_tests(initial_key_number: usize) -> Self { + let mut rng = StdRng::from_seed([0; 32]); + let keys = (0..initial_key_number) + .map(|_| get_key_pair_from_rng(&mut rng)) + .map(|(ad, k)| { + ( + ad, + BTreeMap::from_iter(vec![(BuiltinScheme::Ecdsa, RoochKeyPair::Ecdsa(k))]), + ) + }) + .collect::>>(); + + Self { + keystore: BaseKeyStore { keys }, + } + } + + pub fn new_ecdsa_recoverable_insecure_for_tests(initial_key_number: usize) -> Self { + let mut rng = StdRng::from_seed([0; 32]); + let keys = (0..initial_key_number) + .map(|_| get_key_pair_from_rng(&mut rng)) + .map(|(ad, k)| { + ( + ad, + BTreeMap::from_iter(vec![( + BuiltinScheme::EcdsaRecoverable, + RoochKeyPair::EcdsaRecoverable(k), + )]), + ) + }) + .collect::>>(); + + Self { + keystore: BaseKeyStore { keys }, + } + } + + pub fn new_schnorr_insecure_for_tests(initial_key_number: usize) -> Self { + let mut rng = StdRng::from_seed([0; 32]); + let keys = (0..initial_key_number) + .map(|_| get_key_pair_from_rng(&mut rng)) + .map(|(ad, k)| { + ( + ad, + BTreeMap::from_iter(vec![(BuiltinScheme::Schnorr, RoochKeyPair::Schnorr(k))]), + ) + }) + .collect::>>(); + + Self { + keystore: BaseKeyStore { keys }, + } } } diff --git a/crates/rooch-open-rpc-spec/schemas/openrpc.json b/crates/rooch-open-rpc-spec/schemas/openrpc.json index 086910dd09..a567fa6645 100644 --- a/crates/rooch-open-rpc-spec/schemas/openrpc.json +++ b/crates/rooch-open-rpc-spec/schemas/openrpc.json @@ -295,6 +295,74 @@ } } }, + { + "name": "rooch_listAnnotatedStates", + "description": "List the annotated states by access_path The annotated states include the decoded move value of the state", + "params": [ + { + "name": "access_path", + "required": true, + "schema": { + "$ref": "#/components/schemas/moveos_types::access_path::AccessPath" + } + }, + { + "name": "cursor", + "schema": { + "$ref": "#/components/schemas/alloc::vec::Vec" + } + }, + { + "name": "limit", + "schema": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + } + ], + "result": { + "name": "ListAnnotatedStatesPageView", + "required": true, + "schema": { + "$ref": "#/components/schemas/PageView_for_Nullable_AnnotatedStateView_and_alloc::vec::Vec" + } + } + }, + { + "name": "rooch_listStates", + "description": "List the states by access_path", + "params": [ + { + "name": "access_path", + "required": true, + "schema": { + "$ref": "#/components/schemas/moveos_types::access_path::AccessPath" + } + }, + { + "name": "cursor", + "schema": { + "$ref": "#/components/schemas/alloc::vec::Vec" + } + }, + { + "name": "limit", + "schema": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + } + ], + "result": { + "name": "ListStatesPageView", + "required": true, + "schema": { + "$ref": "#/components/schemas/PageView_for_Nullable_StateView_and_alloc::vec::Vec" + } + } + }, { "name": "rooch_sendRawTransaction", "description": "Send the signed transaction in bcs hex format This method does not block waiting for the transaction to be executed.", @@ -1035,6 +1103,78 @@ } } }, + "PageView_for_Nullable_AnnotatedStateView_and_alloc::vec::Vec": { + "description": "`next_cursor` points to the last item in the page; Reading with `next_cursor` will start from the next item after `next_cursor` if `next_cursor` is `Some`, otherwise it will start from the first item.", + "type": "object", + "required": [ + "data", + "has_next_page" + ], + "properties": { + "data": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/AnnotatedStateView" + }, + { + "type": "null" + } + ] + } + }, + "has_next_page": { + "type": "boolean" + }, + "next_cursor": { + "anyOf": [ + { + "$ref": "#/components/schemas/alloc::vec::Vec" + }, + { + "type": "null" + } + ] + } + } + }, + "PageView_for_Nullable_StateView_and_alloc::vec::Vec": { + "description": "`next_cursor` points to the last item in the page; Reading with `next_cursor` will start from the next item after `next_cursor` if `next_cursor` is `Some`, otherwise it will start from the first item.", + "type": "object", + "required": [ + "data", + "has_next_page" + ], + "properties": { + "data": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/StateView" + }, + { + "type": "null" + } + ] + } + }, + "has_next_page": { + "type": "boolean" + }, + "next_cursor": { + "anyOf": [ + { + "$ref": "#/components/schemas/alloc::vec::Vec" + }, + { + "type": "null" + } + ] + } + } + }, "PageView_for_Nullable_TransactionExecutionInfoView_and_uint128": { "description": "`next_cursor` points to the last item in the page; Reading with `next_cursor` will start from the next item after `next_cursor` if `next_cursor` is `Some`, otherwise it will start from the first item.", "type": "object", diff --git a/crates/rooch-rpc-api/src/api/mod.rs b/crates/rooch-rpc-api/src/api/mod.rs index ff9756f387..a976711c9a 100644 --- a/crates/rooch-rpc-api/src/api/mod.rs +++ b/crates/rooch-rpc-api/src/api/mod.rs @@ -8,6 +8,7 @@ pub mod rooch_api; pub mod wallet_api; pub const MAX_RESULT_LIMIT: u64 = 50; +pub const MAX_RESULT_LIMIT_USIZE: usize = MAX_RESULT_LIMIT as usize; // pub fn validate_limit(limit: Option, max: usize) -> Result { // match limit { diff --git a/crates/rooch-rpc-api/src/api/rooch_api.rs b/crates/rooch-rpc-api/src/api/rooch_api.rs index ab5ae03a4e..0f2cd5a9b6 100644 --- a/crates/rooch-rpc-api/src/api/rooch_api.rs +++ b/crates/rooch-rpc-api/src/api/rooch_api.rs @@ -4,8 +4,8 @@ use crate::jsonrpc_types::{ AccessPathView, AnnotatedEventView, AnnotatedFunctionReturnValueView, AnnotatedStateView, EventFilterView, EventPageView, ExecuteTransactionResponseView, FunctionCallView, H256View, - StateView, StrView, StructTagView, TransactionExecutionInfoView, TransactionInfoPageView, - TransactionView, + ListAnnotatedStatesPageView, ListStatesPageView, StateView, StrView, StructTagView, + TransactionExecutionInfoView, TransactionInfoPageView, TransactionView, }; use jsonrpsee::core::RpcResult; use jsonrpsee::proc_macros::rpc; @@ -47,6 +47,25 @@ pub trait RoochAPI { access_path: AccessPathView, ) -> RpcResult>>; + /// List the states by access_path + #[method(name = "listStates")] + async fn list_states( + &self, + access_path: AccessPathView, + cursor: Option>>, + limit: Option, + ) -> RpcResult; + + /// List the annotated states by access_path + /// The annotated states include the decoded move value of the state + #[method(name = "listAnnotatedStates")] + async fn list_annotated_states( + &self, + access_path: AccessPathView, + cursor: Option>>, + limit: Option, + ) -> RpcResult; + /// Get the events by event handle id #[method(name = "getEventsByEventHandle")] async fn get_events_by_event_handle( diff --git a/crates/rooch-rpc-api/src/jsonrpc_types/execute_tx_response.rs b/crates/rooch-rpc-api/src/jsonrpc_types/execute_tx_response.rs index 5f6db45f24..f7ef90dd33 100644 --- a/crates/rooch-rpc-api/src/jsonrpc_types/execute_tx_response.rs +++ b/crates/rooch-rpc-api/src/jsonrpc_types/execute_tx_response.rs @@ -38,7 +38,7 @@ impl FromStr for AbortLocationView { } } -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(tag = "type", rename_all = "lowercase")] pub enum KeptVMStatusView { Executed, diff --git a/crates/rooch-rpc-api/src/jsonrpc_types/move_types.rs b/crates/rooch-rpc-api/src/jsonrpc_types/move_types.rs index ecd731fb6a..87fcbcd407 100644 --- a/crates/rooch-rpc-api/src/jsonrpc_types/move_types.rs +++ b/crates/rooch-rpc-api/src/jsonrpc_types/move_types.rs @@ -44,7 +44,6 @@ pub type AccessPathView = StrView; pub type AccountAddressView = StrView; impl_str_view_for! {TypeTag StructTag FunctionId AccessPath} -// impl_str_view_for! {TypeTag StructTag ModuleId FunctionId} #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct AnnotatedMoveStructView { diff --git a/crates/rooch-rpc-api/src/jsonrpc_types/rooch_types.rs b/crates/rooch-rpc-api/src/jsonrpc_types/rooch_types.rs index fa649718e4..1677ddeed2 100644 --- a/crates/rooch-rpc-api/src/jsonrpc_types/rooch_types.rs +++ b/crates/rooch-rpc-api/src/jsonrpc_types/rooch_types.rs @@ -3,7 +3,8 @@ use crate::jsonrpc_types::{ move_types::{MoveActionTypeView, MoveActionView}, - AnnotatedMoveStructView, EventView, H256View, StrView, TransactionExecutionInfoView, + AnnotatedMoveStructView, AnnotatedStateView, EventView, H256View, StateView, StrView, + TransactionExecutionInfoView, }; use moveos_types::event::AnnotatedMoveOSEvent; use rooch_types::transaction::{AbstractTransaction, TransactionType, TypedTransaction}; @@ -14,6 +15,8 @@ use super::AccountAddressView; pub type EventPageView = PageView, u64>; pub type TransactionInfoPageView = PageView, u128>; +pub type ListStatesPageView = PageView, StrView>>; +pub type ListAnnotatedStatesPageView = PageView, StrView>>; /// `next_cursor` points to the last item in the page; /// Reading with `next_cursor` will start from the next item after `next_cursor` if diff --git a/crates/rooch-rpc-client/src/wallet_context.rs b/crates/rooch-rpc-client/src/wallet_context.rs index ae61c6774f..cffcbb70cc 100644 --- a/crates/rooch-rpc-client/src/wallet_context.rs +++ b/crates/rooch-rpc-client/src/wallet_context.rs @@ -8,7 +8,7 @@ use move_core_types::account_address::AccountAddress; use moveos_types::transaction::MoveAction; use rooch_config::{rooch_config_dir, Config, PersistedConfig, ROOCH_CLIENT_CONFIG}; use rooch_key::keystore::AccountKeystore; -use rooch_rpc_api::jsonrpc_types::ExecuteTransactionResponseView; +use rooch_rpc_api::jsonrpc_types::{ExecuteTransactionResponseView, KeptVMStatusView}; use rooch_types::address::RoochAddress; use rooch_types::crypto::{BuiltinScheme, Signature}; use rooch_types::error::{RoochError, RoochResult}; @@ -99,17 +99,25 @@ impl WalletContext { &self, sender: RoochAddress, action: MoveAction, + scheme: BuiltinScheme, ) -> RoochResult { - let pk = self.config.keystore.get_key(&sender).ok().ok_or_else(|| { - RoochError::SignMessageError(format!("Cannot find key for address: [{sender}]")) - })?; + let kp = self + .config + .keystore + .get_key_pair_by_scheme(&sender, scheme) + .ok() + .ok_or_else(|| { + RoochError::SignMessageError(format!("Cannot find key for address: [{sender}]")) + })?; let tx_data = self.build_tx_data(sender, action).await?; - let signature = Signature::new_hashed(tx_data.hash().as_bytes(), pk); - let auth = match pk.public().scheme() { + let signature = Signature::new_hashed(tx_data.hash().as_bytes(), kp); + let auth = match kp.public().scheme() { BuiltinScheme::Ed25519 => Authenticator::ed25519(signature), - BuiltinScheme::Ecdsa => todo!(), + BuiltinScheme::Ecdsa => Authenticator::ecdsa(signature), + BuiltinScheme::EcdsaRecoverable => Authenticator::ecdsa_recoverable(signature), BuiltinScheme::MultiEd25519 => todo!(), + BuiltinScheme::Schnorr => Authenticator::schnorr(signature), }; Ok(RoochTransaction::new(tx_data, auth)) } @@ -129,8 +137,9 @@ impl WalletContext { &self, sender: RoochAddress, action: MoveAction, + scheme: BuiltinScheme, ) -> RoochResult { - let tx = self.sign(sender, action).await?; + let tx = self.sign(sender, action, scheme).await?; self.execute(tx).await } @@ -152,4 +161,18 @@ impl WalletContext { Ok(address) } } + + pub fn assert_execute_success( + &self, + result: ExecuteTransactionResponseView, + ) -> RoochResult { + if KeptVMStatusView::Executed != result.execution_info.status { + Err(RoochError::TransactionError(format!( + "Transaction execution failed: {:?}", + result.execution_info.status + ))) + } else { + Ok(result) + } + } } diff --git a/crates/rooch-rpc-server/Cargo.toml b/crates/rooch-rpc-server/Cargo.toml index a42b6302b8..687ad46b03 100644 --- a/crates/rooch-rpc-server/Cargo.toml +++ b/crates/rooch-rpc-server/Cargo.toml @@ -27,6 +27,8 @@ jsonrpsee = { workspace = true } serde = { workspace = true } serde_yaml = { workspace = true } serde_json = { workspace = true } +tower = { workspace = true } +tower-http = { workspace = true } thiserror = { workspace = true } tokio = { features = ["full"], workspace = true } tonic = { workspace = true } @@ -36,6 +38,7 @@ schemars = { workspace = true } serde_with = { workspace = true } rand = { workspace = true } fastcrypto = { workspace = true } +hyper = { workspace = true } move-core-types = { workspace = true } move-resource-viewer = { workspace = true } @@ -44,6 +47,8 @@ move-binary-format = { workspace = true } moveos-store = { workspace = true } moveos-types = { workspace = true } move-bytecode-utils = { workspace = true } +raw-store = { workspace = true } +moveos-config = { workspace = true } rooch-config = { workspace = true } rooch-types = { workspace = true } diff --git a/crates/rooch-rpc-server/src/lib.rs b/crates/rooch-rpc-server/src/lib.rs index ddde5958f8..e7f37bb873 100644 --- a/crates/rooch-rpc-server/src/lib.rs +++ b/crates/rooch-rpc-server/src/lib.rs @@ -8,10 +8,18 @@ use crate::service::RpcService; use anyhow::Result; use coerce::actor::scheduler::timer::Timer; use coerce::actor::{system::ActorSystem, IntoActor}; +use hyper::header::HeaderValue; +use hyper::Method; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::server::ServerBuilder; use jsonrpsee::RpcModule; +use moveos_config::store_config::RocksdbConfig; +use moveos_store::config_store::ConfigStore; +use moveos_store::{MoveOSDB, MoveOSStore}; +use raw_store::rocks::RocksDB; +use raw_store::StoreInstance; use rooch_config::rpc::server_config::ServerConfig; +use rooch_config::store_config::StoreConfig; use rooch_executor::actor::executor::ExecutorActor; use rooch_executor::proxy::ExecutorProxy; use rooch_key::key_derive::generate_new_key; @@ -21,11 +29,13 @@ use rooch_proposer::proxy::ProposerProxy; use rooch_rpc_api::api::RoochRpcModule; use rooch_sequencer::actor::sequencer::SequencerActor; use rooch_sequencer::proxy::SequencerProxy; -use rooch_store::RoochDB; +use rooch_store::RoochStore; use serde_json::json; +use std::env; use std::fmt::Debug; use std::net::SocketAddr; use std::time::Duration; +use tower_http::cors::{AllowOrigin, CorsLayer}; use tracing::info; pub mod server; @@ -67,8 +77,8 @@ impl Service { Self { handle: None } } - pub async fn start(&mut self) -> Result<()> { - self.handle = Some(start_server().await?); + pub async fn start(&mut self, is_mock_storage: bool) -> Result<()> { + self.handle = Some(start_server(is_mock_storage).await?); Ok(()) } @@ -103,27 +113,27 @@ impl RpcModuleBuilder { } // Start json-rpc server -pub async fn start_server() -> Result { +pub async fn start_server(is_mock_storage: bool) -> Result { tracing_subscriber::fmt::init(); let config = ServerConfig::default(); let addr: SocketAddr = format!("{}:{}", config.host, config.port).parse()?; - let rooch_db = RoochDB::new_with_memory_store(); let actor_system = ActorSystem::global_system(); + //Init store + let (moveos_store, rooch_store) = init_stroage(is_mock_storage)?; + // Init executor - let executor = ExecutorActor::new(rooch_db.clone())? + let executor = ExecutorActor::new(moveos_store, rooch_store.clone())? .into_actor(Some("Executor"), &actor_system) .await?; let executor_proxy = ExecutorProxy::new(executor.into()); // Init sequencer //TODO load from config - let (_, kp, _, _) = generate_new_key(rooch_types::crypto::BuiltinScheme::Ed25519, None, None)?; - // let rooch_db = RoochDB::new_with_memory_store(); - let sequencer = SequencerActor::new(kp, rooch_db) + let sequencer = SequencerActor::new(kp, rooch_store) .into_actor(Some("Sequencer"), &actor_system) .await?; let sequencer_proxy = SequencerProxy::new(sequencer.into()); @@ -145,8 +155,33 @@ pub async fn start_server() -> Result { let rpc_service = RpcService::new(executor_proxy, sequencer_proxy, proposer_proxy); + let acl = match env::var("ACCESS_CONTROL_ALLOW_ORIGIN") { + Ok(value) => { + let allow_hosts = value + .split(',') + .map(HeaderValue::from_str) + .collect::, _>>()?; + AllowOrigin::list(allow_hosts) + } + _ => AllowOrigin::any(), + }; + info!(?acl); + + let cors: CorsLayer = CorsLayer::new() + // Allow `POST` when accessing the resource + .allow_methods([Method::POST]) + // Allow requests from any origin + .allow_origin(acl) + .allow_headers([hyper::header::CONTENT_TYPE]); + + // TODO: tracing + let middleware = tower::ServiceBuilder::new().layer(cors); + // Build server - let server = ServerBuilder::default().build(&addr).await?; + let server = ServerBuilder::default() + .set_middleware(middleware) + .build(&addr) + .await?; let mut rpc_module_builder = RpcModuleBuilder::new(); rpc_module_builder @@ -184,3 +219,44 @@ fn _build_rpc_api(mut rpc_module: RpcModule) -> Rpc rpc_module } + +fn init_stroage(is_mock_storage: bool) -> Result<(MoveOSStore, RoochStore)> { + let (rooch_db_path, moveos_db_path) = if !is_mock_storage { + ( + StoreConfig::get_rooch_store_dir(), + StoreConfig::get_moveos_store_dir(), + ) + } else { + ( + moveos_config::temp_dir().path().to_path_buf(), + moveos_config::temp_dir().path().to_path_buf(), + ) + }; + + //Init store + let moveosdb = MoveOSDB::new(StoreInstance::new_db_instance( + RocksDB::new( + moveos_db_path, + moveos_store::StoreMeta::get_column_family_names().to_vec(), + RocksdbConfig::default(), + None, + ) + .unwrap(), + ))?; + let lastest_state_root = moveosdb + .config_store + .get_startup_info()? + .map(|info| info.state_root_hash); + let moveos_store = MoveOSStore::new_with_root(moveosdb, lastest_state_root).unwrap(); + + let rooch_store = RoochStore::new(StoreInstance::new_db_instance( + RocksDB::new( + rooch_db_path, + rooch_store::StoreMeta::get_column_family_names().to_vec(), + RocksdbConfig::default(), + None, + ) + .unwrap(), + ))?; + Ok((moveos_store, rooch_store)) +} diff --git a/crates/rooch-rpc-server/src/server/rooch_server.rs b/crates/rooch-rpc-server/src/server/rooch_server.rs index 529031113b..43256dcee5 100644 --- a/crates/rooch-rpc-server/src/server/rooch_server.rs +++ b/crates/rooch-rpc-server/src/server/rooch_server.rs @@ -7,16 +7,17 @@ use jsonrpsee::{ RpcModule, }; use moveos_types::h256::H256; -use rooch_rpc_api::api::RoochRpcModule; +use rooch_rpc_api::api::{RoochRpcModule, MAX_RESULT_LIMIT_USIZE}; use rooch_rpc_api::jsonrpc_types::{ AccessPathView, AnnotatedEventView, AnnotatedFunctionReturnValueView, AnnotatedStateView, EventFilterView, EventPageView, ExecuteTransactionResponseView, FunctionCallView, H256View, - StateView, StrView, StructTagView, TransactionExecutionInfoView, TransactionInfoPageView, - TransactionView, + ListAnnotatedStatesPageView, ListStatesPageView, StateView, StrView, StructTagView, + TransactionExecutionInfoView, TransactionInfoPageView, TransactionView, }; use rooch_rpc_api::{api::rooch_api::RoochAPIServer, api::MAX_RESULT_LIMIT}; use rooch_types::transaction::rooch::RoochTransaction; use rooch_types::transaction::{AbstractTransaction, TypedTransaction}; +use std::cmp::min; pub struct RoochServer { rpc_service: RpcService, @@ -87,6 +88,78 @@ impl RoochAPIServer for RoochServer { .collect()) } + async fn list_states( + &self, + access_path: AccessPathView, + cursor: Option>>, + limit: Option, + ) -> RpcResult { + let limit_of = min( + limit.unwrap_or(MAX_RESULT_LIMIT_USIZE), + MAX_RESULT_LIMIT_USIZE, + ); + let cursor_of = cursor.clone().map(|v| v.0); + let mut data: Vec, StateView)>> = self + .rpc_service + .list_states(access_path.into(), cursor_of, limit_of + 1) + .await? + .into_iter() + .map(|item| item.map(|(key, state)| (key, StateView::from(state)))) + .collect::>(); + + let has_next_page = data.len() > limit_of; + data.truncate(limit_of); + let next_cursor = data.last().map_or(cursor, |item| { + item.clone().map(|(key, _state)| StrView(key)) + }); + let result = data + .into_iter() + .map(|item| item.map(|(_key, state)| state)) + .collect(); + + Ok(ListStatesPageView { + data: result, + next_cursor, + has_next_page, + }) + } + + async fn list_annotated_states( + &self, + access_path: AccessPathView, + cursor: Option>>, + limit: Option, + ) -> RpcResult { + let limit_of = min( + limit.unwrap_or(MAX_RESULT_LIMIT_USIZE), + MAX_RESULT_LIMIT_USIZE, + ); + let cursor_of = cursor.clone().map(|v| v.0); + let mut data: Vec, AnnotatedStateView)>> = self + .rpc_service + .list_annotated_states(access_path.into(), cursor_of, limit_of + 1) + .await? + .into_iter() + .map(|item| item.map(|(key, state)| (key, AnnotatedStateView::from(state)))) + .collect::>(); + + let has_next_page = data.len() > limit_of; + data.truncate(limit_of); + let next_cursor = data.last().map_or(cursor, |item| { + item.clone().map(|(key, _state)| StrView(key)) + }); + let result = data + .into_iter() + .map(|item| item.map(|(_key, state)| state)) + .collect(); + + Ok(ListAnnotatedStatesPageView { + data: result, + next_cursor, + has_next_page, + }) + } + async fn get_events_by_event_handle( &self, event_handle_type: StructTagView, @@ -94,8 +167,8 @@ impl RoochAPIServer for RoochServer { limit: Option, ) -> RpcResult { // NOTE: fetch one more object to check if there is next page - let limit_of = limit.unwrap_or(MAX_RESULT_LIMIT); - let mut result: Vec> = self + let limit_of = min(limit.unwrap_or(MAX_RESULT_LIMIT), MAX_RESULT_LIMIT); + let mut data: Vec> = self .rpc_service .get_events_by_event_handle(event_handle_type.into(), cursor, limit_of + 1) .await? @@ -103,14 +176,14 @@ impl RoochAPIServer for RoochServer { .map(|event| event.map(AnnotatedEventView::from)) .collect(); - let has_next_page = (result.len() as u64) > limit_of; - result.truncate(limit_of as usize); - let next_cursor = result.last().map_or(cursor, |event| { + let has_next_page = (data.len() as u64) > limit_of; + data.truncate(limit_of as usize); + let next_cursor = data.last().map_or(cursor, |event| { Some(event.clone().unwrap().event.event_id.event_seq) }); Ok(EventPageView { - data: result, + data, next_cursor, has_next_page, }) diff --git a/crates/rooch-rpc-server/src/service/mod.rs b/crates/rooch-rpc-server/src/service/mod.rs index b755b0930a..da432fa02b 100644 --- a/crates/rooch-rpc-server/src/service/mod.rs +++ b/crates/rooch-rpc-server/src/service/mod.rs @@ -89,6 +89,26 @@ impl RpcService { self.executor.get_annotated_states(access_path).await } + pub async fn list_states( + &self, + access_path: AccessPath, + cursor: Option>, + limit: usize, + ) -> Result, State)>>> { + self.executor.list_states(access_path, cursor, limit).await + } + + pub async fn list_annotated_states( + &self, + access_path: AccessPath, + cursor: Option>, + limit: usize, + ) -> Result, AnnotatedState)>>> { + self.executor + .list_annotated_states(access_path, cursor, limit) + .await + } + /// Sign a message with the private key of the given address. pub async fn sign(&self, _address: RoochAddress, _message: Vec) -> Result> { bail!("Not implemented") diff --git a/crates/rooch-sequencer/src/actor/sequencer.rs b/crates/rooch-sequencer/src/actor/sequencer.rs index 68d9b8c2b6..efc2da3bd0 100644 --- a/crates/rooch-sequencer/src/actor/sequencer.rs +++ b/crates/rooch-sequencer/src/actor/sequencer.rs @@ -8,7 +8,8 @@ use anyhow::Result; use async_trait::async_trait; use coerce::actor::{context::ActorContext, message::Handler, Actor}; use moveos_types::h256; -use rooch_store::RoochDB; +use rooch_store::transaction_store::TransactionStore; +use rooch_store::RoochStore; use rooch_types::{ crypto::{RoochKeyPair, Signature}, transaction::AbstractTransaction, @@ -21,15 +22,15 @@ use rooch_types::{ pub struct SequencerActor { last_order: u128, sequencer_key: RoochKeyPair, - rooch_db: RoochDB, + rooch_store: RoochStore, } impl SequencerActor { - pub fn new(sequencer_key: RoochKeyPair, rooch_db: RoochDB) -> Self { + pub fn new(sequencer_key: RoochKeyPair, rooch_store: RoochStore) -> Self { Self { last_order: 0, sequencer_key, - rooch_db, + rooch_store, } } } @@ -52,10 +53,21 @@ impl Handler for SequencerActor { let tx_order_signature = Signature::new_hashed(&witness_hash.0, &self.sequencer_key).into(); self.last_order = tx_order; - self.rooch_db.transaction_store.save_transaction(tx); - self.rooch_db - .transaction_store - .save_tx_seq_info_mapping(tx_order, hash); + let _ = self.rooch_store.save_transaction(tx).map_err(|e| { + anyhow::anyhow!( + "TransactionSequenceMessage handler save transaction failed: {}", + e + ) + }); + let _ = self + .rooch_store + .save_tx_seq_info_mapping(tx_order, hash) + .map_err(|e| { + anyhow::anyhow!( + "TransactionSequenceMessage handler save tx seq mapping failed: {}", + e + ) + }); let tx_accumulator_root = H256::random(); Ok(TransactionSequenceInfo { @@ -73,7 +85,7 @@ impl Handler for SequencerActor { msg: TransactionByHashMessage, _ctx: &mut ActorContext, ) -> Result> { - Ok(self.rooch_db.transaction_store.get_tx_by_hash(msg.hash)) + self.rooch_store.get_tx_by_hash(msg.hash) } } @@ -84,9 +96,6 @@ impl Handler for SequencerActor { msg: TransactionByIndexMessage, _ctx: &mut ActorContext, ) -> Result> { - Ok(self - .rooch_db - .transaction_store - .get_tx_by_index(msg.start, msg.limit)) + self.rooch_store.get_tx_by_index(msg.start, msg.limit) } } diff --git a/crates/rooch-store/Cargo.toml b/crates/rooch-store/Cargo.toml index 18eb4a3f38..cdc7e24bbb 100644 --- a/crates/rooch-store/Cargo.toml +++ b/crates/rooch-store/Cargo.toml @@ -21,9 +21,13 @@ serde = { workspace = true } serde_bytes = { workspace = true } hex = { workspace = true } parking_lot = { workspace = true } +once_cell = { workspace = true } move-core-types = { workspace = true } move-resource-viewer = { workspace = true } - +raw-store = { workspace = true } +moveos-config = { workspace = true } moveos-types = { workspace = true } +moveos-store = { workspace = true } + rooch-types = { workspace = true } \ No newline at end of file diff --git a/crates/rooch-store/src/lib.rs b/crates/rooch-store/src/lib.rs index d5368f8fdb..9811403aeb 100644 --- a/crates/rooch-store/src/lib.rs +++ b/crates/rooch-store/src/lib.rs @@ -1,23 +1,125 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use crate::transaction_store::TransactionDB; +use crate::transaction_store::{TransactionDBStore, TransactionStore}; +use anyhow::Result; +use moveos_config::store_config::RocksdbConfig; +use moveos_config::temp_dir; +use once_cell::sync::Lazy; +use raw_store::rocks::RocksDB; +use raw_store::{ColumnFamilyName, StoreInstance}; +use rooch_types::transaction::{ + TransactionSequenceInfo, TransactionSequenceMapping, TypedTransaction, +}; +use rooch_types::H256; +use std::fmt::{Debug, Display, Formatter}; pub mod transaction_store; +// pub const DEFAULT_PREFIX_NAME: ColumnFamilyName = "default"; +pub const TYPED_TRANSACTION_PREFIX_NAME: ColumnFamilyName = "typed_transaction"; +pub const SEQ_TRANSACTION_PREFIX_NAME: ColumnFamilyName = "seq_transaction"; +pub const TX_SEQ_MAPPING_PREFIX_NAME: ColumnFamilyName = "tx_seq_mapping"; + +///db store use prefix_name vec to init +/// Please note that adding a prefix needs to be added in vec simultaneously, remember!! +static VEC_PREFIX_NAME: Lazy> = Lazy::new(|| { + vec![ + TYPED_TRANSACTION_PREFIX_NAME, + SEQ_TRANSACTION_PREFIX_NAME, + TX_SEQ_MAPPING_PREFIX_NAME, + ] +}); + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct StoreMeta {} + +impl StoreMeta { + pub fn get_column_family_names() -> &'static [ColumnFamilyName] { + &VEC_PREFIX_NAME + } +} + #[derive(Clone)] -pub struct RoochDB { - pub transaction_store: TransactionDB, +pub struct RoochStore { + pub transaction_store: TransactionDBStore, } -impl RoochDB { - pub fn new_with_memory_store() -> Self { - Self { - transaction_store: TransactionDB::new_with_memory_store(), - } +impl RoochStore { + pub fn new(instance: StoreInstance) -> Result { + let store = Self { + transaction_store: TransactionDBStore::new(instance), + }; + Ok(store) + } + + //TODO implement a memory mock store + pub fn mock_rooch_store() -> Self { + Self::new(StoreInstance::new_db_instance( + RocksDB::new( + temp_dir().path(), + moveos_store::StoreMeta::get_column_family_names().to_vec(), + RocksdbConfig::default(), + None, + ) + .expect("init db error"), + )) + .expect("init rooch store error") } - pub fn get_transaction_store(&self) -> &TransactionDB { + pub fn get_transaction_store(&self) -> &TransactionDBStore { &self.transaction_store } } + +impl Display for RoochStore { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "{}", self.clone()) + } +} +impl Debug for RoochStore { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "{}", self) + } +} + +impl TransactionStore for RoochStore { + fn save_transaction(&mut self, transaction: TypedTransaction) -> Result<()> { + self.transaction_store.save_transaction(transaction) + } + + fn get_tx_by_hash(&self, hash: H256) -> Result> { + self.transaction_store.get_tx_by_hash(hash) + } + + fn get_tx_by_index(&self, start: u64, limit: u64) -> Result> { + self.transaction_store.get_tx_by_index(start, limit) + } + + fn save_tx_seq_info(&self, tx_seq_info: TransactionSequenceInfo) -> Result<()> { + self.transaction_store.save_tx_seq_info(tx_seq_info) + } + + fn get_tx_seq_infos_by_tx_order( + &self, + cursor: Option, + limit: u64, + ) -> Result> { + self.transaction_store + .get_tx_seq_infos_by_tx_order(cursor, limit) + } + + fn save_tx_seq_info_mapping(&self, tx_order: u128, tx_hash: H256) -> Result<()> { + self.transaction_store + .save_tx_seq_info_mapping(tx_order, tx_hash) + } + + fn get_tx_seq_mapping_by_tx_order( + &self, + cursor: Option, + limit: u64, + ) -> Result> { + self.transaction_store + .get_tx_seq_mapping_by_tx_order(cursor, limit) + } +} diff --git a/crates/rooch-store/src/transaction_store/mod.rs b/crates/rooch-store/src/transaction_store/mod.rs index 73bfb86df1..9e4ff65356 100644 --- a/crates/rooch-store/src/transaction_store/mod.rs +++ b/crates/rooch-store/src/transaction_store/mod.rs @@ -1,160 +1,154 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -use parking_lot::RwLock; +use anyhow::Result; +use raw_store::CodecKVStore; use rooch_types::transaction::{ AbstractTransaction, TransactionSequenceInfo, TransactionSequenceMapping, TypedTransaction, }; use rooch_types::H256; -use std::collections::BTreeMap; -use std::sync::Arc; + +use crate::{ + SEQ_TRANSACTION_PREFIX_NAME, TX_SEQ_MAPPING_PREFIX_NAME, TYPED_TRANSACTION_PREFIX_NAME, +}; +use raw_store::{derive_store, StoreInstance}; + +derive_store!( + TypedTransactionStore, + H256, + TypedTransaction, + TYPED_TRANSACTION_PREFIX_NAME +); + +derive_store!( + SeqTransactionStore, + u128, + TransactionSequenceInfo, + SEQ_TRANSACTION_PREFIX_NAME +); + +derive_store!(TxSeqMappingStore, u128, H256, TX_SEQ_MAPPING_PREFIX_NAME); pub trait TransactionStore { - fn save_transaction(&mut self, transaction: TypedTransaction); - fn get_tx_by_hash(&self, hash: H256) -> Option; - fn get_tx_by_index(&self, start: u64, limit: u64) -> Vec; + fn save_transaction(&mut self, transaction: TypedTransaction) -> Result<()>; + fn get_tx_by_hash(&self, hash: H256) -> Result>; + fn get_tx_by_index(&self, start: u64, limit: u64) -> Result>; - fn save_tx_seq_info(&self, tx_seq_info: TransactionSequenceInfo); + fn save_tx_seq_info(&self, tx_seq_info: TransactionSequenceInfo) -> Result<()>; fn get_tx_seq_infos_by_tx_order( &self, cursor: Option, limit: u64, - ) -> Vec; - fn save_tx_seq_info_mapping(&self, tx_order: u128, tx_hash: H256); + ) -> Result>; + fn save_tx_seq_info_mapping(&self, tx_order: u128, tx_hash: H256) -> Result<()>; fn get_tx_seq_mapping_by_tx_order( &self, cursor: Option, limit: u64, - ) -> Vec; + ) -> Result>; } #[derive(Clone)] -pub struct TransactionDB { - transaction_db: InMemoryStore, +pub struct TransactionDBStore { + typed_tx_store: TypedTransactionStore, + seq_tx_store: SeqTransactionStore, + tx_seq_mapping: TxSeqMappingStore, } -impl TransactionDB { - pub fn new_with_memory_store() -> Self { - Self { - transaction_db: InMemoryStore::default(), +impl TransactionDBStore { + pub fn new(instance: StoreInstance) -> Self { + TransactionDBStore { + typed_tx_store: TypedTransactionStore::new(instance.clone()), + seq_tx_store: SeqTransactionStore::new(instance.clone()), + tx_seq_mapping: TxSeqMappingStore::new(instance), } } - pub fn save_transaction(&mut self, transaction: TypedTransaction) { - self.transaction_db.save_transaction(transaction); + pub fn save_transaction(&mut self, transaction: TypedTransaction) -> Result<()> { + self.typed_tx_store + .kv_put(transaction.tx_hash(), transaction) } - pub fn get_tx_by_hash(&self, hash: H256) -> Option { - self.transaction_db.get_tx_by_hash(hash) + pub fn get_tx_by_hash(&self, hash: H256) -> Result> { + self.typed_tx_store.kv_get(hash) } - pub fn get_tx_by_index(&self, start: u64, limit: u64) -> Vec { - self.transaction_db.get_tx_by_index(start, limit) + //TODO implements get type tx by index + pub fn get_tx_by_index(&self, _cursor: u64, _limit: u64) -> Result> { + Ok(vec![]) } - pub fn save_tx_seq_info(&self, tx_seq_info: TransactionSequenceInfo) { - self.transaction_db.save_tx_seq_info(tx_seq_info); + pub fn save_tx_seq_info(&self, tx_seq_info: TransactionSequenceInfo) -> Result<()> { + self.seq_tx_store.kv_put(tx_seq_info.tx_order, tx_seq_info) } pub fn get_tx_seq_infos_by_tx_order( &self, cursor: Option, limit: u64, - ) -> Vec { - self.transaction_db - .get_tx_seq_infos_by_tx_order(cursor, limit) - } - - pub fn save_tx_seq_info_mapping(&self, tx_order: u128, tx_hash: H256) { - self.transaction_db - .save_tx_seq_info_mapping(tx_order, tx_hash) - } - - pub fn get_tx_seq_mapping_by_tx_order( - &self, - cursor: Option, - limit: u64, - ) -> Vec { - self.transaction_db - .get_tx_seq_mapping_by_tx_order(cursor, limit) - } -} - -#[derive(Default, Clone)] -pub struct InMemoryStore { - inner_tx: Arc>>, - inner_tx_seq_info: Arc>>, - inner_tx_index: Arc>>, -} - -impl TransactionStore for InMemoryStore { - fn save_transaction(&mut self, transaction: TypedTransaction) { - let mut inner = self.inner_tx.write(); - inner.push(transaction); - } - - fn get_tx_by_hash(&self, hash: H256) -> Option { - let inner = self.inner_tx.read(); - inner.iter().find(|tx| tx.tx_hash() == hash).cloned() - } - - fn get_tx_by_index(&self, start: u64, limit: u64) -> Vec { - let inner = self.inner_tx.read(); - let end = std::cmp::min((start + limit) as usize, inner.len()); - inner[start as usize..end].to_vec() - } - - fn save_tx_seq_info(&self, tx_seq_info: TransactionSequenceInfo) { - let mut locked = self.inner_tx_seq_info.write(); - locked.insert(tx_seq_info.tx_order, tx_seq_info); - } - - fn get_tx_seq_infos_by_tx_order( - &self, - cursor: Option, - limit: u64, - ) -> Vec { + ) -> Result> { + // will not cross the boundary even if the size exceeds the storage capacity, let start = cursor.unwrap_or(0); let end = start + (limit as u128); - let rw_locks = self.inner_tx_seq_info.read(); - let data = rw_locks - .iter() - .filter(|(tx_order, _)| { + let mut iter = self.seq_tx_store.iter()?; + iter.seek(bcs::to_bytes(&start)?).map_err(|e| { + anyhow::anyhow!( + "Rooch TransactionStore get_tx_seq_infos_by_tx_order seek: {:?}", + e + ) + })?; + + let data: Vec = iter + .filter_map(|item| { + let (tx_order, seq_info) = + item.unwrap_or_else(|_| panic!("Get item from store shoule hava a value.")); if Option::is_some(&cursor) { - **tx_order > start && **tx_order <= end - } else { - **tx_order >= start && **tx_order < end + if tx_order > start && tx_order <= end { + return Some(seq_info); + } + } else if tx_order >= start && tx_order < end { + return Some(seq_info); } + None }) - .map(|(_, e)| e.clone()) .collect::>(); - data + Ok(data) } - fn save_tx_seq_info_mapping(&self, tx_order: u128, tx_hash: H256) { - let mut locked = self.inner_tx_index.write(); - locked.insert(tx_order, tx_hash); + pub fn save_tx_seq_info_mapping(&self, tx_order: u128, tx_hash: H256) -> Result<()> { + self.tx_seq_mapping.kv_put(tx_order, tx_hash) } - fn get_tx_seq_mapping_by_tx_order( + pub fn get_tx_seq_mapping_by_tx_order( &self, cursor: Option, limit: u64, - ) -> Vec { + ) -> Result> { + // will not cross the boundary even if the size exceeds the storage capacity, let start = cursor.unwrap_or(0); let end = start + (limit as u128); - let rw_locks = self.inner_tx_index.read(); - let data = rw_locks - .iter() - .filter(|(tx_order, _)| { + let mut iter = self.tx_seq_mapping.iter()?; + iter.seek(bcs::to_bytes(&start)?).map_err(|e| { + anyhow::anyhow!( + "Rooch TransactionStore get_tx_seq_mapping_by_tx_order seek: {:?}", + e + ) + })?; + + let data: Vec = iter + .filter_map(|item| { + let (tx_order, tx_hash) = + item.unwrap_or_else(|_| panic!("Get item from store shoule hava a value.")); if Option::is_some(&cursor) { - **tx_order > start && **tx_order <= end - } else { - **tx_order >= start && **tx_order < end + if tx_order > start && tx_order <= end { + return Some(TransactionSequenceMapping::new(tx_order, tx_hash)); + } + } else if tx_order >= start && tx_order < end { + return Some(TransactionSequenceMapping::new(tx_order, tx_hash)); } + None }) - .map(|(tx_order, tx_hash)| TransactionSequenceMapping::new(*tx_order, *tx_hash)) .collect::>(); - data + Ok(data) } } diff --git a/crates/rooch-types/Cargo.toml b/crates/rooch-types/Cargo.toml index 7eae359b96..9c710946c0 100644 --- a/crates/rooch-types/Cargo.toml +++ b/crates/rooch-types/Cargo.toml @@ -37,6 +37,7 @@ strum = { workspace = true } strum_macros = { workspace = true } async-trait ={ workspace = true } nostr = { workspace = true } +clap = { workspace = true } move-core-types = { workspace = true } move-stdlib = { workspace = true } diff --git a/crates/rooch-types/src/crypto.rs b/crates/rooch-types/src/crypto.rs index a4d33abe1d..076f53d764 100644 --- a/crates/rooch-types/src/crypto.rs +++ b/crates/rooch-types/src/crypto.rs @@ -5,6 +5,7 @@ use crate::{ address::RoochAddress, error::{RoochError, RoochResult}, }; +use clap::ArgEnum; use derive_more::{AsMut, AsRef, From}; pub use enum_dispatch::enum_dispatch; use eyre::eyre; @@ -14,8 +15,8 @@ use fastcrypto::hash::{Blake2b256, HashFunction}; pub use fastcrypto::traits::KeyPair as KeypairTraits; pub use fastcrypto::traits::Signer; pub use fastcrypto::traits::{ - AggregateAuthenticator, Authenticator, EncodeDecodeBase64, SigningKey, ToFromBytes, - VerifyingKey, + AggregateAuthenticator, Authenticator, EncodeDecodeBase64, RecoverableSignature, + RecoverableSigner, SigningKey, ToFromBytes, VerifyingKey, }; use fastcrypto::{ ed25519::{ @@ -23,10 +24,26 @@ use fastcrypto::{ Ed25519SignatureAsBytes, }, secp256k1::{ - Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature, + recoverable::{ + Secp256k1RecoverableKeyPair, Secp256k1RecoverablePublicKey, + Secp256k1RecoverablePublicKeyAsBytes, Secp256k1RecoverableSignature, + Secp256k1RecoverableSignatureAsBytes, + }, + schnorr::{ + SchnorrKeyPair, SchnorrPublicKey, SchnorrPublicKeyAsBytes, SchnorrSignature, + SchnorrSignatureAsBytes, + }, + // TODO wrap it to ecdsa + Secp256k1KeyPair, + Secp256k1PublicKey, + Secp256k1PublicKeyAsBytes, + Secp256k1Signature, Secp256k1SignatureAsBytes, }, }; +use move_core_types::{ + account_address::AccountAddress, identifier::Identifier, language_storage::StructTag, +}; use moveos_types::{h256::H256, serde::Readable}; use rand::{rngs::StdRng, SeedableRng}; use schemars::JsonSchema; @@ -34,7 +51,7 @@ use serde::ser::Serializer; use serde::{Deserialize, Deserializer, Serialize}; use serde_with::{serde_as, Bytes}; use std::{hash::Hash, str::FromStr}; -use strum_macros::EnumString; +use strum_macros::{Display, EnumString}; pub type DefaultHash = Blake2b256; @@ -42,20 +59,43 @@ pub type DefaultHash = Blake2b256; /// It is a part of `AccountAbstraction` /// The Authenticator scheme which builtin Rooch -#[derive(Copy, Clone, Debug, EnumString, strum_macros::Display)] +#[derive( + Copy, + Clone, + Debug, + EnumString, + PartialEq, + Eq, + ArgEnum, + Display, + Ord, + PartialOrd, + Serialize, + Deserialize, +)] #[strum(serialize_all = "lowercase")] pub enum BuiltinScheme { Ed25519, MultiEd25519, Ecdsa, + EcdsaRecoverable, + Schnorr, } impl BuiltinScheme { + const ED25519_FLAG: u8 = 0x00; + const MULTIED25519_FLAG: u8 = 0x01; + const ECDSA_FLAG: u8 = 0x02; + const ECDSARECOVERABLE_FLAG: u8 = 0x03; + const SCHNORR_FLAG: u8 = 0x04; + pub fn flag(&self) -> u8 { match self { - BuiltinScheme::Ed25519 => 0x00, - BuiltinScheme::MultiEd25519 => 0x01, - BuiltinScheme::Ecdsa => 0x02, + BuiltinScheme::Ed25519 => Self::ED25519_FLAG, + BuiltinScheme::MultiEd25519 => Self::MULTIED25519_FLAG, + BuiltinScheme::Ecdsa => Self::ECDSA_FLAG, + BuiltinScheme::EcdsaRecoverable => Self::ECDSARECOVERABLE_FLAG, + BuiltinScheme::Schnorr => Self::SCHNORR_FLAG, } } @@ -63,19 +103,48 @@ impl BuiltinScheme { let byte_int = flag .parse::() .map_err(|_| RoochError::KeyConversionError("Invalid key scheme".to_owned()))?; - Self::from_flag_byte(&byte_int) + Self::from_flag_byte(byte_int) } - pub fn from_flag_byte(byte_int: &u8) -> Result { + pub fn from_flag_byte(byte_int: u8) -> Result { match byte_int { - 0x00 => Ok(BuiltinScheme::Ed25519), - 0x01 => Ok(BuiltinScheme::MultiEd25519), - 0x02 => Ok(BuiltinScheme::Ecdsa), + Self::ED25519_FLAG => Ok(BuiltinScheme::Ed25519), + Self::MULTIED25519_FLAG => Ok(BuiltinScheme::MultiEd25519), + Self::ECDSA_FLAG => Ok(BuiltinScheme::Ecdsa), + Self::ECDSARECOVERABLE_FLAG => Ok(BuiltinScheme::EcdsaRecoverable), + Self::SCHNORR_FLAG => Ok(BuiltinScheme::Schnorr), _ => Err(RoochError::KeyConversionError( "Invalid key scheme".to_owned(), )), } } + + pub fn get_validator_name(&self) -> Result { + match self { + BuiltinScheme::Ed25519 => Ok(String::from(stringify!(Ed25519Validator))), + BuiltinScheme::MultiEd25519 => Ok(String::from(stringify!(MultiEd25519Validator))), + BuiltinScheme::Ecdsa => Ok(String::from(stringify!(EcdsaK1Validator))), + BuiltinScheme::EcdsaRecoverable => { + Ok(String::from(stringify!(EcdsaK1RecoverableValidator))) + } + BuiltinScheme::Schnorr => Ok(String::from(stringify!(SchnorrValidator))), + } + } + + pub fn create_validator_struct_tag( + &self, + address: AccountAddress, + module_name: String, + ) -> Result, RoochError> { + let tag_name: String = self.get_validator_name()?; + + Ok(Box::new(StructTag { + address, + module: Identifier::new(module_name).unwrap(), + name: Identifier::new(tag_name).unwrap(), + type_params: vec![], + })) + } } #[allow(clippy::large_enum_variant)] @@ -83,6 +152,8 @@ impl BuiltinScheme { pub enum RoochKeyPair { Ed25519(Ed25519KeyPair), Ecdsa(Secp256k1KeyPair), + EcdsaRecoverable(Secp256k1RecoverableKeyPair), + Schnorr(SchnorrKeyPair), } impl RoochKeyPair { @@ -90,6 +161,8 @@ impl RoochKeyPair { match self { RoochKeyPair::Ed25519(kp) => PublicKey::Ed25519(kp.public().into()), RoochKeyPair::Ecdsa(kp) => PublicKey::Ecdsa(kp.public().into()), + RoochKeyPair::EcdsaRecoverable(kp) => PublicKey::EcdsaRecoverable(kp.public().into()), + RoochKeyPair::Schnorr(kp) => PublicKey::Schnorr(kp.public().into()), } } } @@ -99,6 +172,8 @@ impl Signer for RoochKeyPair { match self { RoochKeyPair::Ed25519(kp) => kp.sign(msg), RoochKeyPair::Ecdsa(kp) => kp.sign(msg), + RoochKeyPair::EcdsaRecoverable(kp) => kp.sign(msg), + RoochKeyPair::Schnorr(kp) => kp.sign(msg), } } } @@ -125,6 +200,12 @@ impl EncodeDecodeBase64 for RoochKeyPair { RoochKeyPair::Ecdsa(kp) => { bytes.extend_from_slice(kp.as_bytes()); } + RoochKeyPair::EcdsaRecoverable(kp) => { + bytes.extend_from_slice(kp.as_bytes()); + } + RoochKeyPair::Schnorr(kp) => { + bytes.extend_from_slice(kp.as_bytes()); + } } Base64::encode(&bytes[..]) } @@ -132,7 +213,8 @@ impl EncodeDecodeBase64 for RoochKeyPair { /// Decode a RoochKeyPair from `flag || privkey` in Base64. The public key is computed directly from the private key bytes. fn decode_base64(value: &str) -> Result { let bytes = Base64::decode(value).map_err(|e| eyre!("{}", e.to_string()))?; - match BuiltinScheme::from_flag_byte(bytes.first().ok_or_else(|| eyre!("Invalid length"))?) { + match BuiltinScheme::from_flag_byte(*bytes.first().ok_or_else(|| eyre!("Invalid length"))?) + { Ok(x) => match x { BuiltinScheme::Ed25519 => Ok(RoochKeyPair::Ed25519(Ed25519KeyPair::from_bytes( bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, @@ -140,6 +222,14 @@ impl EncodeDecodeBase64 for RoochKeyPair { BuiltinScheme::Ecdsa => Ok(RoochKeyPair::Ecdsa(Secp256k1KeyPair::from_bytes( bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, )?)), + BuiltinScheme::EcdsaRecoverable => Ok(RoochKeyPair::EcdsaRecoverable( + Secp256k1RecoverableKeyPair::from_bytes( + bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, + )?, + )), + BuiltinScheme::Schnorr => Ok(RoochKeyPair::Schnorr(SchnorrKeyPair::from_bytes( + bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, + )?)), _ => Err(eyre!("Invalid flag byte")), }, _ => Err(eyre!("Invalid bytes")), @@ -169,10 +259,12 @@ impl<'de> Deserialize<'de> for RoochKeyPair { } } -#[derive(Debug, Clone, PartialEq, Eq, From, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, JsonSchema)] pub enum PublicKey { Ed25519(Ed25519PublicKeyAsBytes), Ecdsa(Secp256k1PublicKeyAsBytes), + EcdsaRecoverable(Secp256k1RecoverablePublicKeyAsBytes), + Schnorr(SchnorrPublicKeyAsBytes), } impl AsRef<[u8]> for PublicKey { @@ -180,6 +272,8 @@ impl AsRef<[u8]> for PublicKey { match self { PublicKey::Ed25519(pk) => &pk.0, PublicKey::Ecdsa(pk) => &pk.0, + PublicKey::EcdsaRecoverable(pk) => &pk.0, + PublicKey::Schnorr(pk) => &pk.0, } } } @@ -194,7 +288,6 @@ impl EncodeDecodeBase64 for PublicKey { fn decode_base64(value: &str) -> Result { let bytes = Base64::decode(value).map_err(|e| eyre!("{}", e.to_string()))?; - match bytes.first() { Some(x) => { if x == &BuiltinScheme::Ed25519.flag() { @@ -207,6 +300,16 @@ impl EncodeDecodeBase64 for PublicKey { bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, )?; Ok(PublicKey::Ecdsa((&pk).into())) + } else if x == &BuiltinScheme::EcdsaRecoverable.flag() { + let pk = Secp256k1RecoverablePublicKey::from_bytes( + bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, + )?; + Ok(PublicKey::Ecdsa((&pk).into())) + } else if x == &BuiltinScheme::Schnorr.flag() { + let pk: SchnorrPublicKey = SchnorrPublicKey::from_bytes( + bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, + )?; + Ok(PublicKey::Schnorr((&pk).into())) } else { Err(eyre!("Invalid flag byte")) } @@ -240,7 +343,12 @@ impl<'de> Deserialize<'de> for PublicKey { impl PublicKey { pub fn flag(&self) -> u8 { - Ed25519RoochSignature::SCHEME.flag() + match self { + PublicKey::Ed25519(_) => Ed25519RoochSignature::SCHEME.flag(), + PublicKey::Ecdsa(_) => EcdsaRoochSignature::SCHEME.flag(), + PublicKey::EcdsaRecoverable(_) => EcdsaRecoverableRoochSignature::SCHEME.flag(), + PublicKey::Schnorr(_) => SchnorrRoochSignature::SCHEME.flag(), + } } pub fn try_from_bytes( scheme: BuiltinScheme, @@ -253,14 +361,21 @@ impl PublicKey { BuiltinScheme::Ecdsa => Ok(PublicKey::Ecdsa( (&Secp256k1PublicKey::from_bytes(key_bytes)?).into(), )), + BuiltinScheme::EcdsaRecoverable => Ok(PublicKey::EcdsaRecoverable( + (&Secp256k1RecoverablePublicKey::from_bytes(key_bytes)?).into(), + )), + BuiltinScheme::Schnorr => Ok(PublicKey::Schnorr( + (&SchnorrPublicKey::from_bytes(key_bytes)?).into(), + )), _ => Err(eyre!("Unsupported scheme")), } } - pub fn scheme(&self) -> BuiltinScheme { match self { PublicKey::Ed25519(_) => Ed25519RoochSignature::SCHEME, PublicKey::Ecdsa(_) => EcdsaRoochSignature::SCHEME, + PublicKey::EcdsaRecoverable(_) => EcdsaRecoverableRoochSignature::SCHEME, + PublicKey::Schnorr(_) => SchnorrRoochSignature::SCHEME, } } } @@ -277,6 +392,14 @@ impl RoochPublicKey for Secp256k1PublicKey { const SIGNATURE_SCHEME: BuiltinScheme = BuiltinScheme::Ecdsa; } +impl RoochPublicKey for Secp256k1RecoverablePublicKey { + const SIGNATURE_SCHEME: BuiltinScheme = BuiltinScheme::EcdsaRecoverable; +} + +impl RoochPublicKey for SchnorrPublicKey { + const SIGNATURE_SCHEME: BuiltinScheme = BuiltinScheme::Schnorr; +} + impl From<&T> for RoochAddress { fn from(pk: &T) -> Self { let mut hasher = DefaultHash::default(); @@ -356,6 +479,8 @@ pub trait RoochSignatureInner: Sized + ToFromBytes + PartialEq + Eq + Hash { pub enum Signature { Ed25519RoochSignature, EcdsaRoochSignature, + EcdsaRecoverableRoochSignature, + SchnorrRoochSignature, } impl Serialize for Signature { @@ -429,6 +554,22 @@ impl Signature { })?) .into(), )), + BuiltinScheme::EcdsaRecoverable => Ok(CompressedSignature::EcdsaRecoverable( + (&Secp256k1RecoverableSignature::from_bytes(bytes).map_err(|_| { + RoochError::InvalidSignature { + error: "Cannot parse sig".to_owned(), + } + })?) + .into(), + )), + BuiltinScheme::Schnorr => Ok(CompressedSignature::Schnorr( + (&SchnorrSignature::from_bytes(bytes).map_err(|_| { + RoochError::InvalidSignature { + error: "Cannot parse sig".to_owned(), + } + })?) + .into(), + )), _ => Err(RoochError::UnsupportedFeatureError { error: "Unsupported signature scheme in MultiSig".to_owned(), }), @@ -450,6 +591,16 @@ impl Signature { .map_err(|_| RoochError::KeyConversionError("Cannot parse pk".to_owned()))?) .into(), )), + BuiltinScheme::EcdsaRecoverable => Ok(PublicKey::EcdsaRecoverable( + (&Secp256k1RecoverablePublicKey::from_bytes(bytes) + .map_err(|_| RoochError::KeyConversionError("Cannot parse pk".to_owned()))?) + .into(), + )), + BuiltinScheme::Schnorr => Ok(PublicKey::Schnorr( + (&SchnorrPublicKey::from_bytes(bytes) + .map_err(|_| RoochError::KeyConversionError("Cannot parse pk".to_owned()))?) + .into(), + )), _ => Err(RoochError::UnsupportedFeatureError { error: "Unsupported signature scheme in MultiSig".to_owned(), }), @@ -462,6 +613,8 @@ impl AsRef<[u8]> for Signature { match self { Signature::Ed25519RoochSignature(sig) => sig.as_ref(), Signature::EcdsaRoochSignature(sig) => sig.as_ref(), + Signature::EcdsaRecoverableRoochSignature(sig) => sig.as_ref(), + Signature::SchnorrRoochSignature(sig) => sig.as_ref(), } } } @@ -470,6 +623,8 @@ impl AsMut<[u8]> for Signature { match self { Signature::Ed25519RoochSignature(sig) => sig.as_mut(), Signature::EcdsaRoochSignature(sig) => sig.as_mut(), + Signature::EcdsaRecoverableRoochSignature(sig) => sig.as_mut(), + Signature::SchnorrRoochSignature(sig) => sig.as_mut(), } } } @@ -482,6 +637,10 @@ impl ToFromBytes for Signature { Ok(::from_bytes(bytes)?.into()) } else if x == &EcdsaRoochSignature::SCHEME.flag() { Ok(::from_bytes(bytes)?.into()) + } else if x == &EcdsaRecoverableRoochSignature::SCHEME.flag() { + Ok(::from_bytes(bytes)?.into()) + } else if x == &SchnorrRoochSignature::SCHEME.flag() { + Ok(::from_bytes(bytes)?.into()) } else { Err(FastCryptoError::InvalidInput) } @@ -496,6 +655,8 @@ impl ToFromBytes for Signature { pub enum CompressedSignature { Ed25519(Ed25519SignatureAsBytes), Ecdsa(Secp256k1SignatureAsBytes), + EcdsaRecoverable(Secp256k1RecoverableSignatureAsBytes), + Schnorr(SchnorrSignatureAsBytes), } impl AsRef<[u8]> for CompressedSignature { @@ -503,6 +664,8 @@ impl AsRef<[u8]> for CompressedSignature { match self { CompressedSignature::Ed25519(sig) => &sig.0, CompressedSignature::Ecdsa(sig) => &sig.0, + CompressedSignature::EcdsaRecoverable(sig) => &sig.0, + CompressedSignature::Schnorr(sig) => &sig.0, } } } @@ -632,6 +795,88 @@ impl Signer for Secp256k1KeyPair { } } +// +// EcdsaRecoverable Rooch Signature port +// +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] +#[as_ref(forward)] +#[as_mut(forward)] +pub struct EcdsaRecoverableRoochSignature( + #[schemars(with = "Base64")] + #[serde_as(as = "Readable")] + [u8; Secp256k1RecoverablePublicKey::LENGTH + Secp256k1RecoverableSignature::LENGTH + 1], +); + +impl RoochSignatureInner for EcdsaRecoverableRoochSignature { + type Sig = Secp256k1RecoverableSignature; + type PubKey = Secp256k1RecoverablePublicKey; + type KeyPair = Secp256k1RecoverableKeyPair; + const LENGTH: usize = + Secp256k1RecoverablePublicKey::LENGTH + Secp256k1RecoverableSignature::LENGTH + 1; +} + +impl ToFromBytes for EcdsaRecoverableRoochSignature { + fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != Self::LENGTH { + return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + } + let mut sig_bytes = [0; Self::LENGTH]; + sig_bytes.copy_from_slice(bytes); + Ok(Self(sig_bytes)) + } +} + +impl Signer for Secp256k1RecoverableKeyPair { + fn sign(&self, msg: &[u8]) -> Signature { + EcdsaRecoverableRoochSignature::new(self, msg).into() + } +} + +// +// Schnorr Rooch Signature port +// +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] +#[as_ref(forward)] +#[as_mut(forward)] +pub struct SchnorrRoochSignature( + #[schemars(with = "Base64")] + #[serde_as(as = "Readable")] + [u8; SchnorrPublicKey::LENGTH + SchnorrSignature::LENGTH + 1], +); + +// Implementation useful for simplify testing when mock signature is needed +impl Default for SchnorrRoochSignature { + fn default() -> Self { + Self([0; SchnorrPublicKey::LENGTH + SchnorrSignature::LENGTH + 1]) + } +} + +impl ToFromBytes for SchnorrRoochSignature { + fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != Self::LENGTH { + return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + } + let mut sig_bytes = [0; Self::LENGTH]; + sig_bytes.copy_from_slice(bytes); + Ok(Self(sig_bytes)) + } +} + +impl Signer for SchnorrKeyPair { + fn sign(&self, msg: &[u8]) -> Signature { + SchnorrRoochSignature::new(self, msg).into() + } +} + +impl RoochSignatureInner for SchnorrRoochSignature { + type Sig = SchnorrSignature; + type PubKey = SchnorrPublicKey; + type KeyPair = SchnorrKeyPair; + const LENGTH: usize = SchnorrPublicKey::LENGTH + SchnorrSignature::LENGTH + 1; +} + /// Generate a keypair from the specified RNG (useful for testing with seedable rngs). pub fn get_key_pair_from_rng(csprng: &mut R) -> (RoochAddress, KP) where @@ -647,21 +892,60 @@ mod tests { use crate::address::RoochAddress; use fastcrypto::{ ed25519::{Ed25519KeyPair, Ed25519PrivateKey}, + secp256k1::schnorr::{SchnorrKeyPair, SchnorrPrivateKey}, + secp256k1::{ + recoverable::{Secp256k1RecoverableKeyPair, Secp256k1RecoverablePrivateKey}, + Secp256k1KeyPair, Secp256k1PrivateKey, + }, traits::{KeyPair, ToFromBytes}, }; // this test ensure the public key to address keep the same as the old version // we should also keep the public key to address algorithm the same as the move version #[test] - fn test_public_key_to_address() { + fn test_ed25519_public_key_to_address() { let private_key = Ed25519PrivateKey::from_bytes(&[0u8; 32]).unwrap(); let keypair: Ed25519KeyPair = private_key.into(); - //println!("public_key: {}", hex::encode(keypair.public().as_bytes())); let address: RoochAddress = keypair.public().into(); - //println!("address: {:?}", address); assert_eq!( address.to_string(), "0x7a1378aafadef8ce743b72e8b248295c8f61c102c94040161146ea4d51a182b6" ); } + + // this test is to ensure that the other algorithms work for public key to address as well as ed25519 + #[test] + fn test_ecdsa_k1_public_key_to_address() { + let private_key = Secp256k1PrivateKey::from_bytes(&[1u8; 32]).unwrap(); // use 1u8. + let keypair: Secp256k1KeyPair = private_key.into(); + let address: RoochAddress = keypair.public().into(); + assert_eq!( + address.to_string(), + "0x92718e81a52369b4bc3169161737318ddf022945391a69263e8d4289c79a0c67" + ); + } + + // this test is to ensure that the other algorithms work for public key to address as well as ed25519 + #[test] + fn test_ecdsa_k1_recoverable_public_key_to_address() { + let private_key = Secp256k1RecoverablePrivateKey::from_bytes(&[1u8; 32]).unwrap(); // use 1u8. + let keypair: Secp256k1RecoverableKeyPair = private_key.into(); + let address: RoochAddress = keypair.public().into(); + assert_eq!( + address.to_string(), + "0x8c891976da9498ec1d3ff778a5d6c40c217d63cc8c48539c959f8b683eedf5a4" + ); + } + + // this test is to ensure that the other algorithms work for public key to address as well as ed25519 + #[test] + fn test_schnorr_public_key_to_address() { + let private_key = SchnorrPrivateKey::from_bytes(&[1u8; 32]).unwrap(); // ensure not leave 0, use 1u8 + let keypair: SchnorrKeyPair = private_key.into(); + let address: RoochAddress = keypair.public().into(); + assert_eq!( + address.to_string(), + "0xa519b36bbecc294726bbfd962ab46ca4e09baacca7cd90d5d2da2331afb363e6" + ); + } } diff --git a/crates/rooch-types/src/error.rs b/crates/rooch-types/src/error.rs index 785840a317..bb1268ac09 100644 --- a/crates/rooch-types/src/error.rs +++ b/crates/rooch-types/src/error.rs @@ -53,6 +53,12 @@ pub enum RoochError { ViewFunctionError(String), #[error("Import account error: {0}")] ImportAccountError(String), + #[error("Switch account error: {0}")] + SwitchAccountError(String), + #[error("Update account error: {0}")] + UpdateAccountError(String), + #[error("Nullify account error: {0}")] + NullifyAccountError(String), #[error("Generate key error: {0}")] GenerateKeyError(String), @@ -67,6 +73,11 @@ pub enum RoochError { #[error("Key Conversion Error: {0}")] KeyConversionError(String), + #[error("Switch env error: {0}")] + SwitchEnvError(String), + #[error("Remove env error: {0}")] + RemoveEnvError(String), + // Signature verification #[error("Signature is not valid: {}", error)] InvalidSignature { error: String }, diff --git a/crates/rooch-types/src/framework/auth_validator_registry.rs b/crates/rooch-types/src/framework/auth_validator.rs similarity index 57% rename from crates/rooch-types/src/framework/auth_validator_registry.rs rename to crates/rooch-types/src/framework/auth_validator.rs index 4563ac6535..f92b231b11 100644 --- a/crates/rooch-types/src/framework/auth_validator_registry.rs +++ b/crates/rooch-types/src/framework/auth_validator.rs @@ -5,6 +5,7 @@ use crate::addresses::ROOCH_FRAMEWORK_ADDRESS; use move_core_types::{ account_address::AccountAddress, ident_str, identifier::IdentStr, language_storage::ModuleId, }; +use moveos_types::move_option::MoveOption; use moveos_types::{ move_string::MoveAsciiString, move_types::FunctionId, @@ -21,7 +22,7 @@ pub struct AuthValidator { impl MoveStructType for AuthValidator { const ADDRESS: AccountAddress = ROOCH_FRAMEWORK_ADDRESS; - const MODULE_NAME: &'static IdentStr = ident_str!("auth_validator_registry"); + const MODULE_NAME: &'static IdentStr = ident_str!("auth_validator"); const STRUCT_NAME: &'static IdentStr = ident_str!("AuthValidator"); } @@ -57,3 +58,44 @@ impl AuthValidator { ) } } + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TxValidateResult { + pub scheme: u64, + pub auth_validator: MoveOption, + pub session_key: MoveOption>, +} + +impl MoveStructType for TxValidateResult { + const ADDRESS: AccountAddress = ROOCH_FRAMEWORK_ADDRESS; + const MODULE_NAME: &'static IdentStr = ident_str!("transaction_validtor"); + const STRUCT_NAME: &'static IdentStr = ident_str!("TxValidateResult"); +} + +impl MoveStructState for TxValidateResult { + fn struct_layout() -> move_core_types::value::MoveStructLayout { + move_core_types::value::MoveStructLayout::new(vec![ + move_core_types::value::MoveTypeLayout::U64, + move_core_types::value::MoveTypeLayout::Struct( + MoveOption::::struct_layout(), + ), + move_core_types::value::MoveTypeLayout::Vector(Box::new( + move_core_types::value::MoveTypeLayout::U8, + )), + ]) + } +} + +impl TxValidateResult { + pub fn auth_validator(&self) -> Option { + self.auth_validator.clone().into() + } + + pub fn session_key(&self) -> Option> { + self.session_key.clone().into() + } + + pub fn is_validate_via_session_key(&self) -> bool { + self.session_key().is_some() + } +} diff --git a/crates/rooch-types/src/framework/mod.rs b/crates/rooch-types/src/framework/mod.rs index 24a00e62a0..c6e9163877 100644 --- a/crates/rooch-types/src/framework/mod.rs +++ b/crates/rooch-types/src/framework/mod.rs @@ -3,4 +3,5 @@ /// types mapping from Framework Move types to Rust types //TODO should be in the framework crate? -pub mod auth_validator_registry; +pub mod auth_validator; +pub mod session_key; diff --git a/crates/rooch-types/src/framework/session_key.rs b/crates/rooch-types/src/framework/session_key.rs new file mode 100644 index 0000000000..cf4e7b51e7 --- /dev/null +++ b/crates/rooch-types/src/framework/session_key.rs @@ -0,0 +1,76 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + +use crate::addresses::ROOCH_FRAMEWORK_ADDRESS; +use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr}; +use moveos_types::{ + move_string::MoveAsciiString, + state::{MoveStructState, MoveStructType}, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)] +pub struct SessionScope { + pub module_address: AccountAddress, + pub module_name: MoveAsciiString, + pub function_name: MoveAsciiString, +} + +impl SessionScope { + pub fn new(module_address: AccountAddress, module_name: &str, function_name: &str) -> Self { + Self { + module_address, + module_name: MoveAsciiString::from_str(module_name).expect("invalid module name"), + function_name: MoveAsciiString::from_str(function_name).expect("invalid function name"), + } + } +} + +impl MoveStructType for SessionScope { + const ADDRESS: AccountAddress = ROOCH_FRAMEWORK_ADDRESS; + const MODULE_NAME: &'static IdentStr = ident_str!("session_key"); + const STRUCT_NAME: &'static IdentStr = ident_str!("SessionScope"); +} + +impl MoveStructState for SessionScope { + fn struct_layout() -> move_core_types::value::MoveStructLayout { + move_core_types::value::MoveStructLayout::new(vec![ + move_core_types::value::MoveTypeLayout::Address, + MoveAsciiString::type_layout(), + MoveAsciiString::type_layout(), + ]) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SessionKey { + pub authentication_key: Vec, + pub scheme: u64, + pub scopes: Vec, + pub expiration_time: u64, + pub last_active_time: u64, + pub max_inactive_interval: u64, +} + +impl MoveStructType for SessionKey { + const ADDRESS: AccountAddress = ROOCH_FRAMEWORK_ADDRESS; + const MODULE_NAME: &'static IdentStr = ident_str!("session_key"); + const STRUCT_NAME: &'static IdentStr = ident_str!("SessionKey"); +} + +impl MoveStructState for SessionKey { + fn struct_layout() -> move_core_types::value::MoveStructLayout { + move_core_types::value::MoveStructLayout::new(vec![ + move_core_types::value::MoveTypeLayout::Vector(Box::new( + move_core_types::value::MoveTypeLayout::U8, + )), + move_core_types::value::MoveTypeLayout::U64, + move_core_types::value::MoveTypeLayout::Vector(Box::new(SessionScope::type_layout())), + move_core_types::value::MoveTypeLayout::U64, + move_core_types::value::MoveTypeLayout::U64, + move_core_types::value::MoveTypeLayout::U64, + ]) + } +} diff --git a/crates/rooch-types/src/transaction/authenticator.rs b/crates/rooch-types/src/transaction/authenticator.rs index bfa223fd15..58f0e8188e 100644 --- a/crates/rooch-types/src/transaction/authenticator.rs +++ b/crates/rooch-types/src/transaction/authenticator.rs @@ -9,19 +9,22 @@ use crate::crypto::{BuiltinScheme, Signature}; use anyhow::Result; - -#[cfg(any(test, feature = "fuzzing"))] -use ethers::types::U256; #[cfg(any(test, feature = "fuzzing"))] use fastcrypto::ed25519::Ed25519KeyPair; #[cfg(any(test, feature = "fuzzing"))] +use fastcrypto::secp256k1::recoverable::Secp256k1RecoverableKeyPair; +#[cfg(any(test, feature = "fuzzing"))] +use fastcrypto::secp256k1::schnorr::SchnorrKeyPair; +#[cfg(any(test, feature = "fuzzing"))] +use fastcrypto::secp256k1::Secp256k1KeyPair; +#[cfg(any(test, feature = "fuzzing"))] use fastcrypto::traits::KeyPair; #[cfg(any(test, feature = "fuzzing"))] use proptest::{collection::vec, prelude::*}; #[cfg(any(test, feature = "fuzzing"))] use rand::{rngs::StdRng, SeedableRng}; use serde::{Deserialize, Serialize}; -use std::{convert::TryFrom, fmt, str::FromStr}; +use std::{fmt, str::FromStr}; /// A `Authenticator` is an an abstraction of a account authenticator. /// It is a part of `AccountAbstraction` @@ -67,53 +70,55 @@ prop_compose! { } } -// TODO: MultiEd25519 - -#[derive(Clone, Debug)] -pub struct EcdsaAuthenticator { - pub signature: ethers::core::types::Signature, +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SchnorrAuthenticator { + pub signature: Signature, } -impl BuiltinAuthenticator for EcdsaAuthenticator { +impl BuiltinAuthenticator for SchnorrAuthenticator { fn scheme(&self) -> BuiltinScheme { - BuiltinScheme::Ecdsa + BuiltinScheme::Schnorr } fn payload(&self) -> Vec { - self.signature.to_vec() + self.signature.as_ref().to_vec() + } +} +#[cfg(any(test, feature = "fuzzing"))] +impl Arbitrary for SchnorrAuthenticator { + type Parameters = (); + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + arb_schnorr_authenticator().boxed() } + type Strategy = BoxedStrategy; } -impl Serialize for EcdsaAuthenticator { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - #[derive(::serde::Serialize)] - #[serde(rename = "EcdsaAuthenticator")] - struct Value { - signature: Vec, - } - Value { - signature: self.signature.to_vec(), +#[cfg(any(test, feature = "fuzzing"))] +prop_compose! { + fn arb_schnorr_authenticator()( + seed in any::(), + message in vec(any::(), 32) + ) -> SchnorrAuthenticator { + let mut rng = StdRng::seed_from_u64(seed); + let kp = SchnorrKeyPair::generate(&mut rng); + SchnorrAuthenticator { + signature: Signature::new_hashed(&message, &kp) } - .serialize(serializer) } } -impl<'de> Deserialize<'de> for EcdsaAuthenticator { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - #[derive(::serde::Deserialize)] - #[serde(rename = "EcdsaAuthenticator")] - struct Value { - signature: Vec, - } - let value = Value::deserialize(deserializer)?; - let signature = ethers::core::types::Signature::try_from(value.signature.as_slice()) - .map_err(|e| serde::de::Error::custom(e.to_string()))?; - Ok(EcdsaAuthenticator { signature }) +// TODO: MultiEd25519 + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaAuthenticator { + pub signature: Signature, +} + +impl BuiltinAuthenticator for EcdsaAuthenticator { + fn scheme(&self) -> BuiltinScheme { + BuiltinScheme::Ecdsa + } + fn payload(&self) -> Vec { + self.signature.as_ref().to_vec() } } @@ -129,13 +134,50 @@ impl Arbitrary for EcdsaAuthenticator { #[cfg(any(test, feature = "fuzzing"))] prop_compose! { fn arb_ecdsa_authenticator()( - r in vec(any::(), 4..=4).prop_map(|v| U256(v.try_into().unwrap())), - s in vec(any::(), 4..=4).prop_map(|v| U256(v.try_into().unwrap())), - // Although v is an u64 type, it is actually an u8 value. - v in any::().prop_map(::from), + seed in any::(), + message in vec(any::(), 1..1000), ) -> EcdsaAuthenticator { + let mut rng = StdRng::seed_from_u64(seed); + let kp = Secp256k1KeyPair::generate(&mut rng); EcdsaAuthenticator { - signature: ethers::core::types::Signature {r, s, v}, + signature: Signature::new_hashed(&message, &kp) + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaRecoverableAuthenticator { + pub signature: Signature, +} + +impl BuiltinAuthenticator for EcdsaRecoverableAuthenticator { + fn scheme(&self) -> BuiltinScheme { + BuiltinScheme::EcdsaRecoverable + } + fn payload(&self) -> Vec { + self.signature.as_ref().to_vec() + } +} + +#[cfg(any(test, feature = "fuzzing"))] +impl Arbitrary for EcdsaRecoverableAuthenticator { + type Parameters = (); + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + arb_ecdsa_recoverable_authenticator().boxed() + } + type Strategy = BoxedStrategy; +} + +#[cfg(any(test, feature = "fuzzing"))] +prop_compose! { + fn arb_ecdsa_recoverable_authenticator()( + seed in any::(), + message in vec(any::(), 1..1000), + ) -> EcdsaRecoverableAuthenticator { + let mut rng = StdRng::seed_from_u64(seed); + let kp = Secp256k1RecoverableKeyPair::generate(&mut rng); + EcdsaRecoverableAuthenticator { + signature: Signature::new_hashed(&message, &kp) } } } @@ -155,8 +197,10 @@ impl From for Authenticator { fn from(sign: Signature) -> Self { match sign.to_public_key().unwrap().scheme() { BuiltinScheme::Ed25519 => Authenticator::ed25519(sign), - BuiltinScheme::Ecdsa => todo!(), + BuiltinScheme::Ecdsa => Authenticator::ecdsa(sign), + BuiltinScheme::EcdsaRecoverable => Authenticator::ecdsa_recoverable(sign), BuiltinScheme::MultiEd25519 => todo!(), + BuiltinScheme::Schnorr => Authenticator::schnorr(sign), } } } @@ -179,10 +223,20 @@ impl Authenticator { } /// Create a single-signature ecdsa authenticator - pub fn ecdsa(signature: ethers::core::types::Signature) -> Self { + pub fn ecdsa(signature: Signature) -> Self { EcdsaAuthenticator { signature }.into() } + /// Create a single-signature ecdsa recoverable authenticator + pub fn ecdsa_recoverable(signature: Signature) -> Self { + EcdsaRecoverableAuthenticator { signature }.into() + } + + /// Create a single-signature schnorr authenticator + pub fn schnorr(signature: Signature) -> Self { + SchnorrAuthenticator { signature }.into() + } + /// Create a custom authenticator pub fn new(scheme: u64, payload: Vec) -> Self { Self { scheme, payload } @@ -222,11 +276,25 @@ mod tests { assert_eq!(authenticator.signature, deserialized.signature); } + #[test] + fn test_ecdsa_recoverable_authenticator_serialize_deserialize(authenticator in any::()) { + let serialized = serde_json::to_string(&authenticator).unwrap(); + let deserialized: super:: EcdsaRecoverableAuthenticator = serde_json::from_str(&serialized).unwrap(); + assert_eq!(authenticator.signature, deserialized.signature); + } + #[test] fn test_ed25519_authenticator_serialize_deserialize(authenticator in any::()) { let serialized = serde_json::to_string(&authenticator).unwrap(); let deserialized: super::Ed25519Authenticator = serde_json::from_str(&serialized).unwrap(); assert_eq!(authenticator.signature, deserialized.signature); } + + #[test] + fn test_schnorr_authenticator_serialize_deserialize(authenticator in any::()) { + let serialized = serde_json::to_string(&authenticator).unwrap(); + let deserialized: super::SchnorrAuthenticator = serde_json::from_str(&serialized).unwrap(); + assert_eq!(authenticator.signature, deserialized.signature); + } } } diff --git a/crates/rooch-types/src/transaction/ethereum.rs b/crates/rooch-types/src/transaction/ethereum.rs index 09ab7e7129..cbd4bfa5a1 100644 --- a/crates/rooch-types/src/transaction/ethereum.rs +++ b/crates/rooch-types/src/transaction/ethereum.rs @@ -2,9 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 use super::{authenticator::Authenticator, AbstractTransaction, AuthenticatorInfo}; -use crate::address::EthereumAddress; +use crate::{ + address::EthereumAddress, + crypto::{EcdsaRecoverableRoochSignature, EcdsaRoochSignature, Signature}, + error::RoochError, +}; use anyhow::Result; +use bech32::Base32Len; use ethers::utils::rlp::{Decodable, Rlp}; +use fastcrypto::{ + hash::Keccak256, + secp256k1::recoverable::Secp256k1RecoverableSignature, + traits::{RecoverableSignature, ToFromBytes}, +}; use move_core_types::account_address::AccountAddress; use moveos_types::{ h256::H256, @@ -23,6 +33,107 @@ impl EthereumTransaction { bcs::from_bytes(&self.0.input) .map_err(|e| anyhow::anyhow!("decode calldata to action failed: {}", e)) } + + pub fn convert_eth_signature_to_recoverable_secp256k1_signature( + &self, + ) -> Result { + let r = self.0.r; + let s = self.0.s; + // Calculate the "recovery byte": The recovery ID (v) contains information about the network and the signature type. + // To calculate the recovery byte, determine the value of the recovery ID based on the network it's intended for. + // For Ethereum Mainnet and Ropsten, the recovery ID is either 27 or 28, while for other networks like Rinkeby or Goerli, + // it can be 35 or 36. Subtracting 27 (or 35) from the recovery ID gives you the recovery byte (0 or 1). + let v = self.0.v.as_u64(); + let recovery_byte = if v == 27 { + 0 + } else if v == 28 { + 1 + } else if v == 35 { + 0 + } else if v == 36 { + 1 + } else { + return Err(RoochError::TransactionError( + "Invalid recovery ID.".to_owned(), + )); + }; + + // Prepare the signed message (RLP encoding of the transaction) + let message = self.tx_hash().to_fixed_bytes(); + + // Convert `U256` values `r` and `s` to arrays of `u8` + let mut r_bytes = [0u8; 32]; + r.to_big_endian(&mut r_bytes); + let mut s_bytes = [0u8; 32]; + s.to_big_endian(&mut s_bytes); + + // Create a new array to store the 65-byte "rsv" signature + let mut rsv_signature = [0u8; 65]; + rsv_signature[..32].copy_from_slice(&r_bytes); + rsv_signature[32..64].copy_from_slice(&s_bytes); + rsv_signature[64] = recovery_byte as u8; + + println!("r: {:?}", &r_bytes); + println!("s: {:?}", &s_bytes); + println!("v: {:?}", &recovery_byte); + println!("rsv_signature length: {:?}", &rsv_signature.len()); + + // Create the recoverable signature from the rsv signature + let recoverable_signature: Secp256k1RecoverableSignature = + ::from_bytes(&rsv_signature) + .expect("Invalid signature"); + println!("sig base32 length: {}", &recoverable_signature.base32_len()); + println!("msg hash length: {:?}", message.len()); + // TODO FIXME 'Failed to recover public key: GeneralOpaqueError' + // Recover with Keccak256 hash to a public key + let public_key = recoverable_signature + .recover_with_hash::(&message) + .expect("Failed to recover public key"); + println!("pubkey: {:?}", public_key); + + // Combine the recoverable signature and public key to construct the final signature + let mut pubkey_and_rsv_signature = Vec::new(); + pubkey_and_rsv_signature.extend_from_slice(public_key.as_bytes()); + pubkey_and_rsv_signature.extend_from_slice(&rsv_signature); + println!( + "pubkey_and_rsv_signature length: {:?}", + &pubkey_and_rsv_signature.len() + ); + + // Parse the "pubkey_and_rsv_signature" signature + // 98 length with 65 bytes recoverable signature and 33 bytes public key, ignore the scheme length + let signature: Signature = + ::from_bytes(&pubkey_and_rsv_signature) + .unwrap() + .into(); + + Ok(signature) + } + + // TODO FIXME implement nonrecoverable signature + pub fn convert_eth_signature_to_non_recoverable_secp256k1_signature( + &self, + ) -> Result { + let r = self.0.r; + let s = self.0.s; + // Convert `U256` values `r` and `s` to arrays of `u8` + let mut r_bytes = [0u8; 32]; + r.to_big_endian(&mut r_bytes); + let mut s_bytes = [0u8; 32]; + s.to_big_endian(&mut s_bytes); + + // Create a new array to store the 64-byte "rs" signature + let mut rs_signature = [0u8; 64]; + rs_signature[..32].copy_from_slice(&r_bytes); + rs_signature[32..].copy_from_slice(&s_bytes); + + // Create a non-recoverable secp256k1 signature without the recovery ID (v) + let signature: Signature = ::from_bytes(&rs_signature) + .unwrap() + .into(); + + Ok(signature) + } } impl AbstractTransaction for EthereumTransaction { @@ -49,11 +160,11 @@ impl AbstractTransaction for EthereumTransaction { AuthenticatorInfo { //TODO should change the seqence_number to u256? seqence_number: self.0.nonce.as_u64(), - authenticator: Authenticator::ecdsa(ethers::core::types::Signature { - r: self.0.r, - s: self.0.s, - v: self.0.v.as_u64(), - }), + authenticator: Authenticator::ecdsa_recoverable( + // TODO need to support handling ethereum signature to Rooch signature + self.convert_eth_signature_to_recoverable_secp256k1_signature() + .unwrap(), + ), } } diff --git a/crates/rooch/Cargo.toml b/crates/rooch/Cargo.toml index b2b5e30054..6cc6109edb 100644 --- a/crates/rooch/Cargo.toml +++ b/crates/rooch/Cargo.toml @@ -34,6 +34,10 @@ hex = { workspace = true } regex = { workspace = true } serde-reflection = { workspace = true } serde-generate = { workspace = true } +bcs-ext = { package = "bcs-ext", workspace = true } +rust-embed = { workspace = true } +rocket = { workspace = true } +parking_lot = { workspace = true } move-bytecode-utils = { workspace = true } move-binary-format = { workspace = true } diff --git a/crates/rooch/public/dashboard/404.html b/crates/rooch/public/dashboard/404.html new file mode 100644 index 0000000000..572f8d2f95 --- /dev/null +++ b/crates/rooch/public/dashboard/404.html @@ -0,0 +1,12 @@ +404 – Rooch Dashboard
404
This page does not exist
The page you are looking for could not be found.
Home

© Root Branch Ltd. 2023. All rights reserved.

\ No newline at end of file diff --git a/crates/rooch/public/dashboard/_next/static/6tj9psdqv67ajAQV-w4FZ/_buildManifest.js b/crates/rooch/public/dashboard/_next/static/6tj9psdqv67ajAQV-w4FZ/_buildManifest.js new file mode 100644 index 0000000000..0eb63b9e7a --- /dev/null +++ b/crates/rooch/public/dashboard/_next/static/6tj9psdqv67ajAQV-w4FZ/_buildManifest.js @@ -0,0 +1 @@ +self.__BUILD_MANIFEST=function(s,e,a){return{__rewrites:{beforeFiles:[],afterFiles:[{source:"/:path*/_meta",destination:s}],fallback:[]},"/":[e,a,"static/chunks/pages/index-bf12cce56e55b143.js"],"/404":[e,a,"static/chunks/pages/404-9f50848d6ce6a594.js"],"/_error":["static/chunks/pages/_error-19df3536fafc1a52.js"],sortedPages:["/",s,"/_app","/_error"]}}("/404","static/chunks/586-443a7586dff0625f.js","static/chunks/859-74d1e6e294ad010b.js"),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/crates/rooch/public/dashboard/_next/static/6tj9psdqv67ajAQV-w4FZ/_ssgManifest.js b/crates/rooch/public/dashboard/_next/static/6tj9psdqv67ajAQV-w4FZ/_ssgManifest.js new file mode 100644 index 0000000000..0511aa895e --- /dev/null +++ b/crates/rooch/public/dashboard/_next/static/6tj9psdqv67ajAQV-w4FZ/_ssgManifest.js @@ -0,0 +1 @@ +self.__SSG_MANIFEST=new Set,self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB(); \ No newline at end of file diff --git a/crates/rooch/public/dashboard/_next/static/chunks/586-443a7586dff0625f.js b/crates/rooch/public/dashboard/_next/static/chunks/586-443a7586dff0625f.js new file mode 100644 index 0000000000..0da9d5abc9 --- /dev/null +++ b/crates/rooch/public/dashboard/_next/static/chunks/586-443a7586dff0625f.js @@ -0,0 +1,23 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[586],{209:function(ee,en,er){"use strict";function ei(ee){if(null==ee)return window;if("[object Window]"!==ee.toString()){var en=ee.ownerDocument;return en&&en.defaultView||window}return ee}function eo(ee){var en=ei(ee).Element;return ee instanceof en||ee instanceof Element}function es(ee){var en=ei(ee).HTMLElement;return ee instanceof en||ee instanceof HTMLElement}function eu(ee){if("undefined"==typeof ShadowRoot)return!1;var en=ei(ee).ShadowRoot;return ee instanceof en||ee instanceof ShadowRoot}er.d(en,{fi:function(){return tO}});var ec=Math.max,ed=Math.min,ef=Math.round;function eh(){var ee=navigator.userAgentData;return null!=ee&&ee.brands&&Array.isArray(ee.brands)?ee.brands.map(function(ee){return ee.brand+"/"+ee.version}).join(" "):navigator.userAgent}function ep(){return!/^((?!chrome|android).)*safari/i.test(eh())}function em(ee,en,er){void 0===en&&(en=!1),void 0===er&&(er=!1);var eu=ee.getBoundingClientRect(),ec=1,ed=1;en&&es(ee)&&(ec=ee.offsetWidth>0&&ef(eu.width)/ee.offsetWidth||1,ed=ee.offsetHeight>0&&ef(eu.height)/ee.offsetHeight||1);var eh=(eo(ee)?ei(ee):window).visualViewport,em=!ep()&&er,ex=(eu.left+(em&&eh?eh.offsetLeft:0))/ec,eg=(eu.top+(em&&eh?eh.offsetTop:0))/ed,ev=eu.width/ec,ey=eu.height/ed;return{width:ev,height:ey,top:eg,right:ex+ev,bottom:eg+ey,left:ex,x:ex,y:eg}}function ex(ee){var en=ei(ee);return{scrollLeft:en.pageXOffset,scrollTop:en.pageYOffset}}function eg(ee){return{scrollLeft:ee.scrollLeft,scrollTop:ee.scrollTop}}function ev(ee){return ee!==ei(ee)&&es(ee)?eg(ee):ex(ee)}function ey(ee){return ee?(ee.nodeName||"").toLowerCase():null}function eb(ee){return((eo(ee)?ee.ownerDocument:ee.document)||window.document).documentElement}function ew(ee){return em(eb(ee)).left+ex(ee).scrollLeft}function e_(ee){return ei(ee).getComputedStyle(ee)}function ek(ee){var en=e_(ee),er=en.overflow,ei=en.overflowX,eo=en.overflowY;return/auto|scroll|overlay|hidden/.test(er+eo+ei)}function ej(ee){var en=ee.getBoundingClientRect(),er=ef(en.width)/ee.offsetWidth||1,ei=ef(en.height)/ee.offsetHeight||1;return 1!==er||1!==ei}function eE(ee,en,er){void 0===er&&(er=!1);var ei=es(en),eo=es(en)&&ej(en),eu=eb(en),ec=em(ee,eo,er),ed={scrollLeft:0,scrollTop:0},ef={x:0,y:0};return(ei||!ei&&!er)&&(("body"!==ey(en)||ek(eu))&&(ed=ev(en)),es(en)?(ef=em(en,!0),ef.x+=en.clientLeft,ef.y+=en.clientTop):eu&&(ef.x=ew(eu))),{x:ec.left+ed.scrollLeft-ef.x,y:ec.top+ed.scrollTop-ef.y,width:ec.width,height:ec.height}}function eO(ee){var en=em(ee),er=ee.offsetWidth,ei=ee.offsetHeight;return 1>=Math.abs(en.width-er)&&(er=en.width),1>=Math.abs(en.height-ei)&&(ei=en.height),{x:ee.offsetLeft,y:ee.offsetTop,width:er,height:ei}}function eC(ee){return"html"===ey(ee)?ee:ee.assignedSlot||ee.parentNode||(eu(ee)?ee.host:null)||eb(ee)}function eT(ee){return["html","body","#document"].indexOf(ey(ee))>=0?ee.ownerDocument.body:es(ee)&&ek(ee)?ee:eT(eC(ee))}function eS(ee,en){void 0===en&&(en=[]);var er,eo=eT(ee),es=eo===(null==(er=ee.ownerDocument)?void 0:er.body),eu=ei(eo),ec=es?[eu].concat(eu.visualViewport||[],ek(eo)?eo:[]):eo,ed=en.concat(ec);return es?ed:ed.concat(eS(eC(ec)))}function eN(ee){return["table","td","th"].indexOf(ey(ee))>=0}function eR(ee){return es(ee)&&"fixed"!==e_(ee).position?ee.offsetParent:null}function eI(ee){var en=/firefox/i.test(eh());if(/Trident/i.test(eh())&&es(ee)&&"fixed"===e_(ee).position)return null;var er=eC(ee);for(eu(er)&&(er=er.host);es(er)&&0>["html","body"].indexOf(ey(er));){var ei=e_(er);if("none"!==ei.transform||"none"!==ei.perspective||"paint"===ei.contain||-1!==["transform","perspective"].indexOf(ei.willChange)||en&&"filter"===ei.willChange||en&&ei.filter&&"none"!==ei.filter)return er;er=er.parentNode}return null}function eA(ee){for(var en=ei(ee),er=eR(ee);er&&eN(er)&&"static"===e_(er).position;)er=eR(er);return er&&("html"===ey(er)||"body"===ey(er)&&"static"===e_(er).position)?en:er||eI(ee)||en}var eL="top",eP="bottom",eM="right",eZ="left",eD="auto",eF=[eL,eP,eM,eZ],e$="start",ez="end",eH="clippingParents",eW="viewport",eU="popper",eG="reference",eV=eF.reduce(function(ee,en){return ee.concat([en+"-"+e$,en+"-"+ez])},[]),eB=[].concat(eF,[eD]).reduce(function(ee,en){return ee.concat([en,en+"-"+e$,en+"-"+ez])},[]),eK=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function eQ(ee){var en=new Map,er=new Set,ei=[];function eo(ee){er.add(ee.name),[].concat(ee.requires||[],ee.requiresIfExists||[]).forEach(function(ee){if(!er.has(ee)){var ei=en.get(ee);ei&&eo(ei)}}),ei.push(ee)}return ee.forEach(function(ee){en.set(ee.name,ee)}),ee.forEach(function(ee){er.has(ee.name)||eo(ee)}),ei}function eJ(ee){var en=eQ(ee);return eK.reduce(function(ee,er){return ee.concat(en.filter(function(ee){return ee.phase===er}))},[])}function eY(ee){var en;return function(){return en||(en=new Promise(function(er){Promise.resolve().then(function(){en=void 0,er(ee())})})),en}}function eX(ee){var en=ee.reduce(function(ee,en){var er=ee[en.name];return ee[en.name]=er?Object.assign({},er,en,{options:Object.assign({},er.options,en.options),data:Object.assign({},er.data,en.data)}):en,ee},{});return Object.keys(en).map(function(ee){return en[ee]})}var e0={placement:"bottom",modifiers:[],strategy:"absolute"};function e1(){for(var ee=arguments.length,en=Array(ee),er=0;er=0?"x":"y"}function e9(ee){var en,er=ee.reference,ei=ee.element,eo=ee.placement,es=eo?e4(eo):null,eu=eo?e5(eo):null,ec=er.x+er.width/2-ei.width/2,ed=er.y+er.height/2-ei.height/2;switch(es){case eL:en={x:ec,y:er.y-ei.height};break;case eP:en={x:ec,y:er.y+er.height};break;case eM:en={x:er.x+er.width,y:ed};break;case eZ:en={x:er.x-ei.width,y:ed};break;default:en={x:er.x,y:er.y}}var ef=es?e3(es):null;if(null!=ef){var eh="y"===ef?"height":"width";switch(eu){case e$:en[ef]=en[ef]-(er[eh]/2-ei[eh]/2);break;case ez:en[ef]=en[ef]+(er[eh]/2-ei[eh]/2)}}return en}var e7={top:"auto",right:"auto",bottom:"auto",left:"auto"};function e6(ee,en){var er=ee.x,ei=ee.y,eo=en.devicePixelRatio||1;return{x:ef(er*eo)/eo||0,y:ef(ei*eo)/eo||0}}function e8(ee){var en,er,eo=ee.popper,es=ee.popperRect,eu=ee.placement,ec=ee.variation,ed=ee.offsets,ef=ee.position,eh=ee.gpuAcceleration,ep=ee.adaptive,em=ee.roundOffsets,ex=ee.isFixed,eg=ed.x,ev=void 0===eg?0:eg,ey=ed.y,ew=void 0===ey?0:ey,ek="function"==typeof em?em({x:ev,y:ew}):{x:ev,y:ew};ev=ek.x,ew=ek.y;var ej=ed.hasOwnProperty("x"),eE=ed.hasOwnProperty("y"),eO=eZ,eC=eL,eT=window;if(ep){var eS=eA(eo),eN="clientHeight",eR="clientWidth";eS===ei(eo)&&"static"!==e_(eS=eb(eo)).position&&"absolute"===ef&&(eN="scrollHeight",eR="scrollWidth"),(eu===eL||(eu===eZ||eu===eM)&&ec===ez)&&(eC=eP,ew-=(ex&&eS===eT&&eT.visualViewport?eT.visualViewport.height:eS[eN])-es.height,ew*=eh?1:-1),(eu===eZ||(eu===eL||eu===eP)&&ec===ez)&&(eO=eM,ev-=(ex&&eS===eT&&eT.visualViewport?eT.visualViewport.width:eS[eR])-es.width,ev*=eh?1:-1)}var eI=Object.assign({position:ef},ep&&e7),eD=!0===em?e6({x:ev,y:ew},ei(eo)):{x:ev,y:ew};return(ev=eD.x,ew=eD.y,eh)?Object.assign({},eI,((er={})[eC]=eE?"0":"",er[eO]=ej?"0":"",er.transform=1>=(eT.devicePixelRatio||1)?"translate("+ev+"px, "+ew+"px)":"translate3d("+ev+"px, "+ew+"px, 0)",er)):Object.assign({},eI,((en={})[eC]=eE?ew+"px":"",en[eO]=ej?ev+"px":"",en.transform="",en))}function te(ee,en,er){var ei=e4(ee),eo=[eZ,eL].indexOf(ei)>=0?-1:1,es="function"==typeof er?er(Object.assign({},en,{placement:ee})):er,eu=es[0],ec=es[1];return eu=eu||0,ec=(ec||0)*eo,[eZ,eM].indexOf(ei)>=0?{x:ec,y:eu}:{x:eu,y:ec}}var tt={left:"right",right:"left",bottom:"top",top:"bottom"};function tn(ee){return ee.replace(/left|right|bottom|top/g,function(ee){return tt[ee]})}var tr={start:"end",end:"start"};function ti(ee){return ee.replace(/start|end/g,function(ee){return tr[ee]})}function to(ee,en){var er=ei(ee),eo=eb(ee),es=er.visualViewport,eu=eo.clientWidth,ec=eo.clientHeight,ed=0,ef=0;if(es){eu=es.width,ec=es.height;var eh=ep();(eh||!eh&&"fixed"===en)&&(ed=es.offsetLeft,ef=es.offsetTop)}return{width:eu,height:ec,x:ed+ew(ee),y:ef}}function ts(ee){var en,er=eb(ee),ei=ex(ee),eo=null==(en=ee.ownerDocument)?void 0:en.body,es=ec(er.scrollWidth,er.clientWidth,eo?eo.scrollWidth:0,eo?eo.clientWidth:0),eu=ec(er.scrollHeight,er.clientHeight,eo?eo.scrollHeight:0,eo?eo.clientHeight:0),ed=-ei.scrollLeft+ew(ee),ef=-ei.scrollTop;return"rtl"===e_(eo||er).direction&&(ed+=ec(er.clientWidth,eo?eo.clientWidth:0)-es),{width:es,height:eu,x:ed,y:ef}}function tl(ee,en){var er=en.getRootNode&&en.getRootNode();if(ee.contains(en))return!0;if(er&&eu(er)){var ei=en;do{if(ei&&ee.isSameNode(ei))return!0;ei=ei.parentNode||ei.host}while(ei)}return!1}function tu(ee){return Object.assign({},ee,{left:ee.x,top:ee.y,right:ee.x+ee.width,bottom:ee.y+ee.height})}function tc(ee,en){var er=em(ee,!1,"fixed"===en);return er.top=er.top+ee.clientTop,er.left=er.left+ee.clientLeft,er.bottom=er.top+ee.clientHeight,er.right=er.left+ee.clientWidth,er.width=ee.clientWidth,er.height=ee.clientHeight,er.x=er.left,er.y=er.top,er}function td(ee,en,er){return en===eW?tu(to(ee,er)):eo(en)?tc(en,er):tu(ts(eb(ee)))}function tf(ee){var en=eS(eC(ee)),er=["absolute","fixed"].indexOf(e_(ee).position)>=0&&es(ee)?eA(ee):ee;return eo(er)?en.filter(function(ee){return eo(ee)&&tl(ee,er)&&"body"!==ey(ee)}):[]}function th(ee,en,er,ei){var eo=[].concat("clippingParents"===en?tf(ee):[].concat(en),[er]),es=eo[0],eu=eo.reduce(function(en,er){var eo=td(ee,er,ei);return en.top=ec(eo.top,en.top),en.right=ed(eo.right,en.right),en.bottom=ed(eo.bottom,en.bottom),en.left=ec(eo.left,en.left),en},td(ee,es,ei));return eu.width=eu.right-eu.left,eu.height=eu.bottom-eu.top,eu.x=eu.left,eu.y=eu.top,eu}function tp(){return{top:0,right:0,bottom:0,left:0}}function tm(ee){return Object.assign({},tp(),ee)}function tx(ee,en){return en.reduce(function(en,er){return en[er]=ee,en},{})}function tg(ee,en){void 0===en&&(en={});var er=en,ei=er.placement,es=void 0===ei?ee.placement:ei,eu=er.strategy,ec=void 0===eu?ee.strategy:eu,ed=er.boundary,ef=void 0===ed?eH:ed,eh=er.rootBoundary,ep=void 0===eh?eW:eh,ex=er.elementContext,eg=void 0===ex?eU:ex,ev=er.altBoundary,ey=void 0!==ev&&ev,ew=er.padding,e_=void 0===ew?0:ew,ek=tm("number"!=typeof e_?e_:tx(e_,eF)),ej=eg===eU?eG:eU,eE=ee.rects.popper,eO=ee.elements[ey?ej:eg],eC=th(eo(eO)?eO:eO.contextElement||eb(ee.elements.popper),ef,ep,ec),eT=em(ee.elements.reference),eS=e9({reference:eT,element:eE,strategy:"absolute",placement:es}),eN=tu(Object.assign({},eE,eS)),eR=eg===eU?eN:eT,eI={top:eC.top-eR.top+ek.top,bottom:eR.bottom-eC.bottom+ek.bottom,left:eC.left-eR.left+ek.left,right:eR.right-eC.right+ek.right},eA=ee.modifiersData.offset;if(eg===eU&&eA){var eZ=eA[es];Object.keys(eI).forEach(function(ee){var en=[eM,eP].indexOf(ee)>=0?1:-1,er=[eL,eP].indexOf(ee)>=0?"y":"x";eI[ee]+=eZ[er]*en})}return eI}function tv(ee,en){void 0===en&&(en={});var er=en,ei=er.placement,eo=er.boundary,es=er.rootBoundary,eu=er.padding,ec=er.flipVariations,ed=er.allowedAutoPlacements,ef=void 0===ed?eB:ed,eh=e5(ei),ep=eh?ec?eV:eV.filter(function(ee){return e5(ee)===eh}):eF,em=ep.filter(function(ee){return ef.indexOf(ee)>=0});0===em.length&&(em=ep);var ex=em.reduce(function(en,er){return en[er]=tg(ee,{placement:er,boundary:eo,rootBoundary:es,padding:eu})[e4(er)],en},{});return Object.keys(ex).sort(function(ee,en){return ex[ee]-ex[en]})}function ty(ee){if(e4(ee)===eD)return[];var en=tn(ee);return[ti(ee),en,ti(en)]}function tb(ee){return"x"===ee?"y":"x"}function tw(ee,en,er){return ec(ee,ed(en,er))}function t_(ee,en,er){var ei=tw(ee,en,er);return ei>er?er:ei}var tk=function(ee,en){return tm("number"!=typeof(ee="function"==typeof ee?ee(Object.assign({},en.rects,{placement:en.placement})):ee)?ee:tx(ee,eF))};function tj(ee,en,er){return void 0===er&&(er={x:0,y:0}),{top:ee.top-en.height-er.y,right:ee.right-en.width+er.x,bottom:ee.bottom-en.height+er.y,left:ee.left-en.width-er.x}}function tE(ee){return[eL,eM,eP,eZ].some(function(en){return ee[en]>=0})}var tO=function(ee){void 0===ee&&(ee={});var en=ee,er=en.defaultModifiers,ei=void 0===er?[]:er,es=en.defaultOptions,eu=void 0===es?e0:es;return function(ee,en,er){void 0===er&&(er=eu);var es={placement:"bottom",orderedModifiers:[],options:Object.assign({},e0,eu),modifiersData:{},elements:{reference:ee,popper:en},attributes:{},styles:{}},ec=[],ed=!1,ef={state:es,setOptions:function(er){var ec="function"==typeof er?er(es.options):er;ep(),es.options=Object.assign({},eu,es.options,ec),es.scrollParents={reference:eo(ee)?eS(ee):ee.contextElement?eS(ee.contextElement):[],popper:eS(en)};var ed=eJ(eX([].concat(ei,es.options.modifiers)));return es.orderedModifiers=ed.filter(function(ee){return ee.enabled}),eh(),ef.update()},forceUpdate:function(){if(!ed){var ee=es.elements,en=ee.reference,er=ee.popper;if(e1(en,er)){es.rects={reference:eE(en,eA(er),"fixed"===es.options.strategy),popper:eO(er)},es.reset=!1,es.placement=es.options.placement,es.orderedModifiers.forEach(function(ee){return es.modifiersData[ee.name]=Object.assign({},ee.data)});for(var ei=0;ei=0,eA=eI?"width":"height",eF=tg(en,{placement:eS,boundary:eh,rootBoundary:ep,altBoundary:em,padding:ef}),ez=eI?eR?eM:eZ:eR?eP:eL;ek[eA]>ej[eA]&&(ez=tn(ez));var eH=tn(ez),eW=[];if(es&&eW.push(eF[eN]<=0),ec&&eW.push(eF[ez]<=0,eF[eH]<=0),eW.every(function(ee){return ee})){eC=eS,eO=!1;break}eE.set(eS,eW)}if(eO)for(var eU=eg?3:1,eG=function(ee){var en=e_.find(function(en){var er=eE.get(en);if(er)return er.slice(0,ee).every(function(ee){return ee})});if(en)return eC=en,"break"},eV=eU;eV>0&&"break"!==eG(eV);eV--);en.placement!==eC&&(en.modifiersData[ei]._skip=!0,en.placement=eC,en.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}},{name:"preventOverflow",enabled:!0,phase:"main",fn:function(ee){var en=ee.state,er=ee.options,ei=ee.name,eo=er.mainAxis,es=void 0===eo||eo,eu=er.altAxis,ef=void 0!==eu&&eu,eh=er.boundary,ep=er.rootBoundary,em=er.altBoundary,ex=er.padding,eg=er.tether,ev=void 0===eg||eg,ey=er.tetherOffset,eb=void 0===ey?0:ey,ew=tg(en,{boundary:eh,rootBoundary:ep,padding:ex,altBoundary:em}),e_=e4(en.placement),ek=e5(en.placement),ej=!ek,eE=e3(e_),eC=tb(eE),eT=en.modifiersData.popperOffsets,eS=en.rects.reference,eN=en.rects.popper,eR="function"==typeof eb?eb(Object.assign({},en.rects,{placement:en.placement})):eb,eI="number"==typeof eR?{mainAxis:eR,altAxis:eR}:Object.assign({mainAxis:0,altAxis:0},eR),eD=en.modifiersData.offset?en.modifiersData.offset[en.placement]:null,eF={x:0,y:0};if(eT){if(es){var ez,eH="y"===eE?eL:eZ,eW="y"===eE?eP:eM,eU="y"===eE?"height":"width",eG=eT[eE],eV=eG+ew[eH],eB=eG-ew[eW],eK=ev?-eN[eU]/2:0,eQ=ek===e$?eS[eU]:eN[eU],eJ=ek===e$?-eN[eU]:-eS[eU],eY=en.elements.arrow,eX=ev&&eY?eO(eY):{width:0,height:0},e0=en.modifiersData["arrow#persistent"]?en.modifiersData["arrow#persistent"].padding:tp(),e1=e0[eH],e2=e0[eW],e9=tw(0,eS[eU],eX[eU]),e7=ej?eS[eU]/2-eK-e9-e1-eI.mainAxis:eQ-e9-e1-eI.mainAxis,e6=ej?-eS[eU]/2+eK+e9+e2+eI.mainAxis:eJ+e9+e2+eI.mainAxis,e8=en.elements.arrow&&eA(en.elements.arrow),te=e8?"y"===eE?e8.clientTop||0:e8.clientLeft||0:0,tt=null!=(ez=null==eD?void 0:eD[eE])?ez:0,tn=eG+e7-tt-te,tr=eG+e6-tt,ti=tw(ev?ed(eV,tn):eV,eG,ev?ec(eB,tr):eB);eT[eE]=ti,eF[eE]=ti-eG}if(ef){var to,ts="x"===eE?eL:eZ,tl="x"===eE?eP:eM,tu=eT[eC],tc="y"===eC?"height":"width",td=tu+ew[ts],tf=tu-ew[tl],th=-1!==[eL,eZ].indexOf(e_),tm=null!=(to=null==eD?void 0:eD[eC])?to:0,tx=th?td:tu-eS[tc]-eN[tc]-tm+eI.altAxis,tv=th?tu+eS[tc]+eN[tc]-tm-eI.altAxis:tf,ty=ev&&th?t_(tx,tu,tv):tw(ev?tx:td,tu,ev?tv:tf);eT[eC]=ty,eF[eC]=ty-tu}en.modifiersData[ei]=eF}},requiresIfExists:["offset"]},{name:"arrow",enabled:!0,phase:"main",fn:function(ee){var en,er=ee.state,ei=ee.name,eo=ee.options,es=er.elements.arrow,eu=er.modifiersData.popperOffsets,ec=e4(er.placement),ed=e3(ec),ef=[eZ,eM].indexOf(ec)>=0?"height":"width";if(es&&eu){var eh=tk(eo.padding,er),ep=eO(es),em="y"===ed?eL:eZ,ex="y"===ed?eP:eM,eg=er.rects.reference[ef]+er.rects.reference[ed]-eu[ed]-er.rects.popper[ef],ev=eu[ed]-er.rects.reference[ed],ey=eA(es),eb=ey?"y"===ed?ey.clientHeight||0:ey.clientWidth||0:0,ew=eg/2-ev/2,e_=eh[em],ek=eb-ep[ef]-eh[ex],ej=eb/2-ep[ef]/2+ew,eE=tw(e_,ej,ek),eC=ed;er.modifiersData[ei]=((en={})[eC]=eE,en.centerOffset=eE-ej,en)}},effect:function(ee){var en=ee.state,er=ee.options.element,ei=void 0===er?"[data-popper-arrow]":er;null!=ei&&("string"!=typeof ei||(ei=en.elements.popper.querySelector(ei)))&&tl(en.elements.popper,ei)&&(en.elements.arrow=ei)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]},{name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(ee){var en=ee.state,er=ee.name,ei=en.rects.reference,eo=en.rects.popper,es=en.modifiersData.preventOverflow,eu=tg(en,{elementContext:"reference"}),ec=tg(en,{altBoundary:!0}),ed=tj(eu,ei),ef=tj(ec,eo,es),eh=tE(ed),ep=tE(ef);en.modifiersData[er]={referenceClippingOffsets:ed,popperEscapeOffsets:ef,isReferenceHidden:eh,hasPopperEscaped:ep},en.attributes.popper=Object.assign({},en.attributes.popper,{"data-popper-reference-hidden":eh,"data-popper-escaped":ep})}}]})},5924:function(ee,en,er){"use strict";function ei(ee){var en,er,eo="";if("string"==typeof ee||"number"==typeof ee)eo+=ee;else if("object"==typeof ee){if(Array.isArray(ee))for(en=0;en=ei))));en++);if(er)return es?ta(ed,ei,0):void(en[en.length]=ed)}return!er&&ed}function ta(ee,en,er){return ee=1===ee.length?ee[0]:[].concat.apply([],ee),er||ee.length>en?ee.slice(er,er+en):ee}function ua(ee,en,er,ei){return ee=er?(ee=ee[(ei=ei&&en>er)?en:er])&&ee[ei?er:en]:ee[en]}function N(ee,en,er,ei,eo){let es=0;if(ee.constructor===Array){if(eo)-1!==(en=ee.indexOf(en))?1=this.B&&(ep||!eh[eg])){var es=L(em,ei,ex),eu="";switch(this.G){case"full":if(2es;ec--)if(ec-es>=this.B){var ed=L(em,ei,ex,eo,es);M(this,eh,eu=eg.substring(es,ec),ed,ee,er)}break}case"reverse":if(1=this.B&&M(this,eh,eu,L(em,ei,ex,eo,ec),ee,er);eu=""}case"forward":if(1=this.B&&M(this,eh,eu,es,ee,er);break}default:if(this.C&&(es=Math.min(es/this.C(en,eg,ex)|0,em-1)),M(this,eh,eg,es,ee,er),ep&&1=this.B&&!eo[eg]){eo[eg]=1;let en=this.l&&eg>es;M(this,ef,en?es:eg,L(eu+(ei/2>eu?0:1),ei,ex,ec-1,ed-1),ee,er,en?eg:es)}}}}}this.m||(this.register[ee]=1)}}return this},t.search=function(ee,en,er){let ei,eo,es;er||(!en&&C(ee)?ee=(er=ee).query:C(en)&&(er=en));let eu=[],ec,ed,ef=0;if(er){ee=er.query||ee,en=er.limit,ef=er.offset||0;var eh=er.context;ed=er.suggest}if(ee&&1<(ec=(ee=this.encode(""+ee)).length)){er=v();var ep=[];for(let en=0,ei=0,eo;en=this.B&&!er[eo]){if(!this.s&&!ed&&!this.map[eo])return eu;ep[ei++]=eo,er[eo]=1}ec=(ee=ep).length}if(!ec)return eu;for(en||(en=100),eh=this.depth&&1en||er)&&(eo=eo.slice(er,er+en)),ei&&(eo=za.call(this,eo)),{tag:ee,result:eo}}function za(ee){let en=Array(ee.length);for(let er=0,ei;er2?er.slice(1-er.length).join("."):en.source=en.resource,en.git_suffix=/\.git$/.test(en.pathname),en.name=decodeURIComponent((en.pathname||en.href).replace(/(^\/)|(\/$)/g,"").replace(/\.git$/,"")),en.owner=decodeURIComponent(en.user),en.source){case"git.cloudforge.com":en.owner=en.user,en.organization=er[0],en.source="cloudforge.com";break;case"visualstudio.com":if("vs-ssh.visualstudio.com"===en.resource){4===(es=en.name.split("/")).length&&(en.organization=es[1],en.owner=es[2],en.name=es[3],en.full_name=es[2]+"/"+es[3]);break}2===(es=en.name.split("/")).length?(en.owner=es[1],en.name=es[1],en.full_name="_git/"+en.name):3===es.length?(en.name=es[2],"DefaultCollection"===es[0]?(en.owner=es[2],en.organization=es[0],en.full_name=en.organization+"/_git/"+en.name):(en.owner=es[0],en.full_name=en.owner+"/_git/"+en.name)):4===es.length&&(en.organization=es[0],en.owner=es[1],en.name=es[3],en.full_name=en.organization+"/"+en.owner+"/_git/"+en.name);break;case"dev.azure.com":case"azure.com":if("ssh.dev.azure.com"===en.resource){4===(es=en.name.split("/")).length&&(en.organization=es[1],en.owner=es[2],en.name=es[3]);break}5===(es=en.name.split("/")).length?(en.organization=es[0],en.owner=es[1],en.name=es[4],en.full_name="_git/"+en.name):3===es.length?(en.name=es[2],"DefaultCollection"===es[0]?(en.owner=es[2],en.organization=es[0],en.full_name=en.organization+"/_git/"+en.name):(en.owner=es[0],en.full_name=en.owner+"/_git/"+en.name)):4===es.length&&(en.organization=es[0],en.owner=es[1],en.name=es[3],en.full_name=en.organization+"/"+en.owner+"/_git/"+en.name),en.query&&en.query.path&&(en.filepath=en.query.path.replace(/^\/+/g,"")),en.query&&en.query.version&&(en.ref=en.query.version.replace(/^GB/,""));break;default:var eu=(es=en.name.split("/")).length-1;if(es.length>=2){var ec=es.indexOf("-",2),ed=es.indexOf("blob",2),ef=es.indexOf("tree",2),eh=es.indexOf("commit",2),ep=es.indexOf("src",2),em=es.indexOf("raw",2),ex=es.indexOf("edit",2);eu=ec>0?ec-1:ed>0?ed-1:ef>0?ef-1:eh>0?eh-1:ep>0?ep-1:em>0?em-1:ex>0?ex-1:eu,en.owner=es.slice(0,eu).join("/"),en.name=es[eu],eh&&(en.commit=es[eu+2])}en.ref="",en.filepathtype="",en.filepath="";var eg=es.length>eu&&"-"===es[eu+1]?eu+1:eu;es.length>eg+2&&["raw","src","blob","tree","edit"].indexOf(es[eg+1])>=0&&(en.filepathtype=es[eg+1],en.ref=es[eg+2],es.length>eg+3&&(en.filepath=es.slice(eg+3).join("/"))),en.organization=en.owner}!en.full_name&&(en.full_name=en.owner,en.name&&(en.full_name&&(en.full_name+="/"),en.full_name+=en.name)),en.owner.startsWith("scm/")&&(en.source="bitbucket-server",en.owner=en.owner.replace("scm/",""),en.organization=en.owner,en.full_name=en.owner+"/"+en.name);var ev=/(projects|users)\/(.*?)\/repos\/(.*?)((\/.*$)|$)/.exec(en.pathname);return null!=ev&&(en.source="bitbucket-server","users"===ev[1]?en.owner="~"+ev[2]:en.owner=ev[2],en.organization=en.owner,en.name=ev[3],(es=ev[4].split("/")).length>1&&(["raw","browse"].indexOf(es[1])>=0?(en.filepathtype=es[1],es.length>2&&(en.filepath=es.slice(2).join("/"))):"commits"===es[1]&&es.length>2&&(en.commit=es[2])),en.full_name=en.owner+"/"+en.name,en.query.at?en.ref=en.query.at:en.ref=""),en}/*! + * buildToken + * Builds OAuth token prefix (helper function) + * + * @name buildToken + * @function + * @param {GitUrl} obj The parsed Git url object. + * @return {String} token prefix + */function es(ee){return"bitbucket.org"===ee.source?"x-token-auth:"+ee.token+"@":ee.token+"@"}function eu(ee){return"bitbucket-server"===ee.source?"scm/"+ee.full_name:""+ee.full_name}eo.stringify=function(ee,en){en=en||(ee.protocols&&ee.protocols.length?ee.protocols.join("+"):ee.protocol);var er=ee.port?":"+ee.port:"",ei=ee.user||"git",eo=ee.git_suffix?".git":"";switch(en){case"ssh":if(er)return"ssh://"+ei+"@"+ee.resource+er+"/"+ee.full_name+eo;return ei+"@"+ee.resource+":"+ee.full_name+eo;case"git+ssh":case"ssh+git":case"ftp":case"ftps":return en+"://"+ei+"@"+ee.resource+er+"/"+ee.full_name+eo;case"http":case"https":return en+"://"+(ee.token?es(ee):ee.user&&(ee.protocols.includes("http")||ee.protocols.includes("https"))?ee.user+"@":"")+ee.resource+er+"/"+eu(ee)+eo;default:return ee.href}},ee.exports=eo},5983:function(){!function(){"use strict";if("object"==typeof window){if("IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype){"isIntersecting"in window.IntersectionObserverEntry.prototype||Object.defineProperty(window.IntersectionObserverEntry.prototype,"isIntersecting",{get:function(){return this.intersectionRatio>0}});return}var ee=function(ee){for(var en=ee,er=eo(en);er;)er=eo(en=er.ownerDocument);return en}(window.document),en=[],er=null,ei=null;eu.prototype.THROTTLE_TIMEOUT=100,eu.prototype.POLL_INTERVAL=null,eu.prototype.USE_MUTATION_OBSERVER=!0,eu._setupCrossOriginUpdater=function(){return er||(er=function(ee,er){ei=ee&&er?ev(ee,er):ex(),en.forEach(function(ee){ee._checkForIntersections()})}),er},eu._resetCrossOriginUpdater=function(){er=null,ei=null},eu.prototype.observe=function(ee){if(!this._observationTargets.some(function(en){return en.element==ee})){if(!(ee&&1==ee.nodeType))throw Error("target must be an Element");this._registerInstance(),this._observationTargets.push({element:ee,entry:null}),this._monitorIntersections(ee.ownerDocument),this._checkForIntersections()}},eu.prototype.unobserve=function(ee){this._observationTargets=this._observationTargets.filter(function(en){return en.element!=ee}),this._unmonitorIntersections(ee.ownerDocument),0==this._observationTargets.length&&this._unregisterInstance()},eu.prototype.disconnect=function(){this._observationTargets=[],this._unmonitorAllIntersections(),this._unregisterInstance()},eu.prototype.takeRecords=function(){var ee=this._queuedEntries.slice();return this._queuedEntries=[],ee},eu.prototype._initThresholds=function(ee){var en=ee||[0];return Array.isArray(en)||(en=[en]),en.sort().filter(function(ee,en,er){if("number"!=typeof ee||isNaN(ee)||ee<0||ee>1)throw Error("threshold must be a number between 0 and 1 inclusively");return ee!==er[en-1]})},eu.prototype._parseRootMargin=function(ee){var en=(ee||"0px").split(/\s+/).map(function(ee){var en=/^(-?\d*\.?\d+)(px|%)$/.exec(ee);if(!en)throw Error("rootMargin must be specified in pixels or percent");return{value:parseFloat(en[1]),unit:en[2]}});return en[1]=en[1]||en[0],en[2]=en[2]||en[0],en[3]=en[3]||en[1],en},eu.prototype._monitorIntersections=function(en){var er=en.defaultView;if(er&&-1==this._monitoringDocuments.indexOf(en)){var ei=this._checkForIntersections,es=null,eu=null;if(this.POLL_INTERVAL?es=er.setInterval(ei,this.POLL_INTERVAL):(ef(er,"resize",ei,!0),ef(en,"scroll",ei,!0),this.USE_MUTATION_OBSERVER&&"MutationObserver"in er&&(eu=new er.MutationObserver(ei)).observe(en,{attributes:!0,childList:!0,characterData:!0,subtree:!0})),this._monitoringDocuments.push(en),this._monitoringUnsubscribes.push(function(){var ee=en.defaultView;ee&&(es&&ee.clearInterval(es),eh(ee,"resize",ei,!0)),eh(en,"scroll",ei,!0),eu&&eu.disconnect()}),en!=(this.root&&(this.root.ownerDocument||this.root)||ee)){var ec=eo(en);ec&&this._monitorIntersections(ec.ownerDocument)}}},eu.prototype._unmonitorIntersections=function(en){var er=this._monitoringDocuments.indexOf(en);if(-1!=er){var ei=this.root&&(this.root.ownerDocument||this.root)||ee;if(!this._observationTargets.some(function(ee){var er=ee.element.ownerDocument;if(er==en)return!0;for(;er&&er!=ei;){var es=eo(er);if((er=es&&es.ownerDocument)==en)return!0}return!1})){var es=this._monitoringUnsubscribes[er];if(this._monitoringDocuments.splice(er,1),this._monitoringUnsubscribes.splice(er,1),es(),en!=ei){var eu=eo(en);eu&&this._unmonitorIntersections(eu.ownerDocument)}}}},eu.prototype._unmonitorAllIntersections=function(){var ee=this._monitoringUnsubscribes.slice(0);this._monitoringDocuments.length=0,this._monitoringUnsubscribes.length=0;for(var en=0;enen.indexOf(this)&&en.push(this)},eu.prototype._unregisterInstance=function(){var ee=en.indexOf(this);-1!=ee&&en.splice(ee,1)},window.IntersectionObserver=eu,window.IntersectionObserverEntry=es}function eo(ee){try{return ee.defaultView&&ee.defaultView.frameElement||null}catch(ee){return null}}function es(ee){this.time=ee.time,this.target=ee.target,this.rootBounds=eg(ee.rootBounds),this.boundingClientRect=eg(ee.boundingClientRect),this.intersectionRect=eg(ee.intersectionRect||ex()),this.isIntersecting=!!ee.intersectionRect;var en=this.boundingClientRect,er=en.width*en.height,ei=this.intersectionRect,eo=ei.width*ei.height;er?this.intersectionRatio=Number((eo/er).toFixed(4)):this.intersectionRatio=this.isIntersecting?1:0}function eu(ee,en){var er=en||{};if("function"!=typeof ee)throw Error("callback must be a function");if(er.root&&1!=er.root.nodeType&&9!=er.root.nodeType)throw Error("root must be a Document or Element");this._checkForIntersections=ed(this._checkForIntersections.bind(this),this.THROTTLE_TIMEOUT),this._callback=ee,this._observationTargets=[],this._queuedEntries=[],this._rootMarginValues=this._parseRootMargin(er.rootMargin),this.thresholds=this._initThresholds(er.threshold),this.root=er.root||null,this.rootMargin=this._rootMarginValues.map(function(ee){return ee.value+ee.unit}).join(" "),this._monitoringDocuments=[],this._monitoringUnsubscribes=[]}function ec(){return window.performance&&performance.now&&performance.now()}function ed(ee,en){var er=null;return function(){er||(er=setTimeout(function(){ee(),er=null},en))}}function ef(ee,en,er,ei){"function"==typeof ee.addEventListener?ee.addEventListener(en,er,ei||!1):"function"==typeof ee.attachEvent&&ee.attachEvent("on"+en,er)}function eh(ee,en,er,ei){"function"==typeof ee.removeEventListener?ee.removeEventListener(en,er,ei||!1):"function"==typeof ee.detachEvent&&ee.detachEvent("on"+en,er)}function ep(ee,en){var er=Math.max(ee.top,en.top),ei=Math.min(ee.bottom,en.bottom),eo=Math.max(ee.left,en.left),es=Math.min(ee.right,en.right),eu=es-eo,ec=ei-er;return eu>=0&&ec>=0&&{top:er,bottom:ei,left:eo,right:es,width:eu,height:ec}||null}function em(ee){var en;try{en=ee.getBoundingClientRect()}catch(ee){}return en?(en.width&&en.height||(en={top:en.top,right:en.right,bottom:en.bottom,left:en.left,width:en.right-en.left,height:en.bottom-en.top}),en):ex()}function ex(){return{top:0,bottom:0,left:0,right:0,width:0,height:0}}function eg(ee){return!ee||"x"in ee?ee:{top:ee.top,y:ee.top,bottom:ee.bottom,left:ee.left,x:ee.left,right:ee.right,width:ee.width,height:ee.height}}function ev(ee,en){var er=en.top-ee.top,ei=en.left-ee.left;return{top:er,left:ei,height:en.height,width:en.width,bottom:er+en.height,right:ei+en.width}}function ey(ee,en){for(var er=en;er;){if(er==ee)return!0;er=eb(er)}return!1}function eb(en){var er=en.parentNode;return 9==en.nodeType&&en!=ee?eo(en):(er&&er.assignedSlot&&(er=er.assignedSlot.parentNode),er&&11==er.nodeType&&er.host)?er.host:er}function ew(ee){return ee&&9===ee.nodeType}}()},2895:function(ee,en,er){"use strict";var ei=er(6097);function eo(ee){if(Array.isArray(ee))return -1!==ee.indexOf("ssh")||-1!==ee.indexOf("rsync");if("string"!=typeof ee)return!1;var en=ei(ee);if(ee=ee.substring(ee.indexOf("://")+3),eo(en))return!0;var er=RegExp(".([a-zA-Z\\d]+):(\\d+)/");return!ee.match(er)&&ee.indexOf("@")-1}function eJ(ee,en){var er=this.__data__,ei=e5(er,ee);return ei<0?er.push([ee,en]):er[ei][1]=en,this}function eY(ee){var en=-1,er=ee?ee.length:0;for(this.clear();++enee?new ex(ee):null,[ee]);return en}function ev(){return(0,eu.clientHookInServerComponentError)("usePathname"),(0,ei.useContext)(es.PathnameContext)}function ey(){(0,eu.clientHookInServerComponentError)("useRouter");let ee=(0,ei.useContext)(eo.AppRouterContext);if(null===ee)throw Error("invariant expected app router to be mounted");return ee}function eb(ee,en){void 0===en&&(en={});let er=ee[1];for(let ee of Object.values(er)){let er=ee[0],ei=Array.isArray(er),eo=ei?er[1]:er;!eo||eo.startsWith("__PAGE__")||(ei&&(en[er[0]]=er[1]),en=eb(ee,en))}return en}function ew(){(0,eu.clientHookInServerComponentError)("useParams");let ee=(0,ei.useContext)(eo.GlobalLayoutRouterContext);return ee?eb(ee.tree):null}function e_(ee,en,er,ei){let eo;if(void 0===er&&(er=!0),void 0===ei&&(ei=[]),er)eo=ee[1][en];else{var es;let en=ee[1];eo=null!=(es=en.children)?es:Object.values(en)[0]}if(!eo)return ei;let eu=eo[0],ed=(0,ec.getSegmentValue)(eu);return!ed||ed.startsWith("__PAGE__")?ei:(ei.push(ed),e_(eo,en,!1,ei))}function ek(ee){void 0===ee&&(ee="children"),(0,eu.clientHookInServerComponentError)("useSelectedLayoutSegments");let{tree:en}=(0,ei.useContext)(eo.LayoutRouterContext);return e_(en,ee)}function ej(ee){void 0===ee&&(ee="children"),(0,eu.clientHookInServerComponentError)("useSelectedLayoutSegment");let en=ek(ee);return 0===en.length?null:en[0]}("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},5148:function(ee,en){"use strict";Object.defineProperty(en,"__esModule",{value:!0}),function(ee,en){for(var er in en)Object.defineProperty(ee,er,{enumerable:!0,get:en[er]})}(en,{notFound:function(){return ei},isNotFoundError:function(){return eo}});let er="NEXT_NOT_FOUND";function ei(){let ee=Error(er);throw ee.digest=er,ee}function eo(ee){return(null==ee?void 0:ee.digest)===er}("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},4929:function(ee,en,er){"use strict";var ei;Object.defineProperty(en,"__esModule",{value:!0}),function(ee,en){for(var er in en)Object.defineProperty(ee,er,{enumerable:!0,get:en[er]})}(en,{RedirectType:function(){return ei},getRedirectError:function(){return eu},redirect:function(){return ec},isRedirectError:function(){return ed},getURLFromRedirectError:function(){return ef},getRedirectTypeFromError:function(){return eh}});let eo=er(8102),es="NEXT_REDIRECT";function eu(ee,en){let er=Error(es);er.digest=es+";"+en+";"+ee;let ei=eo.requestAsyncStorage.getStore();return ei&&(er.mutableCookies=ei.mutableCookies),er}function ec(ee,en){throw void 0===en&&(en="replace"),eu(ee,en)}function ed(ee){if("string"!=typeof(null==ee?void 0:ee.digest))return!1;let[en,er,ei]=ee.digest.split(";",3);return en===es&&("replace"===er||"push"===er)&&"string"==typeof ei}function ef(ee){return ed(ee)?ee.digest.split(";",3)[2]:null}function eh(ee){if(!ed(ee))throw Error("Not a redirect error");return ee.digest.split(";",3)[1]}!function(ee){ee.push="push",ee.replace="replace"}(ei||(ei={})),("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},8102:function(ee,en,er){"use strict";Object.defineProperty(en,"__esModule",{value:!0}),Object.defineProperty(en,"requestAsyncStorage",{enumerable:!0,get:function(){return eo}});let ei=er(4888),eo=(0,ei.createAsyncLocalStorage)();("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},7250:function(ee,en){"use strict";function er(ee){return Array.isArray(ee)?ee[1]:ee}Object.defineProperty(en,"__esModule",{value:!0}),Object.defineProperty(en,"getSegmentValue",{enumerable:!0,get:function(){return er}}),("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},9295:function(ee,en){"use strict";var er;Object.defineProperty(en,"__esModule",{value:!0}),function(ee,en){for(var er in en)Object.defineProperty(ee,er,{enumerable:!0,get:en[er]})}(en,{PrefetchKind:function(){return er},ACTION_REFRESH:function(){return ei},ACTION_NAVIGATE:function(){return eo},ACTION_RESTORE:function(){return es},ACTION_SERVER_PATCH:function(){return eu},ACTION_PREFETCH:function(){return ec},ACTION_FAST_REFRESH:function(){return ed},ACTION_SERVER_ACTION:function(){return ef}});let ei="refresh",eo="navigate",es="restore",eu="server-patch",ec="prefetch",ed="fast-refresh",ef="server-action";!function(ee){ee.AUTO="auto",ee.FULL="full",ee.TEMPORARY="temporary"}(er||(er={})),("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},2496:function(ee,en){"use strict";function er(ee,en,er,ei){return!1}Object.defineProperty(en,"__esModule",{value:!0}),Object.defineProperty(en,"getDomainLocale",{enumerable:!0,get:function(){return er}}),("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},8463:function(ee,en,er){"use strict";Object.defineProperty(en,"__esModule",{value:!0}),Object.defineProperty(en,"default",{enumerable:!0,get:function(){return eE}});let ei=er(8266),eo=er(7144),es=eo._(er(959)),eu=ei._(er(8747)),ec=er(4112),ed=er(4688),ef=er(3751);er(8561);let eh=ei._(er(6792)),ep={deviceSizes:[640,750,828,1080,1200,1920,2048,3840],imageSizes:[16,32,48,64,96,128,256,384],path:"/_next/image",loader:"default",dangerouslyAllowSVG:!1,unoptimized:!0};function em(ee){return void 0!==ee.default}function ex(ee){return void 0!==ee.src}function eg(ee){return"object"==typeof ee&&(em(ee)||ex(ee))}function ev(ee,en,er){let{deviceSizes:ei,allSizes:eo}=ee;if(er){let ee=/(^|\s)(1?\d?\d)vw/g,en=[];for(let ei;ei=ee.exec(er);ei)en.push(parseInt(ei[2]));if(en.length){let ee=.01*Math.min(...en);return{widths:eo.filter(en=>en>=ei[0]*ee),kind:"w"}}return{widths:eo,kind:"w"}}if("number"!=typeof en)return{widths:ei,kind:"w"};let es=[...new Set([en,2*en].map(ee=>eo.find(en=>en>=ee)||eo[eo.length-1]))];return{widths:es,kind:"x"}}function ey(ee){let{config:en,src:er,unoptimized:ei,width:eo,quality:es,sizes:eu,loader:ec}=ee;if(ei)return{src:er,srcSet:void 0,sizes:void 0};let{widths:ed,kind:ef}=ev(en,eo,eu),eh=ed.length-1;return{sizes:eu||"w"!==ef?eu:"100vw",srcSet:ed.map((ee,ei)=>ec({config:en,src:er,quality:es,width:ee})+" "+("w"===ef?ee:ei+1)+ef).join(", "),src:ec({config:en,src:er,quality:es,width:ed[eh]})}}function eb(ee){return void 0===ee?ee:"number"==typeof ee?Number.isFinite(ee)?ee:NaN:"string"==typeof ee&&/^[0-9]+$/.test(ee)?parseInt(ee,10):NaN}function ew(ee,en,er,ei,eo,es,eu){if(!ee||ee["data-loaded-src"]===en)return;ee["data-loaded-src"]=en;let ec="decode"in ee?ee.decode():Promise.resolve();ec.catch(()=>{}).then(()=>{if(ee.parentElement&&ee.isConnected){if("blur"===er&&es(!0),null==ei?void 0:ei.current){let en=new Event("load");Object.defineProperty(en,"target",{writable:!1,value:ee});let er=!1,eo=!1;ei.current({...en,nativeEvent:en,currentTarget:ee,target:ee,isDefaultPrevented:()=>er,isPropagationStopped:()=>eo,persist:()=>{},preventDefault:()=>{er=!0,en.preventDefault()},stopPropagation:()=>{eo=!0,en.stopPropagation()}})}(null==eo?void 0:eo.current)&&eo.current(ee)}})}function e_(ee){let[en,er]=es.version.split("."),ei=parseInt(en,10),eo=parseInt(er,10);return ei>18||18===ei&&eo>=3?{fetchPriority:ee}:{fetchpriority:ee}}let ek=(0,es.forwardRef)((ee,en)=>{let{imgAttributes:er,heightInt:ei,widthInt:eo,qualityInt:eu,className:ec,imgStyle:ed,blurStyle:ef,isLazy:eh,fetchPriority:ep,fill:em,placeholder:ex,loading:eg,srcString:ev,config:ey,unoptimized:eb,loader:ek,onLoadRef:ej,onLoadingCompleteRef:eE,setBlurComplete:eO,setShowAltText:eC,onLoad:eT,onError:eS,...eN}=ee;return eg=eh?"lazy":eg,es.default.createElement("img",{...eN,...e_(ep),loading:eg,width:eo,height:ei,decoding:"async","data-nimg":em?"fill":"1",className:ec,style:{...ed,...ef},...er,ref:(0,es.useCallback)(ee=>{en&&("function"==typeof en?en(ee):"object"==typeof en&&(en.current=ee)),ee&&(eS&&(ee.src=ee.src),ee.complete&&ew(ee,ev,ex,ej,eE,eO,eb))},[ev,ex,ej,eE,eO,eS,eb,en]),onLoad:ee=>{let en=ee.currentTarget;ew(en,ev,ex,ej,eE,eO,eb)},onError:ee=>{eC(!0),"blur"===ex&&eO(!0),eS&&eS(ee)}})}),ej=(0,es.forwardRef)((ee,en)=>{let er,ei,{src:eo,sizes:ex,unoptimized:ev=!1,priority:ew=!1,loading:ej,className:eE,quality:eO,width:eC,height:eT,fill:eS,style:eN,onLoad:eR,onLoadingComplete:eI,placeholder:eA="empty",blurDataURL:eL,fetchPriority:eP,layout:eM,objectFit:eZ,objectPosition:eD,lazyBoundary:eF,lazyRoot:e$,...ez}=ee,eH=(0,es.useContext)(ef.ImageConfigContext),eW=(0,es.useMemo)(()=>{let ee=ep||eH||ed.imageConfigDefault,en=[...ee.deviceSizes,...ee.imageSizes].sort((ee,en)=>ee-en),er=ee.deviceSizes.sort((ee,en)=>ee-en);return{...ee,allSizes:en,deviceSizes:er}},[eH]),eU=ez,eG=eU.loader||eh.default;delete eU.loader;let eV="__next_img_default"in eG;if(eV){if("custom"===eW.loader)throw Error('Image with src "'+eo+'" is missing "loader" prop.\nRead more: https://nextjs.org/docs/messages/next-image-missing-loader')}else{let ee=eG;eG=en=>{let{config:er,...ei}=en;return ee(ei)}}if(eM){"fill"===eM&&(eS=!0);let ee={intrinsic:{maxWidth:"100%",height:"auto"},responsive:{width:"100%",height:"auto"}},en={responsive:"100vw",fill:"100vw"},er=ee[eM];er&&(eN={...eN,...er});let ei=en[eM];ei&&!ex&&(ex=ei)}let eB="",eK=eb(eC),eQ=eb(eT);if(eg(eo)){let ee=em(eo)?eo.default:eo;if(!ee.src)throw Error("An object should only be passed to the image component src parameter if it comes from a static image import. It must include src. Received "+JSON.stringify(ee));if(!ee.height||!ee.width)throw Error("An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received "+JSON.stringify(ee));if(er=ee.blurWidth,ei=ee.blurHeight,eL=eL||ee.blurDataURL,eB=ee.src,!eS){if(eK||eQ){if(eK&&!eQ){let en=eK/ee.width;eQ=Math.round(ee.height*en)}else if(!eK&&eQ){let en=eQ/ee.height;eK=Math.round(ee.width*en)}}else eK=ee.width,eQ=ee.height}}let eJ=!ew&&("lazy"===ej||void 0===ej);(!(eo="string"==typeof eo?eo:eB)||eo.startsWith("data:")||eo.startsWith("blob:"))&&(ev=!0,eJ=!1),eW.unoptimized&&(ev=!0),eV&&eo.endsWith(".svg")&&!eW.dangerouslyAllowSVG&&(ev=!0),ew&&(eP="high");let[eY,eX]=(0,es.useState)(!1),[e0,e1]=(0,es.useState)(!1),e2=eb(eO),e4=Object.assign(eS?{position:"absolute",height:"100%",width:"100%",left:0,top:0,right:0,bottom:0,objectFit:eZ,objectPosition:eD}:{},e0?{}:{color:"transparent"},eN),e5="blur"===eA&&eL&&!eY?{backgroundSize:e4.objectFit||"cover",backgroundPosition:e4.objectPosition||"50% 50%",backgroundRepeat:"no-repeat",backgroundImage:'url("data:image/svg+xml;charset=utf-8,'+(0,ec.getImageBlurSvg)({widthInt:eK,heightInt:eQ,blurWidth:er,blurHeight:ei,blurDataURL:eL,objectFit:e4.objectFit})+'")'}:{},e3=ey({config:eW,src:eo,unoptimized:ev,width:eK,quality:e2,sizes:ex,loader:eG}),e9=eo,e7=(0,es.useRef)(eR);(0,es.useEffect)(()=>{e7.current=eR},[eR]);let e6=(0,es.useRef)(eI);(0,es.useEffect)(()=>{e6.current=eI},[eI]);let e8={isLazy:eJ,imgAttributes:e3,heightInt:eQ,widthInt:eK,qualityInt:e2,className:eE,imgStyle:e4,blurStyle:e5,loading:ej,config:eW,fetchPriority:eP,fill:eS,unoptimized:ev,placeholder:eA,loader:eG,srcString:e9,onLoadRef:e7,onLoadingCompleteRef:e6,setBlurComplete:eX,setShowAltText:e1,...eU};return es.default.createElement(es.default.Fragment,null,es.default.createElement(ek,{...e8,ref:en}),ew?es.default.createElement(eu.default,null,es.default.createElement("link",{key:"__nimg-"+e3.src+e3.srcSet+e3.sizes,rel:"preload",as:"image",href:e3.srcSet?void 0:e3.src,imageSrcSet:e3.srcSet,imageSizes:e3.sizes,crossOrigin:eU.crossOrigin,referrerPolicy:eU.referrerPolicy,...e_(eP)})):null)}),eE=ej;("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},3881:function(ee,en,er){"use strict";Object.defineProperty(en,"__esModule",{value:!0}),Object.defineProperty(en,"default",{enumerable:!0,get:function(){return eE}});let ei=er(8266),eo=ei._(er(959)),es=er(3059),eu=er(8482),ec=er(5355),ed=er(6635),ef=er(3888),eh=er(870),ep=er(7167),em=er(5015),ex=er(2496),eg=er(4288),ev=er(9295),ey=new Set;function eb(ee,en,er,ei,eo,es){if(!es&&!(0,eu.isLocalURL)(en))return;if(!ei.bypassPrefetchedCheck){let eo=void 0!==ei.locale?ei.locale:"locale"in ee?ee.locale:void 0,es=en+"%"+er+"%"+eo;if(ey.has(es))return;ey.add(es)}let ec=es?ee.prefetch(en,eo):ee.prefetch(en,er,ei);Promise.resolve(ec).catch(ee=>{})}function ew(ee){let en=ee.currentTarget,er=en.getAttribute("target");return er&&"_self"!==er||ee.metaKey||ee.ctrlKey||ee.shiftKey||ee.altKey||ee.nativeEvent&&2===ee.nativeEvent.which}function e_(ee,en,er,ei,es,ec,ed,ef,eh,ep){let{nodeName:em}=ee.currentTarget,ex="A"===em.toUpperCase();if(ex&&(ew(ee)||!eh&&!(0,eu.isLocalURL)(er)))return;ee.preventDefault();let eg=()=>{"beforePopState"in en?en[es?"replace":"push"](er,ei,{shallow:ec,locale:ef,scroll:ed}):en[es?"replace":"push"](ei||er,{forceOptimisticNavigation:!ep})};eh?eo.default.startTransition(eg):eg()}function ek(ee){return"string"==typeof ee?ee:(0,ec.formatUrl)(ee)}let ej=eo.default.forwardRef(function(ee,en){let er,ei;let{href:eu,as:ec,children:ey,prefetch:ew=null,passHref:ej,replace:eE,shallow:eO,scroll:eC,locale:eT,onClick:eS,onMouseEnter:eN,onTouchStart:eR,legacyBehavior:eI=!1,...eA}=ee;er=ey,eI&&("string"==typeof er||"number"==typeof er)&&(er=eo.default.createElement("a",null,er));let eL=!1!==ew,eP=null===ew?ev.PrefetchKind.AUTO:ev.PrefetchKind.FULL,eM=eo.default.useContext(eh.RouterContext),eZ=eo.default.useContext(ep.AppRouterContext),eD=null!=eM?eM:eZ,eF=!eM,{href:e$,as:ez}=eo.default.useMemo(()=>{if(!eM){let ee=ek(eu);return{href:ee,as:ec?ek(ec):ee}}let[ee,en]=(0,es.resolveHref)(eM,eu,!0);return{href:ee,as:ec?(0,es.resolveHref)(eM,ec):en||ee}},[eM,eu,ec]),eH=eo.default.useRef(e$),eW=eo.default.useRef(ez);eI&&(ei=eo.default.Children.only(er));let eU=eI?ei&&"object"==typeof ei&&ei.ref:en,[eG,eV,eB]=(0,em.useIntersection)({rootMargin:"200px"}),eK=eo.default.useCallback(ee=>{(eW.current!==ez||eH.current!==e$)&&(eB(),eW.current=ez,eH.current=e$),eG(ee),eU&&("function"==typeof eU?eU(ee):"object"==typeof eU&&(eU.current=ee))},[ez,eU,e$,eB,eG]);eo.default.useEffect(()=>{eD&&eV&&eL&&eb(eD,e$,ez,{locale:eT},{kind:eP},eF)},[ez,e$,eV,eT,eL,null==eM?void 0:eM.locale,eD,eF,eP]);let eQ={ref:eK,onClick(ee){eI||"function"!=typeof eS||eS(ee),eI&&ei.props&&"function"==typeof ei.props.onClick&&ei.props.onClick(ee),eD&&!ee.defaultPrevented&&e_(ee,eD,e$,ez,eE,eO,eC,eT,eF,eL)},onMouseEnter(ee){eI||"function"!=typeof eN||eN(ee),eI&&ei.props&&"function"==typeof ei.props.onMouseEnter&&ei.props.onMouseEnter(ee),eD&&(eL||!eF)&&eb(eD,e$,ez,{locale:eT,priority:!0,bypassPrefetchedCheck:!0},{kind:eP},eF)},onTouchStart(ee){eI||"function"!=typeof eR||eR(ee),eI&&ei.props&&"function"==typeof ei.props.onTouchStart&&ei.props.onTouchStart(ee),eD&&(eL||!eF)&&eb(eD,e$,ez,{locale:eT,priority:!0,bypassPrefetchedCheck:!0},{kind:eP},eF)}};if((0,ed.isAbsoluteUrl)(ez))eQ.href=ez;else if(!eI||ej||"a"===ei.type&&!("href"in ei.props)){let ee=void 0!==eT?eT:null==eM?void 0:eM.locale,en=(null==eM?void 0:eM.isLocaleDomain)&&(0,ex.getDomainLocale)(ez,ee,null==eM?void 0:eM.locales,null==eM?void 0:eM.domainLocales);eQ.href=en||(0,eg.addBasePath)((0,ef.addLocale)(ez,ee,null==eM?void 0:eM.defaultLocale))}return eI?eo.default.cloneElement(ei,eQ):eo.default.createElement("a",{...eA,...eQ},er)}),eE=ej;("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},5015:function(ee,en,er){"use strict";Object.defineProperty(en,"__esModule",{value:!0}),Object.defineProperty(en,"useIntersection",{enumerable:!0,get:function(){return eh}});let ei=er(959),eo=er(6705),es="function"==typeof IntersectionObserver,eu=new Map,ec=[];function ed(ee){let en;let er={root:ee.root||null,margin:ee.rootMargin||""},ei=ec.find(ee=>ee.root===er.root&&ee.margin===er.margin);if(ei&&(en=eu.get(ei)))return en;let eo=new Map,es=new IntersectionObserver(ee=>{ee.forEach(ee=>{let en=eo.get(ee.target),er=ee.isIntersecting||ee.intersectionRatio>0;en&&er&&en(er)})},ee);return en={id:er,observer:es,elements:eo},ec.push(er),eu.set(er,en),en}function ef(ee,en,er){let{id:ei,observer:eo,elements:es}=ed(er);return es.set(ee,en),eo.observe(ee),function(){if(es.delete(ee),eo.unobserve(ee),0===es.size){eo.disconnect(),eu.delete(ei);let ee=ec.findIndex(ee=>ee.root===ei.root&&ee.margin===ei.margin);ee>-1&&ec.splice(ee,1)}}}function eh(ee){let{rootRef:en,rootMargin:er,disabled:eu}=ee,ec=eu||!es,[ed,eh]=(0,ei.useState)(!1),ep=(0,ei.useRef)(null),em=(0,ei.useCallback)(ee=>{ep.current=ee},[]);(0,ei.useEffect)(()=>{if(es){if(ec||ed)return;let ee=ep.current;if(ee&&ee.tagName){let ei=ef(ee,ee=>ee&&eh(ee),{root:null==en?void 0:en.current,rootMargin:er});return ei}}else if(!ed){let ee=(0,eo.requestIdleCallback)(()=>eh(!0));return()=>(0,eo.cancelIdleCallback)(ee)}},[ec,er,en,ed,ep.current]);let ex=(0,ei.useCallback)(()=>{eh(!1)},[]);return[em,ed,ex]}("function"==typeof en.default||"object"==typeof en.default&&null!==en.default)&&void 0===en.default.__esModule&&(Object.defineProperty(en.default,"__esModule",{value:!0}),Object.assign(en.default,en),ee.exports=en.default)},4112:function(ee,en){"use strict";function er(ee){let{widthInt:en,heightInt:er,blurWidth:ei,blurHeight:eo,blurDataURL:es,objectFit:eu}=ee,ec=ei&&eo?"1":"20",ed=ei||en,ef=eo||er,eh=es.startsWith("data:image/jpeg")?"%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1'/%3E%3C/feComponentTransfer%3E%":"";if(ed&&ef)return"%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 "+ed+" "+ef+"'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='"+ec+"'/%3E"+eh+"%3C/filter%3E%3Cimage preserveAspectRatio='none' filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='"+es+"'/%3E%3C/svg%3E";let ep="contain"===eu?"xMidYMid":"cover"===eu?"xMidYMid slice":"none";return"%3Csvg xmlns='http%3A//www.w3.org/2000/svg'%3E%3Cimage style='filter:blur(20px)' preserveAspectRatio='"+ep+"' x='0' y='0' height='100%25' width='100%25' href='"+es+"'/%3E%3C/svg%3E"}Object.defineProperty(en,"__esModule",{value:!0}),Object.defineProperty(en,"getImageBlurSvg",{enumerable:!0,get:function(){return er}})},6792:function(ee,en){"use strict";function er(ee){let{config:en,src:er,width:ei,quality:eo}=ee;return en.path+"?url="+encodeURIComponent(er)+"&w="+ei+"&q="+(eo||75)}Object.defineProperty(en,"__esModule",{value:!0}),Object.defineProperty(en,"default",{enumerable:!0,get:function(){return ei}}),er.__next_img_default=!0;let ei=er},3106:function(ee,en,er){"use strict";Object.defineProperty(en,"__esModule",{value:!0}),function(ee,en){for(var er in en)Object.defineProperty(ee,er,{enumerable:!0,get:en[er]})}(en,{ServerInsertedHTMLContext:function(){return es},useServerInsertedHTML:function(){return eu}});let ei=er(7144),eo=ei._(er(959)),es=eo.default.createContext(null);function eu(ee){let en=(0,eo.useContext)(es);en&&en(ee)}},5654:function(ee){var en="/";!function(){"use strict";var er={114:function(ee){function en(ee){if("string"!=typeof ee)throw TypeError("Path must be a string. Received "+JSON.stringify(ee))}function er(ee,en){for(var er,ei="",eo=0,es=-1,eu=0,ec=0;ec<=ee.length;++ec){if(ec2){var ed=ei.lastIndexOf("/");if(ed!==ei.length-1){-1===ed?(ei="",eo=0):eo=(ei=ei.slice(0,ed)).length-1-ei.lastIndexOf("/"),es=ec,eu=0;continue}}else if(2===ei.length||1===ei.length){ei="",eo=0,es=ec,eu=0;continue}}en&&(ei.length>0?ei+="/..":ei="..",eo=2)}else ei.length>0?ei+="/"+ee.slice(es+1,ec):ei=ee.slice(es+1,ec),eo=ec-es-1;es=ec,eu=0}else 46===er&&-1!==eu?++eu:eu=-1}return ei}function ei(ee,en){var er=en.dir||en.root,ei=en.base||(en.name||"")+(en.ext||"");return er?er===en.root?er+ei:er+ee+ei:ei}var eo={resolve:function(){for(var ee,ei,eo="",es=!1,eu=arguments.length-1;eu>=-1&&!es;eu--)eu>=0?ei=arguments[eu]:(void 0===ee&&(ee=""),ei=ee),en(ei),0!==ei.length&&(eo=ei+"/"+eo,es=47===ei.charCodeAt(0));return(eo=er(eo,!es),es)?eo.length>0?"/"+eo:"/":eo.length>0?eo:"."},normalize:function(ee){if(en(ee),0===ee.length)return".";var ei=47===ee.charCodeAt(0),eo=47===ee.charCodeAt(ee.length-1);return(0!==(ee=er(ee,!ei)).length||ei||(ee="."),ee.length>0&&eo&&(ee+="/"),ei)?"/"+ee:ee},isAbsolute:function(ee){return en(ee),ee.length>0&&47===ee.charCodeAt(0)},join:function(){if(0==arguments.length)return".";for(var ee,er=0;er0&&(void 0===ee?ee=ei:ee+="/"+ei)}return void 0===ee?".":eo.normalize(ee)},relative:function(ee,er){if(en(ee),en(er),ee===er||(ee=eo.resolve(ee))===(er=eo.resolve(er)))return"";for(var ei=1;eief){if(47===er.charCodeAt(ec+ep))return er.slice(ec+ep+1);if(0===ep)return er.slice(ec+ep)}else eu>ef&&(47===ee.charCodeAt(ei+ep)?eh=ep:0===ep&&(eh=0));break}var em=ee.charCodeAt(ei+ep);if(em!==er.charCodeAt(ec+ep))break;47===em&&(eh=ep)}var ex="";for(ep=ei+eh+1;ep<=es;++ep)(ep===es||47===ee.charCodeAt(ep))&&(0===ex.length?ex+="..":ex+="/..");return ex.length>0?ex+er.slice(ec+eh):(ec+=eh,47===er.charCodeAt(ec)&&++ec,er.slice(ec))},_makeLong:function(ee){return ee},dirname:function(ee){if(en(ee),0===ee.length)return".";for(var er=ee.charCodeAt(0),ei=47===er,eo=-1,es=!0,eu=ee.length-1;eu>=1;--eu)if(47===(er=ee.charCodeAt(eu))){if(!es){eo=eu;break}}else es=!1;return -1===eo?ei?"/":".":ei&&1===eo?"//":ee.slice(0,eo)},basename:function(ee,er){if(void 0!==er&&"string"!=typeof er)throw TypeError('"ext" argument must be a string');en(ee);var ei,eo=0,es=-1,eu=!0;if(void 0!==er&&er.length>0&&er.length<=ee.length){if(er.length===ee.length&&er===ee)return"";var ec=er.length-1,ed=-1;for(ei=ee.length-1;ei>=0;--ei){var ef=ee.charCodeAt(ei);if(47===ef){if(!eu){eo=ei+1;break}}else -1===ed&&(eu=!1,ed=ei+1),ec>=0&&(ef===er.charCodeAt(ec)?-1==--ec&&(es=ei):(ec=-1,es=ed))}return eo===es?es=ed:-1===es&&(es=ee.length),ee.slice(eo,es)}for(ei=ee.length-1;ei>=0;--ei)if(47===ee.charCodeAt(ei)){if(!eu){eo=ei+1;break}}else -1===es&&(eu=!1,es=ei+1);return -1===es?"":ee.slice(eo,es)},extname:function(ee){en(ee);for(var er=-1,ei=0,eo=-1,es=!0,eu=0,ec=ee.length-1;ec>=0;--ec){var ed=ee.charCodeAt(ec);if(47===ed){if(!es){ei=ec+1;break}continue}-1===eo&&(es=!1,eo=ec+1),46===ed?-1===er?er=ec:1!==eu&&(eu=1):-1!==er&&(eu=-1)}return -1===er||-1===eo||0===eu||1===eu&&er===eo-1&&er===ei+1?"":ee.slice(er,eo)},format:function(ee){if(null===ee||"object"!=typeof ee)throw TypeError('The "pathObject" argument must be of type Object. Received type '+typeof ee);return ei("/",ee)},parse:function(ee){en(ee);var er,ei={root:"",dir:"",base:"",ext:"",name:""};if(0===ee.length)return ei;var eo=ee.charCodeAt(0),es=47===eo;es?(ei.root="/",er=1):er=0;for(var eu=-1,ec=0,ed=-1,ef=!0,eh=ee.length-1,ep=0;eh>=er;--eh){if(47===(eo=ee.charCodeAt(eh))){if(!ef){ec=eh+1;break}continue}-1===ed&&(ef=!1,ed=eh+1),46===eo?-1===eu?eu=eh:1!==ep&&(ep=1):-1!==eu&&(ep=-1)}return -1===eu||-1===ed||0===ep||1===ep&&eu===ed-1&&eu===ec+1?-1!==ed&&(0===ec&&es?ei.base=ei.name=ee.slice(1,ed):ei.base=ei.name=ee.slice(ec,ed)):(0===ec&&es?(ei.name=ee.slice(1,eu),ei.base=ee.slice(1,ed)):(ei.name=ee.slice(ec,eu),ei.base=ee.slice(ec,ed)),ei.ext=ee.slice(eu,ed)),ec>0?ei.dir=ee.slice(0,ec-1):es&&(ei.dir="/"),ei},sep:"/",delimiter:":",win32:null,posix:null};eo.posix=eo,ee.exports=eo}},ei={};function eo(ee){var en=ei[ee];if(void 0!==en)return en.exports;var es=ei[ee]={exports:{}},eu=!0;try{er[ee](es,es.exports,eo),eu=!1}finally{eu&&delete ei[ee]}return es.exports}eo.ab=en+"/";var es=eo(114);ee.exports=es}()},3057:function(ee,en,er){ee.exports=er(8747)},2737:function(ee,en,er){ee.exports=er(8463)},4970:function(ee,en,er){ee.exports=er(3881)},7758:function(ee,en,er){ee.exports=er(37)},6149:function(ee,en,er){"use strict";er.d(en,{R5:function(){return aA},ZP:function(){return a9},ZR:function(){return iF}});var ei,eo,es,eu,ec,ed,ef,eh,ep=er(8556),em=er(959);er(2297);var ex=er(5924),eg=er(4238),ev=er(6736),ey=er(8631);!function(ee){function en(ee){}function er(ee){throw Error()}function ei(ee,en=" | "){return ee.map(ee=>"string"==typeof ee?`'${ee}'`:ee).join(en)}ee.assertEqual=ee=>ee,ee.assertIs=en,ee.assertNever=er,ee.arrayToEnum=ee=>{let en={};for(let er of ee)en[er]=er;return en},ee.getValidEnumValues=en=>{let er=ee.objectKeys(en).filter(ee=>"number"!=typeof en[en[ee]]),ei={};for(let ee of er)ei[ee]=en[ee];return ee.objectValues(ei)},ee.objectValues=en=>ee.objectKeys(en).map(function(ee){return en[ee]}),ee.objectKeys="function"==typeof Object.keys?ee=>Object.keys(ee):ee=>{let en=[];for(let er in ee)Object.prototype.hasOwnProperty.call(ee,er)&&en.push(er);return en},ee.find=(ee,en)=>{for(let er of ee)if(en(er))return er},ee.isInteger="function"==typeof Number.isInteger?ee=>Number.isInteger(ee):ee=>"number"==typeof ee&&isFinite(ee)&&Math.floor(ee)===ee,ee.joinValues=ei,ee.jsonStringifyReplacer=(ee,en)=>"bigint"==typeof en?en.toString():en}(eu||(eu={})),function(ee){ee.mergeShapes=(ee,en)=>({...ee,...en})}(ec||(ec={}));let eb=eu.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),ew=ee=>{let en=typeof ee;switch(en){case"undefined":return eb.undefined;case"string":return eb.string;case"number":return isNaN(ee)?eb.nan:eb.number;case"boolean":return eb.boolean;case"function":return eb.function;case"bigint":return eb.bigint;case"symbol":return eb.symbol;case"object":if(Array.isArray(ee))return eb.array;if(null===ee)return eb.null;if(ee.then&&"function"==typeof ee.then&&ee.catch&&"function"==typeof ee.catch)return eb.promise;if("undefined"!=typeof Map&&ee instanceof Map)return eb.map;if("undefined"!=typeof Set&&ee instanceof Set)return eb.set;if("undefined"!=typeof Date&&ee instanceof Date)return eb.date;return eb.object;default:return eb.unknown}},e_=eu.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]),ek=ee=>{let en=JSON.stringify(ee,null,2);return en.replace(/"([^"]+)":/g,"$1:")};class ej extends Error{constructor(ee){super(),this.issues=[],this.addIssue=ee=>{this.issues=[...this.issues,ee]},this.addIssues=(ee=[])=>{this.issues=[...this.issues,...ee]};let en=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,en):this.__proto__=en,this.name="ZodError",this.issues=ee}get errors(){return this.issues}format(ee){let en=ee||function(ee){return ee.message},er={_errors:[]},ei=ee=>{for(let eo of ee.issues)if("invalid_union"===eo.code)eo.unionErrors.map(ei);else if("invalid_return_type"===eo.code)ei(eo.returnTypeError);else if("invalid_arguments"===eo.code)ei(eo.argumentsError);else if(0===eo.path.length)er._errors.push(en(eo));else{let ee=er,ei=0;for(;eiee.message){let en={},er=[];for(let ei of this.issues)ei.path.length>0?(en[ei.path[0]]=en[ei.path[0]]||[],en[ei.path[0]].push(ee(ei))):er.push(ee(ei));return{formErrors:er,fieldErrors:en}}get formErrors(){return this.flatten()}}ej.create=ee=>{let en=new ej(ee);return en};let eE=(ee,en)=>{let er;switch(ee.code){case e_.invalid_type:er=ee.received===eb.undefined?"Required":`Expected ${ee.expected}, received ${ee.received}`;break;case e_.invalid_literal:er=`Invalid literal value, expected ${JSON.stringify(ee.expected,eu.jsonStringifyReplacer)}`;break;case e_.unrecognized_keys:er=`Unrecognized key(s) in object: ${eu.joinValues(ee.keys,", ")}`;break;case e_.invalid_union:er="Invalid input";break;case e_.invalid_union_discriminator:er=`Invalid discriminator value. Expected ${eu.joinValues(ee.options)}`;break;case e_.invalid_enum_value:er=`Invalid enum value. Expected ${eu.joinValues(ee.options)}, received '${ee.received}'`;break;case e_.invalid_arguments:er="Invalid function arguments";break;case e_.invalid_return_type:er="Invalid function return type";break;case e_.invalid_date:er="Invalid date";break;case e_.invalid_string:"object"==typeof ee.validation?"includes"in ee.validation?(er=`Invalid input: must include "${ee.validation.includes}"`,"number"==typeof ee.validation.position&&(er=`${er} at one or more positions greater than or equal to ${ee.validation.position}`)):"startsWith"in ee.validation?er=`Invalid input: must start with "${ee.validation.startsWith}"`:"endsWith"in ee.validation?er=`Invalid input: must end with "${ee.validation.endsWith}"`:eu.assertNever(ee.validation):er="regex"!==ee.validation?`Invalid ${ee.validation}`:"Invalid";break;case e_.too_small:er="array"===ee.type?`Array must contain ${ee.exact?"exactly":ee.inclusive?"at least":"more than"} ${ee.minimum} element(s)`:"string"===ee.type?`String must contain ${ee.exact?"exactly":ee.inclusive?"at least":"over"} ${ee.minimum} character(s)`:"number"===ee.type?`Number must be ${ee.exact?"exactly equal to ":ee.inclusive?"greater than or equal to ":"greater than "}${ee.minimum}`:"date"===ee.type?`Date must be ${ee.exact?"exactly equal to ":ee.inclusive?"greater than or equal to ":"greater than "}${new Date(Number(ee.minimum))}`:"Invalid input";break;case e_.too_big:er="array"===ee.type?`Array must contain ${ee.exact?"exactly":ee.inclusive?"at most":"less than"} ${ee.maximum} element(s)`:"string"===ee.type?`String must contain ${ee.exact?"exactly":ee.inclusive?"at most":"under"} ${ee.maximum} character(s)`:"number"===ee.type?`Number must be ${ee.exact?"exactly":ee.inclusive?"less than or equal to":"less than"} ${ee.maximum}`:"bigint"===ee.type?`BigInt must be ${ee.exact?"exactly":ee.inclusive?"less than or equal to":"less than"} ${ee.maximum}`:"date"===ee.type?`Date must be ${ee.exact?"exactly":ee.inclusive?"smaller than or equal to":"smaller than"} ${new Date(Number(ee.maximum))}`:"Invalid input";break;case e_.custom:er="Invalid input";break;case e_.invalid_intersection_types:er="Intersection results could not be merged";break;case e_.not_multiple_of:er=`Number must be a multiple of ${ee.multipleOf}`;break;case e_.not_finite:er="Number must be finite";break;default:er=en.defaultError,eu.assertNever(ee)}return{message:er}},eO=eE;function eC(ee){eO=ee}function eT(){return eO}let eS=ee=>{let{data:en,path:er,errorMaps:ei,issueData:eo}=ee,es=[...er,...eo.path||[]],eu={...eo,path:es},ec="",ed=ei.filter(ee=>!!ee).slice().reverse();for(let ee of ed)ec=ee(eu,{data:en,defaultError:ec}).message;return{...eo,path:es,message:eo.message||ec}},eN=[];function eR(ee,en){let er=eS({issueData:en,data:ee.data,path:ee.path,errorMaps:[ee.common.contextualErrorMap,ee.schemaErrorMap,eT(),eE].filter(ee=>!!ee)});ee.common.issues.push(er)}class eI{constructor(){this.value="valid"}dirty(){"valid"===this.value&&(this.value="dirty")}abort(){"aborted"!==this.value&&(this.value="aborted")}static mergeArray(ee,en){let er=[];for(let ei of en){if("aborted"===ei.status)return eA;"dirty"===ei.status&&ee.dirty(),er.push(ei.value)}return{status:ee.value,value:er}}static async mergeObjectAsync(ee,en){let er=[];for(let ee of en)er.push({key:await ee.key,value:await ee.value});return eI.mergeObjectSync(ee,er)}static mergeObjectSync(ee,en){let er={};for(let ei of en){let{key:en,value:eo}=ei;if("aborted"===en.status||"aborted"===eo.status)return eA;"dirty"===en.status&&ee.dirty(),"dirty"===eo.status&&ee.dirty(),(void 0!==eo.value||ei.alwaysSet)&&(er[en.value]=eo.value)}return{status:ee.value,value:er}}}let eA=Object.freeze({status:"aborted"}),eL=ee=>({status:"dirty",value:ee}),eP=ee=>({status:"valid",value:ee}),eM=ee=>"aborted"===ee.status,eZ=ee=>"dirty"===ee.status,eD=ee=>"valid"===ee.status,eF=ee=>"undefined"!=typeof Promise&&ee instanceof Promise;!function(ee){ee.errToObj=ee=>"string"==typeof ee?{message:ee}:ee||{},ee.toString=ee=>"string"==typeof ee?ee:null==ee?void 0:ee.message}(ed||(ed={}));class e${constructor(ee,en,er,ei){this._cachedPath=[],this.parent=ee,this.data=en,this._path=er,this._key=ei}get path(){return this._cachedPath.length||(this._key instanceof Array?this._cachedPath.push(...this._path,...this._key):this._cachedPath.push(...this._path,this._key)),this._cachedPath}}let ez=(ee,en)=>{if(eD(en))return{success:!0,data:en.value};if(!ee.common.issues.length)throw Error("Validation failed but no issues detected.");return{success:!1,get error(){if(this._error)return this._error;let en=new ej(ee.common.issues);return this._error=en,this._error}}};function eH(ee){if(!ee)return{};let{errorMap:en,invalid_type_error:er,required_error:ei,description:eo}=ee;if(en&&(er||ei))throw Error('Can\'t use "invalid_type_error" or "required_error" in conjunction with custom error map.');if(en)return{errorMap:en,description:eo};let es=(ee,en)=>"invalid_type"!==ee.code?{message:en.defaultError}:void 0===en.data?{message:null!=ei?ei:en.defaultError}:{message:null!=er?er:en.defaultError};return{errorMap:es,description:eo}}class eW{constructor(ee){this.spa=this.safeParseAsync,this._def=ee,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.brand=this.brand.bind(this),this.default=this.default.bind(this),this.catch=this.catch.bind(this),this.describe=this.describe.bind(this),this.pipe=this.pipe.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this)}get description(){return this._def.description}_getType(ee){return ew(ee.data)}_getOrReturnCtx(ee,en){return en||{common:ee.parent.common,data:ee.data,parsedType:ew(ee.data),schemaErrorMap:this._def.errorMap,path:ee.path,parent:ee.parent}}_processInputParams(ee){return{status:new eI,ctx:{common:ee.parent.common,data:ee.data,parsedType:ew(ee.data),schemaErrorMap:this._def.errorMap,path:ee.path,parent:ee.parent}}}_parseSync(ee){let en=this._parse(ee);if(eF(en))throw Error("Synchronous parse encountered promise.");return en}_parseAsync(ee){let en=this._parse(ee);return Promise.resolve(en)}parse(ee,en){let er=this.safeParse(ee,en);if(er.success)return er.data;throw er.error}safeParse(ee,en){var er;let ei={common:{issues:[],async:null!==(er=null==en?void 0:en.async)&&void 0!==er&&er,contextualErrorMap:null==en?void 0:en.errorMap},path:(null==en?void 0:en.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:ee,parsedType:ew(ee)},eo=this._parseSync({data:ee,path:ei.path,parent:ei});return ez(ei,eo)}async parseAsync(ee,en){let er=await this.safeParseAsync(ee,en);if(er.success)return er.data;throw er.error}async safeParseAsync(ee,en){let er={common:{issues:[],contextualErrorMap:null==en?void 0:en.errorMap,async:!0},path:(null==en?void 0:en.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:ee,parsedType:ew(ee)},ei=this._parse({data:ee,path:er.path,parent:er}),eo=await (eF(ei)?ei:Promise.resolve(ei));return ez(er,eo)}refine(ee,en){let er=ee=>"string"==typeof en||void 0===en?{message:en}:"function"==typeof en?en(ee):en;return this._refinement((en,ei)=>{let eo=ee(en),es=()=>ei.addIssue({code:e_.custom,...er(en)});return"undefined"!=typeof Promise&&eo instanceof Promise?eo.then(ee=>!!ee||(es(),!1)):!!eo||(es(),!1)})}refinement(ee,en){return this._refinement((er,ei)=>!!ee(er)||(ei.addIssue("function"==typeof en?en(er,ei):en),!1))}_refinement(ee){return new tj({schema:this,typeName:ef.ZodEffects,effect:{type:"refinement",refinement:ee}})}superRefine(ee){return this._refinement(ee)}optional(){return tE.create(this,this._def)}nullable(){return tO.create(this,this._def)}nullish(){return this.nullable().optional()}array(){return ti.create(this,this._def)}promise(){return tk.create(this,this._def)}or(ee){return tl.create([this,ee],this._def)}and(ee){return tf.create(this,ee,this._def)}transform(ee){return new tj({...eH(this._def),schema:this,typeName:ef.ZodEffects,effect:{type:"transform",transform:ee}})}default(ee){let en="function"==typeof ee?ee:()=>ee;return new tC({...eH(this._def),innerType:this,defaultValue:en,typeName:ef.ZodDefault})}brand(){return new tR({typeName:ef.ZodBranded,type:this,...eH(this._def)})}catch(ee){let en="function"==typeof ee?ee:()=>ee;return new tT({...eH(this._def),innerType:this,catchValue:en,typeName:ef.ZodCatch})}describe(ee){let en=this.constructor;return new en({...this._def,description:ee})}pipe(ee){return tI.create(this,ee)}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}}let eU=/^c[^\s-]{8,}$/i,eG=/^[a-z][a-z0-9]*$/,eV=/[0-9A-HJKMNP-TV-Z]{26}/,eB=/^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i,eK=/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\])|(\[IPv6:(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))\])|([A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])*(\.[A-Za-z]{2,})+))$/,eQ=/^(\p{Extended_Pictographic}|\p{Emoji_Component})+$/u,eJ=/^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/,eY=/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/,eX=ee=>ee.precision?ee.offset?RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${ee.precision}}(([+-]\\d{2}(:?\\d{2})?)|Z)$`):RegExp(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{${ee.precision}}Z$`):0===ee.precision?ee.offset?RegExp("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(([+-]\\d{2}(:?\\d{2})?)|Z)$"):RegExp("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"):ee.offset?RegExp("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(([+-]\\d{2}(:?\\d{2})?)|Z)$"):RegExp("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$");function e0(ee,en){return!!(("v4"===en||!en)&&eJ.test(ee)||("v6"===en||!en)&&eY.test(ee))}class e1 extends eW{constructor(){super(...arguments),this._regex=(ee,en,er)=>this.refinement(en=>ee.test(en),{validation:en,code:e_.invalid_string,...ed.errToObj(er)}),this.nonempty=ee=>this.min(1,ed.errToObj(ee)),this.trim=()=>new e1({...this._def,checks:[...this._def.checks,{kind:"trim"}]}),this.toLowerCase=()=>new e1({...this._def,checks:[...this._def.checks,{kind:"toLowerCase"}]}),this.toUpperCase=()=>new e1({...this._def,checks:[...this._def.checks,{kind:"toUpperCase"}]})}_parse(ee){let en;this._def.coerce&&(ee.data=String(ee.data));let er=this._getType(ee);if(er!==eb.string){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.string,received:en.parsedType}),eA}let ei=new eI;for(let er of this._def.checks)if("min"===er.kind)ee.data.lengther.value&&(eR(en=this._getOrReturnCtx(ee,en),{code:e_.too_big,maximum:er.value,type:"string",inclusive:!0,exact:!1,message:er.message}),ei.dirty());else if("length"===er.kind){let eo=ee.data.length>er.value,es=ee.data.length"datetime"===ee.kind)}get isEmail(){return!!this._def.checks.find(ee=>"email"===ee.kind)}get isURL(){return!!this._def.checks.find(ee=>"url"===ee.kind)}get isEmoji(){return!!this._def.checks.find(ee=>"emoji"===ee.kind)}get isUUID(){return!!this._def.checks.find(ee=>"uuid"===ee.kind)}get isCUID(){return!!this._def.checks.find(ee=>"cuid"===ee.kind)}get isCUID2(){return!!this._def.checks.find(ee=>"cuid2"===ee.kind)}get isULID(){return!!this._def.checks.find(ee=>"ulid"===ee.kind)}get isIP(){return!!this._def.checks.find(ee=>"ip"===ee.kind)}get minLength(){let ee=null;for(let en of this._def.checks)"min"===en.kind&&(null===ee||en.value>ee)&&(ee=en.value);return ee}get maxLength(){let ee=null;for(let en of this._def.checks)"max"===en.kind&&(null===ee||en.valueei?er:ei,es=parseInt(ee.toFixed(eo).replace(".","")),eu=parseInt(en.toFixed(eo).replace(".",""));return es%eu/Math.pow(10,eo)}e1.create=ee=>{var en;return new e1({checks:[],typeName:ef.ZodString,coerce:null!==(en=null==ee?void 0:ee.coerce)&&void 0!==en&&en,...eH(ee)})};class e4 extends eW{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(ee){let en;this._def.coerce&&(ee.data=Number(ee.data));let er=this._getType(ee);if(er!==eb.number){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.number,received:en.parsedType}),eA}let ei=new eI;for(let er of this._def.checks)if("int"===er.kind)eu.isInteger(ee.data)||(eR(en=this._getOrReturnCtx(ee,en),{code:e_.invalid_type,expected:"integer",received:"float",message:er.message}),ei.dirty());else if("min"===er.kind){let eo=er.inclusive?ee.dataer.value:ee.data>=er.value;eo&&(eR(en=this._getOrReturnCtx(ee,en),{code:e_.too_big,maximum:er.value,type:"number",inclusive:er.inclusive,exact:!1,message:er.message}),ei.dirty())}else"multipleOf"===er.kind?0!==e2(ee.data,er.value)&&(eR(en=this._getOrReturnCtx(ee,en),{code:e_.not_multiple_of,multipleOf:er.value,message:er.message}),ei.dirty()):"finite"===er.kind?Number.isFinite(ee.data)||(eR(en=this._getOrReturnCtx(ee,en),{code:e_.not_finite,message:er.message}),ei.dirty()):eu.assertNever(er);return{status:ei.value,value:ee.data}}gte(ee,en){return this.setLimit("min",ee,!0,ed.toString(en))}gt(ee,en){return this.setLimit("min",ee,!1,ed.toString(en))}lte(ee,en){return this.setLimit("max",ee,!0,ed.toString(en))}lt(ee,en){return this.setLimit("max",ee,!1,ed.toString(en))}setLimit(ee,en,er,ei){return new e4({...this._def,checks:[...this._def.checks,{kind:ee,value:en,inclusive:er,message:ed.toString(ei)}]})}_addCheck(ee){return new e4({...this._def,checks:[...this._def.checks,ee]})}int(ee){return this._addCheck({kind:"int",message:ed.toString(ee)})}positive(ee){return this._addCheck({kind:"min",value:0,inclusive:!1,message:ed.toString(ee)})}negative(ee){return this._addCheck({kind:"max",value:0,inclusive:!1,message:ed.toString(ee)})}nonpositive(ee){return this._addCheck({kind:"max",value:0,inclusive:!0,message:ed.toString(ee)})}nonnegative(ee){return this._addCheck({kind:"min",value:0,inclusive:!0,message:ed.toString(ee)})}multipleOf(ee,en){return this._addCheck({kind:"multipleOf",value:ee,message:ed.toString(en)})}finite(ee){return this._addCheck({kind:"finite",message:ed.toString(ee)})}safe(ee){return this._addCheck({kind:"min",inclusive:!0,value:Number.MIN_SAFE_INTEGER,message:ed.toString(ee)})._addCheck({kind:"max",inclusive:!0,value:Number.MAX_SAFE_INTEGER,message:ed.toString(ee)})}get minValue(){let ee=null;for(let en of this._def.checks)"min"===en.kind&&(null===ee||en.value>ee)&&(ee=en.value);return ee}get maxValue(){let ee=null;for(let en of this._def.checks)"max"===en.kind&&(null===ee||en.value"int"===ee.kind||"multipleOf"===ee.kind&&eu.isInteger(ee.value))}get isFinite(){let ee=null,en=null;for(let er of this._def.checks){if("finite"===er.kind||"int"===er.kind||"multipleOf"===er.kind)return!0;"min"===er.kind?(null===en||er.value>en)&&(en=er.value):"max"===er.kind&&(null===ee||er.valuenew e4({checks:[],typeName:ef.ZodNumber,coerce:(null==ee?void 0:ee.coerce)||!1,...eH(ee)});class e5 extends eW{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte}_parse(ee){let en;this._def.coerce&&(ee.data=BigInt(ee.data));let er=this._getType(ee);if(er!==eb.bigint){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.bigint,received:en.parsedType}),eA}let ei=new eI;for(let er of this._def.checks)if("min"===er.kind){let eo=er.inclusive?ee.dataer.value:ee.data>=er.value;eo&&(eR(en=this._getOrReturnCtx(ee,en),{code:e_.too_big,type:"bigint",maximum:er.value,inclusive:er.inclusive,message:er.message}),ei.dirty())}else"multipleOf"===er.kind?ee.data%er.value!==BigInt(0)&&(eR(en=this._getOrReturnCtx(ee,en),{code:e_.not_multiple_of,multipleOf:er.value,message:er.message}),ei.dirty()):eu.assertNever(er);return{status:ei.value,value:ee.data}}gte(ee,en){return this.setLimit("min",ee,!0,ed.toString(en))}gt(ee,en){return this.setLimit("min",ee,!1,ed.toString(en))}lte(ee,en){return this.setLimit("max",ee,!0,ed.toString(en))}lt(ee,en){return this.setLimit("max",ee,!1,ed.toString(en))}setLimit(ee,en,er,ei){return new e5({...this._def,checks:[...this._def.checks,{kind:ee,value:en,inclusive:er,message:ed.toString(ei)}]})}_addCheck(ee){return new e5({...this._def,checks:[...this._def.checks,ee]})}positive(ee){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!1,message:ed.toString(ee)})}negative(ee){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!1,message:ed.toString(ee)})}nonpositive(ee){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!0,message:ed.toString(ee)})}nonnegative(ee){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!0,message:ed.toString(ee)})}multipleOf(ee,en){return this._addCheck({kind:"multipleOf",value:ee,message:ed.toString(en)})}get minValue(){let ee=null;for(let en of this._def.checks)"min"===en.kind&&(null===ee||en.value>ee)&&(ee=en.value);return ee}get maxValue(){let ee=null;for(let en of this._def.checks)"max"===en.kind&&(null===ee||en.value{var en;return new e5({checks:[],typeName:ef.ZodBigInt,coerce:null!==(en=null==ee?void 0:ee.coerce)&&void 0!==en&&en,...eH(ee)})};class e3 extends eW{_parse(ee){this._def.coerce&&(ee.data=!!ee.data);let en=this._getType(ee);if(en!==eb.boolean){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.boolean,received:en.parsedType}),eA}return eP(ee.data)}}e3.create=ee=>new e3({typeName:ef.ZodBoolean,coerce:(null==ee?void 0:ee.coerce)||!1,...eH(ee)});class e9 extends eW{_parse(ee){let en;this._def.coerce&&(ee.data=new Date(ee.data));let er=this._getType(ee);if(er!==eb.date){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.date,received:en.parsedType}),eA}if(isNaN(ee.data.getTime())){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_date}),eA}let ei=new eI;for(let er of this._def.checks)"min"===er.kind?ee.data.getTime()er.value&&(eR(en=this._getOrReturnCtx(ee,en),{code:e_.too_big,message:er.message,inclusive:!0,exact:!1,maximum:er.value,type:"date"}),ei.dirty()):eu.assertNever(er);return{status:ei.value,value:new Date(ee.data.getTime())}}_addCheck(ee){return new e9({...this._def,checks:[...this._def.checks,ee]})}min(ee,en){return this._addCheck({kind:"min",value:ee.getTime(),message:ed.toString(en)})}max(ee,en){return this._addCheck({kind:"max",value:ee.getTime(),message:ed.toString(en)})}get minDate(){let ee=null;for(let en of this._def.checks)"min"===en.kind&&(null===ee||en.value>ee)&&(ee=en.value);return null!=ee?new Date(ee):null}get maxDate(){let ee=null;for(let en of this._def.checks)"max"===en.kind&&(null===ee||en.valuenew e9({checks:[],coerce:(null==ee?void 0:ee.coerce)||!1,typeName:ef.ZodDate,...eH(ee)});class e7 extends eW{_parse(ee){let en=this._getType(ee);if(en!==eb.symbol){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.symbol,received:en.parsedType}),eA}return eP(ee.data)}}e7.create=ee=>new e7({typeName:ef.ZodSymbol,...eH(ee)});class e6 extends eW{_parse(ee){let en=this._getType(ee);if(en!==eb.undefined){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.undefined,received:en.parsedType}),eA}return eP(ee.data)}}e6.create=ee=>new e6({typeName:ef.ZodUndefined,...eH(ee)});class e8 extends eW{_parse(ee){let en=this._getType(ee);if(en!==eb.null){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.null,received:en.parsedType}),eA}return eP(ee.data)}}e8.create=ee=>new e8({typeName:ef.ZodNull,...eH(ee)});class te extends eW{constructor(){super(...arguments),this._any=!0}_parse(ee){return eP(ee.data)}}te.create=ee=>new te({typeName:ef.ZodAny,...eH(ee)});class tt extends eW{constructor(){super(...arguments),this._unknown=!0}_parse(ee){return eP(ee.data)}}tt.create=ee=>new tt({typeName:ef.ZodUnknown,...eH(ee)});class tn extends eW{_parse(ee){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.never,received:en.parsedType}),eA}}tn.create=ee=>new tn({typeName:ef.ZodNever,...eH(ee)});class tr extends eW{_parse(ee){let en=this._getType(ee);if(en!==eb.undefined){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.void,received:en.parsedType}),eA}return eP(ee.data)}}tr.create=ee=>new tr({typeName:ef.ZodVoid,...eH(ee)});class ti extends eW{_parse(ee){let{ctx:en,status:er}=this._processInputParams(ee),ei=this._def;if(en.parsedType!==eb.array)return eR(en,{code:e_.invalid_type,expected:eb.array,received:en.parsedType}),eA;if(null!==ei.exactLength){let ee=en.data.length>ei.exactLength.value,eo=en.data.lengthei.maxLength.value&&(eR(en,{code:e_.too_big,maximum:ei.maxLength.value,type:"array",inclusive:!0,exact:!1,message:ei.maxLength.message}),er.dirty()),en.common.async)return Promise.all([...en.data].map((ee,er)=>ei.type._parseAsync(new e$(en,ee,en.path,er)))).then(ee=>eI.mergeArray(er,ee));let eo=[...en.data].map((ee,er)=>ei.type._parseSync(new e$(en,ee,en.path,er)));return eI.mergeArray(er,eo)}get element(){return this._def.type}min(ee,en){return new ti({...this._def,minLength:{value:ee,message:ed.toString(en)}})}max(ee,en){return new ti({...this._def,maxLength:{value:ee,message:ed.toString(en)}})}length(ee,en){return new ti({...this._def,exactLength:{value:ee,message:ed.toString(en)}})}nonempty(ee){return this.min(1,ee)}}function to(ee){if(ee instanceof ts){let en={};for(let er in ee.shape){let ei=ee.shape[er];en[er]=tE.create(to(ei))}return new ts({...ee._def,shape:()=>en})}return ee instanceof ti?new ti({...ee._def,type:to(ee.element)}):ee instanceof tE?tE.create(to(ee.unwrap())):ee instanceof tO?tO.create(to(ee.unwrap())):ee instanceof th?th.create(ee.items.map(ee=>to(ee))):ee}ti.create=(ee,en)=>new ti({type:ee,minLength:null,maxLength:null,exactLength:null,typeName:ef.ZodArray,...eH(en)});class ts extends eW{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=this.extend}_getCached(){if(null!==this._cached)return this._cached;let ee=this._def.shape(),en=eu.objectKeys(ee);return this._cached={shape:ee,keys:en}}_parse(ee){let en=this._getType(ee);if(en!==eb.object){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.object,received:en.parsedType}),eA}let{status:er,ctx:ei}=this._processInputParams(ee),{shape:eo,keys:es}=this._getCached(),eu=[];if(!(this._def.catchall instanceof tn&&"strip"===this._def.unknownKeys))for(let ee in ei.data)es.includes(ee)||eu.push(ee);let ec=[];for(let ee of es){let en=eo[ee],er=ei.data[ee];ec.push({key:{status:"valid",value:ee},value:en._parse(new e$(ei,er,ei.path,ee)),alwaysSet:ee in ei.data})}if(this._def.catchall instanceof tn){let ee=this._def.unknownKeys;if("passthrough"===ee)for(let ee of eu)ec.push({key:{status:"valid",value:ee},value:{status:"valid",value:ei.data[ee]}});else if("strict"===ee)eu.length>0&&(eR(ei,{code:e_.unrecognized_keys,keys:eu}),er.dirty());else if("strip"===ee);else throw Error("Internal ZodObject error: invalid unknownKeys value.")}else{let ee=this._def.catchall;for(let en of eu){let er=ei.data[en];ec.push({key:{status:"valid",value:en},value:ee._parse(new e$(ei,er,ei.path,en)),alwaysSet:en in ei.data})}}return ei.common.async?Promise.resolve().then(async()=>{let ee=[];for(let en of ec){let er=await en.key;ee.push({key:er,value:await en.value,alwaysSet:en.alwaysSet})}return ee}).then(ee=>eI.mergeObjectSync(er,ee)):eI.mergeObjectSync(er,ec)}get shape(){return this._def.shape()}strict(ee){return ed.errToObj,new ts({...this._def,unknownKeys:"strict",...void 0!==ee?{errorMap:(en,er)=>{var ei,eo,es,eu;let ec=null!==(es=null===(eo=(ei=this._def).errorMap)||void 0===eo?void 0:eo.call(ei,en,er).message)&&void 0!==es?es:er.defaultError;return"unrecognized_keys"===en.code?{message:null!==(eu=ed.errToObj(ee).message)&&void 0!==eu?eu:ec}:{message:ec}}}:{}})}strip(){return new ts({...this._def,unknownKeys:"strip"})}passthrough(){return new ts({...this._def,unknownKeys:"passthrough"})}extend(ee){return new ts({...this._def,shape:()=>({...this._def.shape(),...ee})})}merge(ee){let en=new ts({unknownKeys:ee._def.unknownKeys,catchall:ee._def.catchall,shape:()=>({...this._def.shape(),...ee._def.shape()}),typeName:ef.ZodObject});return en}setKey(ee,en){return this.augment({[ee]:en})}catchall(ee){return new ts({...this._def,catchall:ee})}pick(ee){let en={};return eu.objectKeys(ee).forEach(er=>{ee[er]&&this.shape[er]&&(en[er]=this.shape[er])}),new ts({...this._def,shape:()=>en})}omit(ee){let en={};return eu.objectKeys(this.shape).forEach(er=>{ee[er]||(en[er]=this.shape[er])}),new ts({...this._def,shape:()=>en})}deepPartial(){return to(this)}partial(ee){let en={};return eu.objectKeys(this.shape).forEach(er=>{let ei=this.shape[er];ee&&!ee[er]?en[er]=ei:en[er]=ei.optional()}),new ts({...this._def,shape:()=>en})}required(ee){let en={};return eu.objectKeys(this.shape).forEach(er=>{if(ee&&!ee[er])en[er]=this.shape[er];else{let ee=this.shape[er],ei=ee;for(;ei instanceof tE;)ei=ei._def.innerType;en[er]=ei}}),new ts({...this._def,shape:()=>en})}keyof(){return tb(eu.objectKeys(this.shape))}}ts.create=(ee,en)=>new ts({shape:()=>ee,unknownKeys:"strip",catchall:tn.create(),typeName:ef.ZodObject,...eH(en)}),ts.strictCreate=(ee,en)=>new ts({shape:()=>ee,unknownKeys:"strict",catchall:tn.create(),typeName:ef.ZodObject,...eH(en)}),ts.lazycreate=(ee,en)=>new ts({shape:ee,unknownKeys:"strip",catchall:tn.create(),typeName:ef.ZodObject,...eH(en)});class tl extends eW{_parse(ee){let{ctx:en}=this._processInputParams(ee),er=this._def.options;function ei(ee){for(let en of ee)if("valid"===en.result.status)return en.result;for(let er of ee)if("dirty"===er.result.status)return en.common.issues.push(...er.ctx.common.issues),er.result;let er=ee.map(ee=>new ej(ee.ctx.common.issues));return eR(en,{code:e_.invalid_union,unionErrors:er}),eA}if(en.common.async)return Promise.all(er.map(async ee=>{let er={...en,common:{...en.common,issues:[]},parent:null};return{result:await ee._parseAsync({data:en.data,path:en.path,parent:er}),ctx:er}})).then(ei);{let ee;let ei=[];for(let eo of er){let er={...en,common:{...en.common,issues:[]},parent:null},es=eo._parseSync({data:en.data,path:en.path,parent:er});if("valid"===es.status)return es;"dirty"!==es.status||ee||(ee={result:es,ctx:er}),er.common.issues.length&&ei.push(er.common.issues)}if(ee)return en.common.issues.push(...ee.ctx.common.issues),ee.result;let eo=ei.map(ee=>new ej(ee));return eR(en,{code:e_.invalid_union,unionErrors:eo}),eA}}get options(){return this._def.options}}tl.create=(ee,en)=>new tl({options:ee,typeName:ef.ZodUnion,...eH(en)});let tu=ee=>{if(ee instanceof tv)return tu(ee.schema);if(ee instanceof tj)return tu(ee.innerType());if(ee instanceof ty)return[ee.value];if(ee instanceof tw)return ee.options;if(ee instanceof t_)return Object.keys(ee.enum);if(ee instanceof tC)return tu(ee._def.innerType);if(ee instanceof e6)return[void 0];else if(ee instanceof e8)return[null];else return null};class tc extends eW{_parse(ee){let{ctx:en}=this._processInputParams(ee);if(en.parsedType!==eb.object)return eR(en,{code:e_.invalid_type,expected:eb.object,received:en.parsedType}),eA;let er=this.discriminator,ei=en.data[er],eo=this.optionsMap.get(ei);return eo?en.common.async?eo._parseAsync({data:en.data,path:en.path,parent:en}):eo._parseSync({data:en.data,path:en.path,parent:en}):(eR(en,{code:e_.invalid_union_discriminator,options:Array.from(this.optionsMap.keys()),path:[er]}),eA)}get discriminator(){return this._def.discriminator}get options(){return this._def.options}get optionsMap(){return this._def.optionsMap}static create(ee,en,er){let ei=new Map;for(let er of en){let en=tu(er.shape[ee]);if(!en)throw Error(`A discriminator value for key \`${ee}\` could not be extracted from all schema options`);for(let eo of en){if(ei.has(eo))throw Error(`Discriminator property ${String(ee)} has duplicate value ${String(eo)}`);ei.set(eo,er)}}return new tc({typeName:ef.ZodDiscriminatedUnion,discriminator:ee,options:en,optionsMap:ei,...eH(er)})}}function td(ee,en){let er=ew(ee),ei=ew(en);if(ee===en)return{valid:!0,data:ee};if(er===eb.object&&ei===eb.object){let er=eu.objectKeys(en),ei=eu.objectKeys(ee).filter(ee=>-1!==er.indexOf(ee)),eo={...ee,...en};for(let er of ei){let ei=td(ee[er],en[er]);if(!ei.valid)return{valid:!1};eo[er]=ei.data}return{valid:!0,data:eo}}if(er===eb.array&&ei===eb.array){if(ee.length!==en.length)return{valid:!1};let er=[];for(let ei=0;ei{if(eM(ee)||eM(ei))return eA;let eo=td(ee.value,ei.value);return eo.valid?((eZ(ee)||eZ(ei))&&en.dirty(),{status:en.value,value:eo.data}):(eR(er,{code:e_.invalid_intersection_types}),eA)};return er.common.async?Promise.all([this._def.left._parseAsync({data:er.data,path:er.path,parent:er}),this._def.right._parseAsync({data:er.data,path:er.path,parent:er})]).then(([ee,en])=>ei(ee,en)):ei(this._def.left._parseSync({data:er.data,path:er.path,parent:er}),this._def.right._parseSync({data:er.data,path:er.path,parent:er}))}}tf.create=(ee,en,er)=>new tf({left:ee,right:en,typeName:ef.ZodIntersection,...eH(er)});class th extends eW{_parse(ee){let{status:en,ctx:er}=this._processInputParams(ee);if(er.parsedType!==eb.array)return eR(er,{code:e_.invalid_type,expected:eb.array,received:er.parsedType}),eA;if(er.data.lengththis._def.items.length&&(eR(er,{code:e_.too_big,maximum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),en.dirty());let eo=[...er.data].map((ee,en)=>{let ei=this._def.items[en]||this._def.rest;return ei?ei._parse(new e$(er,ee,er.path,en)):null}).filter(ee=>!!ee);return er.common.async?Promise.all(eo).then(ee=>eI.mergeArray(en,ee)):eI.mergeArray(en,eo)}get items(){return this._def.items}rest(ee){return new th({...this._def,rest:ee})}}th.create=(ee,en)=>{if(!Array.isArray(ee))throw Error("You must pass an array of schemas to z.tuple([ ... ])");return new th({items:ee,typeName:ef.ZodTuple,rest:null,...eH(en)})};class tp extends eW{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(ee){let{status:en,ctx:er}=this._processInputParams(ee);if(er.parsedType!==eb.object)return eR(er,{code:e_.invalid_type,expected:eb.object,received:er.parsedType}),eA;let ei=[],eo=this._def.keyType,es=this._def.valueType;for(let ee in er.data)ei.push({key:eo._parse(new e$(er,ee,er.path,ee)),value:es._parse(new e$(er,er.data[ee],er.path,ee))});return er.common.async?eI.mergeObjectAsync(en,ei):eI.mergeObjectSync(en,ei)}get element(){return this._def.valueType}static create(ee,en,er){return new tp(en instanceof eW?{keyType:ee,valueType:en,typeName:ef.ZodRecord,...eH(er)}:{keyType:e1.create(),valueType:ee,typeName:ef.ZodRecord,...eH(en)})}}class tm extends eW{_parse(ee){let{status:en,ctx:er}=this._processInputParams(ee);if(er.parsedType!==eb.map)return eR(er,{code:e_.invalid_type,expected:eb.map,received:er.parsedType}),eA;let ei=this._def.keyType,eo=this._def.valueType,es=[...er.data.entries()].map(([ee,en],es)=>({key:ei._parse(new e$(er,ee,er.path,[es,"key"])),value:eo._parse(new e$(er,en,er.path,[es,"value"]))}));if(er.common.async){let ee=new Map;return Promise.resolve().then(async()=>{for(let er of es){let ei=await er.key,eo=await er.value;if("aborted"===ei.status||"aborted"===eo.status)return eA;("dirty"===ei.status||"dirty"===eo.status)&&en.dirty(),ee.set(ei.value,eo.value)}return{status:en.value,value:ee}})}{let ee=new Map;for(let er of es){let ei=er.key,eo=er.value;if("aborted"===ei.status||"aborted"===eo.status)return eA;("dirty"===ei.status||"dirty"===eo.status)&&en.dirty(),ee.set(ei.value,eo.value)}return{status:en.value,value:ee}}}}tm.create=(ee,en,er)=>new tm({valueType:en,keyType:ee,typeName:ef.ZodMap,...eH(er)});class tx extends eW{_parse(ee){let{status:en,ctx:er}=this._processInputParams(ee);if(er.parsedType!==eb.set)return eR(er,{code:e_.invalid_type,expected:eb.set,received:er.parsedType}),eA;let ei=this._def;null!==ei.minSize&&er.data.sizeei.maxSize.value&&(eR(er,{code:e_.too_big,maximum:ei.maxSize.value,type:"set",inclusive:!0,exact:!1,message:ei.maxSize.message}),en.dirty());let eo=this._def.valueType;function es(ee){let er=new Set;for(let ei of ee){if("aborted"===ei.status)return eA;"dirty"===ei.status&&en.dirty(),er.add(ei.value)}return{status:en.value,value:er}}let eu=[...er.data.values()].map((ee,en)=>eo._parse(new e$(er,ee,er.path,en)));return er.common.async?Promise.all(eu).then(ee=>es(ee)):es(eu)}min(ee,en){return new tx({...this._def,minSize:{value:ee,message:ed.toString(en)}})}max(ee,en){return new tx({...this._def,maxSize:{value:ee,message:ed.toString(en)}})}size(ee,en){return this.min(ee,en).max(ee,en)}nonempty(ee){return this.min(1,ee)}}tx.create=(ee,en)=>new tx({valueType:ee,minSize:null,maxSize:null,typeName:ef.ZodSet,...eH(en)});class tg extends eW{constructor(){super(...arguments),this.validate=this.implement}_parse(ee){let{ctx:en}=this._processInputParams(ee);if(en.parsedType!==eb.function)return eR(en,{code:e_.invalid_type,expected:eb.function,received:en.parsedType}),eA;function er(ee,er){return eS({data:ee,path:en.path,errorMaps:[en.common.contextualErrorMap,en.schemaErrorMap,eT(),eE].filter(ee=>!!ee),issueData:{code:e_.invalid_arguments,argumentsError:er}})}function ei(ee,er){return eS({data:ee,path:en.path,errorMaps:[en.common.contextualErrorMap,en.schemaErrorMap,eT(),eE].filter(ee=>!!ee),issueData:{code:e_.invalid_return_type,returnTypeError:er}})}let eo={errorMap:en.common.contextualErrorMap},es=en.data;return this._def.returns instanceof tk?eP(async(...ee)=>{let en=new ej([]),eu=await this._def.args.parseAsync(ee,eo).catch(ei=>{throw en.addIssue(er(ee,ei)),en}),ec=await es(...eu),ed=await this._def.returns._def.type.parseAsync(ec,eo).catch(ee=>{throw en.addIssue(ei(ec,ee)),en});return ed}):eP((...ee)=>{let en=this._def.args.safeParse(ee,eo);if(!en.success)throw new ej([er(ee,en.error)]);let eu=es(...en.data),ec=this._def.returns.safeParse(eu,eo);if(!ec.success)throw new ej([ei(eu,ec.error)]);return ec.data})}parameters(){return this._def.args}returnType(){return this._def.returns}args(...ee){return new tg({...this._def,args:th.create(ee).rest(tt.create())})}returns(ee){return new tg({...this._def,returns:ee})}implement(ee){let en=this.parse(ee);return en}strictImplement(ee){let en=this.parse(ee);return en}static create(ee,en,er){return new tg({args:ee||th.create([]).rest(tt.create()),returns:en||tt.create(),typeName:ef.ZodFunction,...eH(er)})}}class tv extends eW{get schema(){return this._def.getter()}_parse(ee){let{ctx:en}=this._processInputParams(ee),er=this._def.getter();return er._parse({data:en.data,path:en.path,parent:en})}}tv.create=(ee,en)=>new tv({getter:ee,typeName:ef.ZodLazy,...eH(en)});class ty extends eW{_parse(ee){if(ee.data!==this._def.value){let en=this._getOrReturnCtx(ee);return eR(en,{received:en.data,code:e_.invalid_literal,expected:this._def.value}),eA}return{status:"valid",value:ee.data}}get value(){return this._def.value}}function tb(ee,en){return new tw({values:ee,typeName:ef.ZodEnum,...eH(en)})}ty.create=(ee,en)=>new ty({value:ee,typeName:ef.ZodLiteral,...eH(en)});class tw extends eW{_parse(ee){if("string"!=typeof ee.data){let en=this._getOrReturnCtx(ee),er=this._def.values;return eR(en,{expected:eu.joinValues(er),received:en.parsedType,code:e_.invalid_type}),eA}if(-1===this._def.values.indexOf(ee.data)){let en=this._getOrReturnCtx(ee),er=this._def.values;return eR(en,{received:en.data,code:e_.invalid_enum_value,options:er}),eA}return eP(ee.data)}get options(){return this._def.values}get enum(){let ee={};for(let en of this._def.values)ee[en]=en;return ee}get Values(){let ee={};for(let en of this._def.values)ee[en]=en;return ee}get Enum(){let ee={};for(let en of this._def.values)ee[en]=en;return ee}extract(ee){return tw.create(ee)}exclude(ee){return tw.create(this.options.filter(en=>!ee.includes(en)))}}tw.create=tb;class t_ extends eW{_parse(ee){let en=eu.getValidEnumValues(this._def.values),er=this._getOrReturnCtx(ee);if(er.parsedType!==eb.string&&er.parsedType!==eb.number){let ee=eu.objectValues(en);return eR(er,{expected:eu.joinValues(ee),received:er.parsedType,code:e_.invalid_type}),eA}if(-1===en.indexOf(ee.data)){let ee=eu.objectValues(en);return eR(er,{received:er.data,code:e_.invalid_enum_value,options:ee}),eA}return eP(ee.data)}get enum(){return this._def.values}}t_.create=(ee,en)=>new t_({values:ee,typeName:ef.ZodNativeEnum,...eH(en)});class tk extends eW{unwrap(){return this._def.type}_parse(ee){let{ctx:en}=this._processInputParams(ee);if(en.parsedType!==eb.promise&&!1===en.common.async)return eR(en,{code:e_.invalid_type,expected:eb.promise,received:en.parsedType}),eA;let er=en.parsedType===eb.promise?en.data:Promise.resolve(en.data);return eP(er.then(ee=>this._def.type.parseAsync(ee,{path:en.path,errorMap:en.common.contextualErrorMap})))}}tk.create=(ee,en)=>new tk({type:ee,typeName:ef.ZodPromise,...eH(en)});class tj extends eW{innerType(){return this._def.schema}sourceType(){return this._def.schema._def.typeName===ef.ZodEffects?this._def.schema.sourceType():this._def.schema}_parse(ee){let{status:en,ctx:er}=this._processInputParams(ee),ei=this._def.effect||null;if("preprocess"===ei.type){let ee=ei.transform(er.data);return er.common.async?Promise.resolve(ee).then(ee=>this._def.schema._parseAsync({data:ee,path:er.path,parent:er})):this._def.schema._parseSync({data:ee,path:er.path,parent:er})}let eo={addIssue:ee=>{eR(er,ee),ee.fatal?en.abort():en.dirty()},get path(){return er.path}};if(eo.addIssue=eo.addIssue.bind(eo),"refinement"===ei.type){let ee=ee=>{let en=ei.refinement(ee,eo);if(er.common.async)return Promise.resolve(en);if(en instanceof Promise)throw Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return ee};if(!1!==er.common.async)return this._def.schema._parseAsync({data:er.data,path:er.path,parent:er}).then(er=>"aborted"===er.status?eA:("dirty"===er.status&&en.dirty(),ee(er.value).then(()=>({status:en.value,value:er.value}))));{let ei=this._def.schema._parseSync({data:er.data,path:er.path,parent:er});return"aborted"===ei.status?eA:("dirty"===ei.status&&en.dirty(),ee(ei.value),{status:en.value,value:ei.value})}}if("transform"===ei.type){if(!1!==er.common.async)return this._def.schema._parseAsync({data:er.data,path:er.path,parent:er}).then(ee=>eD(ee)?Promise.resolve(ei.transform(ee.value,eo)).then(ee=>({status:en.value,value:ee})):ee);{let ee=this._def.schema._parseSync({data:er.data,path:er.path,parent:er});if(!eD(ee))return ee;let es=ei.transform(ee.value,eo);if(es instanceof Promise)throw Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:en.value,value:es}}}eu.assertNever(ei)}}tj.create=(ee,en,er)=>new tj({schema:ee,typeName:ef.ZodEffects,effect:en,...eH(er)}),tj.createWithPreprocess=(ee,en,er)=>new tj({schema:en,effect:{type:"preprocess",transform:ee},typeName:ef.ZodEffects,...eH(er)});class tE extends eW{_parse(ee){let en=this._getType(ee);return en===eb.undefined?eP(void 0):this._def.innerType._parse(ee)}unwrap(){return this._def.innerType}}tE.create=(ee,en)=>new tE({innerType:ee,typeName:ef.ZodOptional,...eH(en)});class tO extends eW{_parse(ee){let en=this._getType(ee);return en===eb.null?eP(null):this._def.innerType._parse(ee)}unwrap(){return this._def.innerType}}tO.create=(ee,en)=>new tO({innerType:ee,typeName:ef.ZodNullable,...eH(en)});class tC extends eW{_parse(ee){let{ctx:en}=this._processInputParams(ee),er=en.data;return en.parsedType===eb.undefined&&(er=this._def.defaultValue()),this._def.innerType._parse({data:er,path:en.path,parent:en})}removeDefault(){return this._def.innerType}}tC.create=(ee,en)=>new tC({innerType:ee,typeName:ef.ZodDefault,defaultValue:"function"==typeof en.default?en.default:()=>en.default,...eH(en)});class tT extends eW{_parse(ee){let{ctx:en}=this._processInputParams(ee),er={...en,common:{...en.common,issues:[]}},ei=this._def.innerType._parse({data:er.data,path:er.path,parent:{...er}});return eF(ei)?ei.then(ee=>({status:"valid",value:"valid"===ee.status?ee.value:this._def.catchValue({get error(){return new ej(er.common.issues)},input:er.data})})):{status:"valid",value:"valid"===ei.status?ei.value:this._def.catchValue({get error(){return new ej(er.common.issues)},input:er.data})}}removeCatch(){return this._def.innerType}}tT.create=(ee,en)=>new tT({innerType:ee,typeName:ef.ZodCatch,catchValue:"function"==typeof en.catch?en.catch:()=>en.catch,...eH(en)});class tS extends eW{_parse(ee){let en=this._getType(ee);if(en!==eb.nan){let en=this._getOrReturnCtx(ee);return eR(en,{code:e_.invalid_type,expected:eb.nan,received:en.parsedType}),eA}return{status:"valid",value:ee.data}}}tS.create=ee=>new tS({typeName:ef.ZodNaN,...eH(ee)});let tN=Symbol("zod_brand");class tR extends eW{_parse(ee){let{ctx:en}=this._processInputParams(ee),er=en.data;return this._def.type._parse({data:er,path:en.path,parent:en})}unwrap(){return this._def.type}}class tI extends eW{_parse(ee){let{status:en,ctx:er}=this._processInputParams(ee);if(er.common.async){let ee=async()=>{let ee=await this._def.in._parseAsync({data:er.data,path:er.path,parent:er});return"aborted"===ee.status?eA:"dirty"===ee.status?(en.dirty(),eL(ee.value)):this._def.out._parseAsync({data:ee.value,path:er.path,parent:er})};return ee()}{let ee=this._def.in._parseSync({data:er.data,path:er.path,parent:er});return"aborted"===ee.status?eA:"dirty"===ee.status?(en.dirty(),{status:"dirty",value:ee.value}):this._def.out._parseSync({data:ee.value,path:er.path,parent:er})}}static create(ee,en){return new tI({in:ee,out:en,typeName:ef.ZodPipeline})}}let tA=(ee,en={},er)=>ee?te.create().superRefine((ei,eo)=>{var es,eu;if(!ee(ei)){let ee="function"==typeof en?en(ei):"string"==typeof en?{message:en}:en,ec=null===(eu=null!==(es=ee.fatal)&&void 0!==es?es:er)||void 0===eu||eu,ed="string"==typeof ee?{message:ee}:ee;eo.addIssue({code:"custom",...ed,fatal:ec})}}):te.create(),tL={object:ts.lazycreate};!function(ee){ee.ZodString="ZodString",ee.ZodNumber="ZodNumber",ee.ZodNaN="ZodNaN",ee.ZodBigInt="ZodBigInt",ee.ZodBoolean="ZodBoolean",ee.ZodDate="ZodDate",ee.ZodSymbol="ZodSymbol",ee.ZodUndefined="ZodUndefined",ee.ZodNull="ZodNull",ee.ZodAny="ZodAny",ee.ZodUnknown="ZodUnknown",ee.ZodNever="ZodNever",ee.ZodVoid="ZodVoid",ee.ZodArray="ZodArray",ee.ZodObject="ZodObject",ee.ZodUnion="ZodUnion",ee.ZodDiscriminatedUnion="ZodDiscriminatedUnion",ee.ZodIntersection="ZodIntersection",ee.ZodTuple="ZodTuple",ee.ZodRecord="ZodRecord",ee.ZodMap="ZodMap",ee.ZodSet="ZodSet",ee.ZodFunction="ZodFunction",ee.ZodLazy="ZodLazy",ee.ZodLiteral="ZodLiteral",ee.ZodEnum="ZodEnum",ee.ZodEffects="ZodEffects",ee.ZodNativeEnum="ZodNativeEnum",ee.ZodOptional="ZodOptional",ee.ZodNullable="ZodNullable",ee.ZodDefault="ZodDefault",ee.ZodCatch="ZodCatch",ee.ZodPromise="ZodPromise",ee.ZodBranded="ZodBranded",ee.ZodPipeline="ZodPipeline"}(ef||(ef={}));let tP=(ee,en={message:`Input not instance of ${ee.name}`})=>tA(en=>en instanceof ee,en),tM=e1.create,tZ=e4.create,tD=tS.create,tF=e5.create,t$=e3.create,tz=e9.create,tH=e7.create,tW=e6.create,tU=e8.create,tG=te.create,tV=tt.create,tB=tn.create,tq=tr.create,tK=ti.create,tQ=ts.create,tJ=ts.strictCreate,tY=tl.create,tX=tc.create,t0=tf.create,t1=th.create,t2=tp.create,t4=tm.create,t5=tx.create,t3=tg.create,t9=tv.create,t7=ty.create,t6=tw.create,t8=t_.create,ne=tk.create,nt=tj.create,nn=tE.create,nr=tO.create,ni=tj.createWithPreprocess,no=tI.create,ns=()=>tM().optional(),nl=()=>tZ().optional(),nu=()=>t$().optional(),nc={string:ee=>e1.create({...ee,coerce:!0}),number:ee=>e4.create({...ee,coerce:!0}),boolean:ee=>e3.create({...ee,coerce:!0}),bigint:ee=>e5.create({...ee,coerce:!0}),date:ee=>e9.create({...ee,coerce:!0})},nd=eA;var nf=Object.freeze({__proto__:null,defaultErrorMap:eE,setErrorMap:eC,getErrorMap:eT,makeIssue:eS,EMPTY_PATH:eN,addIssueToContext:eR,ParseStatus:eI,INVALID:eA,DIRTY:eL,OK:eP,isAborted:eM,isDirty:eZ,isValid:eD,isAsync:eF,get util(){return eu},get objectUtil(){return ec},ZodParsedType:eb,getParsedType:ew,ZodType:eW,ZodString:e1,ZodNumber:e4,ZodBigInt:e5,ZodBoolean:e3,ZodDate:e9,ZodSymbol:e7,ZodUndefined:e6,ZodNull:e8,ZodAny:te,ZodUnknown:tt,ZodNever:tn,ZodVoid:tr,ZodArray:ti,ZodObject:ts,ZodUnion:tl,ZodDiscriminatedUnion:tc,ZodIntersection:tf,ZodTuple:th,ZodRecord:tp,ZodMap:tm,ZodSet:tx,ZodFunction:tg,ZodLazy:tv,ZodLiteral:ty,ZodEnum:tw,ZodNativeEnum:t_,ZodPromise:tk,ZodEffects:tj,ZodTransformer:tj,ZodOptional:tE,ZodNullable:tO,ZodDefault:tC,ZodCatch:tT,ZodNaN:tS,BRAND:tN,ZodBranded:tR,ZodPipeline:tI,custom:tA,Schema:eW,ZodSchema:eW,late:tL,get ZodFirstPartyTypeKind(){return ef},coerce:nc,any:tG,array:tK,bigint:tF,boolean:t$,date:tz,discriminatedUnion:tX,effect:nt,enum:t6,function:t3,instanceof:tP,intersection:t0,lazy:t9,literal:t7,map:t4,nan:tD,nativeEnum:t8,never:tB,null:tU,nullable:nr,number:tZ,object:tQ,oboolean:nu,onumber:nl,optional:nn,ostring:ns,pipeline:no,preprocess:ni,promise:ne,record:t2,set:t5,strictObject:tJ,string:tM,symbol:tH,transformer:nt,tuple:t1,undefined:tW,union:tY,unknown:tV,void:tq,NEVER:nd,ZodIssueCode:e_,quotelessJson:ek,ZodError:ej}),nh=er(4970),np=er.n(nh),nm={i8:"13.4.7"};er(5983);var nx=er(1527);let ng=["light","dark"],nv="(prefers-color-scheme: dark)",ny="undefined"==typeof window,nb=(0,em.createContext)(void 0),nw={setTheme:ee=>{},themes:[]},n_=()=>{var ee;return null!==(ee=(0,em.useContext)(nb))&&void 0!==ee?ee:nw},nk=ee=>(0,em.useContext)(nb)?em.createElement(em.Fragment,null,ee.children):em.createElement(nE,ee),nj=["light","dark"],nE=({forcedTheme:ee,disableTransitionOnChange:en=!1,enableSystem:er=!0,enableColorScheme:ei=!0,storageKey:eo="theme",themes:es=nj,defaultTheme:eu=er?"system":"light",attribute:ec="data-theme",value:ed,children:ef,nonce:eh})=>{let[ep,ex]=(0,em.useState)(()=>nC(eo,eu)),[eg,ev]=(0,em.useState)(()=>nC(eo)),ey=ed?Object.values(ed):es,eb=(0,em.useCallback)(ee=>{let eo=ee;if(!eo)return;"system"===ee&&er&&(eo=nS());let es=ed?ed[eo]:eo,ef=en?nT():null,eh=document.documentElement;if("class"===ec?(eh.classList.remove(...ey),es&&eh.classList.add(es)):es?eh.setAttribute(ec,es):eh.removeAttribute(ec),ei){let ee=ng.includes(eu)?eu:null,en=ng.includes(eo)?eo:ee;eh.style.colorScheme=en}null==ef||ef()},[]),ew=(0,em.useCallback)(ee=>{ex(ee);try{localStorage.setItem(eo,ee)}catch(ee){}},[ee]),e_=(0,em.useCallback)(en=>{let ei=nS(en);ev(ei),"system"===ep&&er&&!ee&&eb("system")},[ep,ee]);(0,em.useEffect)(()=>{let ee=window.matchMedia(nv);return ee.addListener(e_),e_(ee),()=>ee.removeListener(e_)},[e_]),(0,em.useEffect)(()=>{let ee=ee=>{ee.key===eo&&ew(ee.newValue||eu)};return window.addEventListener("storage",ee),()=>window.removeEventListener("storage",ee)},[ew]),(0,em.useEffect)(()=>{eb(null!=ee?ee:ep)},[ee,ep]);let ek=(0,em.useMemo)(()=>({theme:ep,setTheme:ew,forcedTheme:ee,resolvedTheme:"system"===ep?eg:ep,themes:er?[...es,"system"]:es,systemTheme:er?eg:void 0}),[ep,ew,ee,eg,er,es]);return em.createElement(nb.Provider,{value:ek},em.createElement(nO,{forcedTheme:ee,disableTransitionOnChange:en,enableSystem:er,enableColorScheme:ei,storageKey:eo,themes:es,defaultTheme:eu,attribute:ec,value:ed,children:ef,attrs:ey,nonce:eh}),ef)},nO=(0,em.memo)(({forcedTheme:ee,storageKey:en,attribute:er,enableSystem:ei,enableColorScheme:eo,defaultTheme:es,value:eu,attrs:ec,nonce:ed})=>{let ef="system"===es,eh="class"===er?`var d=document.documentElement,c=d.classList;c.remove(${ec.map(ee=>`'${ee}'`).join(",")});`:`var d=document.documentElement,n='${er}',s='setAttribute';`,ep=eo?ng.includes(es)&&es?`if(e==='light'||e==='dark'||!e)d.style.colorScheme=e||'${es}'`:"if(e==='light'||e==='dark')d.style.colorScheme=e":"",ex=(ee,en=!1,ei=!0)=>{let es=eu?eu[ee]:ee,ec=en?ee+"|| ''":`'${es}'`,ed="";return eo&&ei&&!en&&ng.includes(ee)&&(ed+=`d.style.colorScheme = '${ee}';`),"class"===er?ed+=en||es?`c.add(${ec})`:"null":es&&(ed+=`d[s](n,${ec})`),ed},eg=ee?`!function(){${eh}${ex(ee)}}()`:ei?`!function(){try{${eh}var e=localStorage.getItem('${en}');if('system'===e||(!e&&${ef})){var t='${nv}',m=window.matchMedia(t);if(m.media!==t||m.matches){${ex("dark")}}else{${ex("light")}}}else if(e){${eu?`var x=${JSON.stringify(eu)};`:""}${ex(eu?"x[e]":"e",!0)}}${ef?"":"else{"+ex(es,!1,!1)+"}"}${ep}}catch(e){}}()`:`!function(){try{${eh}var e=localStorage.getItem('${en}');if(e){${eu?`var x=${JSON.stringify(eu)};`:""}${ex(eu?"x[e]":"e",!0)}}else{${ex(es,!1,!1)};}${ep}}catch(t){}}();`;return em.createElement("script",{nonce:ed,dangerouslySetInnerHTML:{__html:eg}})},()=>!0),nC=(ee,en)=>{let er;if(!ny){try{er=localStorage.getItem(ee)||void 0}catch(ee){}return er||en}},nT=()=>{let ee=document.createElement("style");return ee.appendChild(document.createTextNode("*{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}")),document.head.appendChild(ee),()=>{window.getComputedStyle(document.body),setTimeout(()=>{document.head.removeChild(ee)},1)}},nS=ee=>(ee||(ee=window.matchMedia(nv)),ee.matches?"dark":"light");var nN=er(1835),nR={breadcrumb:!0,collapsed:!1,footer:!0,layout:"default",navbar:!0,pagination:!0,sidebar:!0,timestamp:!0,toc:!0,typesetting:"default"},nI=nf.strictObject({breadcrumb:nf.boolean(),collapsed:nf.boolean(),footer:nf.boolean(),layout:nf.enum(["default","full","raw"]),navbar:nf.boolean(),pagination:nf.boolean(),sidebar:nf.boolean(),timestamp:nf.boolean(),toc:nf.boolean(),typesetting:nf.enum(["default","article"])}),nA=nf.enum(["normal","hidden","children"]),nL=nf.string(),nP=nf.strictObject({href:nf.string(),newWindow:nf.boolean(),title:nL}),nM=nf.strictObject({display:nA.optional(),items:nf.record(nP.partial({href:!0,newWindow:!0})),title:nL,type:nf.literal("menu")}),nZ=nf.strictObject({title:nL,type:nf.literal("separator")}),nD=nP.extend({display:nA,theme:nI,title:nL,type:nf.enum(["page","doc"])}).deepPartial();function nF(ee={},en){"string"==typeof ee&&(ee={title:ee});let er=Object.assign({},en.theme,ee.theme);return Object.assign({},en,ee,{theme:er})}function n$(ee){for(let en of ee){if(en.route)return en.route;if(en.children){let ee=n$(en.children);if(ee)return ee}}}function nz({list:ee,locale:en,defaultLocale:er,route:ei,docsRoot:eo="",underCurrentDocsRoot:es=!1,pageThemeContext:eu=nR}){let ec,ed;for(let er of ee)if("Meta"===er.kind){if(er.locale===en){ec=er.data;break}ec||(ec=er.data)}let ef=ec||{},eh=Object.keys(ef);for(let ee of eh)"string"==typeof ef[ee]&&(ef[ee]={title:ef[ee]});let ep=[],em=[],ex=[],eg=[],ev=[],ey=0,eb=eu,ew=[],e_=-1,ek=ef["*"]||{};delete ek.title,delete ek.href;let ej=ee.filter(ee=>"Meta"!==ee.kind&&!ee.name.startsWith("_")&&(!("locale"in ee)||!ee.locale||[en,er].includes(ee.locale))).sort((ee,en)=>{let er=eh.indexOf(ee.name),ei=eh.indexOf(en.name);return -1===er&&-1===ei?ee.name{let en;let er=[],ei=eh.indexOf(ee.name);if(-1!==ei){for(let ee=e_+1;ee({...ec,type:eE,...eS&&{title:eS},...e_&&{display:e_},...eT&&{children:[]}}),eR=eN(),eI=eN(),eA=eN();if(eI.isUnderCurrentDocsTree=eC,"separator"===eE&&(eR.isUnderCurrentDocsTree=eC),ec.route===ei)switch(ew=[eR],ed=eE,eb={...eb,...eO},eE){case"page":case"menu":ey=ev.length;break;case"doc":ey=eg.length}if(!("hidden"===e_&&"Folder"!==eR.kind||nN.hV.has(ec.route))){if(eT){if(void 0!==eT.activeIndex&&void 0!==eT.activeType){switch(eb=eT.activeThemeContext,ed=eT.activeType,ew=[eR,...eT.activePath],ed){case"page":case"menu":ey=ev.length+eT.activeIndex;break;case"doc":ey=eg.length+eT.activeIndex}ec.withIndexPage&&"doc"===eE&&ey++}switch(eE){case"page":case"menu":eA.children.push(...eT.directories),ex.push(...eT.docsDirectories),eT.flatDirectories.length?(eA.firstChildRoute=n$(eT.flatDirectories),ev.push(eA)):eA.withIndexPage&&ev.push(eA);break;case"doc":Array.isArray(eI.children)&&eI.children.push(...eT.docsDirectories),eR.withIndexPage&&"children"!==e_&&eg.push(eI)}em.push(...eT.flatDirectories),eg.push(...eT.flatDocsDirectories),Array.isArray(eR.children)&&eR.children.push(...eT.directories)}else switch(em.push(eR),eE){case"page":case"menu":ev.push(eA);break;case"doc":eg.push(eI)}switch("doc"===eE&&"children"===e_?eI.children&&(ep.push(...eI.children),ex.push(...eI.children)):ep.push(eR),eE){case"page":case"menu":ex.push(eA);break;case"doc":"children"!==e_&&ex.push(eI);break;case"separator":ex.push(eR)}}}return{activeType:ed,activeIndex:ey,activeThemeContext:eb,activePath:ew,directories:ep,flatDirectories:em,docsDirectories:ex,flatDocsDirectories:eg,topLevelNavbarItems:ev}}nf.string().or(nM).or(nZ).or(nD);var nH=er(4728),nW=er.n(nH),nU=er(209),nG=er(9752),nV=er.n(nG),nB=er(6194),nq=er(4288),nK=er(1479),nQ=er(422),nJ=er(3057),nY=er.n(nJ);function nX(){return(nX=Object.assign?Object.assign.bind():function(ee){for(var en=1;en=0||(eo[er]=ee[er]);return eo}var n1=["keyOverride"],n2=["crossOrigin"],n4={templateTitle:"",noindex:!1,nofollow:!1,defaultOpenGraphImageWidth:0,defaultOpenGraphImageHeight:0,defaultOpenGraphVideoWidth:0,defaultOpenGraphVideoHeight:0},n5=function(ee,en,er){void 0===en&&(en=[]);var ei=void 0===er?{}:er,eo=ei.defaultWidth,es=ei.defaultHeight;return en.reduce(function(en,er,ei){return en.push(em.createElement("meta",{key:"og:"+ee+":0"+ei,property:"og:"+ee,content:er.url})),er.alt&&en.push(em.createElement("meta",{key:"og:"+ee+":alt0"+ei,property:"og:"+ee+":alt",content:er.alt})),er.secureUrl&&en.push(em.createElement("meta",{key:"og:"+ee+":secure_url0"+ei,property:"og:"+ee+":secure_url",content:er.secureUrl.toString()})),er.type&&en.push(em.createElement("meta",{key:"og:"+ee+":type0"+ei,property:"og:"+ee+":type",content:er.type.toString()})),er.width?en.push(em.createElement("meta",{key:"og:"+ee+":width0"+ei,property:"og:"+ee+":width",content:er.width.toString()})):eo&&en.push(em.createElement("meta",{key:"og:"+ee+":width0"+ei,property:"og:"+ee+":width",content:eo.toString()})),er.height?en.push(em.createElement("meta",{key:"og:"+ee+":height"+ei,property:"og:"+ee+":height",content:er.height.toString()})):es&&en.push(em.createElement("meta",{key:"og:"+ee+":height"+ei,property:"og:"+ee+":height",content:es.toString()})),en},[])},n3=function(ee){var en,er,ei,eo,es,eu=[];ee.titleTemplate&&(n4.templateTitle=ee.titleTemplate);var ec="";ee.title?(ec=ee.title,n4.templateTitle&&(ec=n4.templateTitle.replace(/%s/g,function(){return ec}))):ee.defaultTitle&&(ec=ee.defaultTitle),ec&&eu.push(em.createElement("title",{key:"title"},ec));var ed=void 0===ee.noindex?n4.noindex||ee.dangerouslySetAllPagesToNoIndex:ee.noindex,ef=void 0===ee.nofollow?n4.nofollow||ee.dangerouslySetAllPagesToNoFollow:ee.nofollow,eh="";if(ee.robotsProps){var ep=ee.robotsProps,ex=ep.nosnippet,eg=ep.maxSnippet,ev=ep.maxImagePreview,ey=ep.maxVideoPreview,eb=ep.noarchive,ew=ep.noimageindex,e_=ep.notranslate,ek=ep.unavailableAfter;eh=(ex?",nosnippet":"")+(eg?",max-snippet:"+eg:"")+(ev?",max-image-preview:"+ev:"")+(eb?",noarchive":"")+(ek?",unavailable_after:"+ek:"")+(ew?",noimageindex":"")+(ey?",max-video-preview:"+ey:"")+(e_?",notranslate":"")}if(ed||ef?(ee.dangerouslySetAllPagesToNoIndex&&(n4.noindex=!0),ee.dangerouslySetAllPagesToNoFollow&&(n4.nofollow=!0),eu.push(em.createElement("meta",{key:"robots",name:"robots",content:(ed?"noindex":"index")+","+(ef?"nofollow":"follow")+eh}))):eu.push(em.createElement("meta",{key:"robots",name:"robots",content:"index,follow"+eh})),ee.description&&eu.push(em.createElement("meta",{key:"description",name:"description",content:ee.description})),ee.themeColor&&eu.push(em.createElement("meta",{key:"theme-color",name:"theme-color",content:ee.themeColor})),ee.mobileAlternate&&eu.push(em.createElement("link",{rel:"alternate",key:"mobileAlternate",media:ee.mobileAlternate.media,href:ee.mobileAlternate.href})),ee.languageAlternates&&ee.languageAlternates.length>0&&ee.languageAlternates.forEach(function(ee){eu.push(em.createElement("link",{rel:"alternate",key:"languageAlternate-"+ee.hrefLang,hrefLang:ee.hrefLang,href:ee.href}))}),ee.twitter&&(ee.twitter.cardType&&eu.push(em.createElement("meta",{key:"twitter:card",name:"twitter:card",content:ee.twitter.cardType})),ee.twitter.site&&eu.push(em.createElement("meta",{key:"twitter:site",name:"twitter:site",content:ee.twitter.site})),ee.twitter.handle&&eu.push(em.createElement("meta",{key:"twitter:creator",name:"twitter:creator",content:ee.twitter.handle}))),ee.facebook&&ee.facebook.appId&&eu.push(em.createElement("meta",{key:"fb:app_id",property:"fb:app_id",content:ee.facebook.appId})),(null!=(en=ee.openGraph)&&en.title||ec)&&eu.push(em.createElement("meta",{key:"og:title",property:"og:title",content:(null==(eo=ee.openGraph)?void 0:eo.title)||ec})),(null!=(er=ee.openGraph)&&er.description||ee.description)&&eu.push(em.createElement("meta",{key:"og:description",property:"og:description",content:(null==(es=ee.openGraph)?void 0:es.description)||ee.description})),ee.openGraph){if((ee.openGraph.url||ee.canonical)&&eu.push(em.createElement("meta",{key:"og:url",property:"og:url",content:ee.openGraph.url||ee.canonical})),ee.openGraph.type){var ej=ee.openGraph.type.toLowerCase();eu.push(em.createElement("meta",{key:"og:type",property:"og:type",content:ej})),"profile"===ej&&ee.openGraph.profile?(ee.openGraph.profile.firstName&&eu.push(em.createElement("meta",{key:"profile:first_name",property:"profile:first_name",content:ee.openGraph.profile.firstName})),ee.openGraph.profile.lastName&&eu.push(em.createElement("meta",{key:"profile:last_name",property:"profile:last_name",content:ee.openGraph.profile.lastName})),ee.openGraph.profile.username&&eu.push(em.createElement("meta",{key:"profile:username",property:"profile:username",content:ee.openGraph.profile.username})),ee.openGraph.profile.gender&&eu.push(em.createElement("meta",{key:"profile:gender",property:"profile:gender",content:ee.openGraph.profile.gender}))):"book"===ej&&ee.openGraph.book?(ee.openGraph.book.authors&&ee.openGraph.book.authors.length&&ee.openGraph.book.authors.forEach(function(ee,en){eu.push(em.createElement("meta",{key:"book:author:0"+en,property:"book:author",content:ee}))}),ee.openGraph.book.isbn&&eu.push(em.createElement("meta",{key:"book:isbn",property:"book:isbn",content:ee.openGraph.book.isbn})),ee.openGraph.book.releaseDate&&eu.push(em.createElement("meta",{key:"book:release_date",property:"book:release_date",content:ee.openGraph.book.releaseDate})),ee.openGraph.book.tags&&ee.openGraph.book.tags.length&&ee.openGraph.book.tags.forEach(function(ee,en){eu.push(em.createElement("meta",{key:"book:tag:0"+en,property:"book:tag",content:ee}))})):"article"===ej&&ee.openGraph.article?(ee.openGraph.article.publishedTime&&eu.push(em.createElement("meta",{key:"article:published_time",property:"article:published_time",content:ee.openGraph.article.publishedTime})),ee.openGraph.article.modifiedTime&&eu.push(em.createElement("meta",{key:"article:modified_time",property:"article:modified_time",content:ee.openGraph.article.modifiedTime})),ee.openGraph.article.expirationTime&&eu.push(em.createElement("meta",{key:"article:expiration_time",property:"article:expiration_time",content:ee.openGraph.article.expirationTime})),ee.openGraph.article.authors&&ee.openGraph.article.authors.length&&ee.openGraph.article.authors.forEach(function(ee,en){eu.push(em.createElement("meta",{key:"article:author:0"+en,property:"article:author",content:ee}))}),ee.openGraph.article.section&&eu.push(em.createElement("meta",{key:"article:section",property:"article:section",content:ee.openGraph.article.section})),ee.openGraph.article.tags&&ee.openGraph.article.tags.length&&ee.openGraph.article.tags.forEach(function(ee,en){eu.push(em.createElement("meta",{key:"article:tag:0"+en,property:"article:tag",content:ee}))})):("video.movie"===ej||"video.episode"===ej||"video.tv_show"===ej||"video.other"===ej)&&ee.openGraph.video&&(ee.openGraph.video.actors&&ee.openGraph.video.actors.length&&ee.openGraph.video.actors.forEach(function(ee,en){ee.profile&&eu.push(em.createElement("meta",{key:"video:actor:0"+en,property:"video:actor",content:ee.profile})),ee.role&&eu.push(em.createElement("meta",{key:"video:actor:role:0"+en,property:"video:actor:role",content:ee.role}))}),ee.openGraph.video.directors&&ee.openGraph.video.directors.length&&ee.openGraph.video.directors.forEach(function(ee,en){eu.push(em.createElement("meta",{key:"video:director:0"+en,property:"video:director",content:ee}))}),ee.openGraph.video.writers&&ee.openGraph.video.writers.length&&ee.openGraph.video.writers.forEach(function(ee,en){eu.push(em.createElement("meta",{key:"video:writer:0"+en,property:"video:writer",content:ee}))}),ee.openGraph.video.duration&&eu.push(em.createElement("meta",{key:"video:duration",property:"video:duration",content:ee.openGraph.video.duration.toString()})),ee.openGraph.video.releaseDate&&eu.push(em.createElement("meta",{key:"video:release_date",property:"video:release_date",content:ee.openGraph.video.releaseDate})),ee.openGraph.video.tags&&ee.openGraph.video.tags.length&&ee.openGraph.video.tags.forEach(function(ee,en){eu.push(em.createElement("meta",{key:"video:tag:0"+en,property:"video:tag",content:ee}))}),ee.openGraph.video.series&&eu.push(em.createElement("meta",{key:"video:series",property:"video:series",content:ee.openGraph.video.series})))}ee.defaultOpenGraphImageWidth&&(n4.defaultOpenGraphImageWidth=ee.defaultOpenGraphImageWidth),ee.defaultOpenGraphImageHeight&&(n4.defaultOpenGraphImageHeight=ee.defaultOpenGraphImageHeight),ee.openGraph.images&&ee.openGraph.images.length&&eu.push.apply(eu,n5("image",ee.openGraph.images,{defaultWidth:n4.defaultOpenGraphImageWidth,defaultHeight:n4.defaultOpenGraphImageHeight})),ee.defaultOpenGraphVideoWidth&&(n4.defaultOpenGraphVideoWidth=ee.defaultOpenGraphVideoWidth),ee.defaultOpenGraphVideoHeight&&(n4.defaultOpenGraphVideoHeight=ee.defaultOpenGraphVideoHeight),ee.openGraph.videos&&ee.openGraph.videos.length&&eu.push.apply(eu,n5("video",ee.openGraph.videos,{defaultWidth:n4.defaultOpenGraphVideoWidth,defaultHeight:n4.defaultOpenGraphVideoHeight})),ee.openGraph.audio&&eu.push.apply(eu,n5("audio",ee.openGraph.audio)),ee.openGraph.locale&&eu.push(em.createElement("meta",{key:"og:locale",property:"og:locale",content:ee.openGraph.locale})),(ee.openGraph.siteName||ee.openGraph.site_name)&&eu.push(em.createElement("meta",{key:"og:site_name",property:"og:site_name",content:ee.openGraph.siteName||ee.openGraph.site_name}))}return ee.canonical&&eu.push(em.createElement("link",{rel:"canonical",href:ee.canonical,key:"canonical"})),ee.additionalMetaTags&&ee.additionalMetaTags.length>0&&ee.additionalMetaTags.forEach(function(ee){var en,er,ei=ee.keyOverride,eo=n0(ee,n1);eu.push(em.createElement("meta",nX({key:"meta:"+(null!=(en=null!=(er=null!=ei?ei:eo.name)?er:eo.property)?en:eo.httpEquiv)},eo)))}),null!=(ei=ee.additionalLinkTags)&&ei.length&&ee.additionalLinkTags.forEach(function(ee){var en,er=ee.crossOrigin,ei=n0(ee,n2),eo="anonymous"===er||"use-credentials"===er||""===er?er:void 0;eu.push(em.createElement("link",nX({key:"link"+(null!=(en=ei.keyOverride)?en:ei.href)+ei.rel},ei,{crossOrigin:eo})))}),eu},n9=function(ee){return em.createElement(nY(),null,n3(ee))},n7=function(ee){var en=ee.title,er=ee.themeColor,ei=ee.noindex,eo=ee.nofollow,es=ee.robotsProps,eu=ee.description,ec=ee.canonical,ed=ee.openGraph,ef=ee.facebook,eh=ee.twitter,ep=ee.additionalMetaTags,ex=ee.titleTemplate,eg=ee.defaultTitle,ev=ee.mobileAlternate,ey=ee.languageAlternates,eb=ee.additionalLinkTags;return em.createElement(em.Fragment,null,em.createElement(n9,{title:en,themeColor:er,noindex:ei,nofollow:eo,robotsProps:es,description:eu,canonical:ec,facebook:ef,openGraph:ed,additionalMetaTags:ep,twitter:eh,titleTemplate:ex,defaultTitle:eg,mobileAlternate:ev,languageAlternates:ey,additionalLinkTags:eb}))};RegExp("["+Object.keys(Object.freeze({"&":"&","<":"<",">":">",'"':""","'":"'"})).join("")+"]","g");var n6=er(3015),n8=er(1806),rt=er(6050),rn=er(3275),rr=er(2138),ri=er(851),ro=er(9772),rs=er(310),rl=er(531),ru=er(8913),rc=er(1720),rd=er(537),rf=er(9322);function rh({container:ee,accept:en,walk:er,enabled:ei=!0}){let eo=(0,em.useRef)(en),es=(0,em.useRef)(er);(0,em.useEffect)(()=>{eo.current=en,es.current=er},[en,er]),(0,rr.e)(()=>{if(!ee||!ei)return;let en=(0,rf.r)(ee);if(!en)return;let er=eo.current,eu=es.current,ec=Object.assign(ee=>er(ee),{acceptNode:er}),ed=en.createTreeWalker(ee,NodeFilter.SHOW_ELEMENT,ec,!1);for(;ed.nextNode();)eu(ed.currentNode)},[ee,ei,eo,es])}var rp=er(221),rm=er(5198);function rx(...ee){return(0,em.useMemo)(()=>(0,rf.r)(...ee),[...ee])}var rg=er(8902),rv=er(5694),ry=er(7840),rb=((ei=rb||{})[ei.Open=0]="Open",ei[ei.Closed=1]="Closed",ei),rw=((eo=rw||{})[eo.Pointer=0]="Pointer",eo[eo.Other=1]="Other",eo),r_=((es=r_||{})[es.OpenMenu=0]="OpenMenu",es[es.CloseMenu=1]="CloseMenu",es[es.GoToItem=2]="GoToItem",es[es.Search=3]="Search",es[es.ClearSearch=4]="ClearSearch",es[es.RegisterItem=5]="RegisterItem",es[es.UnregisterItem=6]="UnregisterItem",es);function rk(ee,en=ee=>ee){let er=null!==ee.activeItemIndex?ee.items[ee.activeItemIndex]:null,ei=(0,rc.z2)(en(ee.items.slice()),ee=>ee.dataRef.current.domRef.current),eo=er?ei.indexOf(er):null;return -1===eo&&(eo=null),{items:ei,activeItemIndex:eo}}let rj={1:ee=>1===ee.menuState?ee:{...ee,activeItemIndex:null,menuState:1},0:ee=>0===ee.menuState?ee:{...ee,__demoMode:!1,menuState:0},2:(ee,en)=>{var er;let ei=rk(ee),eo=(0,rl.d)(en,{resolveItems:()=>ei.items,resolveActiveIndex:()=>ei.activeItemIndex,resolveId:ee=>ee.id,resolveDisabled:ee=>ee.dataRef.current.disabled});return{...ee,...ei,searchQuery:"",activeItemIndex:eo,activationTrigger:null!=(er=en.trigger)?er:1}},3:(ee,en)=>{let er=""!==ee.searchQuery?0:1,ei=ee.searchQuery+en.value.toLowerCase(),eo=(null!==ee.activeItemIndex?ee.items.slice(ee.activeItemIndex+er).concat(ee.items.slice(0,ee.activeItemIndex+er)):ee.items).find(ee=>{var en;return(null==(en=ee.dataRef.current.textValue)?void 0:en.startsWith(ei))&&!ee.dataRef.current.disabled}),es=eo?ee.items.indexOf(eo):-1;return -1===es||es===ee.activeItemIndex?{...ee,searchQuery:ei}:{...ee,searchQuery:ei,activeItemIndex:es,activationTrigger:1}},4:ee=>""===ee.searchQuery?ee:{...ee,searchQuery:"",searchActiveItemIndex:null},5:(ee,en)=>{let er=rk(ee,ee=>[...ee,{id:en.id,dataRef:en.dataRef}]);return{...ee,...er}},6:(ee,en)=>{let er=rk(ee,ee=>{let er=ee.findIndex(ee=>ee.id===en.id);return -1!==er&&ee.splice(er,1),ee});return{...ee,...er,activationTrigger:1}}},rE=(0,em.createContext)(null);function rO(ee){let en=(0,em.useContext)(rE);if(null===en){let en=Error(`<${ee} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(en,rO),en}return en}function rC(ee,en){return(0,n6.E)(en.type,rj,ee,en)}rE.displayName="MenuContext";let rT=em.Fragment;function rS(ee,en){let{__demoMode:er=!1,...ei}=ee,eo=(0,em.useReducer)(rC,{__demoMode:er,menuState:er?0:1,buttonRef:(0,em.createRef)(),itemsRef:(0,em.createRef)(),items:[],searchQuery:"",activeItemIndex:null,activationTrigger:1}),[{menuState:es,itemsRef:eu,buttonRef:ec},ed]=eo,ef=(0,ri.T)(en);(0,rd.O)([ec,eu],(ee,en)=>{var er;ed({type:1}),(0,rc.sP)(en,rc.tJ.Loose)||(ee.preventDefault(),null==(er=ec.current)||er.focus())},0===es);let eh=(0,rg.z)(()=>{ed({type:1})}),ep=(0,em.useMemo)(()=>({open:0===es,close:eh}),[es,eh]),ex={ref:ef};return em.createElement(rE.Provider,{value:eo},em.createElement(rp.up,{value:(0,n6.E)(es,{0:rp.ZM.Open,1:rp.ZM.Closed})},(0,n8.sY)({ourProps:ex,theirProps:ei,slot:ep,defaultTag:rT,name:"Menu"})))}let rN="button";function rR(ee,en){var er;let ei=(0,ro.M)(),{id:eo=`headlessui-menu-button-${ei}`,...es}=ee,[eu,ec]=rO("Menu.Button"),ed=(0,ri.T)(eu.buttonRef,en),ef=(0,rn.G)(),eh=(0,rg.z)(ee=>{switch(ee.key){case rs.R.Space:case rs.R.Enter:case rs.R.ArrowDown:ee.preventDefault(),ee.stopPropagation(),ec({type:0}),ef.nextFrame(()=>ec({type:2,focus:rl.T.First}));break;case rs.R.ArrowUp:ee.preventDefault(),ee.stopPropagation(),ec({type:0}),ef.nextFrame(()=>ec({type:2,focus:rl.T.Last}))}}),ep=(0,rg.z)(ee=>{ee.key===rs.R.Space&&ee.preventDefault()}),ex=(0,rg.z)(en=>{if((0,ru.P)(en.currentTarget))return en.preventDefault();ee.disabled||(0===eu.menuState?(ec({type:1}),ef.nextFrame(()=>{var ee;return null==(ee=eu.buttonRef.current)?void 0:ee.focus({preventScroll:!0})})):(en.preventDefault(),ec({type:0})))}),eg=(0,em.useMemo)(()=>({open:0===eu.menuState}),[eu]),ev={ref:ed,id:eo,type:(0,rm.f)(ee,eu.buttonRef),"aria-haspopup":"menu","aria-controls":null==(er=eu.itemsRef.current)?void 0:er.id,"aria-expanded":ee.disabled?void 0:0===eu.menuState,onKeyDown:eh,onKeyUp:ep,onClick:ex};return(0,n8.sY)({ourProps:ev,theirProps:es,slot:eg,defaultTag:rN,name:"Menu.Button"})}let rI="div",rA=n8.AN.RenderStrategy|n8.AN.Static;function rL(ee,en){var er,ei;let eo=(0,ro.M)(),{id:es=`headlessui-menu-items-${eo}`,...eu}=ee,[ec,ed]=rO("Menu.Items"),ef=(0,ri.T)(ec.itemsRef,en),eh=rx(ec.itemsRef),ep=(0,rn.G)(),ex=(0,rp.oJ)(),eg=null!==ex?(ex&rp.ZM.Open)===rp.ZM.Open:0===ec.menuState;(0,em.useEffect)(()=>{let ee=ec.itemsRef.current;ee&&0===ec.menuState&&ee!==(null==eh?void 0:eh.activeElement)&&ee.focus({preventScroll:!0})},[ec.menuState,ec.itemsRef,eh]),rh({container:ec.itemsRef.current,enabled:0===ec.menuState,accept:ee=>"menuitem"===ee.getAttribute("role")?NodeFilter.FILTER_REJECT:ee.hasAttribute("role")?NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT,walk(ee){ee.setAttribute("role","none")}});let ev=(0,rg.z)(ee=>{var en,er;switch(ep.dispose(),ee.key){case rs.R.Space:if(""!==ec.searchQuery)return ee.preventDefault(),ee.stopPropagation(),ed({type:3,value:ee.key});case rs.R.Enter:if(ee.preventDefault(),ee.stopPropagation(),ed({type:1}),null!==ec.activeItemIndex){let{dataRef:ee}=ec.items[ec.activeItemIndex];null==(er=null==(en=ee.current)?void 0:en.domRef.current)||er.click()}(0,rc.wI)(ec.buttonRef.current);break;case rs.R.ArrowDown:return ee.preventDefault(),ee.stopPropagation(),ed({type:2,focus:rl.T.Next});case rs.R.ArrowUp:return ee.preventDefault(),ee.stopPropagation(),ed({type:2,focus:rl.T.Previous});case rs.R.Home:case rs.R.PageUp:return ee.preventDefault(),ee.stopPropagation(),ed({type:2,focus:rl.T.First});case rs.R.End:case rs.R.PageDown:return ee.preventDefault(),ee.stopPropagation(),ed({type:2,focus:rl.T.Last});case rs.R.Escape:ee.preventDefault(),ee.stopPropagation(),ed({type:1}),(0,rt.k)().nextFrame(()=>{var ee;return null==(ee=ec.buttonRef.current)?void 0:ee.focus({preventScroll:!0})});break;case rs.R.Tab:ee.preventDefault(),ee.stopPropagation(),ed({type:1}),(0,rt.k)().nextFrame(()=>{(0,rc.EO)(ec.buttonRef.current,ee.shiftKey?rc.TO.Previous:rc.TO.Next)});break;default:1===ee.key.length&&(ed({type:3,value:ee.key}),ep.setTimeout(()=>ed({type:4}),350))}}),ey=(0,rg.z)(ee=>{ee.key===rs.R.Space&&ee.preventDefault()}),eb=(0,em.useMemo)(()=>({open:0===ec.menuState}),[ec]),ew={"aria-activedescendant":null===ec.activeItemIndex||null==(er=ec.items[ec.activeItemIndex])?void 0:er.id,"aria-labelledby":null==(ei=ec.buttonRef.current)?void 0:ei.id,id:es,onKeyDown:ev,onKeyUp:ey,role:"menu",tabIndex:0,ref:ef};return(0,n8.sY)({ourProps:ew,theirProps:eu,slot:eb,defaultTag:rI,features:rA,visible:eg,name:"Menu.Items"})}let rP=em.Fragment;function rM(ee,en){let er=(0,ro.M)(),{id:ei=`headlessui-menu-item-${er}`,disabled:eo=!1,...es}=ee,[eu,ec]=rO("Menu.Item"),ed=null!==eu.activeItemIndex&&eu.items[eu.activeItemIndex].id===ei,ef=(0,em.useRef)(null),eh=(0,ri.T)(en,ef);(0,rr.e)(()=>{if(eu.__demoMode||0!==eu.menuState||!ed||0===eu.activationTrigger)return;let ee=(0,rt.k)();return ee.requestAnimationFrame(()=>{var ee,en;null==(en=null==(ee=ef.current)?void 0:ee.scrollIntoView)||en.call(ee,{block:"nearest"})}),ee.dispose},[eu.__demoMode,ef,ed,eu.menuState,eu.activationTrigger,eu.activeItemIndex]);let ep=(0,ry.x)(ef),ex=(0,em.useRef)({disabled:eo,domRef:ef,get textValue(){return ep()}});(0,rr.e)(()=>{ex.current.disabled=eo},[ex,eo]),(0,rr.e)(()=>(ec({type:5,id:ei,dataRef:ex}),()=>ec({type:6,id:ei})),[ex,ei]);let eg=(0,rg.z)(()=>{ec({type:1})}),ev=(0,rg.z)(ee=>{if(eo)return ee.preventDefault();ec({type:1}),(0,rc.wI)(eu.buttonRef.current)}),ey=(0,rg.z)(()=>{if(eo)return ec({type:2,focus:rl.T.Nothing});ec({type:2,focus:rl.T.Specific,id:ei})}),eb=(0,rv.g)(),ew=(0,rg.z)(ee=>eb.update(ee)),e_=(0,rg.z)(ee=>{eb.wasMoved(ee)&&(eo||ed||ec({type:2,focus:rl.T.Specific,id:ei,trigger:0}))}),ek=(0,rg.z)(ee=>{eb.wasMoved(ee)&&(eo||ed&&ec({type:2,focus:rl.T.Nothing}))}),ej=(0,em.useMemo)(()=>({active:ed,disabled:eo,close:eg}),[ed,eo,eg]);return(0,n8.sY)({ourProps:{id:ei,ref:eh,role:"menuitem",tabIndex:!0===eo?void 0:-1,"aria-disabled":!0===eo||void 0,disabled:void 0,onClick:ev,onFocus:ey,onPointerEnter:ew,onMouseEnter:ew,onPointerMove:e_,onMouseMove:e_,onPointerLeave:ek,onMouseLeave:ek},theirProps:es,slot:ej,defaultTag:rP,name:"Menu.Item"})}let rZ=Object.assign((0,n8.yV)(rS),{Button:(0,n8.yV)(rR),Items:(0,n8.yV)(rL),Item:(0,n8.yV)(rM)}),rD=ee=>"object"==typeof ee&&null!=ee&&1===ee.nodeType,rF=(ee,en)=>(!en||"hidden"!==ee)&&"visible"!==ee&&"clip"!==ee,r$=(ee,en)=>{if(ee.clientHeight{let en=(ee=>{if(!ee.ownerDocument||!ee.ownerDocument.defaultView)return null;try{return ee.ownerDocument.defaultView.frameElement}catch(ee){return null}})(ee);return!!en&&(en.clientHeightesen||es>ee&&eu=en&&ec>=er?es-ee-ei:eu>en&&ecer?eu-en+eo:0,rH=ee=>{let en=ee.parentElement;return null==en?ee.getRootNode().host||null:en},rW=(ee,en)=>{var er,ei,eo,es;if("undefined"==typeof document)return[];let{scrollMode:eu,block:ec,inline:ed,boundary:ef,skipOverflowHiddenElements:eh}=en,ep="function"==typeof ef?ef:ee=>ee!==ef;if(!rD(ee))throw TypeError("Invalid target");let em=document.scrollingElement||document.documentElement,ex=[],eg=ee;for(;rD(eg)&&ep(eg);){if((eg=rH(eg))===em){ex.push(eg);break}null!=eg&&eg===document.body&&r$(eg)&&!r$(document.documentElement)||null!=eg&&r$(eg,eh)&&ex.push(eg)}let ev=null!=(ei=null==(er=window.visualViewport)?void 0:er.width)?ei:innerWidth,ey=null!=(es=null==(eo=window.visualViewport)?void 0:eo.height)?es:innerHeight,{scrollX:eb,scrollY:ew}=window,{height:e_,width:ek,top:ej,right:eE,bottom:eO,left:eC}=ee.getBoundingClientRect(),eT="start"===ec||"nearest"===ec?ej:"end"===ec?eO:ej+e_/2,eS="center"===ed?eC+ek/2:"end"===ed?eE:eC,eN=[];for(let ee=0;ee=0&&eC>=0&&eO<=ey&&eE<=ev&&ej>=eo&&eO<=ef&&eC>=eh&&eE<=es)break;let ep=getComputedStyle(en),eg=parseInt(ep.borderLeftWidth,10),eR=parseInt(ep.borderTopWidth,10),eI=parseInt(ep.borderRightWidth,10),eA=parseInt(ep.borderBottomWidth,10),eL=0,eP=0,eM="offsetWidth"in en?en.offsetWidth-en.clientWidth-eg-eI:0,eZ="offsetHeight"in en?en.offsetHeight-en.clientHeight-eR-eA:0,eD="offsetWidth"in en?0===en.offsetWidth?0:ei/en.offsetWidth:0,eF="offsetHeight"in en?0===en.offsetHeight?0:er/en.offsetHeight:0;if(em===en)eL="start"===ec?eT:"end"===ec?eT-ey:"nearest"===ec?rz(ew,ew+ey,ey,eR,eA,ew+eT,ew+eT+e_,e_):eT-ey/2,eP="start"===ed?eS:"center"===ed?eS-ev/2:"end"===ed?eS-ev:rz(eb,eb+ev,ev,eg,eI,eb+eS,eb+eS+ek,ek),eL=Math.max(0,eL+ew),eP=Math.max(0,eP+eb);else{eL="start"===ec?eT-eo-eR:"end"===ec?eT-ef+eA+eZ:"nearest"===ec?rz(eo,ef,er,eR,eA+eZ,eT,eT+e_,e_):eT-(eo+er/2)+eZ/2,eP="start"===ed?eS-eh-eg:"center"===ed?eS-(eh+ei/2)+eM/2:"end"===ed?eS-es+eI+eM:rz(eh,es,ei,eg,eI+eM,eS,eS+ek,ek);let{scrollLeft:ee,scrollTop:eu}=en;eL=Math.max(0,Math.min(eu+eL/eF,en.scrollHeight-er/eF+eZ)),eP=Math.max(0,Math.min(ee+eP/eD,en.scrollWidth-ei/eD+eM)),eT+=eu-eL,eS+=ee-eP}eN.push({el:en,top:eL,left:eP})}return eN},rU=ee=>{var ei;return!1===ee?{block:"end",inline:"nearest"}:(ei=ee,ei===Object(ei)&&0!==Object.keys(ei).length)?ee:{block:"start",inline:"nearest"}};function rG(ee,en){var ei;if(!ee.isConnected||!(ee=>{let en=ee;for(;en&&en.parentNode;){if(en.parentNode===document)return!0;en=en.parentNode instanceof ShadowRoot?en.parentNode.host:en.parentNode}return!1})(ee))return;if("object"==typeof(ei=en)&&"function"==typeof ei.behavior)return en.behavior(rW(ee,en));let er="boolean"==typeof en||null==en?void 0:en.behavior;for(let{el:ei,top:eo,left:es}of rW(ee,rU(en)))ei.scroll({top:eo,left:es,behavior:er})}function rV(){return(rV=Object.assign?Object.assign.bind():function(ee){for(var en=1;en=(void 0===ef?eu:ef)&&ee.push(rV({},ec,{item:ei,index:es})),ee}}function rY(ee,en,er,ei){if(!en){var eo=ee;return{rankedValue:eo,rank:rX(eo,er,ei),keyIndex:-1,keyThreshold:ei.threshold}}return r9(ee,en).reduce(function(ee,en,eo){var es=ee.rank,eu=ee.rankedValue,ec=ee.keyIndex,ed=ee.keyThreshold,ef=en.itemValue,eh=en.attributes,ep=rX(ef,er,ei),em=eu,ex=eh.minRanking,eg=eh.maxRanking,ev=eh.threshold;return ep=rK.MATCHES?ep=ex:ep>eg&&(ep=eg),ep>es&&(es=ep,ec=eo,ed=ev,em=ef),{rankedValue:em,rank:es,keyIndex:ec,keyThreshold:ed}},{rankedValue:ee,rank:rK.NO_MATCH,keyIndex:-1,keyThreshold:ei.threshold})}function rX(ee,en,er){return(ee=r4(ee,er),(en=r4(en,er)).length>ee.length)?rK.NO_MATCH:ee===en?rK.CASE_SENSITIVE_EQUAL:(ee=ee.toLowerCase())===(en=en.toLowerCase())?rK.EQUAL:ee.startsWith(en)?rK.STARTS_WITH:ee.includes(" "+en)?rK.WORD_STARTS_WITH:ee.includes(en)?rK.CONTAINS:1===en.length?rK.NO_MATCH:r0(ee).includes(en)?rK.ACRONYM:r1(ee,en)}function r0(ee){var en="";return ee.split(" ").forEach(function(ee){ee.split("-").forEach(function(ee){en+=ee.substr(0,1)})}),en}function r1(ee,en){var er=0,ei=0;function eo(ee,en,ei){for(var eo=ei,es=en.length;eo-1))return rK.NO_MATCH;return es(ei-eu)}function r2(ee,en,er){var ei=-1,eo=1,es=ee.rank,eu=ee.keyIndex,ec=en.rank,ed=en.keyIndex;return es!==ec?es>ec?ei:eo:eu===ed?er(ee,en):eu(0,nx.jsx)("button",{className:(0,ex.Z)("nextra-button nx-transition-all active:nx-opacity-50","nx-bg-primary-700/5 nx-border nx-border-black/5 nx-text-gray-600 hover:nx-text-gray-900 nx-rounded-md nx-p-1.5","dark:nx-bg-primary-300/10 dark:nx-border-white/10 dark:nx-text-gray-400 dark:hover:nx-text-gray-50",en),...er,children:ee});(0,nx.jsx)(ey.AV,{className:"nx-mt-1"}),(0,ex.Z)("nx-border-orange-100 nx-bg-orange-50 nx-text-orange-800 dark:nx-border-orange-400/30 dark:nx-bg-orange-400/20 dark:nx-text-orange-300"),(0,ex.Z)("nx-border-red-200 nx-bg-red-100 nx-text-red-900 dark:nx-border-red-200/30 dark:nx-bg-red-900/30 dark:nx-text-red-200"),(0,ex.Z)("nx-border-blue-200 nx-bg-blue-100 nx-text-blue-900 dark:nx-border-blue-200/30 dark:nx-bg-blue-900/30 dark:nx-text-blue-200"),(0,ex.Z)("nx-border-yellow-100 nx-bg-yellow-50 nx-text-yellow-900 dark:nx-border-yellow-200/30 dark:nx-bg-yellow-700/30 dark:nx-text-yellow-200");var ir=({getValue:ee,...en})=>{let[er,ei]=(0,em.useState)(!1);(0,em.useEffect)(()=>{if(!er)return;let ee=setTimeout(()=>{ei(!1)},2e3);return()=>{clearTimeout(ee)}},[er]);let eo=(0,em.useCallback)(async()=>{ei(!0),navigator?.clipboard||console.error("Access to clipboard rejected!");try{await navigator.clipboard.writeText(ee())}catch{console.error("Failed to copy!")}},[ee]),es=er?ey.nQ:ey.TI;return(0,nx.jsx)(r8,{onClick:eo,title:"Copy code",tabIndex:0,...en,children:(0,nx.jsx)(es,{className:"nextra-copy-icon nx-pointer-events-none nx-h-4 nx-w-4"})})},ii=({children:ee,className:en,...er})=>{let ei="data-line-numbers"in er;return(0,nx.jsx)("code",{className:(0,ex.Z)("nx-border-black nx-border-opacity-[0.04] nx-bg-opacity-[0.03] nx-bg-black nx-break-words nx-rounded-md nx-border nx-py-0.5 nx-px-[.25em] nx-text-[.9em]","dark:nx-border-white/10 dark:nx-bg-white/10",ei&&"[counter-reset:line]",en),dir:"ltr",...er,children:ee})},io=({children:ee,className:en,hasCopyCode:er,filename:ei,...eo})=>{let es=(0,em.useRef)(null),eu=(0,em.useCallback)(()=>{let ee=document.documentElement.dataset,en="nextraWordWrap"in ee;en?delete ee.nextraWordWrap:ee.nextraWordWrap=""},[]);return(0,nx.jsxs)("div",{className:"nextra-code-block nx-relative nx-mt-6 first:nx-mt-0",children:[ei&&(0,nx.jsx)("div",{className:"nx-absolute nx-top-0 nx-z-[1] nx-w-full nx-truncate nx-rounded-t-xl nx-bg-primary-700/5 nx-py-2 nx-px-4 nx-text-xs nx-text-gray-700 dark:nx-bg-primary-300/10 dark:nx-text-gray-200",children:ei}),(0,nx.jsx)("pre",{className:(0,ex.Z)("nx-bg-primary-700/5 nx-mb-4 nx-overflow-x-auto nx-rounded-xl nx-font-medium nx-subpixel-antialiased dark:nx-bg-primary-300/10 nx-text-[.9em]","contrast-more:nx-border contrast-more:nx-border-primary-900/20 contrast-more:nx-contrast-150 contrast-more:dark:nx-border-primary-100/40",ei?"nx-pt-12 nx-pb-4":"nx-py-4",en),ref:es,...eo,children:ee}),(0,nx.jsxs)("div",{className:(0,ex.Z)("nx-opacity-0 nx-transition [div:hover>&]:nx-opacity-100 focus-within:nx-opacity-100","nx-flex nx-gap-1 nx-absolute nx-m-[11px] nx-right-0",ei?"nx-top-8":"nx-top-0"),children:[(0,nx.jsx)(r8,{onClick:eu,className:"md:nx-hidden",title:"Toggle word wrap",children:(0,nx.jsx)(ey.NK,{className:"nx-pointer-events-none nx-h-4 nx-w-4"})}),er&&(0,nx.jsx)(ir,{getValue:()=>es.current?.querySelector("code")?.textContent||""})]})]})},is=({className:ee="",...en})=>(0,nx.jsx)("td",{className:"nx-m-0 nx-border nx-border-gray-300 nx-px-4 nx-py-2 dark:nx-border-gray-600 "+ee,...en}),il=({className:ee="",...en})=>(0,nx.jsx)("table",{className:"nx-block nx-overflow-x-scroll "+ee,...en}),iu=({className:ee="",...en})=>(0,nx.jsx)("th",{className:"nx-m-0 nx-border nx-border-gray-300 nx-px-4 nx-py-2 nx-font-semibold dark:nx-border-gray-600 "+ee,...en}),ic=({className:ee="",...en})=>(0,nx.jsx)("tr",{className:"nx-m-0 nx-border-t nx-border-gray-300 nx-p-0 dark:nx-border-gray-600 even:nx-bg-gray-100 even:dark:nx-bg-gray-600/20 "+ee,...en}),ih=Object.defineProperty,im=Object.defineProperties,ix=Object.getOwnPropertyDescriptors,ig=Object.getOwnPropertySymbols,iv=Object.prototype.hasOwnProperty,iy=Object.prototype.propertyIsEnumerable,ib=(ee,en,er)=>en in ee?ih(ee,en,{enumerable:!0,configurable:!0,writable:!0,value:er}):ee[en]=er,iw=(ee,en)=>{for(var er in en||(en={}))iv.call(en,er)&&ib(ee,er,en[er]);if(ig)for(var er of ig(en))iy.call(en,er)&&ib(ee,er,en[er]);return ee},i_=(ee,en)=>im(ee,ix(en)),ik=(ee,en)=>{var er={};for(var ei in ee)iv.call(ee,ei)&&0>en.indexOf(ei)&&(er[ei]=ee[ei]);if(null!=ee&&ig)for(var ei of ig(ee))0>en.indexOf(ei)&&iy.call(ee,ei)&&(er[ei]=ee[ei]);return er},ij=(ee,en,er)=>new Promise((ei,eo)=>{var es=ee=>{try{ec(er.next(ee))}catch(ee){eo(ee)}},eu=ee=>{try{ec(er.throw(ee))}catch(ee){eo(ee)}},ec=ee=>ee.done?ei(ee.value):Promise.resolve(ee.value).then(es,eu);ec((er=er.apply(ee,en)).next())}),iE=(0,em.createContext)({}),iO=(0,em.createContext)(ee=>ee),iC=(0,em.createContext)(null),iT=new WeakMap,iS=(0,em.createContext)(iT),iN=()=>(0,em.useContext)(iE),iR=()=>(0,em.useContext)(iO),iI=()=>(0,em.useContext)(iC),iA=()=>(0,em.useContext)(iS),iL=({children:ee})=>{let[en,er]=(0,em.useState)({}),ei=(0,em.useRef)(null);return aD&&!ei.current&&(ei.current=new IntersectionObserver(ee=>{er(en=>{let er=iw({},en);for(let en of ee)if((null==en?void 0:en.rootBounds)&&iT.has(en.target)){let[ee,ei]=iT.get(en.target),eo=en.boundingClientRect.y+en.boundingClientRect.height<=en.rootBounds.y+en.rootBounds.height,es=en.intersectionRatio>0;er[ee]={index:ei,aboveHalfViewport:eo,insideHalfViewport:es}}let ei="",eo=1/0,es=-1;for(let ee in er)er[ee].isActive=!1,er[ee].insideHalfViewport&&er[ee].indexes&&(es=er[ee].index,ei=ee);return er[ei]&&(er[ei].isActive=!0),er})},{rootMargin:"0px 0px -50%",threshold:[0,1]})),(0,nx.jsx)(iE.Provider,{value:en,children:(0,nx.jsx)(iO.Provider,{value:er,children:(0,nx.jsx)(iS.Provider,{value:iT,children:(0,nx.jsx)(iC.Provider,{value:ei.current,children:ee})})})})},iP=(0,em.createContext)({menu:!1,setMenu:()=>!1}),iM=()=>(0,em.useContext)(iP),iZ=iP.Provider,iD=(0,em.createContext)(iw({title:"",frontMatter:{}},aB));function iF(){return(0,em.useContext)(iD)}var i$=({children:ee,value:{themeConfig:en,pageOpts:er}})=>{let[ei,eo]=(0,em.useState)(!1);eh||(eh=iw(iw({},aB),Object.fromEntries(Object.entries(en).map(([ee,en])=>[ee,en&&"object"==typeof en&&aq.includes(ee)?iw(iw({},aB[ee]),en):en]))));let es=i_(iw(i_(iw({},eh),{flexsearch:er.flexsearch}),"boolean"==typeof er.newNextLinkBehavior&&{newNextLinkBehavior:er.newNextLinkBehavior}),{title:er.title,frontMatter:er.frontMatter}),{nextThemes:eu}=es;return(0,nx.jsx)(nk,{attribute:"class",disableTransitionOnChange:!0,defaultTheme:eu.defaultTheme,storageKey:eu.storageKey,forcedTheme:eu.forcedTheme,children:(0,nx.jsx)(iD.Provider,{value:es,children:(0,nx.jsx)(iZ,{value:{menu:ei,setMenu:eo},children:ee})})})},iz=(0,em.createContext)(ee=>ee),iH=()=>(0,em.useContext)(iz),iW=iz.Provider,iU=Number(nm.i8.split(".")[0]),iG=(0,em.forwardRef)(function(ee,en){var er=ee,{href:ei="",children:eo,newWindow:es}=er,eu=ik(er,["href","children","newWindow"]);let ec=iF();return es?(0,nx.jsxs)("a",i_(iw({ref:en,href:ei,target:"_blank",rel:"noreferrer"},eu),{children:[eo,(0,nx.jsx)("span",{className:"nx-sr-only",children:" (opens in a new tab)"})]})):ei?iU>12||ec.newNextLinkBehavior?(0,nx.jsx)(np(),i_(iw({ref:en,href:ei},eu),{children:eo})):(0,nx.jsx)(np(),{href:ei,passHref:!0,children:(0,nx.jsx)("a",i_(iw({ref:en},eu),{children:eo}))}):(0,nx.jsx)("a",i_(iw({ref:en},eu),{children:eo}))});iG.displayName="Anchor";var iV=({repository:ee="",title:en,labels:er})=>{let ei=nW()(ee);if(!ei)throw Error("Invalid `docsRepositoryBase` URL!");return ei.resource.includes("gitlab")?`${ei.protocol}://${ei.resource}/${ei.owner}/${ei.name}/-/issues/new?issue[title]=${encodeURIComponent(en)}`:ei.resource.includes("github")?`${ei.protocol}://${ei.resource}/${ei.owner}/${ei.name}/issues/new?title=${encodeURIComponent(en)}&labels=${er||""}`:"#"};function iB(ee,en){return ee?"function"!=typeof ee?ee:(0,nx.jsx)(ee,iw({},en)):null}function iq(ee,en={}){let er="function"==typeof ee?ee(en):ee;return er||""}function iK(ee){let en=(0,em.useRef)(null),er=(0,em.useRef)(null),ei=(0,em.useRef)(),eo=(0,em.useCallback)(()=>{var eo;en.current&&er.current&&(null==(eo=ei.current)||eo.call(ei),ei.current=(0,nU.fi)(en.current,er.current,ee).destroy)},[en,er,ei,ee]);return(0,em.useMemo)(()=>[ee=>{en.current=ee,eo()},ee=>{er.current=ee,eo()}],[en,er,eo])}function iQ(ee=""){let en=iF(),er=nW()(en.docsRepositoryBase||"");if(!er)throw Error("Invalid `docsRepositoryBase` URL!");return`${er.href}/${ee}`}function iJ(){let{banner:ee}=iF();if(!ee.text)return null;let en=`try{if(localStorage.getItem(${JSON.stringify(ee.key)})==='0'){document.body.classList.add('nextra-banner-hidden')}}catch(e){}`;return(0,nx.jsxs)(nx.Fragment,{children:[(0,nx.jsx)("script",{dangerouslySetInnerHTML:{__html:en}}),(0,nx.jsxs)("div",{className:(0,ex.Z)("nextra-banner-container nx-sticky nx-top-0 nx-z-20 nx-flex nx-items-center md:nx-relative","nx-h-[var(--nextra-banner-height)] [body.nextra-banner-hidden_&]:nx-hidden","nx-text-slate-50 dark:nx-text-white nx-bg-neutral-900 dark:nx-bg-[linear-gradient(1deg,#383838,#212121)]","nx-px-2 ltr:nx-pl-10 rtl:nx-pr-10 print:nx-hidden"),children:[(0,nx.jsx)("div",{className:"nx-w-full nx-truncate nx-px-4 nx-text-center nx-font-medium nx-text-sm",children:iB(ee.text)}),ee.dismissible&&(0,nx.jsx)("button",{type:"button","aria-label":"Dismiss banner",className:"nx-w-8 nx-h-8 nx-opacity-80 hover:nx-opacity-100",onClick:()=>{try{localStorage.setItem(ee.key,"0")}catch(ee){}document.body.classList.add("nextra-banner-hidden")},children:(0,nx.jsx)(ey.b0,{className:"nx-mx-auto nx-h-4 nx-w-4"})})]})]})}function iY({activePath:ee}){return(0,nx.jsx)("div",{className:"nextra-breadcrumb nx-mt-1.5 nx-flex nx-items-center nx-gap-1 nx-overflow-hidden nx-text-sm nx-text-gray-500 dark:nx-text-gray-400 contrast-more:nx-text-current",children:ee.map((en,er)=>{let ei=!en.children||en.withIndexPage,eo=er===ee.length-1;return(0,nx.jsxs)(em.Fragment,{children:[er>0&&(0,nx.jsx)(ey.LZ,{className:"nx-w-3.5 nx-shrink-0"}),(0,nx.jsx)("div",{className:(0,ex.Z)("nx-whitespace-nowrap nx-transition-colors",eo?"nx-font-medium nx-text-gray-700 contrast-more:nx-font-bold contrast-more:nx-text-current dark:nx-text-gray-100 contrast-more:dark:nx-text-current":["nx-min-w-[24px] nx-overflow-hidden nx-text-ellipsis",ei&&"hover:nx-text-gray-900 dark:hover:nx-text-gray-100"]),title:en.title,children:ei&&!eo?(0,nx.jsx)(iG,{href:en.route,children:en.title}):en.title})]},en.route+en.name)})})}function iX({children:ee,className:en,isOpen:er,horizontal:ei=!1}){let eo=(0,em.useRef)(null),es=(0,em.useRef)(null),eu=(0,em.useRef)(0),ec=(0,em.useRef)(er),ed=(0,em.useRef)(!0);return(0,em.useEffect)(()=>{let ee=eo.current,en=es.current,ec=eu.current;ec&&clearTimeout(ec),!ed.current&&ee&&en&&(ee.classList.toggle("nx-duration-500",!er),ee.classList.toggle("nx-duration-300",er),ei?(en.style.width=`${en.clientWidth}px`,ee.style.width=`${en.clientWidth}px`):ee.style.height=`${en.clientHeight}px`,er?eu.current=window.setTimeout(()=>{ee.style.removeProperty("height")},300):setTimeout(()=>{ei?ee.style.width="0px":ee.style.height="0px"},0))},[ei,er]),(0,em.useEffect)(()=>{ed.current=!1},[]),(0,nx.jsx)("div",{ref:eo,className:"nx-transform-gpu nx-overflow-hidden nx-transition-all nx-ease-in-out motion-reduce:nx-transition-none",style:ec.current||ei?void 0:{height:0},children:(0,nx.jsx)("div",{ref:es,className:(0,ex.Z)("nx-transition-opacity nx-duration-500 nx-ease-in-out motion-reduce:nx-transition-none",er?"nx-opacity-100":"nx-opacity-0",en),children:ee})})}(0,ex.Z)("nextra-cards nx-mt-4 nx-gap-4 nx-grid"),(0,ex.Z)("nextra-card nx-group nx-flex nx-flex-col nx-justify-start nx-overflow-hidden nx-rounded-lg nx-border nx-border-gray-200","nx-text-current nx-no-underline dark:nx-shadow-none","hover:nx-shadow-gray-100 dark:hover:nx-shadow-none nx-shadow-gray-100","active:nx-shadow-sm active:nx-shadow-gray-200","nx-transition-all nx-duration-200 hover:nx-border-gray-300"),(0,ex.Z)("nx-flex nx-font-semibold nx-items-start nx-gap-2 nx-p-4 nx-text-gray-700 hover:nx-text-gray-900"),(0,nx.jsx)("span",{className:"nx-transition-transform nx-duration-75 group-hover:nx-translate-x-[2px]",children:"→"});var i0=(0,em.createContext)(0);function i1(){return(0,em.useContext)(i0)}var i2=({children:ee})=>(0,nx.jsx)("div",{className:"nx-mt-6 nx-select-none nx-text-sm nx-text-gray-800 dark:nx-text-gray-300",children:(0,nx.jsx)("div",{className:"nx-inline-flex nx-flex-col nx-rounded-lg nx-border nx-px-4 nx-py-2 dark:nx-border-neutral-800",children:ee})});function i4(){let ee=i1();return(0,nx.jsx)(nx.Fragment,{children:[...Array(ee)].map((ee,en)=>(0,nx.jsx)("span",{className:"nx-inline-block nx-w-5"},en))})}var i5=(0,em.memo)(({label:ee,name:en,open:er,children:ei,defaultOpen:eo=!1,onToggle:es})=>{let eu=i1(),[ec,ed]=(0,em.useState)(eo),ef=(0,em.useCallback)(()=>{null==es||es(!ec),ed(!ec)},[ec,es]),eh=void 0===er?ec:er;return(0,nx.jsxs)("li",{className:"nx-flex nx-list-none nx-flex-col",children:[(0,nx.jsxs)("a",{onClick:ef,title:en,className:"nx-inline-flex nx-cursor-pointer nx-items-center nx-py-1 hover:nx-opacity-60",children:[(0,nx.jsx)(i4,{}),(0,nx.jsx)("svg",{width:"1em",height:"1em",viewBox:"0 0 24 24",children:(0,nx.jsx)("path",{fill:"none",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:eh?"M5 19a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h4l2 2h4a2 2 0 0 1 2 2v1M5 19h14a2 2 0 0 0 2-2v-5a2 2 0 0 0-2-2H9a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2Z":"M3 7v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-6l-2-2H5a2 2 0 0 0-2 2Z"})}),(0,nx.jsx)("span",{className:"nx-ml-1",children:null!=ee?ee:en})]}),eh&&(0,nx.jsx)("ul",{children:(0,nx.jsx)(i0.Provider,{value:eu+1,children:ei})})]})});i5.displayName="Folder";var i3=(0,em.memo)(({label:ee,name:en,active:er})=>(0,nx.jsx)("li",{className:(0,ex.Z)("nx-flex nx-list-none",er&&"nx-text-primary-600 contrast-more:nx-underline"),children:(0,nx.jsxs)("a",{className:"nx-inline-flex nx-cursor-default nx-items-center nx-py-1",children:[(0,nx.jsx)(i4,{}),(0,nx.jsx)("svg",{width:"1em",height:"1em",viewBox:"0 0 24 24",children:(0,nx.jsx)("path",{fill:"none",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M9 12h6m-6 4h6m2 5H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5.586a1 1 0 0 1 .707.293l5.414 5.414a1 1 0 0 1 .293.707V19a2 2 0 0 1-2 2Z"})}),(0,nx.jsx)("span",{className:"nx-ml-1",children:null!=ee?ee:en})]})}));i3.displayName="File",Object.assign(i2,{Folder:i5,File:i3});var i9=(0,em.memo)(function({value:ee,match:en}){let er;let ei=ee?ee.split(""):[],eo=en.trim().replace(/[|\\{}()[\]^$+*?.]/g,"\\$&"),es=RegExp("("+eo.replaceAll(" ","|")+")","ig"),eu=0,ec=0,ed=[];if(ee)for(;null!==(er=es.exec(ee));)ed.push((0,nx.jsxs)(em.Fragment,{children:[ei.splice(0,er.index-ec).join(""),(0,nx.jsx)("span",{className:"nx-text-primary-600",children:ei.splice(0,es.lastIndex-er.index).join("")})]},eu++)),ec=es.lastIndex;return(0,nx.jsxs)(nx.Fragment,{children:[ed,ei.join("")]})}),i7=(0,em.forwardRef)((ee,en)=>{var er=ee,{className:ei,suffix:eo}=er,es=ik(er,["className","suffix"]);return(0,nx.jsxs)("div",{className:"nx-relative nx-flex nx-items-center nx-text-gray-900 contrast-more:nx-text-gray-800 dark:nx-text-gray-300 contrast-more:dark:nx-text-gray-300",children:[(0,nx.jsx)("input",iw({ref:en,spellCheck:!1,className:(0,ex.Z)(ei,"nx-block nx-w-full nx-appearance-none nx-rounded-lg nx-px-3 nx-py-2 nx-transition-colors","nx-text-base nx-leading-tight md:nx-text-sm","nx-bg-black/[.05] dark:nx-bg-gray-50/10","focus:nx-bg-white dark:focus:nx-bg-dark","placeholder:nx-text-gray-500 dark:placeholder:nx-text-gray-400","contrast-more:nx-border contrast-more:nx-border-current")},es)),eo]})});i7.displayName="Input";var i6=["input","select","button","textarea"];function i8({className:ee,overlayClassName:en,value:er,onChange:ei,onActive:eo,loading:es,error:eu,results:ec}){let[ed,ef]=(0,em.useState)(!1),eh=iF(),[ev,eb]=(0,em.useState)(0),ew=(0,ep.useRouter)(),{setMenu:e_}=iM(),ek=(0,em.useRef)(null),ej=(0,em.useRef)(null),[eE,eO]=(0,em.useState)(!1);(0,em.useEffect)(()=>{eb(0)},[er]),(0,em.useEffect)(()=>{let ee=ee=>{let en=document.activeElement,er=null==en?void 0:en.tagName.toLowerCase();!ek.current||!er||i6.includes(er)||null!=en&&en.isContentEditable||("/"===ee.key||"k"===ee.key&&(ee.metaKey||ee.ctrlKey)?(ee.preventDefault(),ek.current.focus()):"Escape"===ee.key&&(ef(!1),ek.current.blur()))};return window.addEventListener("keydown",ee),()=>{window.removeEventListener("keydown",ee)}},[]);let eC=(0,em.useCallback)(()=>{var ee;null==(ee=ek.current)||ee.blur(),ei(""),ef(!1),e_(!1)},[ei,e_]),eT=(0,em.useCallback)(ee=>{let{index:en}=ee.currentTarget.dataset;eb(Number(en))},[]),eS=(0,em.useCallback)(function(ee){var en,er,ei;switch(ee.key){case"ArrowDown":if(ev+1 a`);er&&(ee.preventDefault(),eT({currentTarget:er}),er.focus())}break;case"ArrowUp":if(ev-1>=0){let en=null==(er=ej.current)?void 0:er.querySelector(`li:nth-of-type(${ev}) > a`);en&&(ee.preventDefault(),eT({currentTarget:en}),en.focus())}break;case"Enter":{let ee=ec[ev];ee&&(ew.push(ee.route),eC());break}case"Escape":ef(!1),null==(ei=ek.current)||ei.blur()}},[ev,ec,ew,eC,eT]),eN=(0,eg.s)(),eR=ed&&!!er,eI=(0,nx.jsx)(nB.u,{show:eN&&(!ed||!!er),as:em.Fragment,enter:"nx-transition-opacity",enterFrom:"nx-opacity-0",enterTo:"nx-opacity-100",leave:"nx-transition-opacity",leaveFrom:"nx-opacity-100",leaveTo:"nx-opacity-0",children:(0,nx.jsx)("kbd",{className:(0,ex.Z)("nx-absolute nx-my-1.5 nx-select-none ltr:nx-right-1.5 rtl:nx-left-1.5","nx-h-5 nx-rounded nx-bg-white nx-px-1.5 nx-font-mono nx-text-[10px] nx-font-medium nx-text-gray-500","nx-border dark:nx-border-gray-100/20 dark:nx-bg-dark/50","contrast-more:nx-border-current contrast-more:nx-text-current contrast-more:dark:nx-border-current","nx-items-center nx-gap-1 nx-transition-opacity",er?"nx-z-20 nx-flex nx-cursor-pointer hover:nx-opacity-70":"nx-pointer-events-none nx-hidden sm:nx-flex"),title:er?"Clear":void 0,onClick:()=>{ei("")},children:er&&eE?"ESC":eN&&(navigator.userAgent.includes("Macintosh")?(0,nx.jsxs)(nx.Fragment,{children:[(0,nx.jsx)("span",{className:"nx-text-xs",children:"⌘"}),"K"]}):"CTRL K")})});return(0,nx.jsxs)("div",{className:(0,ex.Z)("nextra-search nx-relative md:nx-w-64",ee),children:[eR&&(0,nx.jsx)("div",{className:"nx-fixed nx-inset-0 nx-z-10",onClick:()=>ef(!1)}),(0,nx.jsx)(i7,{ref:ek,value:er,onChange:ee=>{let{value:en}=ee.target;ei(en),ef(!!en)},onFocus:()=>{null==eo||eo(!0),eO(!0)},onBlur:()=>{eO(!1)},type:"search",placeholder:iq(eh.search.placeholder),onKeyDown:eS,suffix:eI}),(0,nx.jsx)(nB.u,{show:eR,as:nB.u.Child,leave:"nx-transition-opacity nx-duration-100",leaveFrom:"nx-opacity-100",leaveTo:"nx-opacity-0",children:(0,nx.jsx)("ul",{className:(0,ex.Z)("nextra-scrollbar","nx-border nx-border-gray-200 nx-bg-white nx-text-gray-100 dark:nx-border-neutral-800 dark:nx-bg-neutral-900","nx-absolute nx-top-full nx-z-20 nx-mt-2 nx-overflow-auto nx-overscroll-contain nx-rounded-xl nx-py-2.5 nx-shadow-xl","nx-max-h-[min(calc(50vh-11rem-env(safe-area-inset-bottom)),400px)]","md:nx-max-h-[min(calc(100vh-5rem-env(safe-area-inset-bottom)),400px)]","nx-inset-x-0 ltr:md:nx-left-auto rtl:md:nx-right-auto","contrast-more:nx-border contrast-more:nx-border-gray-900 contrast-more:dark:nx-border-gray-50",en),ref:ej,style:{transition:"max-height .2s ease"},children:eu?(0,nx.jsxs)("span",{className:"nx-flex nx-select-none nx-justify-center nx-gap-2 nx-p-8 nx-text-center nx-text-sm nx-text-red-500",children:[(0,nx.jsx)(ey.AV,{className:"nx-h-5 nx-w-5"}),iq(eh.search.error)]}):es?(0,nx.jsxs)("span",{className:"nx-flex nx-select-none nx-justify-center nx-gap-2 nx-p-8 nx-text-center nx-text-sm nx-text-gray-400",children:[(0,nx.jsx)(ey.L4,{className:"nx-h-5 nx-w-5 nx-animate-spin"}),iB(eh.search.loading)]}):ec.length>0?ec.map(({route:ee,prefix:en,children:er,id:ei},eo)=>(0,nx.jsxs)(em.Fragment,{children:[en,(0,nx.jsx)("li",{className:(0,ex.Z)("nx-mx-2.5 nx-break-words nx-rounded-md","contrast-more:nx-border",eo===ev?"nx-bg-primary-500/10 nx-text-primary-600 contrast-more:nx-border-primary-500":"nx-text-gray-800 contrast-more:nx-border-transparent dark:nx-text-gray-300"),children:(0,nx.jsx)(iG,{className:"nx-block nx-scroll-m-12 nx-px-2.5 nx-py-2",href:ee,"data-index":eo,onFocus:eT,onMouseMove:eT,onClick:eC,onKeyDown:eS,children:er})})]},ei)):iB(eh.search.emptyResult)})})]})}var at={},an=new Map,ar=(ee,en)=>{let er=ee+"@"+en;if(an.has(er))return an.get(er);let ei=ai(ee,en);return an.set(er,ei),ei},ai=(ee,en)=>ij(void 0,null,function*(){let er=yield fetch(`${ee}/_next/static/chunks/nextra-data-${en}.json`),ei=yield er.json(),eo=new(nV()).Document({cache:100,tokenize:"full",document:{id:"id",index:"content",store:["title"]},context:{resolution:9,depth:2,bidirectional:!0}}),es=new(nV()).Document({cache:100,tokenize:"full",document:{id:"id",index:"content",tag:"pageId",store:["title","content","url","display"]},context:{resolution:9,depth:2,bidirectional:!0}}),eu=0;for(let ee in ei){let en="";for(let er in++eu,ei[ee].data){let[eo,ec]=er.split("#"),ed=ee+(eo?"#"+eo:""),ef=ec||ei[ee].title,eh=ei[ee].data[er]||"",ep=eh.split("\n").filter(Boolean);es.add(iw({id:ed,url:ed,title:ef,pageId:`page_${eu}`,content:ef},ep[0]&&{display:ep[0]}));for(let ee=0;ee{var er,ei;if(!ee)return;let[eo,es]=at[en],eu=(null==(er=eo.search(ee,5,{enrich:!0,suggest:!0})[0])?void 0:er.result)||[],ec=[],ef={};for(let en=0;enee._page_rk===en._page_rk?ee._section_rk-en._section_rk:ef[ee._page_rk]!==ef[en._page_rk]?ef[en._page_rk]-ef[ee._page_rk]:ee._page_rk-en._page_rk).map(ee=>({id:`${ee._page_rk}_${ee._section_rk}`,route:ee.route,prefix:ee.prefix,children:ee.children})))},ev=(0,em.useCallback)(ee=>ij(this,null,function*(){if(ee&&!at[en]){eo(!0);try{yield ar(er,en)}catch(ee){eu(!0)}eo(!1)}}),[en,er]),ey=ee=>ij(this,null,function*(){if(eh(ee),!ei){if(!at[en]){eo(!0);try{yield ar(er,en)}catch(ee){eu(!0)}eo(!1)}eg(ee)}});return(0,nx.jsx)(i8,{loading:ei,error:es,value:ef,onChange:ey,onActive:ev,className:ee,overlayClassName:"nx-w-screen nx-min-h-[100px] nx-max-w-[min(calc(100vw-2rem),calc(100%+20rem))]",results:ec})}function al({options:ee,selected:en,onChange:er,title:ei,className:eo}){let[es,eu]=iK({strategy:"fixed",placement:"top-start",modifiers:[{name:"offset",options:{offset:[0,10]}},{name:"sameWidth",enabled:!0,fn({state:ee}){ee.styles.popper.minWidth=`${ee.rects.reference.width}px`},phase:"beforeWrite",requires:["computeStyles"]}]});return(0,nx.jsx)(nK.R,{value:en,onChange:er,children:({open:er})=>(0,nx.jsxs)(nK.R.Button,{ref:es,title:ei,className:(0,ex.Z)("nx-h-7 nx-rounded-md nx-px-2 nx-text-left nx-text-xs nx-font-medium nx-text-gray-600 nx-transition-colors dark:nx-text-gray-400",er?"nx-bg-gray-200 nx-text-gray-900 dark:nx-bg-primary-100/10 dark:nx-text-gray-50":"hover:nx-bg-gray-100 hover:nx-text-gray-900 dark:hover:nx-bg-primary-100/5 dark:hover:nx-text-gray-50",eo),children:[en.name,(0,nx.jsx)(au,{children:(0,nx.jsx)(nB.u,{ref:eu,show:er,as:nK.R.Options,className:"nx-z-20 nx-max-h-64 nx-overflow-auto nx-rounded-md nx-ring-1 nx-ring-black/5 nx-bg-white nx-py-1 nx-text-sm nx-shadow-lg dark:nx-ring-white/20 dark:nx-bg-neutral-800",leave:"nx-transition-opacity",leaveFrom:"nx-opacity-100",leaveTo:"nx-opacity-0",children:ee.map(ee=>(0,nx.jsxs)(nK.R.Option,{value:ee,className:({active:ee})=>(0,ex.Z)(ee?"nx-bg-primary-50 nx-text-primary-600 dark:nx-bg-primary-500/10":"nx-text-gray-800 dark:nx-text-gray-100","nx-relative nx-cursor-pointer nx-whitespace-nowrap nx-py-1.5","nx-transition-colors ltr:nx-pl-3 ltr:nx-pr-9 rtl:nx-pr-3 rtl:nx-pl-9"),children:[ee.name,ee.key===en.key&&(0,nx.jsx)("span",{className:"nx-absolute nx-inset-y-0 nx-flex nx-items-center ltr:nx-right-3 rtl:nx-left-3",children:(0,nx.jsx)(ey.nQ,{})})]},ee.key))})})]})})}function au(ee){let en=(0,eg.s)();return en?(0,nQ.createPortal)(ee.children,document.body):null}function ac({options:ee,lite:en,className:er}){let{locale:ei,asPath:eo}=(0,ep.useRouter)(),es=ee.find(ee=>ei===ee.locale);return(0,nx.jsx)(al,{title:"Change language",className:er,onChange:ee=>{let en=new Date(Date.now()+31536e6);document.cookie=`NEXT_LOCALE=${ee.key}; expires=${en.toUTCString()}; path=/`,location.href=(0,nq.addBasePath)(eo)},selected:{key:(null==es?void 0:es.locale)||"",name:(0,nx.jsxs)("span",{className:"nx-flex nx-items-center nx-gap-2",children:[(0,nx.jsx)(ey.n9,{}),(0,nx.jsx)("span",{className:en?"nx-hidden":"",children:null==es?void 0:es.text})]})},options:ee.map(ee=>({key:ee.locale,name:ee.text}))})}function ad({menu:ee}){let en=iF();return(0,nx.jsxs)("footer",{className:"nx-bg-gray-100 nx-pb-[env(safe-area-inset-bottom)] dark:nx-bg-neutral-900 print:nx-bg-transparent",children:[(0,nx.jsxs)("div",{className:(0,ex.Z)("nx-mx-auto nx-flex nx-max-w-[90rem] nx-gap-2 nx-py-2 nx-px-4",ee&&(en.i18n.length>0||en.darkMode)?"nx-flex":"nx-hidden"),children:[en.i18n.length>0&&(0,nx.jsx)(ac,{options:en.i18n}),en.darkMode&&iB(en.themeSwitch.component)]}),(0,nx.jsx)("hr",{className:"dark:nx-border-neutral-800"}),(0,nx.jsx)("div",{className:(0,ex.Z)("nx-mx-auto nx-flex nx-max-w-[90rem] nx-justify-center nx-py-12 nx-text-gray-600 dark:nx-text-gray-400 md:nx-justify-start","nx-pl-[max(env(safe-area-inset-left),1.5rem)] nx-pr-[max(env(safe-area-inset-right),1.5rem)]"),children:iB(en.footer.text)})]})}function af(){var ee;let en=iF(),{resolvedTheme:er}=n_(),ei=(0,eg.s)(),eo="function"==typeof en.head?en.head({}):en.head,es=en.primaryHue,{dark:eu,light:ec}="number"==typeof es?{dark:es,light:es}:es,ed=en.frontMatter;return(0,nx.jsxs)(nx.Fragment,{children:[(0,nx.jsx)(n7,iw({title:en.title,description:ed.description,canonical:ed.canonical,openGraph:ed.openGraph},null==(ee=en.useNextSeoProps)?void 0:ee.call(en))),(0,nx.jsxs)(nY(),{children:[en.faviconGlyph?(0,nx.jsx)("link",{rel:"icon",href:`data:image/svg+xml;utf8,${en.faviconGlyph}`}):null,ei?(0,nx.jsx)("meta",{name:"theme-color",content:"dark"===er?"#111":"#fff"}):(0,nx.jsxs)(nx.Fragment,{children:[(0,nx.jsx)("meta",{name:"theme-color",content:"#fff",media:"(prefers-color-scheme: light)"}),(0,nx.jsx)("meta",{name:"theme-color",content:"#111",media:"(prefers-color-scheme: dark)"})]}),(0,nx.jsx)("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0, viewport-fit=cover"}),(0,nx.jsx)("style",{children:` + :root { + --nextra-primary-hue: ${ec}deg; + --nextra-navbar-height: 4rem; + --nextra-menu-height: 3.75rem; + --nextra-banner-height: 2.5rem; + } + + .dark { + --nextra-primary-hue: ${eu}deg; + } + `}),eo]})]})}var ah={link:(0,ex.Z)("nx-flex nx-max-w-[50%] nx-items-center nx-gap-1 nx-py-4 nx-text-base nx-font-medium nx-text-gray-600 nx-transition-colors [word-break:break-word] hover:nx-text-primary-600 dark:nx-text-gray-300 md:nx-text-lg"),icon:(0,ex.Z)("nx-inline nx-h-5 nx-shrink-0")},ap=({flatDirectories:ee,currentIndex:en})=>{let er=iF(),ei=er.navigation,eo="boolean"==typeof ei?{prev:ei,next:ei}:ei,es=eo.prev&&ee[en-1],eu=eo.next&&ee[en+1];return(es&&!es.isUnderCurrentDocsTree&&(es=!1),eu&&!eu.isUnderCurrentDocsTree&&(eu=!1),es||eu)?(0,nx.jsxs)("div",{className:(0,ex.Z)("nx-mb-8 nx-flex nx-items-center nx-border-t nx-pt-8 dark:nx-border-neutral-800","contrast-more:nx-border-neutral-400 dark:contrast-more:nx-border-neutral-400","print:nx-hidden"),children:[es&&(0,nx.jsxs)(iG,{href:es.route,title:es.title,className:(0,ex.Z)(ah.link,"ltr:nx-pr-4 rtl:nx-pl-4"),children:[(0,nx.jsx)(ey.LZ,{className:(0,ex.Z)(ah.icon,"ltr:nx-rotate-180")}),es.title]}),eu&&(0,nx.jsxs)(iG,{href:eu.route,title:eu.title,className:(0,ex.Z)(ah.link,"ltr:nx-ml-auto ltr:nx-pl-4 ltr:nx-text-right rtl:nx-mr-auto rtl:nx-pr-4 rtl:nx-text-left"),children:[eu.title,(0,nx.jsx)(ey.LZ,{className:(0,ex.Z)(ah.icon,"rtl:nx-rotate-180")})]})]}):null},am={link:(0,ex.Z)("nx-text-sm contrast-more:nx-text-gray-700 contrast-more:dark:nx-text-gray-100"),active:(0,ex.Z)("nx-font-medium nx-subpixel-antialiased"),inactive:(0,ex.Z)("nx-text-gray-600 hover:nx-text-gray-800 dark:nx-text-gray-400 dark:hover:nx-text-gray-200")};function ax({className:ee,menu:en,children:er}){let{items:ei}=en,eo=Object.fromEntries((en.children||[]).map(ee=>[ee.name,ee]));return(0,nx.jsx)("div",{className:"nx-relative nx-inline-block",children:(0,nx.jsxs)(rZ,{children:[(0,nx.jsx)(rZ.Button,{className:(0,ex.Z)(ee,"-nx-ml-2 nx-hidden nx-items-center nx-whitespace-nowrap nx-rounded nx-p-2 md:nx-inline-flex",am.inactive),children:er}),(0,nx.jsx)(nB.u,{leave:"nx-transition-opacity",leaveFrom:"nx-opacity-100",leaveTo:"nx-opacity-0",children:(0,nx.jsx)(rZ.Items,{className:"nx-absolute nx-right-0 nx-z-20 nx-mt-1 nx-max-h-64 nx-min-w-full nx-overflow-auto nx-rounded-md nx-ring-1 nx-ring-black/5 nx-bg-white nx-py-1 nx-text-sm nx-shadow-lg dark:nx-ring-white/20 dark:nx-bg-neutral-800",tabIndex:0,children:Object.entries(ei||{}).map(([ee,er])=>{var ei;return(0,nx.jsx)(rZ.Item,{children:(0,nx.jsx)(iG,{href:er.href||(null==(ei=eo[ee])?void 0:ei.route)||en.route+"/"+ee,className:(0,ex.Z)("nx-relative nx-hidden nx-w-full nx-select-none nx-whitespace-nowrap nx-text-gray-600 hover:nx-text-gray-900 dark:nx-text-gray-400 dark:hover:nx-text-gray-100 md:nx-inline-block","nx-py-1.5 nx-transition-colors ltr:nx-pl-3 ltr:nx-pr-9 rtl:nx-pr-3 rtl:nx-pl-9"),newWindow:er.newWindow,children:er.title||ee})},ee)})})})]})})}function ag({flatDirectories:ee,items:en}){let er=iF(),ei=(0,eg.$)(),{menu:eo,setMenu:es}=iM();return(0,nx.jsxs)("div",{className:"nextra-nav-container nx-sticky nx-top-0 nx-z-20 nx-w-full nx-bg-transparent print:nx-hidden",children:[(0,nx.jsx)("div",{className:(0,ex.Z)("nextra-nav-container-blur","nx-pointer-events-none nx-absolute nx-z-[-1] nx-h-full nx-w-full nx-bg-white dark:nx-bg-dark","nx-shadow-[0_2px_4px_rgba(0,0,0,.02),0_1px_0_rgba(0,0,0,.06)] dark:nx-shadow-[0_-1px_0_rgba(255,255,255,.1)_inset]","contrast-more:nx-shadow-[0_0_0_1px_#000] contrast-more:dark:nx-shadow-[0_0_0_1px_#fff]")}),(0,nx.jsxs)("nav",{className:"nx-mx-auto nx-flex nx-h-[var(--nextra-navbar-height)] nx-max-w-[90rem] nx-items-center nx-justify-end nx-gap-2 nx-pl-[max(env(safe-area-inset-left),1.5rem)] nx-pr-[max(env(safe-area-inset-right),1.5rem)]",children:[er.logoLink?(0,nx.jsx)(iG,{href:"string"==typeof er.logoLink?er.logoLink:"/",className:"nx-flex nx-items-center hover:nx-opacity-75 ltr:nx-mr-auto rtl:nx-ml-auto",children:iB(er.logo)}):(0,nx.jsx)("div",{className:"nx-flex nx-items-center ltr:nx-mr-auto rtl:nx-ml-auto",children:iB(er.logo)}),en.map(ee=>{if("hidden"===ee.display)return null;if("menu"===ee.type){let en=ee,er=en.route===ei||ei.startsWith(en.route+"/");return(0,nx.jsxs)(ax,{className:(0,ex.Z)(am.link,"nx-flex nx-gap-1",er?am.active:am.inactive),menu:en,children:[en.title,(0,nx.jsx)(ey.LZ,{className:"nx-h-[18px] nx-min-w-[18px] nx-rounded-sm nx-p-0.5",pathClassName:"nx-origin-center nx-transition-transform nx-rotate-90"})]},en.title)}let en=ee,er=en.href||en.route||"#";en.children&&(er=(en.withIndexPage?en.route:en.firstChildRoute)||er);let eo=en.route===ei||ei.startsWith(en.route+"/");return(0,nx.jsxs)(iG,{href:er,className:(0,ex.Z)(am.link,"nx-relative -nx-ml-2 nx-hidden nx-whitespace-nowrap nx-p-2 md:nx-inline-block",!eo||en.newWindow?am.inactive:am.active),newWindow:en.newWindow,"aria-current":!en.newWindow&&eo,children:[(0,nx.jsx)("span",{className:"nx-absolute nx-inset-x-0 nx-text-center",children:en.title}),(0,nx.jsx)("span",{className:"nx-invisible nx-font-medium",children:en.title})]},er)}),iB(er.search.component,{directories:ee,className:"nx-hidden md:nx-inline-block mx-min-w-[200px]"}),er.project.link?(0,nx.jsx)(iG,{className:"nx-p-2 nx-text-current",href:er.project.link,newWindow:!0,children:iB(er.project.icon)}):null,er.chat.link?(0,nx.jsx)(iG,{className:"nx-p-2 nx-text-current",href:er.chat.link,newWindow:!0,children:iB(er.chat.icon)}):null,iB(er.navbar.extraContent),(0,nx.jsx)("button",{type:"button","aria-label":"Menu",className:"nextra-hamburger -nx-mr-2 nx-rounded nx-p-2 active:nx-bg-gray-400/20 md:nx-hidden",onClick:()=>es(!eo),children:(0,nx.jsx)(ey.Oq,{className:(0,ex.Z)({open:eo})})})]})]})}var av=Object.create(null),ay=(0,em.createContext)(null),ab=(0,em.createContext)(null),aw=(0,em.createContext)(0),a_=(0,em.memo)(function(ee){let en=(0,em.useContext)(aw);return(0,nx.jsx)(aw.Provider,{value:en+1,children:(0,nx.jsx)(aj,iw({},ee))})}),ak={link:(0,ex.Z)("nx-flex nx-rounded nx-px-2 nx-py-1.5 nx-text-sm nx-transition-colors [word-break:break-word]","nx-cursor-pointer [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none] contrast-more:nx-border"),inactive:(0,ex.Z)("nx-text-gray-500 hover:nx-bg-gray-100 hover:nx-text-gray-900","dark:nx-text-neutral-400 dark:hover:nx-bg-primary-100/5 dark:hover:nx-text-gray-50","contrast-more:nx-text-gray-900 contrast-more:dark:nx-text-gray-50","contrast-more:nx-border-transparent contrast-more:hover:nx-border-gray-900 contrast-more:dark:hover:nx-border-gray-50"),active:(0,ex.Z)("nx-bg-primary-100 nx-font-semibold nx-text-primary-800 dark:nx-bg-primary-400/10 dark:nx-text-primary-600","contrast-more:nx-border-primary-500 contrast-more:dark:nx-border-primary-500"),list:(0,ex.Z)("nx-flex nx-flex-col nx-gap-1"),border:(0,ex.Z)("nx-relative before:nx-absolute before:nx-inset-y-1",'before:nx-w-px before:nx-bg-gray-200 before:nx-content-[""] dark:before:nx-bg-neutral-800',"ltr:nx-pl-3 ltr:before:nx-left-0 rtl:nx-pr-3 rtl:before:nx-right-0")};function aj({item:ee,anchors:en}){let er=(0,eg.$)(),[ei]=er.split("#"),eo=[ei,ei+"/"].includes(ee.route+"/"),es=eo||ei.startsWith(ee.route+"/"),eu=(0,em.useContext)(ay),ec=!!(null==eu?void 0:eu.startsWith(ee.route+"/")),ed=(0,em.useContext)(aw),{setMenu:ef}=iM(),eh=iF(),{theme:ep}=ee,ev=void 0===av[ee.route]?eo||es||ec||(ep&&"collapsed"in ep?!ep.collapsed:ed{let en=()=>{(es||ec)&&(av[ee.route]=!0)},er=()=>{es&&ec?av[ee.route]=!0:delete av[ee.route]};eh.sidebar.autoCollapse?er():en()},[es,ec,ee.route,eh.sidebar.autoCollapse]),"menu"===ee.type){let en=ee,er=Object.fromEntries((en.children||[]).map(ee=>[ee.name,ee]));ee.children=Object.entries(en.items||{}).map(([ee,ei])=>{let eo=er[ee]||i_(iw({name:ee},"locale"in en&&{locale:en.locale}),{route:en.route+"/"+ee});return iw(iw({},eo),ei)})}let ew="withIndexPage"in ee&&ee.withIndexPage,e_=ew?iG:"button";return(0,nx.jsxs)("li",{className:(0,ex.Z)({open:ev,active:eo}),children:[(0,nx.jsxs)(e_,{href:ew?ee.route:void 0,className:(0,ex.Z)("nx-items-center nx-justify-between nx-gap-2",!ew&&"nx-text-left nx-w-full",ak.link,eo?ak.active:ak.inactive),onClick:en=>{let er=["svg","path"].includes(en.target.tagName.toLowerCase());if(er&&en.preventDefault(),ew){eo||er?av[ee.route]=!ev:(av[ee.route]=!0,ef(!1)),eb({});return}eo||(av[ee.route]=!ev,eb({}))},children:[iB(eh.sidebar.titleComponent,{title:ee.title,type:ee.type,route:ee.route}),(0,nx.jsx)(ey.LZ,{className:"nx-h-[18px] nx-min-w-[18px] nx-rounded-sm nx-p-0.5 hover:nx-bg-gray-800/5 dark:hover:nx-bg-gray-100/5",pathClassName:(0,ex.Z)("nx-origin-center nx-transition-transform rtl:-nx-rotate-180",ev&&"ltr:nx-rotate-90 rtl:nx-rotate-[-270deg]")})]}),(0,nx.jsx)(iX,{className:"ltr:nx-pr-0 rtl:nx-pl-0 nx-pt-1",isOpen:ev,children:Array.isArray(ee.children)?(0,nx.jsx)(aC,{className:(0,ex.Z)(ak.border,"ltr:nx-ml-3 rtl:nx-mr-3"),directories:ee.children,base:ee.route,anchors:en}):null})]})}function aE({title:ee}){let en=iF();return(0,nx.jsx)("li",{className:(0,ex.Z)("[word-break:break-word]",ee?"nx-mt-5 nx-mb-2 nx-px-2 nx-py-1.5 nx-text-sm nx-font-semibold nx-text-gray-900 first:nx-mt-0 dark:nx-text-gray-100":"nx-my-4"),children:ee?iB(en.sidebar.titleComponent,{title:ee,type:"separator",route:""}):(0,nx.jsx)("hr",{className:"nx-mx-2 nx-border-t nx-border-gray-200 dark:nx-border-primary-100/10"})})}function aO({item:ee,anchors:en}){let er=(0,eg.$)(),ei=(0,em.useContext)(ab),eo=ee.route&&[er,er+"/"].includes(ee.route+"/"),es=iN(),{setMenu:eu}=iM(),ec=iF();return"separator"===ee.type?(0,nx.jsx)(aE,{title:ee.title}):(0,nx.jsxs)("li",{className:(0,ex.Z)(ak.list,{active:eo}),children:[(0,nx.jsx)(iG,{href:ee.href||ee.route,newWindow:ee.newWindow,className:(0,ex.Z)(ak.link,eo?ak.active:ak.inactive),onClick:()=>{eu(!1)},onFocus:()=>{null==ei||ei(ee.route)},onBlur:()=>{null==ei||ei(null)},children:iB(ec.sidebar.titleComponent,{title:ee.title,type:ee.type,route:ee.route})}),eo&&en.length>0&&(0,nx.jsx)("ul",{className:(0,ex.Z)(ak.list,ak.border,"ltr:nx-ml-3 rtl:nx-mr-3"),children:en.map(({id:ee,value:en})=>{var er;return(0,nx.jsx)("li",{children:(0,nx.jsx)("a",{href:`#${ee}`,className:(0,ex.Z)(ak.link,'nx-flex nx-gap-2 before:nx-opacity-25 before:nx-content-["#"]',(null==(er=es[ee])?void 0:er.isActive)?ak.active:ak.inactive),onClick:()=>{eu(!1)},children:en})},ee)})})]})}function aC({directories:ee,anchors:en,className:er,onlyCurrentDocs:ei}){return(0,nx.jsx)("ul",{className:(0,ex.Z)(ak.list,er),children:ee.map(ee=>!ei||ee.isUnderCurrentDocsTree?"menu"===ee.type||ee.children&&(ee.children.length||!ee.withIndexPage)?(0,nx.jsx)(a_,{item:ee,anchors:en},ee.name):(0,nx.jsx)(aO,{item:ee,anchors:en},ee.name):null)})}function aT({docsDirectories:ee,flatDirectories:en,fullDirectories:er,asPopover:ei=!1,headings:eo,includePlaceholder:es}){let eu=iF(),{menu:ec,setMenu:ed}=iM(),ef=(0,ep.useRouter)(),[eh,eg]=(0,em.useState)(null),[ev,eb]=(0,em.useState)(!0),[ew,e_]=(0,em.useState)(!1),ek=(0,em.useMemo)(()=>eo.filter(ee=>2===ee.depth),[eo]),ej=(0,em.useRef)(null),eE=(0,em.useRef)(null);(0,em.useEffect)(()=>{ec?document.body.classList.add("nx-overflow-hidden","md:nx-overflow-auto"):document.body.classList.remove("nx-overflow-hidden","md:nx-overflow-auto")},[ec]),(0,em.useEffect)(()=>{var ee;let en=null==(ee=ej.current)?void 0:ee.querySelector("li.active");if(en&&(window.innerWidth>767||ec)){let ee=()=>{rG(en,{block:"center",inline:"center",scrollMode:"always",boundary:eE.current})};ec?setTimeout(ee,300):ee()}},[ec]),(0,em.useEffect)(()=>{ed(!1)},[ef.asPath,ed]);let eO=eu.i18n.length>0,eC=eu.darkMode||eO;return(0,nx.jsxs)(nx.Fragment,{children:[es&&ei?(0,nx.jsx)("div",{className:"max-xl:nx-hidden nx-h-0 nx-w-64 nx-shrink-0"}):null,(0,nx.jsx)("div",{className:(0,ex.Z)("motion-reduce:nx-transition-none [transition:background-color_1.5s_ease]",ec?"nx-fixed nx-inset-0 nx-z-10 nx-bg-black/80 dark:nx-bg-black/60":"nx-bg-transparent"),onClick:()=>ed(!1)}),(0,nx.jsxs)("aside",{className:(0,ex.Z)("nextra-sidebar-container nx-flex nx-flex-col","md:nx-top-16 md:nx-shrink-0 motion-reduce:nx-transform-none","nx-transform-gpu nx-transition-all nx-ease-in-out","print:nx-hidden",ev?"md:nx-w-64":"md:nx-w-20",ei?"md:nx-hidden":"md:nx-sticky md:nx-self-start",ec?"max-md:[transform:translate3d(0,0,0)]":"max-md:[transform:translate3d(0,-100%,0)]"),ref:eE,children:[(0,nx.jsx)("div",{className:"nx-px-4 nx-pt-4 md:nx-hidden",children:iB(eu.search.component,{directories:en})}),(0,nx.jsx)(ay.Provider,{value:eh,children:(0,nx.jsx)(ab.Provider,{value:ee=>{eg(ee)},children:(0,nx.jsxs)("div",{className:(0,ex.Z)("nx-overflow-y-auto nx-overflow-x-hidden","nx-p-4 nx-grow md:nx-h-[calc(100vh-var(--nextra-navbar-height)-var(--nextra-menu-height))]",ev?"nextra-scrollbar":"no-scrollbar"),ref:ej,children:[(!ei||!ev)&&(0,nx.jsx)(iX,{isOpen:ev,horizontal:!0,children:(0,nx.jsx)(aC,{className:"max-md:nx-hidden",directories:ee,anchors:eu.toc.float?[]:ek,onlyCurrentDocs:!0})}),(0,nx.jsx)(aC,{className:"md:nx-hidden",directories:er,anchors:ek})]})})}),eC&&(0,nx.jsxs)("div",{className:(0,ex.Z)("nx-sticky nx-bottom-0","nx-bg-white dark:nx-bg-dark","nx-mx-4 nx-py-4 nx-shadow-[0_-12px_16px_#fff]","nx-flex nx-items-center nx-gap-2","dark:nx-border-neutral-800 dark:nx-shadow-[0_-12px_16px_#111]","contrast-more:nx-border-neutral-400 contrast-more:nx-shadow-none contrast-more:dark:nx-shadow-none",ev?(0,ex.Z)(eO&&"nx-justify-end","nx-border-t"):"nx-py-4 nx-flex-wrap nx-justify-center"),"data-toggle-animation":ew?ev?"show":"hide":"off",children:[eO&&(0,nx.jsx)(ac,{options:eu.i18n,lite:!ev,className:(0,ex.Z)(ev?"nx-grow":"max-md:nx-grow")}),eu.darkMode&&(0,nx.jsx)("div",{className:ev&&!eO?"nx-grow nx-flex nx-flex-col":"",children:iB(eu.themeSwitch.component,{lite:!ev||eO})}),eu.sidebar.toggleButton&&(0,nx.jsx)("button",{title:ev?"Hide sidebar":"Show sidebar",className:"max-md:nx-hidden nx-h-7 nx-rounded-md nx-transition-colors nx-text-gray-600 dark:nx-text-gray-400 nx-px-2 hover:nx-bg-gray-100 hover:nx-text-gray-900 dark:hover:nx-bg-primary-100/5 dark:hover:nx-text-gray-50",onClick:()=>{eb(!ev),e_(!0)},children:(0,nx.jsx)(ey.Qq,{isOpen:ev})})]})]})]})}var aS="reach-skip-nav",aN="Skip to content";(0,em.forwardRef)(function(ee,en){var er=ee,{className:ei,id:eo,label:es=aN,styled:eu}=er,ec=ik(er,["className","id","label","styled"]);let ed=void 0===ei?eu?(0,ex.Z)("nx-sr-only","focus:nx-not-sr-only focus:nx-fixed focus:nx-z-50 focus:nx-m-3 focus:nx-ml-4 focus:nx-h-[calc(var(--nextra-navbar-height)-1.5rem)] focus:nx-rounded-lg focus:nx-border focus:nx-px-3 focus:nx-py-2 focus:nx-align-middle focus:nx-text-sm focus:nx-font-bold","focus:nx-text-gray-900 focus:dark:nx-text-gray-100","focus:nx-bg-white focus:dark:nx-bg-neutral-900","focus:nx-border-neutral-400 focus:dark:nx-border-neutral-800"):"":ei;return(0,nx.jsx)("a",i_(iw({},ec),{ref:en,href:`#${eo||aS}`,className:ed,"data-reach-skip-link":"",children:es}))}).displayName="SkipNavLink";var aR=(0,em.forwardRef)(function(ee,en){var er=ee,{id:ei}=er,eo=ik(er,["id"]);return(0,nx.jsx)("div",i_(iw({},eo),{ref:en,id:ei||aS}))});aR.displayName="SkipNavContent";var aI=nf.strictObject({light:nf.string(),dark:nf.string(),system:nf.string()});function aA({lite:ee,className:en}){let{setTheme:er,resolvedTheme:ei,theme:eo=""}=n_(),es=(0,eg.s)(),eu=iF().themeSwitch,ec=es&&"dark"===ei?ey.kL:ey.NW,ed="function"==typeof eu.useOptions?eu.useOptions():eu.useOptions;return(0,nx.jsx)(al,{className:en,title:"Change theme",options:[{key:"light",name:ed.light},{key:"dark",name:ed.dark},{key:"system",name:ed.system}],onChange:ee=>{er(ee.key)},selected:{key:eo,name:(0,nx.jsxs)("div",{className:"nx-flex nx-items-center nx-gap-2 nx-capitalize",children:[(0,nx.jsx)(ec,{}),(0,nx.jsx)("span",{className:ee?"md:nx-hidden":"",children:es?ed[eo]:ed.light})]})}})}var aL=(0,ex.Z)("nx-text-xs nx-font-medium nx-text-gray-500 hover:nx-text-gray-900 dark:nx-text-gray-400 dark:hover:nx-text-gray-100","contrast-more:nx-text-gray-800 contrast-more:dark:nx-text-gray-50");function aP({headings:ee,filePath:en}){var er;let ei=iN(),eo=iF(),es=(0,em.useRef)(null),eu=(0,em.useMemo)(()=>ee.filter(ee=>ee.depth>1),[ee]),ec=eu.length>0,ed=!!(eo.feedback.content||eo.editLink.component||eo.toc.extraContent),ef=null==(er=Object.entries(ei).find(([,{isActive:ee}])=>ee))?void 0:er[0];return(0,em.useEffect)(()=>{var ee;if(!ef)return;let en=null==(ee=es.current)?void 0:ee.querySelector(`li > a[href="#${ef}"]`);en&&rG(en,{behavior:"smooth",block:"center",inline:"center",scrollMode:"always",boundary:es.current})},[ef]),(0,nx.jsxs)("div",{ref:es,className:(0,ex.Z)("nextra-scrollbar nx-sticky nx-top-16 nx-overflow-y-auto nx-pr-4 nx-pt-6 nx-text-sm [hyphens:auto]","nx-max-h-[calc(100vh-var(--nextra-navbar-height)-env(safe-area-inset-bottom))] ltr:-nx-mr-4 rtl:-nx-ml-4"),children:[ec&&(0,nx.jsxs)(nx.Fragment,{children:[(0,nx.jsx)("p",{className:"nx-mb-4 nx-font-semibold nx-tracking-tight",children:iB(eo.toc.title)}),(0,nx.jsx)("ul",{children:eu.map(({id:ee,value:en,depth:er})=>{var es,eu,ec,ed;return(0,nx.jsx)("li",{className:"nx-my-2 nx-scroll-my-6 nx-scroll-py-6",children:(0,nx.jsx)("a",{href:`#${ee}`,className:(0,ex.Z)({2:"nx-font-semibold",3:"ltr:nx-pl-4 rtl:nx-pr-4",4:"ltr:nx-pl-8 rtl:nx-pr-8",5:"ltr:nx-pl-12 rtl:nx-pr-12",6:"ltr:nx-pl-16 rtl:nx-pr-16"}[er],"nx-inline-block",(null==(es=ei[ee])?void 0:es.isActive)?"nx-text-primary-600 nx-subpixel-antialiased contrast-more:!nx-text-primary-600":"nx-text-gray-500 hover:nx-text-gray-900 dark:nx-text-gray-400 dark:hover:nx-text-gray-300","contrast-more:nx-text-gray-900 contrast-more:nx-underline contrast-more:dark:nx-text-gray-50 nx-w-full nx-break-words"),children:null!=(ed=null==(ec=(eu=eo.toc).headingComponent)?void 0:ec.call(eu,{id:ee,children:en}))?ed:en})},ee)})})]}),ed&&(0,nx.jsxs)("div",{className:(0,ex.Z)(ec&&"nx-mt-8 nx-border-t nx-bg-white nx-pt-8 nx-shadow-[0_-12px_16px_white] dark:nx-bg-dark dark:nx-shadow-[0_-12px_16px_#111]","nx-sticky nx-bottom-0 nx-flex nx-flex-col nx-items-start nx-gap-2 nx-pb-8 dark:nx-border-neutral-800","contrast-more:nx-border-t contrast-more:nx-border-neutral-400 contrast-more:nx-shadow-none contrast-more:dark:nx-border-neutral-400"),children:[eo.feedback.content?(0,nx.jsx)(iG,{className:aL,href:eo.feedback.useLink(),newWindow:!0,children:iB(eo.feedback.content)}):null,iB(eo.editLink.component,{filePath:en,className:aL,children:iB(eo.editLink.text)}),iB(eo.toc.extraContent)]})]})}function aM({className:ee,directories:en}){let[er,ei]=(0,em.useState)(""),eo=(0,em.useMemo)(()=>er?rJ(en,er,{keys:["title"]}).map(({route:ee,title:en})=>({id:ee+en,route:ee,children:(0,nx.jsx)(i9,{value:en,match:er})})):[],[er,en]);return(0,nx.jsx)(i8,{value:er,onChange:ei,className:ee,overlayClassName:"nx-w-full",results:eo})}var aZ="en-US",aD="undefined"!=typeof window;function aF(ee){return null==ee||az(ee)||a$(ee)||(0,em.isValidElement)(ee)}function a$(ee){return"function"==typeof ee}function az(ee){return"string"==typeof ee}var aH=nf.array(nf.strictObject({direction:nf.enum(["ltr","rtl"]).optional(),locale:nf.string(),text:nf.string()})),aW=[aF,{message:"Must be React.ReactNode or React.FC"}],aU=[a$,{message:"Must be React.FC"}];nf.strictObject({banner:nf.strictObject({dismissible:nf.boolean(),key:nf.string(),text:nf.custom(...aW).optional()}),chat:nf.strictObject({icon:nf.custom(...aW),link:nf.string().startsWith("https://").optional()}),components:nf.record(nf.custom(...aU)).optional(),darkMode:nf.boolean(),direction:nf.enum(["ltr","rtl"]),docsRepositoryBase:nf.string().startsWith("https://"),editLink:nf.strictObject({component:nf.custom(...aU),text:nf.custom(...aW)}),faviconGlyph:nf.string().optional(),feedback:nf.strictObject({content:nf.custom(...aW),labels:nf.string(),useLink:nf.function().returns(nf.string())}),footer:nf.strictObject({component:nf.custom(...aW),text:nf.custom(...aW)}),gitTimestamp:nf.custom(...aW),head:nf.custom(...aW),i18n:aH,logo:nf.custom(...aW),logoLink:nf.boolean().or(nf.string()),main:nf.custom(...aU).optional(),navbar:nf.strictObject({component:nf.custom(...aW),extraContent:nf.custom(...aW).optional()}),navigation:nf.boolean().or(nf.strictObject({next:nf.boolean(),prev:nf.boolean()})),nextThemes:nf.strictObject({defaultTheme:nf.string(),forcedTheme:nf.string().optional(),storageKey:nf.string()}),notFound:nf.strictObject({content:nf.custom(...aW),labels:nf.string()}),primaryHue:nf.number().or(nf.strictObject({dark:nf.number(),light:nf.number()})),project:nf.strictObject({icon:nf.custom(...aW),link:nf.string().startsWith("https://").optional()}),search:nf.strictObject({component:nf.custom(...aW),emptyResult:nf.custom(...aW),error:nf.string().or(nf.function().returns(nf.string())),loading:nf.custom(...aW),placeholder:nf.string().or(nf.function().returns(nf.string()))}),serverSideError:nf.strictObject({content:nf.custom(...aW),labels:nf.string()}),sidebar:nf.strictObject({autoCollapse:nf.boolean().optional(),defaultMenuCollapseLevel:nf.number().min(1).int(),titleComponent:nf.custom(...aW),toggleButton:nf.boolean()}),themeSwitch:nf.strictObject({component:nf.custom(...aW),useOptions:aI.or(nf.function().returns(aI))}),toc:nf.strictObject({component:nf.custom(...aW),extraContent:nf.custom(...aW),float:nf.boolean(),headingComponent:nf.custom(...aU).optional(),title:nf.custom(...aW)}),useNextSeoProps:nf.custom(a$)}).deepPartial().extend({i18n:aH.optional()});var aG={"en-US":"Loading",fr:"Сhargement",ru:"Загрузка","zh-CN":"正在加载"},aV={"en-US":"Search documentation",fr:"Rechercher documents",ru:"Поиск документации","zh-CN":"搜索文档"},aB={banner:{dismissible:!0,key:"nextra-banner"},chat:{icon:(0,nx.jsxs)(nx.Fragment,{children:[(0,nx.jsx)(ey.D7,{}),(0,nx.jsx)("span",{className:"nx-sr-only",children:"Discord"})]})},darkMode:!0,direction:"ltr",docsRepositoryBase:"https://github.com/shuding/nextra",editLink:{component:function({className:ee,filePath:en,children:er}){let ei=iQ(en);return ei?(0,nx.jsx)(iG,{className:ee,href:ei,children:er}):null},text:"Edit this page"},feedback:{content:"Question? Give us feedback →",labels:"feedback",useLink(){let ee=iF();return iV({labels:ee.feedback.labels,repository:ee.docsRepositoryBase,title:`Feedback for \u201C${ee.title}\u201D`})}},footer:{component:ad,text:`MIT ${new Date().getFullYear()} \xa9 Nextra.`},gitTimestamp:function({timestamp:ee}){let{locale:en=aZ}=(0,ep.useRouter)();return(0,nx.jsxs)(nx.Fragment,{children:["Last updated on"," ",(0,nx.jsx)("time",{dateTime:ee.toISOString(),children:ee.toLocaleDateString(en,{day:"numeric",month:"long",year:"numeric"})})]})},head:(0,nx.jsxs)(nx.Fragment,{children:[(0,nx.jsx)("meta",{name:"msapplication-TileColor",content:"#fff"}),(0,nx.jsx)("meta",{httpEquiv:"Content-Language",content:"en"}),(0,nx.jsx)("meta",{name:"description",content:"Nextra: the next docs builder"}),(0,nx.jsx)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,nx.jsx)("meta",{name:"twitter:site",content:"@shuding_"}),(0,nx.jsx)("meta",{property:"og:title",content:"Nextra: the next docs builder"}),(0,nx.jsx)("meta",{property:"og:description",content:"Nextra: the next docs builder"}),(0,nx.jsx)("meta",{name:"apple-mobile-web-app-title",content:"Nextra"})]}),i18n:[],logo:(0,nx.jsxs)(nx.Fragment,{children:[(0,nx.jsx)("span",{className:"nx-font-extrabold",children:"Nextra"}),(0,nx.jsx)("span",{className:"nx-ml-2 nx-hidden nx-font-normal nx-text-gray-600 md:nx-inline",children:"The Next Docs Builder"})]}),logoLink:!0,navbar:{component:ag},navigation:!0,nextThemes:{defaultTheme:"system",storageKey:"theme"},notFound:{content:"Submit an issue about broken link →",labels:"bug"},primaryHue:{dark:204,light:212},project:{icon:(0,nx.jsxs)(nx.Fragment,{children:[(0,nx.jsx)(ey.fy,{}),(0,nx.jsx)("span",{className:"nx-sr-only",children:"GitHub"})]})},search:{component:function({className:ee,directories:en}){let er=iF();return er.flexsearch?(0,nx.jsx)(ao,{className:ee}):(0,nx.jsx)(aM,{className:ee,directories:en})},emptyResult:(0,nx.jsx)("span",{className:"nx-block nx-select-none nx-p-8 nx-text-center nx-text-sm nx-text-gray-400",children:"No results found."}),error:"Failed to load search index.",loading:function(){let{locale:ee,defaultLocale:en=aZ}=(0,ep.useRouter)(),er=ee&&aG[ee]||aG[en];return(0,nx.jsxs)(nx.Fragment,{children:[er,"…"]})},placeholder:function(){let{locale:ee,defaultLocale:en=aZ}=(0,ep.useRouter)(),er=ee&&aV[ee]||aV[en];return`${er}\u2026`}},serverSideError:{content:"Submit an issue about error in url →",labels:"bug"},sidebar:{defaultMenuCollapseLevel:2,titleComponent:({title:ee})=>(0,nx.jsx)(nx.Fragment,{children:ee}),toggleButton:!1},themeSwitch:{component:aA,useOptions(){let{locale:ee}=(0,ep.useRouter)();return"zh-CN"===ee?{dark:"深色主题",light:"浅色主题",system:"系统默认"}:{dark:"Dark",light:"Light",system:"System"}}},toc:{component:aP,float:!0,title:"On This Page"},useNextSeoProps:()=>({titleTemplate:"%s – Nextra"})},aq=Object.entries(aB).map(([ee,en])=>{let er=en&&"object"==typeof en&&!Array.isArray(en)&&!(0,em.isValidElement)(en);if(er)return ee}).filter(Boolean);if(aD){let ee;let en=()=>{document.body.classList.add("resizing"),clearTimeout(ee),ee=setTimeout(()=>{document.body.classList.remove("resizing")},200)};window.addEventListener("resize",en)}function aK(ee){var en=ee,{tag:er,context:ei,children:eo,id:es}=en,eu=ik(en,["tag","context","children","id"]);let ec=iR(),ed=iA(),ef=iI(),eh=(0,em.useRef)(null);return(0,em.useEffect)(()=>{if(!es)return;let ee=eh.current;if(ee)return ed.set(ee,[es,ei.index+=1]),null==ef||ef.observe(ee),()=>{null==ef||ef.disconnect(),ed.delete(ee),ec(ee=>{let en=iw({},ee);return delete en[es],en})}},[es,ei,ed,ef,ec]),(0,nx.jsxs)(er,i_(iw({className:(0,ex.Z)("nx-font-semibold nx-tracking-tight nx-text-slate-900 dark:nx-text-slate-100",{h2:"nx-mt-10 nx-border-b nx-pb-1 nx-text-3xl nx-border-neutral-200/70 contrast-more:nx-border-neutral-400 dark:nx-border-primary-100/10 contrast-more:dark:nx-border-neutral-400",h3:"nx-mt-8 nx-text-2xl",h4:"nx-mt-8 nx-text-xl",h5:"nx-mt-8 nx-text-lg",h6:"nx-mt-8 nx-text-base"}[er])},eu),{children:[eo,(0,nx.jsx)("span",{className:"nx-absolute -nx-mt-20",id:es,ref:eh}),(0,nx.jsx)("a",{href:`#${es}`,className:"subheading-anchor","aria-label":"Permalink for this section"})]}))}var aQ=ee=>{let en=null,er=[];return em.Children.forEach(ee,(ee,ei)=>{var eo;if(ee&&ee.type===aY){en||(en=ee);return}let es=ee;if(!en&&ee&&"object"==typeof ee&&ee.type!==aJ&&"props"in ee&&ee.props){let er=aQ(ee.props.children);en=er[0],es=(0,em.cloneElement)(ee,i_(iw({},ee.props),{children:(null==(eo=er[1])?void 0:eo.length)?er[1]:void 0,key:ei}))}er.push(es)}),[en,er]},aJ=ee=>{var en=ee,{children:er,open:ei}=en,eo=ik(en,["children","open"]);let[es,eu]=(0,em.useState)(!!ei),[ec,ed]=aQ(er),[ef,eh]=(0,em.useState)(es);return(0,em.useEffect)(()=>{if(es)eh(!0);else{let ee=setTimeout(()=>eh(es),500);return()=>clearTimeout(ee)}},[es]),(0,nx.jsxs)("details",i_(iw(i_(iw({className:"nx-my-4 nx-rounded nx-border nx-border-gray-200 nx-bg-white nx-p-2 nx-shadow-sm first:nx-mt-0 dark:nx-border-neutral-800 dark:nx-bg-neutral-900"},eo),{open:ef}),es&&{"data-expanded":!0}),{children:[(0,nx.jsx)(iW,{value:eu,children:ec}),(0,nx.jsx)(iX,{isOpen:es,children:ed})]}))},aY=ee=>{let en=iH();return(0,nx.jsx)("summary",i_(iw({className:(0,ex.Z)("nx-flex nx-items-center nx-cursor-pointer nx-list-none nx-p-1 nx-transition-colors hover:nx-bg-gray-100 dark:hover:nx-bg-neutral-800","before:nx-mr-1 before:nx-inline-block before:nx-transition-transform before:nx-content-[''] dark:before:nx-invert","rtl:before:nx-rotate-180 [[data-expanded]>&]:before:nx-rotate-90")},ee),{onClick:ee=>{ee.preventDefault(),en(ee=>!ee)}}))},aX=/https?:\/\//,a0=ee=>{var en=ee,{href:er="",className:ei}=en,eo=ik(en,["href","className"]);return(0,nx.jsx)(iG,iw({href:er,newWindow:aX.test(er),className:(0,ex.Z)("nx-text-primary-600 nx-underline nx-decoration-from-font [text-underline-position:from-font]",ei)},eo))},a1=ee=>{var en=ee,{href:er=""}=en,ei=ik(en,["href"]);return(0,nx.jsx)(iG,iw({href:er,newWindow:aX.test(er)},ei))},a2=({isRawLayout:ee,components:en})=>{if(ee)return{a:a1};let er={index:0};return iw({h1:ee=>(0,nx.jsx)("h1",iw({className:"nx-mt-2 nx-text-4xl nx-font-bold nx-tracking-tight nx-text-slate-900 dark:nx-text-slate-100"},ee)),h2:ee=>(0,nx.jsx)(aK,iw({tag:"h2",context:er},ee)),h3:ee=>(0,nx.jsx)(aK,iw({tag:"h3",context:er},ee)),h4:ee=>(0,nx.jsx)(aK,iw({tag:"h4",context:er},ee)),h5:ee=>(0,nx.jsx)(aK,iw({tag:"h5",context:er},ee)),h6:ee=>(0,nx.jsx)(aK,iw({tag:"h6",context:er},ee)),ul:ee=>(0,nx.jsx)("ul",iw({className:"nx-mt-6 nx-list-disc first:nx-mt-0 ltr:nx-ml-6 rtl:nx-mr-6"},ee)),ol:ee=>(0,nx.jsx)("ol",iw({className:"nx-mt-6 nx-list-decimal first:nx-mt-0 ltr:nx-ml-6 rtl:nx-mr-6"},ee)),li:ee=>(0,nx.jsx)("li",iw({className:"nx-my-2"},ee)),blockquote:ee=>(0,nx.jsx)("blockquote",iw({className:(0,ex.Z)("nx-mt-6 nx-border-gray-300 nx-italic nx-text-gray-700 dark:nx-border-gray-700 dark:nx-text-gray-400","first:nx-mt-0 ltr:nx-border-l-2 ltr:nx-pl-6 rtl:nx-border-r-2 rtl:nx-pr-6")},ee)),hr:ee=>(0,nx.jsx)("hr",iw({className:"nx-my-8 dark:nx-border-gray-900"},ee)),a:a0,table:ee=>(0,nx.jsx)(il,iw({className:"nextra-scrollbar nx-mt-6 nx-p-0 first:nx-mt-0"},ee)),p:ee=>(0,nx.jsx)("p",iw({className:"nx-mt-6 nx-leading-7 first:nx-mt-0"},ee)),tr:ic,th:iu,td:is,details:aJ,summary:aY,pre:io,code:ii},en)},a4={toc:(0,ex.Z)("nextra-toc nx-order-last nx-hidden nx-w-64 nx-shrink-0 xl:nx-block print:nx-hidden"),main:(0,ex.Z)("nx-w-full nx-break-words")},a5=({themeContext:ee,breadcrumb:en,timestamp:er,navigation:ei,children:eo})=>{var es;let eu=iF(),ec=(0,eg.s)();if("raw"===ee.layout)return(0,nx.jsx)("div",{className:a4.main,children:eo});let ed=ee.timestamp&&eu.gitTimestamp&&er?new Date(er):null,ef=ec&&ed?(0,nx.jsx)("div",{className:"nx-mt-12 nx-mb-8 nx-block nx-text-xs nx-text-gray-500 ltr:nx-text-right rtl:nx-text-left dark:nx-text-gray-400",children:iB(eu.gitTimestamp,{timestamp:ed})}):(0,nx.jsx)("div",{className:"nx-mt-16"}),eh=(0,nx.jsxs)(nx.Fragment,{children:[eo,ef,ei]}),ep=(null==(es=eu.main)?void 0:es.call(eu,{children:eh}))||eh;return"full"===ee.layout?(0,nx.jsx)("article",{className:(0,ex.Z)(a4.main,"nextra-content nx-min-h-[calc(100vh-var(--nextra-navbar-height))] nx-pl-[max(env(safe-area-inset-left),1.5rem)] nx-pr-[max(env(safe-area-inset-right),1.5rem)]"),children:ep}):(0,nx.jsx)("article",{className:(0,ex.Z)(a4.main,"nextra-content nx-flex nx-min-h-[calc(100vh-var(--nextra-navbar-height))] nx-min-w-0 nx-justify-center nx-pb-8 nx-pr-[calc(env(safe-area-inset-right)-1.5rem)]","article"===ee.typesetting&&"nextra-body-typesetting-article"),children:(0,nx.jsxs)("main",{className:"nx-w-full nx-min-w-0 nx-max-w-6xl nx-px-6 nx-pt-4 md:nx-px-12",children:[en,ep]})})},a3=({filePath:ee,pageMap:en,frontMatter:er,headings:ei,timestamp:eo,children:es})=>{let eu=iF(),{locale:ec=aZ,defaultLocale:ed}=(0,ep.useRouter)(),ef=(0,eg.$)(),{activeType:eh,activeIndex:ey,activeThemeContext:eb,activePath:ew,topLevelNavbarItems:e_,docsDirectories:ek,flatDirectories:ej,flatDocsDirectories:eE,directories:eO}=(0,em.useMemo)(()=>nz({list:en,locale:ec,defaultLocale:ed,route:ef}),[en,ec,ed,ef]),eC=iw(iw({},eb),er),eT=!eC.sidebar||"raw"===eC.layout||"page"===eh,eS="page"!==eh&&eC.toc&&"default"===eC.layout?(0,nx.jsx)("nav",{className:(0,ex.Z)(a4.toc,"nx-px-4"),"aria-label":"table of contents",children:iB(eu.toc.component,{headings:eu.toc.float?ei:[],filePath:ee})}):"full"!==eC.layout&&"raw"!==eC.layout&&(0,nx.jsx)("nav",{className:a4.toc,"aria-label":"table of contents"}),eN=eu.i18n.find(ee=>ee.locale===ec),eR=eN?"rtl"===eN.direction:"rtl"===eu.direction,eI=eR?"rtl":"ltr";return(0,nx.jsxs)("div",{dir:eI,children:[(0,nx.jsx)("script",{dangerouslySetInnerHTML:{__html:`document.documentElement.setAttribute('dir','${eI}')`}}),(0,nx.jsx)(af,{}),(0,nx.jsx)(iJ,{}),eC.navbar&&iB(eu.navbar.component,{flatDirectories:ej,items:e_}),(0,nx.jsx)("div",{className:(0,ex.Z)("nx-mx-auto nx-flex","raw"!==eC.layout&&"nx-max-w-[90rem]"),children:(0,nx.jsxs)(iL,{children:[(0,nx.jsx)(aT,{docsDirectories:ek,flatDirectories:ej,fullDirectories:eO,headings:ei,asPopover:eT,includePlaceholder:"default"===eC.layout}),eS,(0,nx.jsx)(aR,{}),(0,nx.jsx)(a5,{themeContext:eC,breadcrumb:"page"!==eh&&eC.breadcrumb?(0,nx.jsx)(iY,{activePath:ew}):null,timestamp:eo,navigation:"page"!==eh&&eC.pagination?(0,nx.jsx)(ap,{flatDirectories:eE,currentIndex:ey}):null,children:(0,nx.jsx)(ev.Zo,{components:a2({isRawLayout:"raw"===eC.layout,components:eu.components}),children:es})})]})}),eC.footer&&iB(eu.footer.component,{menu:eT})]})};function a9(ee){var en=ee,{children:er}=en,ei=ik(en,["children"]);return(0,nx.jsx)(i$,{value:ei,children:(0,nx.jsx)(a3,i_(iw({},ei.pageOpts),{children:er}))})}},4238:function(ee,en,er){"use strict";er.d(en,{$:function(){return ed},s:function(){return eo}});var ei=er(959);function eo(){let[ee,en]=(0,ei.useState)(!1);return(0,ei.useEffect)(()=>{en(!0)},[]),ee}var es=er(8556),eu=er(1835),ec="https://nextra.vercel.app",ed=()=>{let{locale:ee=eu.ZW,asPath:en,route:er}=(0,es.useRouter)();return(0,ei.useMemo)(()=>{let ei=eu.hV.has(er)?er:en,{pathname:eo}=new URL(ei,ec),es=ee?eo.replace(RegExp(`\\.${ee}(\\/|$)`),"$1"):eo;return es.replace(/\/index(\/|$)/,"$1").replace(/\/$/,"")||"/"},[en,ee,er])}},8631:function(ee,en,er){"use strict";er.d(en,{LZ:function(){return eo},nQ:function(){return es},TI:function(){return eu},D7:function(){return ec},Qq:function(){return ed},fy:function(){return ef},n9:function(){return eh},AV:function(){return ep},Oq:function(){return em},kL:function(){return ex},L4:function(){return eg},NW:function(){return ev},NK:function(){return ey},b0:function(){return eb}});var ei=er(1527);function eo({pathClassName:ee,...en}){return(0,ei.jsx)("svg",{fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",...en,children:(0,ei.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M9 5l7 7-7 7",className:ee})})}function es(ee){return(0,ei.jsx)("svg",{viewBox:"0 0 20 20",width:"1em",height:"1em",fill:"currentColor",...ee,children:(0,ei.jsx)("path",{fillRule:"evenodd",d:"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",clipRule:"evenodd"})})}function eu(ee){return(0,ei.jsxs)("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",stroke:"currentColor",...ee,children:[(0,ei.jsx)("rect",{x:"9",y:"9",width:"13",height:"13",rx:"2",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),(0,ei.jsx)("path",{d:"M5 15H4C2.89543 15 2 14.1046 2 13V4C2 2.89543 2.89543 2 4 2H13C14.1046 2 15 2.89543 15 4V5",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]})}function ec(ee){return(0,ei.jsxs)("svg",{width:"24",height:"24",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 5 30.67 23.25",...ee,children:[(0,ei.jsx)("title",{children:"Discord"}),(0,ei.jsx)("path",{d:"M26.0015 6.9529C24.0021 6.03845 21.8787 5.37198 19.6623 5C19.3833 5.48048 19.0733 6.13144 18.8563 6.64292C16.4989 6.30193 14.1585 6.30193 11.8336 6.64292C11.6166 6.13144 11.2911 5.48048 11.0276 5C8.79575 5.37198 6.67235 6.03845 4.6869 6.9529C0.672601 12.8736 -0.41235 18.6548 0.130124 24.3585C2.79599 26.2959 5.36889 27.4739 7.89682 28.2489C8.51679 27.4119 9.07477 26.5129 9.55525 25.5675C8.64079 25.2265 7.77283 24.808 6.93587 24.312C7.15286 24.1571 7.36986 23.9866 7.57135 23.8161C12.6241 26.1255 18.0969 26.1255 23.0876 23.8161C23.3046 23.9866 23.5061 24.1571 23.7231 24.312C22.8861 24.808 22.0182 25.2265 21.1037 25.5675C21.5842 26.5129 22.1422 27.4119 22.7621 28.2489C25.2885 27.4739 27.8769 26.2959 30.5288 24.3585C31.1952 17.7559 29.4733 12.0212 26.0015 6.9529ZM10.2527 20.8402C8.73376 20.8402 7.49382 19.4608 7.49382 17.7714C7.49382 16.082 8.70276 14.7025 10.2527 14.7025C11.7871 14.7025 13.0425 16.082 13.0115 17.7714C13.0115 19.4608 11.7871 20.8402 10.2527 20.8402ZM20.4373 20.8402C18.9183 20.8402 17.6768 19.4608 17.6768 17.7714C17.6768 16.082 18.8873 14.7025 20.4373 14.7025C21.9717 14.7025 23.2271 16.082 23.1961 17.7714C23.1961 19.4608 21.9872 20.8402 20.4373 20.8402Z"})]})}function ed({isOpen:ee,...en}){return(0,ei.jsxs)("svg",{height:"12",width:"12",viewBox:"0 0 16 16",fill:"currentColor",...en,children:[(0,ei.jsx)("path",{fillRule:"evenodd",d:"M4.177 7.823l2.396-2.396A.25.25 0 017 5.604v4.792a.25.25 0 01-.427.177L4.177 8.177a.25.25 0 010-.354z",className:ee?"":"nx-origin-[35%] nx-rotate-180"}),(0,ei.jsx)("path",{fillRule:"evenodd",d:"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0114.25 16H1.75A1.75 1.75 0 010 14.25V1.75zm1.75-.25a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25H9.5v-13H1.75zm12.5 13H11v-13h3.25a.25.25 0 01.25.25v12.5a.25.25 0 01-.25.25z"})]})}function ef(ee){return(0,ei.jsxs)("svg",{width:"24",height:"24",fill:"currentColor",viewBox:"3 3 18 18",...ee,children:[(0,ei.jsx)("title",{children:"GitHub"}),(0,ei.jsx)("path",{d:"M12 3C7.0275 3 3 7.12937 3 12.2276C3 16.3109 5.57625 19.7597 9.15374 20.9824C9.60374 21.0631 9.77249 20.7863 9.77249 20.5441C9.77249 20.3249 9.76125 19.5982 9.76125 18.8254C7.5 19.2522 6.915 18.2602 6.735 17.7412C6.63375 17.4759 6.19499 16.6569 5.8125 16.4378C5.4975 16.2647 5.0475 15.838 5.80124 15.8264C6.51 15.8149 7.01625 16.4954 7.18499 16.7723C7.99499 18.1679 9.28875 17.7758 9.80625 17.5335C9.885 16.9337 10.1212 16.53 10.38 16.2993C8.3775 16.0687 6.285 15.2728 6.285 11.7432C6.285 10.7397 6.63375 9.9092 7.20749 9.26326C7.1175 9.03257 6.8025 8.08674 7.2975 6.81794C7.2975 6.81794 8.05125 6.57571 9.77249 7.76377C10.4925 7.55615 11.2575 7.45234 12.0225 7.45234C12.7875 7.45234 13.5525 7.55615 14.2725 7.76377C15.9937 6.56418 16.7475 6.81794 16.7475 6.81794C17.2424 8.08674 16.9275 9.03257 16.8375 9.26326C17.4113 9.9092 17.76 10.7281 17.76 11.7432C17.76 15.2843 15.6563 16.0687 13.6537 16.2993C13.98 16.5877 14.2613 17.1414 14.2613 18.0065C14.2613 19.2407 14.25 20.2326 14.25 20.5441C14.25 20.7863 14.4188 21.0746 14.8688 20.9824C16.6554 20.364 18.2079 19.1866 19.3078 17.6162C20.4077 16.0457 20.9995 14.1611 21 12.2276C21 7.12937 16.9725 3 12 3Z"})]})}function eh(ee){return(0,ei.jsx)("svg",{viewBox:"2 2 16 16",width:"12",height:"12",fill:"currentColor",...ee,children:(0,ei.jsx)("path",{fillRule:"evenodd",d:"M4.083 9h1.946c.089-1.546.383-2.97.837-4.118A6.004 6.004 0 004.083 9zM10 2a8 8 0 100 16 8 8 0 000-16zm0 2c-.076 0-.232.032-.465.262-.238.234-.497.623-.737 1.182-.389.907-.673 2.142-.766 3.556h3.936c-.093-1.414-.377-2.649-.766-3.556-.24-.56-.5-.948-.737-1.182C10.232 4.032 10.076 4 10 4zm3.971 5c-.089-1.546-.383-2.97-.837-4.118A6.004 6.004 0 0115.917 9h-1.946zm-2.003 2H8.032c.093 1.414.377 2.649.766 3.556.24.56.5.948.737 1.182.233.23.389.262.465.262.076 0 .232-.032.465-.262.238-.234.498-.623.737-1.182.389-.907.673-2.142.766-3.556zm1.166 4.118c.454-1.147.748-2.572.837-4.118h1.946a6.004 6.004 0 01-2.783 4.118zm-6.268 0C6.412 13.97 6.118 12.546 6.03 11H4.083a6.004 6.004 0 002.783 4.118z",clipRule:"evenodd"})})}function ep(ee){return(0,ei.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor",width:"20",height:"20",...ee,children:(0,ei.jsx)("path",{fillRule:"evenodd",clipRule:"evenodd",d:"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"})})}function em(ee){return(0,ei.jsxs)("svg",{fill:"none",width:"24",height:"24",viewBox:"0 0 24 24",stroke:"currentColor",...ee,children:[(0,ei.jsx)("g",{children:(0,ei.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M4 6h16"})}),(0,ei.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M4 12h16"}),(0,ei.jsx)("g",{children:(0,ei.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M4 18h16"})})]})}function ex(ee){return(0,ei.jsx)("svg",{fill:"none",viewBox:"2 2 20 20",width:"12",height:"12",stroke:"currentColor",...ee,children:(0,ei.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",fill:"currentColor",d:"M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"})})}function eg(ee){return(0,ei.jsxs)("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",width:"24",height:"24",...ee,children:[(0,ei.jsx)("circle",{className:"nx-opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),(0,ei.jsx)("path",{className:"nx-opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]})}function ev(ee){return(0,ei.jsx)("svg",{fill:"none",viewBox:"3 3 18 18",width:"12",height:"12",stroke:"currentColor",...ee,children:(0,ei.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",fill:"currentColor",d:"M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"})})}function ey(ee){return(0,ei.jsx)("svg",{viewBox:"0 0 24 24",width:"24",height:"24",...ee,children:(0,ei.jsx)("path",{fill:"currentColor",d:"M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3l3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"})})}function eb(ee){return(0,ei.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 20 20",fill:"currentColor",...ee,children:(0,ei.jsx)("path",{fillRule:"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",clipRule:"evenodd"})})}},452:function(ee,en,er){"use strict";er.d(en,{j:function(){return ej}});var ei,eo=er(5554),es=er.n(eo),eu=er(1835),ec=er(959);er(6067),"undefined"!=typeof window&&(window.requestIdleCallback=window.requestIdleCallback||function(ee){var en=Date.now();return setTimeout(function(){ee({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-en))}})},1)},window.cancelIdleCallback=window.cancelIdleCallback||function(ee){clearTimeout(ee)});var ed=er(1527),ef=(0,ec.createContext)(!1),eh=er(8556);function ep(){let ee=globalThis[eu.eZ],{route:en}=(0,eh.useRouter)();(0,ec.useState)({})[1];let er=ee.context[en];if(!er)throw Error("No content found for the current route. This is a Nextra bug.");return{context:er,Layout:ee.Layout}}function em({__nextra_pageMap:ee,__nextra_dynamic_opts:en,...er}){let{context:ei,Layout:eo}=ep(),{Content:es,...eu}=ei;if(ee&&(eu.pageOpts={...eu.pageOpts,pageMap:ee}),en){let ee=JSON.parse(en);eu.pageOpts={...eu.pageOpts,headings:ee.headings,title:ee.title||eu.pageOpts.title,frontMatter:ee.frontMatter}}return(0,ed.jsx)(eo,{...eu,pageProps:er,children:(0,ed.jsx)(ef.Provider,{value:er,children:(0,ed.jsx)(es,{...er})})})}var ex=er(5654),eg=er(4473),ev=er(9378);function ey(ee,en){return eg(ex.join(ee,en.replace(/^index$/,"")))}function eb(ee){return ev(ee.replace(/[-_]/g," "))}function ew(ee){return!!ee&&"object"==typeof ee&&"folder"===ee.type}function e_(ee){return Object.fromEntries(Object.entries(ee).map(([ee,en])=>{if(ew(en)){let er=ee.replace("/","");return[er,en.title||eb(er)]}return[ee,en||eb(ee)]}))}function ek(ee,en,er=!0){if(er){ek(ee,{kind:"Meta",data:en.data,locale:en.locale},!1),en.data=e_(en.data);return}for(let[er,ei]of Object.entries(en.data)){if(!ew(ei)){if("*"===er)continue;ee.children.push({kind:"MdxPage",...en.locale&&{locale:en.locale},name:er,route:ey(ee.route,er)});continue}let eo=er.replace("/",""),es={kind:"Folder",name:eo,route:`${ee.route}/${eo}`,children:[{kind:"Meta",...en.locale&&{locale:en.locale},data:e_(ei.items)}]};ee.children.push(es),ek(es,{kind:"Meta",data:ei.items,locale:en.locale},!1)}}function ej({pageNextRoute:ee,pageOpts:en,nextraLayout:er,themeConfig:eo,MDXContent:ec,hot:ed,pageOptsChecksum:ef,dynamicMetaModules:eh=[]}){var ep;"undefined"==typeof window&&(globalThis.__nextra_resolvePageMap=async()=>{if(ei)return ei;let ee=JSON.parse(JSON.stringify(ex.pageMap));return await Promise.all(eh.map(async([en,{metaObjectKeyPath:er,metaParentKeyPath:ei}])=>{let eo=await en,eu=await eo.default(),ec=es()(ee,er);ec.data=eu;let ed=es()(ee,ei);ek(ed,ec)})),ei=ee});let ex=globalThis[ep=eu.eZ]||(globalThis[ep]=Object.create(null));return en.pageMap?(ex.pageMap=en.pageMap,ex.Layout=er):(en={...en,pageMap:ex.pageMap,flexsearch:ex.flexsearch},eo=ex.themeConfig),en={frontMatter:{},...en},ex.route=en.route,ex.context||(ex.context=Object.create(null)),ex.context[ee]={Content:ec,pageOpts:en,themeConfig:eo},em}},5182:function(ee,en,er){"use strict";var ei=er(6097);function eo(ee){var en={protocols:[],protocol:null,port:null,resource:"",host:"",user:"",password:"",pathname:"",hash:"",search:"",href:ee,query:{},parse_failed:!1};try{var er=new URL(ee);en.protocols=ei(er),en.protocol=en.protocols[0],en.port=er.port,en.resource=er.hostname,en.host=er.host,en.user=er.username||"",en.password=er.password||"",en.pathname=er.pathname,en.hash=er.hash.slice(1),en.search=er.search.slice(1),en.href=er.href,en.query=Object.fromEntries(er.searchParams)}catch(er){en.protocols=["file"],en.protocol=en.protocols[0],en.port="",en.resource="",en.user="",en.pathname="",en.hash="",en.search="",en.href=ee,en.query={},en.parse_failed=!0}return en}ee.exports=eo},8593:function(ee,en,er){"use strict";var ei=function(ee){return ee&&"object"==typeof ee&&"default"in ee?ee:{default:ee}}(er(5182));let eo="text/plain",es="us-ascii",eu=(ee,en)=>en.some(en=>en instanceof RegExp?en.test(ee):en===ee),ec=(ee,{stripHash:en})=>{let er=/^data:(?[^,]*?),(?[^#]*?)(?:#(?.*))?$/.exec(ee);if(!er)throw Error(`Invalid URL: ${ee}`);let{type:ei,data:eu,hash:ec}=er.groups,ed=ei.split(";");ec=en?"":ec;let ef=!1;"base64"===ed[ed.length-1]&&(ed.pop(),ef=!0);let eh=(ed.shift()||"").toLowerCase(),ep=ed.map(ee=>{let[en,er=""]=ee.split("=").map(ee=>ee.trim());return"charset"===en&&(er=er.toLowerCase())===es?"":`${en}${er?`=${er}`:""}`}).filter(Boolean),em=[...ep];return ef&&em.push("base64"),(em.length>0||eh&&eh!==eo)&&em.unshift(eh),`data:${em.join(";")},${ef?eu.trim():eu}${ec?`#${ec}`:""}`};function ed(ee,en){if(en={defaultProtocol:"http:",normalizeProtocol:!0,forceHttp:!1,forceHttps:!1,stripAuthentication:!0,stripHash:!1,stripTextFragment:!0,stripWWW:!0,removeQueryParameters:[/^utm_\w+/i],removeTrailingSlash:!0,removeSingleSlash:!0,removeDirectoryIndex:!1,sortQueryParameters:!0,...en},ee=ee.trim(),/^data:/i.test(ee))return ec(ee,en);if(/^view-source:/i.test(ee))throw Error("`view-source:` is not supported as it is a non-standard protocol");let er=ee.startsWith("//"),ei=!er&&/^\.*\//.test(ee);ei||(ee=ee.replace(/^(?!(?:\w+:)?\/\/)|^\/\//,en.defaultProtocol));let eo=new URL(ee);if(en.forceHttp&&en.forceHttps)throw Error("The `forceHttp` and `forceHttps` options cannot be used together");if(en.forceHttp&&"https:"===eo.protocol&&(eo.protocol="http:"),en.forceHttps&&"http:"===eo.protocol&&(eo.protocol="https:"),en.stripAuthentication&&(eo.username="",eo.password=""),en.stripHash?eo.hash="":en.stripTextFragment&&(eo.hash=eo.hash.replace(/#?:~:text.*?$/i,"")),eo.pathname){let ee=/\b[a-z][a-z\d+\-.]{1,50}:\/\//g,en=0,er="";for(;;){let ei=ee.exec(eo.pathname);if(!ei)break;let es=ei[0],eu=ei.index,ec=eo.pathname.slice(en,eu);er+=ec.replace(/\/{2,}/g,"/")+es,en=eu+es.length}let ei=eo.pathname.slice(en,eo.pathname.length);er+=ei.replace(/\/{2,}/g,"/"),eo.pathname=er}if(eo.pathname)try{eo.pathname=decodeURI(eo.pathname)}catch{}if(!0===en.removeDirectoryIndex&&(en.removeDirectoryIndex=[/^index\.[a-z]+$/]),Array.isArray(en.removeDirectoryIndex)&&en.removeDirectoryIndex.length>0){let ee=eo.pathname.split("/"),er=ee[ee.length-1];eu(er,en.removeDirectoryIndex)&&(ee=ee.slice(0,-1),eo.pathname=ee.slice(1).join("/")+"/")}if(eo.hostname&&(eo.hostname=eo.hostname.replace(/\.$/,""),en.stripWWW&&/^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(eo.hostname)&&(eo.hostname=eo.hostname.replace(/^www\./,""))),Array.isArray(en.removeQueryParameters))for(let ee of[...eo.searchParams.keys()])eu(ee,en.removeQueryParameters)&&eo.searchParams.delete(ee);if(!0===en.removeQueryParameters&&(eo.search=""),en.sortQueryParameters){eo.searchParams.sort();try{eo.search=decodeURIComponent(eo.search)}catch{}}en.removeTrailingSlash&&(eo.pathname=eo.pathname.replace(/\/$/,""));let es=ee;return ee=eo.toString(),en.removeSingleSlash||"/"!==eo.pathname||es.endsWith("/")||""!==eo.hash||(ee=ee.replace(/\/$/,"")),(en.removeTrailingSlash||"/"===eo.pathname)&&""===eo.hash&&en.removeSingleSlash&&(ee=ee.replace(/\/$/,"")),er&&!en.normalizeProtocol&&(ee=ee.replace(/^http:\/\//,"//")),en.stripProtocol&&(ee=ee.replace(/^(?:https?:)?\/\//,"")),ee}let ef=(ee,en=!1)=>{let er=/^(?:([a-z_][a-z0-9_-]{0,31})@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/,eo=en=>{let er=Error(en);throw er.subject_url=ee,er};"string"==typeof ee&&ee.trim()||eo("Invalid url."),ee.length>ef.MAX_INPUT_LENGTH&&eo("Input exceeds maximum length. If needed, change the value of parseUrl.MAX_INPUT_LENGTH."),en&&("object"!=typeof en&&(en={stripHash:!1}),ee=ed(ee,en));let es=ei.default(ee);if(es.parse_failed){let ee=es.href.match(er);ee?(es.protocols=["ssh"],es.protocol="ssh",es.resource=ee[2],es.host=ee[2],es.user=ee[1],es.pathname=`/${ee[3]}`,es.parse_failed=!1):eo("URL parsing failed.")}return es};ef.MAX_INPUT_LENGTH=2048,ee.exports=ef},6097:function(ee){"use strict";ee.exports=function(ee,en){!0===en&&(en=0);var er="";if("string"==typeof ee)try{er=new URL(ee).protocol}catch(ee){}else ee&&ee.constructor===URL&&(er=ee.protocol);var ei=er.split(/\:|\+/).filter(Boolean);return"number"==typeof en?ei[en]:ei}},6593:function(ee){var en={À:"A",Á:"A",Â:"A",Ã:"A",Ä:"A",Å:"A",Ấ:"A",Ắ:"A",Ẳ:"A",Ẵ:"A",Ặ:"A",Æ:"AE",Ầ:"A",Ằ:"A",Ȃ:"A",Ç:"C",Ḉ:"C",È:"E",É:"E",Ê:"E",Ë:"E",Ế:"E",Ḗ:"E",Ề:"E",Ḕ:"E",Ḝ:"E",Ȇ:"E",Ì:"I",Í:"I",Î:"I",Ï:"I",Ḯ:"I",Ȋ:"I",Ð:"D",Ñ:"N",Ò:"O",Ó:"O",Ô:"O",Õ:"O",Ö:"O",Ø:"O",Ố:"O",Ṍ:"O",Ṓ:"O",Ȏ:"O",Ù:"U",Ú:"U",Û:"U",Ü:"U",Ý:"Y",à:"a",á:"a",â:"a",ã:"a",ä:"a",å:"a",ấ:"a",ắ:"a",ẳ:"a",ẵ:"a",ặ:"a",æ:"ae",ầ:"a",ằ:"a",ȃ:"a",ç:"c",ḉ:"c",è:"e",é:"e",ê:"e",ë:"e",ế:"e",ḗ:"e",ề:"e",ḕ:"e",ḝ:"e",ȇ:"e",ì:"i",í:"i",î:"i",ï:"i",ḯ:"i",ȋ:"i",ð:"d",ñ:"n",ò:"o",ó:"o",ô:"o",õ:"o",ö:"o",ø:"o",ố:"o",ṍ:"o",ṓ:"o",ȏ:"o",ù:"u",ú:"u",û:"u",ü:"u",ý:"y",ÿ:"y",Ā:"A",ā:"a",Ă:"A",ă:"a",Ą:"A",ą:"a",Ć:"C",ć:"c",Ĉ:"C",ĉ:"c",Ċ:"C",ċ:"c",Č:"C",č:"c",C̆:"C",c̆:"c",Ď:"D",ď:"d",Đ:"D",đ:"d",Ē:"E",ē:"e",Ĕ:"E",ĕ:"e",Ė:"E",ė:"e",Ę:"E",ę:"e",Ě:"E",ě:"e",Ĝ:"G",Ǵ:"G",ĝ:"g",ǵ:"g",Ğ:"G",ğ:"g",Ġ:"G",ġ:"g",Ģ:"G",ģ:"g",Ĥ:"H",ĥ:"h",Ħ:"H",ħ:"h",Ḫ:"H",ḫ:"h",Ĩ:"I",ĩ:"i",Ī:"I",ī:"i",Ĭ:"I",ĭ:"i",Į:"I",į:"i",İ:"I",ı:"i",IJ:"IJ",ij:"ij",Ĵ:"J",ĵ:"j",Ķ:"K",ķ:"k",Ḱ:"K",ḱ:"k",K̆:"K",k̆:"k",Ĺ:"L",ĺ:"l",Ļ:"L",ļ:"l",Ľ:"L",ľ:"l",Ŀ:"L",ŀ:"l",Ł:"l",ł:"l",Ḿ:"M",ḿ:"m",M̆:"M",m̆:"m",Ń:"N",ń:"n",Ņ:"N",ņ:"n",Ň:"N",ň:"n",ʼn:"n",N̆:"N",n̆:"n",Ō:"O",ō:"o",Ŏ:"O",ŏ:"o",Ő:"O",ő:"o",Œ:"OE",œ:"oe",P̆:"P",p̆:"p",Ŕ:"R",ŕ:"r",Ŗ:"R",ŗ:"r",Ř:"R",ř:"r",R̆:"R",r̆:"r",Ȓ:"R",ȓ:"r",Ś:"S",ś:"s",Ŝ:"S",ŝ:"s",Ş:"S",Ș:"S",ș:"s",ş:"s",Š:"S",š:"s",Ţ:"T",ţ:"t",ț:"t",Ț:"T",Ť:"T",ť:"t",Ŧ:"T",ŧ:"t",T̆:"T",t̆:"t",Ũ:"U",ũ:"u",Ū:"U",ū:"u",Ŭ:"U",ŭ:"u",Ů:"U",ů:"u",Ű:"U",ű:"u",Ų:"U",ų:"u",Ȗ:"U",ȗ:"u",V̆:"V",v̆:"v",Ŵ:"W",ŵ:"w",Ẃ:"W",ẃ:"w",X̆:"X",x̆:"x",Ŷ:"Y",ŷ:"y",Ÿ:"Y",Y̆:"Y",y̆:"y",Ź:"Z",ź:"z",Ż:"Z",ż:"z",Ž:"Z",ž:"z",ſ:"s",ƒ:"f",Ơ:"O",ơ:"o",Ư:"U",ư:"u",Ǎ:"A",ǎ:"a",Ǐ:"I",ǐ:"i",Ǒ:"O",ǒ:"o",Ǔ:"U",ǔ:"u",Ǖ:"U",ǖ:"u",Ǘ:"U",ǘ:"u",Ǚ:"U",ǚ:"u",Ǜ:"U",ǜ:"u",Ứ:"U",ứ:"u",Ṹ:"U",ṹ:"u",Ǻ:"A",ǻ:"a",Ǽ:"AE",ǽ:"ae",Ǿ:"O",ǿ:"o",Þ:"TH",þ:"th",Ṕ:"P",ṕ:"p",Ṥ:"S",ṥ:"s",X́:"X",x́:"x",Ѓ:"Г",ѓ:"г",Ќ:"К",ќ:"к",A̋:"A",a̋:"a",E̋:"E",e̋:"e",I̋:"I",i̋:"i",Ǹ:"N",ǹ:"n",Ồ:"O",ồ:"o",Ṑ:"O",ṑ:"o",Ừ:"U",ừ:"u",Ẁ:"W",ẁ:"w",Ỳ:"Y",ỳ:"y",Ȁ:"A",ȁ:"a",Ȅ:"E",ȅ:"e",Ȉ:"I",ȉ:"i",Ȍ:"O",ȍ:"o",Ȑ:"R",ȑ:"r",Ȕ:"U",ȕ:"u",B̌:"B",b̌:"b",Č̣:"C",č̣:"c",Ê̌:"E",ê̌:"e",F̌:"F",f̌:"f",Ǧ:"G",ǧ:"g",Ȟ:"H",ȟ:"h",J̌:"J",ǰ:"j",Ǩ:"K",ǩ:"k",M̌:"M",m̌:"m",P̌:"P",p̌:"p",Q̌:"Q",q̌:"q",Ř̩:"R",ř̩:"r",Ṧ:"S",ṧ:"s",V̌:"V",v̌:"v",W̌:"W",w̌:"w",X̌:"X",x̌:"x",Y̌:"Y",y̌:"y",A̧:"A",a̧:"a",B̧:"B",b̧:"b",Ḑ:"D",ḑ:"d",Ȩ:"E",ȩ:"e",Ɛ̧:"E",ɛ̧:"e",Ḩ:"H",ḩ:"h",I̧:"I",i̧:"i",Ɨ̧:"I",ɨ̧:"i",M̧:"M",m̧:"m",O̧:"O",o̧:"o",Q̧:"Q",q̧:"q",U̧:"U",u̧:"u",X̧:"X",x̧:"x",Z̧:"Z",z̧:"z"},er=Object.keys(en).join("|"),ei=RegExp(er,"g"),eo=RegExp(er,""),es=function(ee){return ee.replace(ei,function(ee){return en[ee]})},eu=function(ee){return!!ee.match(eo)};ee.exports=es,ee.exports.has=eu,ee.exports.remove=es},4473:function(ee){"use strict";ee.exports=ee=>{let en=/^\\\\\?\\/.test(ee),er=/[^\u0000-\u0080]+/.test(ee);return en||er?ee:ee.replace(/\\/g,"/")}},9378:function(ee,en,er){let ei=er(3587),eo=er(2168),es="[^\\s'’\\(\\)!?;:\"-]",eu=RegExp(`(?:(?:(\\s?(?:^|[.\\(\\)!?;:"-])\\s*)(${es}))|(${es}))(${es}*[’']*${es}*)`,"g"),ec=ee=>ee.map(ee=>[RegExp(`\\b${ee}\\b`,"gi"),ee]);function ed(ee){let en=ee[0];return/\s/.test(en)?ee.slice(1):/[\(\)]/.test(en)?null:ee}ee.exports=(ee,en={})=>{ee=ee.toLowerCase().replace(eu,(ee,en="",er,eo,es,eu,ec)=>{let ef=ee.length+eu>=ec.length,eh=ed(ee);if(!eh)return ee;if(!er){let ee=eo+es;if(ei.has(ee)&&!ef)return eh}return en+(eo||er).toUpperCase()+es});let er=en.special||[],es=[...eo,...er],ef=ec(es);return ef.forEach(([en,er])=>{ee=ee.replace(en,er)}),ee}},3587:function(ee){let en=["for","and","nor","but","or","yet","so"],er=["a","an","the"],ei=["aboard","about","above","across","after","against","along","amid","among","anti","around","as","at","before","behind","below","beneath","beside","besides","between","beyond","but","by","concerning","considering","despite","down","during","except","excepting","excluding","following","for","from","in","inside","into","like","minus","near","of","off","on","onto","opposite","over","past","per","plus","regarding","round","save","since","than","through","to","toward","towards","under","underneath","unlike","until","up","upon","versus","via","with","within","without"];ee.exports=new Set([...en,...er,...ei])},2168:function(ee){let en=["ZEIT","ZEIT Inc.","Vercel","Vercel Inc.","CLI","API","HTTP","HTTPS","JSX","DNS","URL","now.sh","now.json","vercel.app","vercel.json","CI","CD","CDN","package.json","package.lock","yarn.lock","GitHub","GitLab","CSS","Sass","JS","JavaScript","TypeScript","HTML","WordPress","Next.js","Node.js","Webpack","Docker","Bash","Kubernetes","SWR","TinaCMS","UI","UX","TS","TSX","iPhone","iPad","watchOS","iOS","iPadOS","macOS","PHP","composer.json","composer.lock","CMS","SQL","C","C#","GraphQL","GraphiQL","JWT","JWTs"];ee.exports=en},6067:function(ee,en,er){er(1527)},310:function(ee,en,er){"use strict";er.d(en,{R:function(){return eo}});var ei,eo=((ei=eo||{}).Space=" ",ei.Enter="Enter",ei.Escape="Escape",ei.Backspace="Backspace",ei.Delete="Delete",ei.ArrowLeft="ArrowLeft",ei.ArrowUp="ArrowUp",ei.ArrowRight="ArrowRight",ei.ArrowDown="ArrowDown",ei.Home="Home",ei.End="End",ei.PageUp="PageUp",ei.PageDown="PageDown",ei.Tab="Tab",ei)},1479:function(ee,en,er){"use strict";er.d(en,{R:function(){return e8}});var ei,eo,es,eu,ec,ed=er(959),ef=er(3275),eh=er(9772),ep=er(2138),em=er(5132);function ex(ee,en){let[er,ei]=(0,ed.useState)(ee),eo=(0,em.E)(ee);return(0,ep.e)(()=>ei(eo.current),[eo,ei,...en]),er}var eg=er(851),ev=er(1806),ey=er(3015),eb=er(6050),ew=er(310),e_=er(531),ek=er(8913),ej=er(1720),eE=er(221),eO=er(5198),eC=er(537);let eT="div";var eS=((ei=eS||{})[ei.None=1]="None",ei[ei.Focusable=2]="Focusable",ei[ei.Hidden=4]="Hidden",ei);function eN(ee,en){let{features:er=1,...ei}=ee,eo={ref:en,"aria-hidden":(2&er)==2||void 0,style:{position:"fixed",top:1,left:1,width:1,height:0,padding:0,margin:-1,overflow:"hidden",clip:"rect(0, 0, 0, 0)",whiteSpace:"nowrap",borderWidth:"0",...(4&er)==4&&(2&er)!=2&&{display:"none"}}};return(0,ev.sY)({ourProps:eo,theirProps:ei,slot:{},defaultTag:eT,name:"Hidden"})}let eR=(0,ev.yV)(eN);function eI(ee={},en=null,er=[]){for(let[ei,eo]of Object.entries(ee))eL(er,eA(en,ei),eo);return er}function eA(ee,en){return ee?ee+"["+en+"]":en}function eL(ee,en,er){if(Array.isArray(er))for(let[ei,eo]of er.entries())eL(ee,eA(en,ei.toString()),eo);else er instanceof Date?ee.push([en,er.toISOString()]):"boolean"==typeof er?ee.push([en,er?"1":"0"]):"string"==typeof er?ee.push([en,er]):"number"==typeof er?ee.push([en,`${er}`]):null==er?ee.push([en,""]):eI(er,en,ee)}var eP=er(9322),eM=er(8902);function eZ(ee,en,er){let[ei,eo]=(0,ed.useState)(er),es=void 0!==ee,eu=(0,ed.useRef)(es),ec=(0,ed.useRef)(!1),ef=(0,ed.useRef)(!1);return!es||eu.current||ec.current?es||!eu.current||ef.current||(ef.current=!0,eu.current=es,console.error("A component is changing from controlled to uncontrolled. This may be caused by the value changing from a defined value to undefined, which should not happen.")):(ec.current=!0,eu.current=es,console.error("A component is changing from uncontrolled to controlled. This may be caused by the value changing from undefined to a defined value, which should not happen.")),[es?ee:ei,(0,eM.z)(ee=>(es||eo(ee),null==en?void 0:en(ee)))]}var eD=er(5694),eF=er(7840),e$=((eo=e$||{})[eo.Open=0]="Open",eo[eo.Closed=1]="Closed",eo),ez=((es=ez||{})[es.Single=0]="Single",es[es.Multi=1]="Multi",es),eH=((eu=eH||{})[eu.Pointer=0]="Pointer",eu[eu.Other=1]="Other",eu),eW=((ec=eW||{})[ec.OpenListbox=0]="OpenListbox",ec[ec.CloseListbox=1]="CloseListbox",ec[ec.GoToOption=2]="GoToOption",ec[ec.Search=3]="Search",ec[ec.ClearSearch=4]="ClearSearch",ec[ec.RegisterOption=5]="RegisterOption",ec[ec.UnregisterOption=6]="UnregisterOption",ec[ec.RegisterLabel=7]="RegisterLabel",ec);function eU(ee,en=ee=>ee){let er=null!==ee.activeOptionIndex?ee.options[ee.activeOptionIndex]:null,ei=(0,ej.z2)(en(ee.options.slice()),ee=>ee.dataRef.current.domRef.current),eo=er?ei.indexOf(er):null;return -1===eo&&(eo=null),{options:ei,activeOptionIndex:eo}}let eG={1:ee=>ee.dataRef.current.disabled||1===ee.listboxState?ee:{...ee,activeOptionIndex:null,listboxState:1},0(ee){if(ee.dataRef.current.disabled||0===ee.listboxState)return ee;let en=ee.activeOptionIndex,{isSelected:er}=ee.dataRef.current,ei=ee.options.findIndex(ee=>er(ee.dataRef.current.value));return -1!==ei&&(en=ei),{...ee,listboxState:0,activeOptionIndex:en}},2(ee,en){var er;if(ee.dataRef.current.disabled||1===ee.listboxState)return ee;let ei=eU(ee),eo=(0,e_.d)(en,{resolveItems:()=>ei.options,resolveActiveIndex:()=>ei.activeOptionIndex,resolveId:ee=>ee.id,resolveDisabled:ee=>ee.dataRef.current.disabled});return{...ee,...ei,searchQuery:"",activeOptionIndex:eo,activationTrigger:null!=(er=en.trigger)?er:1}},3:(ee,en)=>{if(ee.dataRef.current.disabled||1===ee.listboxState)return ee;let er=""!==ee.searchQuery?0:1,ei=ee.searchQuery+en.value.toLowerCase(),eo=(null!==ee.activeOptionIndex?ee.options.slice(ee.activeOptionIndex+er).concat(ee.options.slice(0,ee.activeOptionIndex+er)):ee.options).find(ee=>{var en;return!ee.dataRef.current.disabled&&(null==(en=ee.dataRef.current.textValue)?void 0:en.startsWith(ei))}),es=eo?ee.options.indexOf(eo):-1;return -1===es||es===ee.activeOptionIndex?{...ee,searchQuery:ei}:{...ee,searchQuery:ei,activeOptionIndex:es,activationTrigger:1}},4:ee=>ee.dataRef.current.disabled||1===ee.listboxState||""===ee.searchQuery?ee:{...ee,searchQuery:""},5:(ee,en)=>{let er={id:en.id,dataRef:en.dataRef},ei=eU(ee,ee=>[...ee,er]);return null===ee.activeOptionIndex&&ee.dataRef.current.isSelected(en.dataRef.current.value)&&(ei.activeOptionIndex=ei.options.indexOf(er)),{...ee,...ei}},6:(ee,en)=>{let er=eU(ee,ee=>{let er=ee.findIndex(ee=>ee.id===en.id);return -1!==er&&ee.splice(er,1),ee});return{...ee,...er,activationTrigger:1}},7:(ee,en)=>({...ee,labelId:en.id})},eV=(0,ed.createContext)(null);function eB(ee){let en=(0,ed.useContext)(eV);if(null===en){let en=Error(`<${ee} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(en,eB),en}return en}eV.displayName="ListboxActionsContext";let eK=(0,ed.createContext)(null);function eQ(ee){let en=(0,ed.useContext)(eK);if(null===en){let en=Error(`<${ee} /> is missing a parent component.`);throw Error.captureStackTrace&&Error.captureStackTrace(en,eQ),en}return en}function eJ(ee,en){return(0,ey.E)(en.type,eG,ee,en)}eK.displayName="ListboxDataContext";let eY=ed.Fragment;function eX(ee,en){let{value:er,defaultValue:ei,form:eo,name:es,onChange:eu,by:ec=(ee,en)=>ee===en,disabled:eh=!1,horizontal:em=!1,multiple:ex=!1,...eb}=ee,ew=em?"horizontal":"vertical",ek=(0,eg.T)(en),[eO=ex?[]:void 0,eT]=eZ(er,eu,ei),[eN,eA]=(0,ed.useReducer)(eJ,{dataRef:(0,ed.createRef)(),listboxState:1,options:[],searchQuery:"",labelId:null,activeOptionIndex:null,activationTrigger:1}),eL=(0,ed.useRef)({static:!1,hold:!1}),eP=(0,ed.useRef)(null),eD=(0,ed.useRef)(null),eF=(0,ed.useRef)(null),e$=(0,eM.z)("string"==typeof ec?(ee,en)=>{let er=ec;return(null==ee?void 0:ee[er])===(null==en?void 0:en[er])}:ec),ez=(0,ed.useCallback)(ee=>(0,ey.E)(eH.mode,{1:()=>eO.some(en=>e$(en,ee)),0:()=>e$(eO,ee)}),[eO]),eH=(0,ed.useMemo)(()=>({...eN,value:eO,disabled:eh,mode:ex?1:0,orientation:ew,compare:e$,isSelected:ez,optionsPropsRef:eL,labelRef:eP,buttonRef:eD,optionsRef:eF}),[eO,eh,ex,eN]);(0,ep.e)(()=>{eN.dataRef.current=eH},[eH]),(0,eC.O)([eH.buttonRef,eH.optionsRef],(ee,en)=>{var er;eA({type:1}),(0,ej.sP)(en,ej.tJ.Loose)||(ee.preventDefault(),null==(er=eH.buttonRef.current)||er.focus())},0===eH.listboxState);let eW=(0,ed.useMemo)(()=>({open:0===eH.listboxState,disabled:eh,value:eO}),[eH,eh,eO]),eU=(0,eM.z)(ee=>{let en=eH.options.find(en=>en.id===ee);en&&e2(en.dataRef.current.value)}),eG=(0,eM.z)(()=>{if(null!==eH.activeOptionIndex){let{dataRef:ee,id:en}=eH.options[eH.activeOptionIndex];e2(ee.current.value),eA({type:2,focus:e_.T.Specific,id:en})}}),eB=(0,eM.z)(()=>eA({type:0})),eQ=(0,eM.z)(()=>eA({type:1})),eX=(0,eM.z)((ee,en,er)=>ee===e_.T.Specific?eA({type:2,focus:e_.T.Specific,id:en,trigger:er}):eA({type:2,focus:ee,trigger:er})),e0=(0,eM.z)((ee,en)=>(eA({type:5,id:ee,dataRef:en}),()=>eA({type:6,id:ee}))),e1=(0,eM.z)(ee=>(eA({type:7,id:ee}),()=>eA({type:7,id:null}))),e2=(0,eM.z)(ee=>(0,ey.E)(eH.mode,{0:()=>null==eT?void 0:eT(ee),1(){let en=eH.value.slice(),er=en.findIndex(en=>e$(en,ee));return -1===er?en.push(ee):en.splice(er,1),null==eT?void 0:eT(en)}})),e4=(0,eM.z)(ee=>eA({type:3,value:ee})),e5=(0,eM.z)(()=>eA({type:4})),e3=(0,ed.useMemo)(()=>({onChange:e2,registerOption:e0,registerLabel:e1,goToOption:eX,closeListbox:eQ,openListbox:eB,selectActiveOption:eG,selectOption:eU,search:e4,clearSearch:e5}),[]),e9={ref:ek},e7=(0,ed.useRef)(null),e6=(0,ef.G)();return(0,ed.useEffect)(()=>{e7.current&&void 0!==ei&&e6.addEventListener(e7.current,"reset",()=>{e2(ei)})},[e7,e2]),ed.createElement(eV.Provider,{value:e3},ed.createElement(eK.Provider,{value:eH},ed.createElement(eE.up,{value:(0,ey.E)(eH.listboxState,{0:eE.ZM.Open,1:eE.ZM.Closed})},null!=es&&null!=eO&&eI({[es]:eO}).map(([ee,en],er)=>ed.createElement(eR,{features:eS.Hidden,ref:0===er?ee=>{var en;e7.current=null!=(en=null==ee?void 0:ee.closest("form"))?en:null}:void 0,...(0,ev.oA)({key:ee,as:"input",type:"hidden",hidden:!0,readOnly:!0,form:eo,name:ee,value:en})})),(0,ev.sY)({ourProps:e9,theirProps:eb,slot:eW,defaultTag:eY,name:"Listbox"}))))}let e0="button";function e1(ee,en){var er;let ei=(0,eh.M)(),{id:eo=`headlessui-listbox-button-${ei}`,...es}=ee,eu=eQ("Listbox.Button"),ec=eB("Listbox.Button"),ep=(0,eg.T)(eu.buttonRef,en),em=(0,ef.G)(),ey=(0,eM.z)(ee=>{switch(ee.key){case ew.R.Space:case ew.R.Enter:case ew.R.ArrowDown:ee.preventDefault(),ec.openListbox(),em.nextFrame(()=>{eu.value||ec.goToOption(e_.T.First)});break;case ew.R.ArrowUp:ee.preventDefault(),ec.openListbox(),em.nextFrame(()=>{eu.value||ec.goToOption(e_.T.Last)})}}),eb=(0,eM.z)(ee=>{ee.key===ew.R.Space&&ee.preventDefault()}),ej=(0,eM.z)(ee=>{if((0,ek.P)(ee.currentTarget))return ee.preventDefault();0===eu.listboxState?(ec.closeListbox(),em.nextFrame(()=>{var ee;return null==(ee=eu.buttonRef.current)?void 0:ee.focus({preventScroll:!0})})):(ee.preventDefault(),ec.openListbox())}),eE=ex(()=>{if(eu.labelId)return[eu.labelId,eo].join(" ")},[eu.labelId,eo]),eC=(0,ed.useMemo)(()=>({open:0===eu.listboxState,disabled:eu.disabled,value:eu.value}),[eu]),eT={ref:ep,id:eo,type:(0,eO.f)(ee,eu.buttonRef),"aria-haspopup":"listbox","aria-controls":null==(er=eu.optionsRef.current)?void 0:er.id,"aria-expanded":eu.disabled?void 0:0===eu.listboxState,"aria-labelledby":eE,disabled:eu.disabled,onKeyDown:ey,onKeyUp:eb,onClick:ej};return(0,ev.sY)({ourProps:eT,theirProps:es,slot:eC,defaultTag:e0,name:"Listbox.Button"})}let e2="label";function e4(ee,en){let er=(0,eh.M)(),{id:ei=`headlessui-listbox-label-${er}`,...eo}=ee,es=eQ("Listbox.Label"),eu=eB("Listbox.Label"),ec=(0,eg.T)(es.labelRef,en);(0,ep.e)(()=>eu.registerLabel(ei),[ei]);let ef=(0,eM.z)(()=>{var ee;return null==(ee=es.buttonRef.current)?void 0:ee.focus({preventScroll:!0})}),em=(0,ed.useMemo)(()=>({open:0===es.listboxState,disabled:es.disabled}),[es]);return(0,ev.sY)({ourProps:{ref:ec,id:ei,onClick:ef},theirProps:eo,slot:em,defaultTag:e2,name:"Listbox.Label"})}let e5="ul",e3=ev.AN.RenderStrategy|ev.AN.Static;function e9(ee,en){var er;let ei=(0,eh.M)(),{id:eo=`headlessui-listbox-options-${ei}`,...es}=ee,eu=eQ("Listbox.Options"),ec=eB("Listbox.Options"),ep=(0,eg.T)(eu.optionsRef,en),em=(0,ef.G)(),ek=(0,ef.G)(),ej=(0,eE.oJ)(),eO=null!==ej?(ej&eE.ZM.Open)===eE.ZM.Open:0===eu.listboxState;(0,ed.useEffect)(()=>{var ee;let en=eu.optionsRef.current;en&&0===eu.listboxState&&en!==(null==(ee=(0,eP.r)(en))?void 0:ee.activeElement)&&en.focus({preventScroll:!0})},[eu.listboxState,eu.optionsRef]);let eC=(0,eM.z)(ee=>{switch(ek.dispose(),ee.key){case ew.R.Space:if(""!==eu.searchQuery)return ee.preventDefault(),ee.stopPropagation(),ec.search(ee.key);case ew.R.Enter:if(ee.preventDefault(),ee.stopPropagation(),null!==eu.activeOptionIndex){let{dataRef:ee}=eu.options[eu.activeOptionIndex];ec.onChange(ee.current.value)}0===eu.mode&&(ec.closeListbox(),(0,eb.k)().nextFrame(()=>{var ee;return null==(ee=eu.buttonRef.current)?void 0:ee.focus({preventScroll:!0})}));break;case(0,ey.E)(eu.orientation,{vertical:ew.R.ArrowDown,horizontal:ew.R.ArrowRight}):return ee.preventDefault(),ee.stopPropagation(),ec.goToOption(e_.T.Next);case(0,ey.E)(eu.orientation,{vertical:ew.R.ArrowUp,horizontal:ew.R.ArrowLeft}):return ee.preventDefault(),ee.stopPropagation(),ec.goToOption(e_.T.Previous);case ew.R.Home:case ew.R.PageUp:return ee.preventDefault(),ee.stopPropagation(),ec.goToOption(e_.T.First);case ew.R.End:case ew.R.PageDown:return ee.preventDefault(),ee.stopPropagation(),ec.goToOption(e_.T.Last);case ew.R.Escape:return ee.preventDefault(),ee.stopPropagation(),ec.closeListbox(),em.nextFrame(()=>{var ee;return null==(ee=eu.buttonRef.current)?void 0:ee.focus({preventScroll:!0})});case ew.R.Tab:ee.preventDefault(),ee.stopPropagation();break;default:1===ee.key.length&&(ec.search(ee.key),ek.setTimeout(()=>ec.clearSearch(),350))}}),eT=ex(()=>{var ee,en,er;return null!=(er=null==(ee=eu.labelRef.current)?void 0:ee.id)?er:null==(en=eu.buttonRef.current)?void 0:en.id},[eu.labelRef.current,eu.buttonRef.current]),eS=(0,ed.useMemo)(()=>({open:0===eu.listboxState}),[eu]),eN={"aria-activedescendant":null===eu.activeOptionIndex||null==(er=eu.options[eu.activeOptionIndex])?void 0:er.id,"aria-multiselectable":1===eu.mode||void 0,"aria-labelledby":eT,"aria-orientation":eu.orientation,id:eo,onKeyDown:eC,role:"listbox",tabIndex:0,ref:ep};return(0,ev.sY)({ourProps:eN,theirProps:es,slot:eS,defaultTag:e5,features:e3,visible:eO,name:"Listbox.Options"})}let e7="li";function e6(ee,en){let er=(0,eh.M)(),{id:ei=`headlessui-listbox-option-${er}`,disabled:eo=!1,value:es,...eu}=ee,ec=eQ("Listbox.Option"),ef=eB("Listbox.Option"),ex=null!==ec.activeOptionIndex&&ec.options[ec.activeOptionIndex].id===ei,ey=ec.isSelected(es),ew=(0,ed.useRef)(null),ek=(0,eF.x)(ew),ej=(0,em.E)({disabled:eo,value:es,domRef:ew,get textValue(){return ek()}}),eE=(0,eg.T)(en,ew);(0,ep.e)(()=>{if(0!==ec.listboxState||!ex||0===ec.activationTrigger)return;let ee=(0,eb.k)();return ee.requestAnimationFrame(()=>{var ee,en;null==(en=null==(ee=ew.current)?void 0:ee.scrollIntoView)||en.call(ee,{block:"nearest"})}),ee.dispose},[ew,ex,ec.listboxState,ec.activationTrigger,ec.activeOptionIndex]),(0,ep.e)(()=>ef.registerOption(ei,ej),[ej,ei]);let eO=(0,eM.z)(ee=>{if(eo)return ee.preventDefault();ef.onChange(es),0===ec.mode&&(ef.closeListbox(),(0,eb.k)().nextFrame(()=>{var ee;return null==(ee=ec.buttonRef.current)?void 0:ee.focus({preventScroll:!0})}))}),eC=(0,eM.z)(()=>{if(eo)return ef.goToOption(e_.T.Nothing);ef.goToOption(e_.T.Specific,ei)}),eT=(0,eD.g)(),eS=(0,eM.z)(ee=>eT.update(ee)),eN=(0,eM.z)(ee=>{eT.wasMoved(ee)&&(eo||ex||ef.goToOption(e_.T.Specific,ei,0))}),eR=(0,eM.z)(ee=>{eT.wasMoved(ee)&&(eo||ex&&ef.goToOption(e_.T.Nothing))}),eI=(0,ed.useMemo)(()=>({active:ex,selected:ey,disabled:eo}),[ex,ey,eo]);return(0,ev.sY)({ourProps:{id:ei,ref:eE,role:"option",tabIndex:!0===eo?void 0:-1,"aria-disabled":!0===eo||void 0,"aria-selected":ey,disabled:void 0,onClick:eO,onFocus:eC,onPointerEnter:eS,onMouseEnter:eS,onPointerMove:eN,onMouseMove:eN,onPointerLeave:eR,onMouseLeave:eR},theirProps:eu,slot:eI,defaultTag:e7,name:"Listbox.Option"})}let e8=Object.assign((0,ev.yV)(eX),{Button:(0,ev.yV)(e1),Label:(0,ev.yV)(e4),Options:(0,ev.yV)(e9),Option:(0,ev.yV)(e6)})},6194:function(ee,en,er){"use strict";er.d(en,{u:function(){return eB}});var ei,eo=er(959),es=er(1806),eu=er(221),ec=er(3015),ed=er(2138);function ef(){let ee=(0,eo.useRef)(!1);return(0,ed.e)(()=>(ee.current=!0,()=>{ee.current=!1}),[]),ee}var eh=er(5132),ep=er(7536),em=er(851);function ex(ee){let en={called:!1};return(...er)=>{if(!en.called)return en.called=!0,ee(...er)}}var eg=er(6050);function ev(ee,...en){ee&&en.length>0&&ee.classList.add(...en)}function ey(ee,...en){ee&&en.length>0&&ee.classList.remove(...en)}function eb(ee,en){let er=(0,eg.k)();if(!ee)return er.dispose;let{transitionDuration:ei,transitionDelay:eo}=getComputedStyle(ee),[es,eu]=[ei,eo].map(ee=>{let[en=0]=ee.split(",").filter(Boolean).map(ee=>ee.includes("ms")?parseFloat(ee):1e3*parseFloat(ee)).sort((ee,en)=>en-ee);return en}),ec=es+eu;if(0!==ec){er.group(er=>{er.setTimeout(()=>{en(),er.dispose()},ec),er.addEventListener(ee,"transitionrun",ee=>{ee.target===ee.currentTarget&&er.dispose()})});let ei=er.addEventListener(ee,"transitionend",ee=>{ee.target===ee.currentTarget&&(en(),ei())})}else en();return er.add(()=>en()),er.dispose}function ew(ee,en,er,ei){let eo=er?"enter":"leave",es=(0,eg.k)(),eu=void 0!==ei?ex(ei):()=>{};"enter"===eo&&(ee.removeAttribute("hidden"),ee.style.display="");let ed=(0,ec.E)(eo,{enter:()=>en.enter,leave:()=>en.leave}),ef=(0,ec.E)(eo,{enter:()=>en.enterTo,leave:()=>en.leaveTo}),eh=(0,ec.E)(eo,{enter:()=>en.enterFrom,leave:()=>en.leaveFrom});return ey(ee,...en.enter,...en.enterTo,...en.enterFrom,...en.leave,...en.leaveFrom,...en.leaveTo,...en.entered),ev(ee,...ed,...eh),es.nextFrame(()=>{ey(ee,...eh),ev(ee,...ef),eb(ee,()=>(ey(ee,...ed),ev(ee,...en.entered),eu()))}),es.dispose}var e_=er(3275);function ek({container:ee,direction:en,classes:er,onStart:ei,onStop:eo}){let es=ef(),eu=(0,e_.G)(),ec=(0,eh.E)(en);(0,ed.e)(()=>{let en=(0,eg.k)();eu.add(en.dispose);let ed=ee.current;if(ed&&"idle"!==ec.current&&es.current)return en.dispose(),ei.current(ec.current),en.add(ew(ed,er.current,"enter"===ec.current,()=>{en.dispose(),eo.current(ec.current)})),en.dispose},[en])}var ej=er(8902),eE=er(9465);function eO(ee=0){let[en,er]=(0,eo.useState)(ee),ei=ef(),es=(0,eo.useCallback)(ee=>{ei.current&&er(en=>en|ee)},[en,ei]),eu=(0,eo.useCallback)(ee=>!!(en&ee),[en]);return{flags:en,addFlag:es,hasFlag:eu,removeFlag:(0,eo.useCallback)(ee=>{ei.current&&er(en=>en&~ee)},[er,ei]),toggleFlag:(0,eo.useCallback)(ee=>{ei.current&&er(en=>en^ee)},[er])}}function eC(ee=""){return ee.split(" ").filter(ee=>ee.trim().length>1)}let eT=(0,eo.createContext)(null);eT.displayName="TransitionContext";var eS=((ei=eS||{}).Visible="visible",ei.Hidden="hidden",ei);function eN(){let ee=(0,eo.useContext)(eT);if(null===ee)throw Error("A is used but it is missing a parent or .");return ee}function eR(){let ee=(0,eo.useContext)(eI);if(null===ee)throw Error("A is used but it is missing a parent or .");return ee}let eI=(0,eo.createContext)(null);function eA(ee){return"children"in ee?eA(ee.children):ee.current.filter(({el:ee})=>null!==ee.current).filter(({state:ee})=>"visible"===ee).length>0}function eL(ee,en){let er=(0,eh.E)(ee),ei=(0,eo.useRef)([]),eu=ef(),ed=(0,e_.G)(),ep=(0,ej.z)((ee,en=es.l4.Hidden)=>{let eo=ei.current.findIndex(({el:en})=>en===ee);-1!==eo&&((0,ec.E)(en,{[es.l4.Unmount](){ei.current.splice(eo,1)},[es.l4.Hidden](){ei.current[eo].state="hidden"}}),ed.microTask(()=>{var ee;!eA(ei)&&eu.current&&(null==(ee=er.current)||ee.call(er))}))}),em=(0,ej.z)(ee=>{let en=ei.current.find(({el:en})=>en===ee);return en?"visible"!==en.state&&(en.state="visible"):ei.current.push({el:ee,state:"visible"}),()=>ep(ee,es.l4.Unmount)}),ex=(0,eo.useRef)([]),eg=(0,eo.useRef)(Promise.resolve()),ev=(0,eo.useRef)({enter:[],leave:[],idle:[]}),ey=(0,ej.z)((ee,er,ei)=>{ex.current.splice(0),en&&(en.chains.current[er]=en.chains.current[er].filter(([en])=>en!==ee)),null==en||en.chains.current[er].push([ee,new Promise(ee=>{ex.current.push(ee)})]),null==en||en.chains.current[er].push([ee,new Promise(ee=>{Promise.all(ev.current[er].map(([ee,en])=>en)).then(()=>ee())})]),"enter"===er?eg.current=eg.current.then(()=>null==en?void 0:en.wait.current).then(()=>ei(er)):ei(er)}),eb=(0,ej.z)((ee,en,er)=>{Promise.all(ev.current[en].splice(0).map(([ee,en])=>en)).then(()=>{var ee;null==(ee=ex.current.shift())||ee()}).then(()=>er(en))});return(0,eo.useMemo)(()=>({children:ei,register:em,unregister:ep,onStart:ey,onStop:eb,wait:eg,chains:ev}),[em,ep,ei,ey,eb,ev,eg])}function eP(){}eI.displayName="NestingContext";let eM=["beforeEnter","afterEnter","beforeLeave","afterLeave"];function eZ(ee){var en;let er={};for(let ei of eM)er[ei]=null!=(en=ee[ei])?en:eP;return er}function eD(ee){let en=(0,eo.useRef)(eZ(ee));return(0,eo.useEffect)(()=>{en.current=eZ(ee)},[ee]),en}let eF="div",e$=es.AN.RenderStrategy;function ez(ee,en){let{beforeEnter:er,afterEnter:ei,beforeLeave:ed,afterLeave:ef,enter:ex,enterFrom:eg,enterTo:ev,entered:ey,leave:eb,leaveFrom:ew,leaveTo:e_,...eT}=ee,eS=(0,eo.useRef)(null),eP=(0,em.T)(eS,en),eM=eT.unmount?es.l4.Unmount:es.l4.Hidden,{show:eZ,appear:ez,initial:eH}=eN(),[eW,eU]=(0,eo.useState)(eZ?"visible":"hidden"),eG=eR(),{register:eV,unregister:eB}=eG,eK=(0,eo.useRef)(null);(0,eo.useEffect)(()=>eV(eS),[eV,eS]),(0,eo.useEffect)(()=>{if(eM===es.l4.Hidden&&eS.current){if(eZ&&"visible"!==eW){eU("visible");return}return(0,ec.E)(eW,{hidden:()=>eB(eS),visible:()=>eV(eS)})}},[eW,eS,eV,eB,eZ,eM]);let eQ=(0,eh.E)({enter:eC(ex),enterFrom:eC(eg),enterTo:eC(ev),entered:eC(ey),leave:eC(eb),leaveFrom:eC(ew),leaveTo:eC(e_)}),eJ=eD({beforeEnter:er,afterEnter:ei,beforeLeave:ed,afterLeave:ef}),eY=(0,ep.H)();(0,eo.useEffect)(()=>{if(eY&&"visible"===eW&&null===eS.current)throw Error("Did you forget to passthrough the `ref` to the actual DOM node?")},[eS,eW,eY]);let eX=eH&&!ez,e0=!eY||eX||eK.current===eZ?"idle":eZ?"enter":"leave",e1=eO(0),e2=(0,ej.z)(ee=>(0,ec.E)(ee,{enter:()=>{e1.addFlag(eu.ZM.Opening),eJ.current.beforeEnter()},leave:()=>{e1.addFlag(eu.ZM.Closing),eJ.current.beforeLeave()},idle:()=>{}})),e4=(0,ej.z)(ee=>(0,ec.E)(ee,{enter:()=>{e1.removeFlag(eu.ZM.Opening),eJ.current.afterEnter()},leave:()=>{e1.removeFlag(eu.ZM.Closing),eJ.current.afterLeave()},idle:()=>{}})),e5=eL(()=>{eU("hidden"),eB(eS)},eG);ek({container:eS,classes:eQ,direction:e0,onStart:(0,eh.E)(ee=>{e5.onStart(eS,ee,e2)}),onStop:(0,eh.E)(ee=>{e5.onStop(eS,ee,e4),"leave"!==ee||eA(e5)||(eU("hidden"),eB(eS))})}),(0,eo.useEffect)(()=>{eX&&(eM===es.l4.Hidden?eK.current=null:eK.current=eZ)},[eZ,eX,eW]);let e3=eT,e9={ref:eP};return ez&&eZ&&eH&&(e3={...e3,className:(0,eE.A)(eT.className,...eQ.current.enter,...eQ.current.enterFrom)}),eo.createElement(eI.Provider,{value:e5},eo.createElement(eu.up,{value:(0,ec.E)(eW,{visible:eu.ZM.Open,hidden:eu.ZM.Closed})|e1.flags},(0,es.sY)({ourProps:e9,theirProps:e3,defaultTag:eF,features:e$,visible:"visible"===eW,name:"Transition.Child"})))}function eH(ee,en){let{show:er,appear:ei=!1,unmount:ec,...ef}=ee,eh=(0,eo.useRef)(null),ex=(0,em.T)(eh,en);(0,ep.H)();let eg=(0,eu.oJ)();if(void 0===er&&null!==eg&&(er=(eg&eu.ZM.Open)===eu.ZM.Open),![!0,!1].includes(er))throw Error("A is used but it is missing a `show={true | false}` prop.");let[ev,ey]=(0,eo.useState)(er?"visible":"hidden"),eb=eL(()=>{ey("hidden")}),[ew,e_]=(0,eo.useState)(!0),ek=(0,eo.useRef)([er]);(0,ed.e)(()=>{!1!==ew&&ek.current[ek.current.length-1]!==er&&(ek.current.push(er),e_(!1))},[ek,er]);let eE=(0,eo.useMemo)(()=>({show:er,appear:ei,initial:ew}),[er,ei,ew]);(0,eo.useEffect)(()=>{if(er)ey("visible");else if(eA(eb)){let ee=eh.current;if(!ee)return;let en=ee.getBoundingClientRect();0===en.x&&0===en.y&&0===en.width&&0===en.height&&ey("hidden")}else ey("hidden")},[er,eb]);let eO={unmount:ec},eC=(0,ej.z)(()=>{var en;ew&&e_(!1),null==(en=ee.beforeEnter)||en.call(ee)}),eS=(0,ej.z)(()=>{var en;ew&&e_(!1),null==(en=ee.beforeLeave)||en.call(ee)});return eo.createElement(eI.Provider,{value:eb},eo.createElement(eT.Provider,{value:eE},(0,es.sY)({ourProps:{...eO,as:eo.Fragment,children:eo.createElement(eG,{ref:ex,...eO,...ef,beforeEnter:eC,beforeLeave:eS})},theirProps:{},defaultTag:eo.Fragment,features:e$,visible:"visible"===ev,name:"Transition"})))}function eW(ee,en){let er=null!==(0,eo.useContext)(eT),ei=null!==(0,eu.oJ)();return eo.createElement(eo.Fragment,null,!er&&ei?eo.createElement(eU,{ref:en,...ee}):eo.createElement(eG,{ref:en,...ee}))}let eU=(0,es.yV)(eH),eG=(0,es.yV)(ez),eV=(0,es.yV)(eW),eB=Object.assign(eU,{Child:eV,Root:eU})},3275:function(ee,en,er){"use strict";er.d(en,{G:function(){return es}});var ei=er(959),eo=er(6050);function es(){let[ee]=(0,ei.useState)(eo.k);return(0,ei.useEffect)(()=>()=>ee.dispose(),[ee]),ee}},8902:function(ee,en,er){"use strict";er.d(en,{z:function(){return es}});var ei=er(959),eo=er(5132);let es=function(ee){let en=(0,eo.E)(ee);return ei.useCallback((...ee)=>en.current(...ee),[en])}},9772:function(ee,en,er){"use strict";er.d(en,{M:function(){return ed}});var ei,eo=er(959),es=er(2138),eu=er(7536),ec=er(1124);let ed=null!=(ei=eo.useId)?ei:function(){let ee=(0,eu.H)(),[en,er]=eo.useState(ee?()=>ec.O.nextId():null);return(0,es.e)(()=>{null===en&&er(ec.O.nextId())},[en]),null!=en?""+en:void 0}},2138:function(ee,en,er){"use strict";er.d(en,{e:function(){return es}});var ei=er(959),eo=er(1124);let es=(ee,en)=>{eo.O.isServer?(0,ei.useEffect)(ee,en):(0,ei.useLayoutEffect)(ee,en)}},5132:function(ee,en,er){"use strict";er.d(en,{E:function(){return es}});var ei=er(959),eo=er(2138);function es(ee){let en=(0,ei.useRef)(ee);return(0,eo.e)(()=>{en.current=ee},[ee]),en}},537:function(ee,en,er){"use strict";er.d(en,{O:function(){return ed}});var ei=er(959),eo=er(1720),es=er(5132);function eu(ee,en,er){let eo=(0,es.E)(en);(0,ei.useEffect)(()=>{function en(ee){eo.current(ee)}return document.addEventListener(ee,en,er),()=>document.removeEventListener(ee,en,er)},[ee,er])}function ec(ee,en,er){let eo=(0,es.E)(en);(0,ei.useEffect)(()=>{function en(ee){eo.current(ee)}return window.addEventListener(ee,en,er),()=>window.removeEventListener(ee,en,er)},[ee,er])}function ed(ee,en,er=!0){let es=(0,ei.useRef)(!1);function ed(er,ei){if(!es.current||er.defaultPrevented)return;let eu=ei(er);if(null!==eu&&eu.getRootNode().contains(eu)){for(let en of function ee(en){return"function"==typeof en?ee(en()):Array.isArray(en)||en instanceof Set?en:[en]}(ee)){if(null===en)continue;let ee=en instanceof HTMLElement?en:en.current;if(null!=ee&&ee.contains(eu)||er.composed&&er.composedPath().includes(ee))return}return(0,eo.sP)(eu,eo.tJ.Loose)||-1===eu.tabIndex||er.preventDefault(),en(er,eu)}}(0,ei.useEffect)(()=>{requestAnimationFrame(()=>{es.current=er})},[er]);let ef=(0,ei.useRef)(null);eu("mousedown",ee=>{var en,er;es.current&&(ef.current=(null==(er=null==(en=ee.composedPath)?void 0:en.call(ee))?void 0:er[0])||ee.target)},!0),eu("click",ee=>{ef.current&&(ed(ee,()=>ef.current),ef.current=null)},!0),ec("blur",ee=>ed(ee,()=>window.document.activeElement instanceof HTMLIFrameElement?window.document.activeElement:null),!0)}},5198:function(ee,en,er){"use strict";er.d(en,{f:function(){return eu}});var ei=er(959),eo=er(2138);function es(ee){var en;if(ee.type)return ee.type;let er=null!=(en=ee.as)?en:"button";if("string"==typeof er&&"button"===er.toLowerCase())return"button"}function eu(ee,en){let[er,eu]=(0,ei.useState)(()=>es(ee));return(0,eo.e)(()=>{eu(es(ee))},[ee.type,ee.as]),(0,eo.e)(()=>{er||en.current&&en.current instanceof HTMLButtonElement&&!en.current.hasAttribute("type")&&eu("button")},[er,en]),er}},7536:function(ee,en,er){"use strict";er.d(en,{H:function(){return es}});var ei=er(959),eo=er(1124);function es(){let[ee,en]=(0,ei.useState)(eo.O.isHandoffComplete);return ee&&!1===eo.O.isHandoffComplete&&en(!1),(0,ei.useEffect)(()=>{!0!==ee&&en(!0)},[ee]),(0,ei.useEffect)(()=>eo.O.handoff(),[]),ee}},851:function(ee,en,er){"use strict";er.d(en,{T:function(){return eu}});var ei=er(959),eo=er(8902);let es=Symbol();function eu(...ee){let en=(0,ei.useRef)(ee);(0,ei.useEffect)(()=>{en.current=ee},[ee]);let er=(0,eo.z)(ee=>{for(let er of en.current)null!=er&&("function"==typeof er?er(ee):er.current=ee)});return ee.every(ee=>null==ee||(null==ee?void 0:ee[es]))?void 0:er}},7840:function(ee,en,er){"use strict";er.d(en,{x:function(){return ed}});var ei=er(959);let eo=/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g;function es(ee){var en,er;let ei=null!=(en=ee.innerText)?en:"",es=ee.cloneNode(!0);if(!(es instanceof HTMLElement))return ei;let eu=!1;for(let ee of es.querySelectorAll('[hidden],[aria-hidden],[role="img"]'))ee.remove(),eu=!0;let ec=eu?null!=(er=es.innerText)?er:"":ei;return eo.test(ec)&&(ec=ec.replace(eo,"")),ec}function eu(ee){let en=ee.getAttribute("aria-label");if("string"==typeof en)return en.trim();let er=ee.getAttribute("aria-labelledby");if(er){let ee=er.split(" ").map(ee=>{let en=document.getElementById(ee);if(en){let ee=en.getAttribute("aria-label");return"string"==typeof ee?ee.trim():es(en).trim()}return null}).filter(Boolean);if(ee.length>0)return ee.join(", ")}return es(ee).trim()}var ec=er(8902);function ed(ee){let en=(0,ei.useRef)(""),er=(0,ei.useRef)("");return(0,ec.z)(()=>{let ei=ee.current;if(!ei)return"";let eo=ei.innerText;if(en.current===eo)return er.current;let es=eu(ei).trim().toLowerCase();return en.current=eo,er.current=es,es})}},5694:function(ee,en,er){"use strict";er.d(en,{g:function(){return es}});var ei=er(959);function eo(ee){return[ee.screenX,ee.screenY]}function es(){let ee=(0,ei.useRef)([-1,-1]);return{wasMoved(en){let er=eo(en);return(ee.current[0]!==er[0]||ee.current[1]!==er[1])&&(ee.current=er,!0)},update(en){ee.current=eo(en)}}}},221:function(ee,en,er){"use strict";er.d(en,{ZM:function(){return eu},oJ:function(){return ec},up:function(){return ed}});var ei,eo=er(959);let es=(0,eo.createContext)(null);es.displayName="OpenClosedContext";var eu=((ei=eu||{})[ei.Open=1]="Open",ei[ei.Closed=2]="Closed",ei[ei.Closing=4]="Closing",ei[ei.Opening=8]="Opening",ei);function ec(){return(0,eo.useContext)(es)}function ed({value:ee,children:en}){return eo.createElement(es.Provider,{value:ee},en)}},8913:function(ee,en,er){"use strict";function ei(ee){let en=ee.parentElement,er=null;for(;en&&!(en instanceof HTMLFieldSetElement);)en instanceof HTMLLegendElement&&(er=en),en=en.parentElement;let ei=(null==en?void 0:en.getAttribute("disabled"))==="";return!(ei&&eo(er))&&ei}function eo(ee){if(!ee)return!1;let en=ee.previousElementSibling;for(;null!==en;){if(en instanceof HTMLLegendElement)return!1;en=en.previousElementSibling}return!0}er.d(en,{P:function(){return ei}})},531:function(ee,en,er){"use strict";function ei(ee){throw Error("Unexpected object: "+ee)}er.d(en,{T:function(){return es},d:function(){return eu}});var eo,es=((eo=es||{})[eo.First=0]="First",eo[eo.Previous=1]="Previous",eo[eo.Next=2]="Next",eo[eo.Last=3]="Last",eo[eo.Specific=4]="Specific",eo[eo.Nothing=5]="Nothing",eo);function eu(ee,en){let er=en.resolveItems();if(er.length<=0)return null;let eo=en.resolveActiveIndex(),es=null!=eo?eo:-1,eu=(()=>{switch(ee.focus){case 0:return er.findIndex(ee=>!en.resolveDisabled(ee));case 1:{let ee=er.slice().reverse().findIndex((ee,er,ei)=>(-1===es||!(ei.length-er-1>=es))&&!en.resolveDisabled(ee));return -1===ee?ee:er.length-1-ee}case 2:return er.findIndex((ee,er)=>!(er<=es)&&!en.resolveDisabled(ee));case 3:{let ee=er.slice().reverse().findIndex(ee=>!en.resolveDisabled(ee));return -1===ee?ee:er.length-1-ee}case 4:return er.findIndex(er=>en.resolveId(er)===ee.id);case 5:return null;default:ei(ee)}})();return -1===eu?eo:eu}},9465:function(ee,en,er){"use strict";function ei(...ee){return ee.filter(Boolean).join(" ")}er.d(en,{A:function(){return ei}})},6050:function(ee,en,er){"use strict";function ei(ee){"function"==typeof queueMicrotask?queueMicrotask(ee):Promise.resolve().then(ee).catch(ee=>setTimeout(()=>{throw ee}))}function eo(){let ee=[],en={addEventListener:(ee,er,ei,eo)=>(ee.addEventListener(er,ei,eo),en.add(()=>ee.removeEventListener(er,ei,eo))),requestAnimationFrame(...ee){let er=requestAnimationFrame(...ee);return en.add(()=>cancelAnimationFrame(er))},nextFrame:(...ee)=>en.requestAnimationFrame(()=>en.requestAnimationFrame(...ee)),setTimeout(...ee){let er=setTimeout(...ee);return en.add(()=>clearTimeout(er))},microTask(...ee){let er={current:!0};return ei(()=>{er.current&&ee[0]()}),en.add(()=>{er.current=!1})},style(ee,en,er){let ei=ee.style.getPropertyValue(en);return Object.assign(ee.style,{[en]:er}),this.add(()=>{Object.assign(ee.style,{[en]:ei})})},group(ee){let en=eo();return ee(en),this.add(()=>en.dispose())},add:en=>(ee.push(en),()=>{let er=ee.indexOf(en);if(er>=0)for(let en of ee.splice(er,1))en()}),dispose(){for(let en of ee.splice(0))en()}};return en}er.d(en,{k:function(){return eo}})},1124:function(ee,en,er){"use strict";er.d(en,{O:function(){return ec}});var ei=Object.defineProperty,eo=(ee,en,er)=>en in ee?ei(ee,en,{enumerable:!0,configurable:!0,writable:!0,value:er}):ee[en]=er,es=(ee,en,er)=>(eo(ee,"symbol"!=typeof en?en+"":en,er),er);class eu{constructor(){es(this,"current",this.detect()),es(this,"handoffState","pending"),es(this,"currentId",0)}set(ee){this.current!==ee&&(this.handoffState="pending",this.currentId=0,this.current=ee)}reset(){this.set(this.detect())}nextId(){return++this.currentId}get isServer(){return"server"===this.current}get isClient(){return"client"===this.current}detect(){return"undefined"==typeof window||"undefined"==typeof document?"server":"client"}handoff(){"pending"===this.handoffState&&(this.handoffState="complete")}get isHandoffComplete(){return"complete"===this.handoffState}}let ec=new eu},1720:function(ee,en,er){"use strict";er.d(en,{EO:function(){return eC},TO:function(){return em},sP:function(){return eb},tJ:function(){return ey},wI:function(){return ew},z2:function(){return eO}});var ei,eo,es,eu,ec,ed=er(6050),ef=er(3015),eh=er(9322);let ep=["[contentEditable=true]","[tabindex]","a[href]","area[href]","button:not([disabled])","iframe","input:not([disabled])","select:not([disabled])","textarea:not([disabled])"].map(ee=>`${ee}:not([tabindex='-1'])`).join(",");var em=((ei=em||{})[ei.First=1]="First",ei[ei.Previous=2]="Previous",ei[ei.Next=4]="Next",ei[ei.Last=8]="Last",ei[ei.WrapAround=16]="WrapAround",ei[ei.NoScroll=32]="NoScroll",ei),ex=((eo=ex||{})[eo.Error=0]="Error",eo[eo.Overflow=1]="Overflow",eo[eo.Success=2]="Success",eo[eo.Underflow=3]="Underflow",eo),eg=((es=eg||{})[es.Previous=-1]="Previous",es[es.Next=1]="Next",es);function ev(ee=document.body){return null==ee?[]:Array.from(ee.querySelectorAll(ep)).sort((ee,en)=>Math.sign((ee.tabIndex||Number.MAX_SAFE_INTEGER)-(en.tabIndex||Number.MAX_SAFE_INTEGER)))}var ey=((eu=ey||{})[eu.Strict=0]="Strict",eu[eu.Loose=1]="Loose",eu);function eb(ee,en=0){var er;return ee!==(null==(er=(0,eh.r)(ee))?void 0:er.body)&&(0,ef.E)(en,{0:()=>ee.matches(ep),1(){let en=ee;for(;null!==en;){if(en.matches(ep))return!0;en=en.parentElement}return!1}})}function ew(ee){let en=(0,eh.r)(ee);(0,ed.k)().nextFrame(()=>{en&&!eb(en.activeElement,0)&&ek(ee)})}var e_=((ec=e_||{})[ec.Keyboard=0]="Keyboard",ec[ec.Mouse=1]="Mouse",ec);function ek(ee){null==ee||ee.focus({preventScroll:!0})}"undefined"!=typeof window&&"undefined"!=typeof document&&(document.addEventListener("keydown",ee=>{ee.metaKey||ee.altKey||ee.ctrlKey||(document.documentElement.dataset.headlessuiFocusVisible="")},!0),document.addEventListener("click",ee=>{1===ee.detail?delete document.documentElement.dataset.headlessuiFocusVisible:0===ee.detail&&(document.documentElement.dataset.headlessuiFocusVisible="")},!0));let ej="textarea,input";function eE(ee){var en,er;return null!=(er=null==(en=null==ee?void 0:ee.matches)?void 0:en.call(ee,ej))&&er}function eO(ee,en=ee=>ee){return ee.slice().sort((ee,er)=>{let ei=en(ee),eo=en(er);if(null===ei||null===eo)return 0;let es=ei.compareDocumentPosition(eo);return es&Node.DOCUMENT_POSITION_FOLLOWING?-1:es&Node.DOCUMENT_POSITION_PRECEDING?1:0})}function eC(ee,en){return eT(ev(),en,{relativeTo:ee})}function eT(ee,en,{sorted:er=!0,relativeTo:ei=null,skipElements:eo=[]}={}){let es=Array.isArray(ee)?ee.length>0?ee[0].ownerDocument:document:ee.ownerDocument,eu=Array.isArray(ee)?er?eO(ee):ee:ev(ee);eo.length>0&&eu.length>1&&(eu=eu.filter(ee=>!eo.includes(ee))),ei=null!=ei?ei:es.activeElement;let ec=(()=>{if(5&en)return 1;if(10&en)return -1;throw Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),ed=(()=>{if(1&en)return 0;if(2&en)return Math.max(0,eu.indexOf(ei))-1;if(4&en)return Math.max(0,eu.indexOf(ei))+1;if(8&en)return eu.length-1;throw Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last")})(),ef=32&en?{preventScroll:!0}:{},eh=0,ep=eu.length,em;do{if(eh>=ep||eh+ep<=0)return 0;let ee=ed+eh;if(16&en)ee=(ee+ep)%ep;else{if(ee<0)return 3;if(ee>=ep)return 1}null==(em=eu[ee])||em.focus(ef),eh+=ec}while(em!==es.activeElement);return 6&en&&eE(em)&&em.select(),2}},3015:function(ee,en,er){"use strict";function ei(ee,en,...er){if(ee in en){let ei=en[ee];return"function"==typeof ei?ei(...er):ei}let eo=Error(`Tried to handle "${ee}" but there is no handler defined. Only defined handlers are: ${Object.keys(en).map(ee=>`"${ee}"`).join(", ")}.`);throw Error.captureStackTrace&&Error.captureStackTrace(eo,ei),eo}er.d(en,{E:function(){return ei}})},9322:function(ee,en,er){"use strict";er.d(en,{r:function(){return eo}});var ei=er(1124);function eo(ee){return ei.O.isServer?null:ee instanceof Node?ee.ownerDocument:null!=ee&&ee.hasOwnProperty("current")&&ee.current instanceof Node?ee.current.ownerDocument:document}},1806:function(ee,en,er){"use strict";er.d(en,{AN:function(){return ed},l4:function(){return ef},oA:function(){return ev},sY:function(){return eh},yV:function(){return eg}});var ei,eo,es=er(959),eu=er(9465),ec=er(3015),ed=((ei=ed||{})[ei.None=0]="None",ei[ei.RenderStrategy=1]="RenderStrategy",ei[ei.Static=2]="Static",ei),ef=((eo=ef||{})[eo.Unmount=0]="Unmount",eo[eo.Hidden=1]="Hidden",eo);function eh({ourProps:ee,theirProps:en,slot:er,defaultTag:ei,features:eo,visible:es=!0,name:eu}){let ed=ex(en,ee);if(es)return ep(ed,er,ei,eu);let ef=null!=eo?eo:0;if(2&ef){let{static:ee=!1,...en}=ed;if(ee)return ep(en,er,ei,eu)}if(1&ef){let{unmount:ee=!0,...en}=ed;return(0,ec.E)(ee?0:1,{0:()=>null,1:()=>ep({...en,hidden:!0,style:{display:"none"}},er,ei,eu)})}return ep(ed,er,ei,eu)}function ep(ee,en={},er,ei){let{as:eo=er,children:ec,refName:ed="ref",...ef}=ey(ee,["unmount","static"]),eh=void 0!==ee.ref?{[ed]:ee.ref}:{},ep="function"==typeof ec?ec(en):ec;"className"in ef&&ef.className&&"function"==typeof ef.className&&(ef.className=ef.className(en));let eg={};if(en){let ee=!1,er=[];for(let[ei,eo]of Object.entries(en))"boolean"==typeof eo&&(ee=!0),!0===eo&&er.push(ei);ee&&(eg["data-headlessui-state"]=er.join(" "))}if(eo===es.Fragment&&Object.keys(ev(ef)).length>0){if(!(0,es.isValidElement)(ep)||Array.isArray(ep)&&ep.length>1)throw Error(['Passing props on "Fragment"!',"",`The current component <${ei} /> is rendering a "Fragment".`,"However we need to passthrough the following props:",Object.keys(ef).map(ee=>` - ${ee}`).join(` +`),"","You can apply a few solutions:",['Add an `as="..."` prop, to ensure that we render an actual element instead of a "Fragment".',"Render a single element as the child so that we can forward the props onto that element."].map(ee=>` - ${ee}`).join(` +`)].join(` +`));let ee=ep.props,en="function"==typeof(null==ee?void 0:ee.className)?(...en)=>(0,eu.A)(null==ee?void 0:ee.className(...en),ef.className):(0,eu.A)(null==ee?void 0:ee.className,ef.className),er=en?{className:en}:{};return(0,es.cloneElement)(ep,Object.assign({},ex(ep.props,ev(ey(ef,["ref"]))),eg,eh,em(ep.ref,eh.ref),er))}return(0,es.createElement)(eo,Object.assign({},ey(ef,["ref"]),eo!==es.Fragment&&eh,eo!==es.Fragment&&eg),ep)}function em(...ee){return{ref:ee.every(ee=>null==ee)?void 0:en=>{for(let er of ee)null!=er&&("function"==typeof er?er(en):er.current=en)}}}function ex(...ee){var en;if(0===ee.length)return{};if(1===ee.length)return ee[0];let er={},ei={};for(let eo of ee)for(let ee in eo)ee.startsWith("on")&&"function"==typeof eo[ee]?(null!=(en=ei[ee])||(ei[ee]=[]),ei[ee].push(eo[ee])):er[ee]=eo[ee];if(er.disabled||er["aria-disabled"])return Object.assign(er,Object.fromEntries(Object.keys(ei).map(ee=>[ee,void 0])));for(let ee in ei)Object.assign(er,{[ee](en,...er){for(let eo of ei[ee]){if((en instanceof Event||(null==en?void 0:en.nativeEvent)instanceof Event)&&en.defaultPrevented)return;eo(en,...er)}}});return er}function eg(ee){var en;return Object.assign((0,es.forwardRef)(ee),{displayName:null!=(en=ee.displayName)?en:ee.name})}function ev(ee){let en=Object.assign({},ee);for(let ee in en)void 0===en[ee]&&delete en[ee];return en}function ey(ee,en=[]){let er=Object.assign({},ee);for(let ee of en)ee in er&&delete er[ee];return er}},6736:function(ee,en,er){"use strict";er.d(en,{Zo:function(){return ec},ah:function(){return es}});var ei=er(959);let eo=ei.createContext({});function es(ee){let en=ei.useContext(eo);return ei.useMemo(()=>"function"==typeof ee?ee(en):{...en,...ee},[en,ee])}let eu={};function ec({components:ee,children:en,disableParentContext:er}){let ec;return ec=er?"function"==typeof ee?ee({}):ee||eu:es(ee),ei.createElement(eo.Provider,{value:ec},en)}},1835:function(ee,en,er){"use strict";er.d(en,{ZW:function(){return es},eZ:function(){return ec},hV:function(){return ed}});var ei=er(5654),eo=er(8506),es="en-US",eu=eo.cwd();ei.join(eu,"public");var ec=Symbol.for("__nextra_internal__"),ed=new Set(["/404","/500"])}}]); \ No newline at end of file diff --git a/crates/rooch/public/dashboard/_next/static/chunks/859-74d1e6e294ad010b.js b/crates/rooch/public/dashboard/_next/static/chunks/859-74d1e6e294ad010b.js new file mode 100644 index 0000000000..ae9c17cbca --- /dev/null +++ b/crates/rooch/public/dashboard/_next/static/chunks/859-74d1e6e294ad010b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[859],{3859:function(e,t,n){n.d(t,{Z:function(){return y}});var r=n(1527),a=n(6149),o=n(8556),s=n(5924),l=n(1479),c=n(6194),i=n(8631),x=n(959),h=n(209),m=n(422),d=n(4238);function g(e){let{options:t,selected:n,onChange:a,title:o,className:m}=e,[d,g]=function(e){let t=(0,x.useRef)(null),n=(0,x.useRef)(null),r=(0,x.useRef)(),a=(0,x.useCallback)(()=>{var a;t.current&&n.current&&(null===(a=r.current)||void 0===a||a.call(r),r.current=(0,h.fi)(t.current,n.current,e).destroy)},[t,n,r,e]);return(0,x.useMemo)(()=>[e=>{t.current=e,a()},e=>{n.current=e,a()}],[t,n,a])}({strategy:"fixed",placement:"top-start",modifiers:[{name:"offset",options:{offset:[0,10]}},{name:"sameWidth",enabled:!0,fn(e){let{state:t}=e;t.styles.popper.minWidth="".concat(t.rects.reference.width,"px")},phase:"beforeWrite",requires:["computeStyles"]}]});return(0,r.jsx)(l.R,{value:n,onChange:a,children:e=>{let{open:a}=e;return(0,r.jsxs)(l.R.Button,{ref:d,title:o,className:(0,s.Z)("nx-h-7 nx-rounded-md nx-px-2 nx-text-left nx-text-xs nx-font-medium nx-text-gray-600 nx-transition-colors dark:nx-text-gray-400",a?"nx-bg-gray-200 nx-text-gray-900 dark:nx-bg-primary-100/10 dark:nx-text-gray-50":"hover:nx-bg-gray-100 hover:nx-text-gray-900 dark:hover:nx-bg-primary-100/5 dark:hover:nx-text-gray-50",m),children:[n.name,(0,r.jsx)(u,{children:(0,r.jsx)(c.u,{ref:g,show:a,as:l.R.Options,className:"nx-z-20 nx-max-h-64 nx-overflow-auto nx-rounded-md nx-ring-1 nx-ring-black/5 nx-bg-white nx-py-1 nx-text-sm nx-shadow-lg dark:nx-ring-white/20 dark:nx-bg-neutral-800",leave:"nx-transition-opacity",leaveFrom:"nx-opacity-100",leaveTo:"nx-opacity-0",children:t.map(e=>(0,r.jsxs)(l.R.Option,{value:e,className:e=>{let{active:t}=e;return(0,s.Z)(t?"nx-bg-primary-50 nx-text-primary-600 dark:nx-bg-primary-500/10":"nx-text-gray-800 dark:nx-text-gray-100","nx-relative nx-cursor-pointer nx-whitespace-nowrap nx-py-1.5","nx-transition-colors ltr:nx-pl-3 ltr:nx-pr-9 rtl:nx-pr-3 rtl:nx-pl-9")},children:[e.name,e.key===n.key&&(0,r.jsx)("span",{className:"nx-absolute nx-inset-y-0 nx-flex nx-items-center ltr:nx-right-3 rtl:nx-left-3",children:(0,r.jsx)(i.nQ,{})})]},e.key))})})]})}})}function u(e){let t=(0,d.s)();return t?(0,m.createPortal)(e.children,document.body):null}var p=n(4288);function f(e){let{options:t,lite:n,className:a}=e,{locale:s,asPath:l}=(0,o.useRouter)(),c=t.find(e=>s===e.locale);return(0,r.jsx)(g,{title:"Change language",className:a,onChange:e=>{let t=new Date(Date.now()+31536e6);document.cookie="NEXT_LOCALE=".concat(e.key,"; expires=").concat(t.toUTCString(),"; path=/"),location.href=(0,p.addBasePath)(l)},selected:{key:(null==c?void 0:c.locale)||"",name:(0,r.jsxs)("div",{className:"nx-flex nx-items-center nx-gap-2",children:[(0,r.jsx)(i.n9,{}),(0,r.jsx)("span",{className:n?"nx-hidden":"",children:null==c?void 0:c.text})]})},options:t.map(e=>({key:e.locale,name:e.text}))})}var w=n(7758),j=n(2737),k=n.n(j);let v={docsRepositoryBase:"https://github.com/rooch-network/rooch.git",nextThemes:{defaultTheme:"light"},logo:(0,r.jsxs)("div",{className:"flex nx-items-center",children:[(0,r.jsx)(k(),{src:"/logo/rooch_black_logo.png",alt:"Rooch Architecture",width:20,height:30,className:"dark:hidden"}),(0,r.jsx)("span",{className:"nx-ml-4 font-face",children:"Rooch Dashboard"})]}),useNextSeoProps(){let{asPath:e}=(0,o.useRouter)();return"/"===e?{titleTemplate:"%s"}:e.includes("/docs/")?{titleTemplate:"%s – Rooch Dashboard Tools"}:{titleTemplate:"%s – Rooch Dashboard"}},head:function(){let{title:e,frontMatter:t}=(0,a.ZR)(),{asPath:n}=(0,o.useRouter)(),s=(0,o.useRouter)(),l="/logo/rooch-banner.png";s.locale;let c=t.description?t.description:"Dashbord";return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("meta",{name:"msapplication-TileColor",content:"#ffffff"}),(0,r.jsx)("meta",{name:"theme-color",content:"#ffffff"}),(0,r.jsx)("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0"}),(0,r.jsx)("link",{rel:"alternate",href:"https://rooch.network"+n,hrefLang:"x-default"}),(0,r.jsx)("link",{rel:"alternate",href:"https://rooch.network"+n,hrefLang:"en-us"}),(0,r.jsx)("link",{rel:"alternate",href:"https://rooch.network"+n,hrefLang:"en"}),(0,r.jsx)("link",{rel:"alternate",href:"https://rooch.network/zh-CN"+n,hrefLang:"zh-cn"}),(0,r.jsx)("link",{rel:"alternate",href:"https://rooch.network/zh-CN"+n,hrefLang:"zh"}),(0,r.jsx)("meta",{name:"description",content:c}),(0,r.jsx)("meta",{name:"og:description",content:c}),(0,r.jsx)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,r.jsx)("meta",{name:"twitter:image",content:l}),(0,r.jsx)("meta",{name:"twitter:site:domain",content:"rooch.network"}),(0,r.jsx)("meta",{name:"twitter:url",content:"https://rooch.network"}),(0,r.jsx)("meta",{name:"og:image",content:l}),(0,r.jsx)("meta",{name:"apple-mobile-web-app-title",content:"Rooch Network"}),(0,r.jsx)("link",{rel:"icon",href:"/logo/rooch_black_logo.svg",type:"image/svg+xml"}),(0,r.jsx)("link",{rel:"icon",href:"/logo/rooch_black_logo.png",type:"image/png"}),(0,r.jsx)("link",{rel:"icon",href:"/logo/rooch_white_logo.svg",type:"image/svg+xml",media:"(prefers-color-scheme: dark)"}),(0,r.jsx)("link",{rel:"icon",href:"/logo/rooch_white_logo.png",type:"image/png",media:"(prefers-color-scheme: dark)"})]})},footer:{component:function(){let e="inline-flex justify-center items-center w-10 h-10 text-center text-gray-500 hover:bg-gray-100 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-offset-2 focus:ring-offset-white transition dark:text-gray-500 dark:hover:text-gray-200 dark:hover:bg-gray-800",t=(0,w.usePathname)(),n=(0,a.ZR)(),[o,s]=(0,x.useState)(!1);return(0,x.useEffect)(()=>{t.includes("/docs")?s(!1):s(!0)},[t]),(0,r.jsxs)("footer",{className:"mt-auto w-full max-w-[85rem] py-10 px-4 sm:px-6 lg:px-8 mx-auto",children:[o?(0,r.jsxs)("div",{className:"text-left pb-4",children:[(0,r.jsx)(f,{options:n.i18n}),(0,r.jsx)(a.R5,{})]}):null,(0,r.jsx)("hr",{}),(0,r.jsxs)("div",{className:"text-center grid grid-cols-2 content-between",children:[(0,r.jsx)("p",{className:"text-gray-500 text-left mt-3",children:"\xa9 Root Branch Ltd. 2023. All rights reserved."}),(0,r.jsxs)("div",{className:"space-x-2 text-right",children:[(0,r.jsx)("a",{className:e,href:"https://github.com/rooch-network/",children:(0,r.jsx)("svg",{className:"w-3.5 h-3.5",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",fill:"currentColor",viewBox:"0 0 16 16",children:(0,r.jsx)("path",{d:"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"})})}),(0,r.jsx)("a",{className:e,href:"https://twitter.com/RoochNetwork",children:(0,r.jsx)("svg",{className:"w-3.5 h-3.5",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",fill:"currentColor",viewBox:"0 0 16 16",children:(0,r.jsx)("path",{d:"M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"})})}),(0,r.jsx)("a",{className:e,href:"https://discord.gg/rooch",children:(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",fill:"currentColor",className:"bi bi-discord",viewBox:"0 0 16 16",children:(0,r.jsx)("path",{d:"M13.545 2.907a13.227 13.227 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 0 0-3.658 0 8.258 8.258 0 0 0-.412-.833.051.051 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019c.308-.42.582-.863.818-1.329a.05.05 0 0 0-.01-.059.051.051 0 0 0-.018-.011 8.875 8.875 0 0 1-1.248-.595.05.05 0 0 1-.02-.066.051.051 0 0 1 .015-.019c.084-.063.168-.129.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 0 1 .053.007c.08.066.164.132.248.195a.051.051 0 0 1-.004.085 8.254 8.254 0 0 1-1.249.594.05.05 0 0 0-.03.03.052.052 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.235 13.235 0 0 0 4.001-2.02.049.049 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 0 0-.02-.019Zm-8.198 7.307c-.789 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612Zm5.316 0c-.788 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612Z"})})}),(0,r.jsx)("a",{className:e,href:"https://medium.com/rooch-network",children:(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"16",height:"16",fill:"currentColor",className:"bi bi-medium",viewBox:"0 0 16 16",children:(0,r.jsx)("path",{d:"M9.025 8c0 2.485-2.02 4.5-4.513 4.5A4.506 4.506 0 0 1 0 8c0-2.486 2.02-4.5 4.512-4.5A4.506 4.506 0 0 1 9.025 8zm4.95 0c0 2.34-1.01 4.236-2.256 4.236-1.246 0-2.256-1.897-2.256-4.236 0-2.34 1.01-4.236 2.256-4.236 1.246 0 2.256 1.897 2.256 4.236zM16 8c0 2.096-.355 3.795-.794 3.795-.438 0-.793-1.7-.793-3.795 0-2.096.355-3.795.794-3.795.438 0 .793 1.699.793 3.795z"})})})]})]})]})}},search:{component:()=>void 0}};var y=v}}]); \ No newline at end of file diff --git a/crates/rooch/public/dashboard/_next/static/chunks/902.e935e5bbc17f8604.js b/crates/rooch/public/dashboard/_next/static/chunks/902.e935e5bbc17f8604.js new file mode 100644 index 0000000000..5a971d7d37 --- /dev/null +++ b/crates/rooch/public/dashboard/_next/static/chunks/902.e935e5bbc17f8604.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[902],{5902:function(t){self,t.exports=(()=>{"use strict";var t={661:(t,e,o)=>{function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function n(t,e){return(n=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function i(t){return(i=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var a=function(t){!function(t,e){if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&n(t,e)}(s,t);var e,o,a=(o=function(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(t){return!1}}(),function(){var t,e=i(s);if(o){var n=i(this).constructor;t=Reflect.construct(e,arguments,n)}else t=e.apply(this,arguments);return function(t,e){if(e&&("object"===r(e)||"function"==typeof e))return e;if(void 0!==e)throw TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,t)});function s(){return function(t,e){if(!(t instanceof e))throw TypeError("Cannot call a class as a function")}(this,s),a.call(this,".hs-accordion")}return e=[{key:"init",value:function(){var t=this;document.addEventListener("click",function(e){var o=e.target,r=o.closest(t.selector),n=o.closest(".hs-accordion-toggle"),i=o.closest(".hs-accordion-group");r&&i&&n&&(t._hideAll(r),t.show(r))})}},{key:"show",value:function(t){var e=this;if(t.classList.contains("active"))return this.hide(t);t.classList.add("active");var o=t.querySelector(".hs-accordion-content");o.style.display="block",o.style.height=0,setTimeout(function(){o.style.height="".concat(o.scrollHeight,"px")}),this.afterTransition(o,function(){t.classList.contains("active")&&(o.style.height="",e._fireEvent("open",t),e._dispatch("open.hs.accordion",t,t))})}},{key:"hide",value:function(t){var e=this,o=t.querySelector(".hs-accordion-content");o.style.height="".concat(o.scrollHeight,"px"),setTimeout(function(){o.style.height=0}),this.afterTransition(o,function(){t.classList.contains("active")||(o.style.display="",e._fireEvent("hide",t),e._dispatch("hide.hs.accordion",t,t))}),t.classList.remove("active")}},{key:"_hideAll",value:function(t){var e=this,o=t.closest(".hs-accordion-group");o.hasAttribute("data-hs-accordion-always-open")||o.querySelectorAll(this.selector).forEach(function(o){t!==o&&e.hide(o)})}}],function(t,e){for(var o=0;o{function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function n(t,e){(null==e||e>t.length)&&(e=t.length);for(var o=0,r=Array(e);o{var r=o(714),n=o(765);let i={historyIndex:-1,addHistory:function(t){this.historyIndex=t},existsInHistory:function(t){return t>this.historyIndex},clearHistory:function(){this.historyIndex=-1}};function a(t){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function s(t){return function(t){if(Array.isArray(t))return c(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return c(t,e);var o=Object.prototype.toString.call(t).slice(8,-1);return"Object"===o&&t.constructor&&(o=t.constructor.name),"Map"===o||"Set"===o?Array.from(t):"Arguments"===o||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o)?c(t,e):void 0}}(t)||function(){throw TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function c(t,e){(null==e||e>t.length)&&(e=t.length);for(var o=0,r=Array(e);o2&&/MacIntel/.test(navigator.platform)||navigator.maxTouchPoints&&navigator.maxTouchPoints>2&&/MacIntel/.test(navigator.platform)||t._hover(o)}}),document.addEventListener("keydown",this._keyboardSupport.bind(this)),window.addEventListener("resize",function(){document.querySelectorAll(".hs-dropdown.open").forEach(function(e){t.close(e,!0)})})}},{key:"_closeOthers",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;document.querySelectorAll("".concat(this.selector,".open")).forEach(function(o){if(!e||e.closest(".hs-dropdown.open")!==o){var r=(window.getComputedStyle(o).getPropertyValue("--auto-close")||"").replace(" ","");"false"!=r&&"outside"!=r&&t.close(o)}})}},{key:"_hover",value:function(t){var e=this,o=t.closest(this.selector);this.open(o),document.addEventListener("mousemove",function t(r){r.target.closest(e.selector)&&r.target.closest(e.selector)!==o.parentElement.closest(e.selector)||(e.close(o),document.removeEventListener("mousemove",t,!0))},!0)}},{key:"close",value:function(t){var e=this,o=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=t.querySelector(".hs-dropdown-menu"),n=function(){t.classList.contains("open")||(r.classList.remove("block"),r.classList.add("hidden"),r.style.inset=null,r.style.position=null,t._popper&&t._popper.destroy())};o||this.afterTransition(t.querySelector("[data-hs-dropdown-transition]")||r,function(){n()}),r.style.margin=null,t.classList.remove("open"),o&&n(),this._fireEvent("close",t),this._dispatch("close.hs.dropdown",t,t),r.querySelectorAll(".hs-dropdown.open").forEach(function(t){e.close(t,!0)})}},{key:"open",value:function(t){var e=t.querySelector(".hs-dropdown-menu"),o=(window.getComputedStyle(t).getPropertyValue("--placement")||"").replace(" ",""),n=(window.getComputedStyle(t).getPropertyValue("--strategy")||"fixed").replace(" ",""),i=((window.getComputedStyle(t).getPropertyValue("--adaptive")||"adaptive").replace(" ",""),parseInt((window.getComputedStyle(t).getPropertyValue("--offset")||"10").replace(" ","")));if("static"!==n){t._popper&&t._popper.destroy();var a=(0,r.fi)(t,e,{placement:this.positions[o]||"bottom-start",strategy:n,modifiers:[].concat(s("fixed"!==n?this.absoluteStrategyModifiers(t):[]),[{name:"offset",options:{offset:[0,i]}}])});t._popper=a}e.style.margin=null,e.classList.add("block"),e.classList.remove("hidden"),setTimeout(function(){t.classList.add("open")}),this._fireEvent("open",t),this._dispatch("open.hs.dropdown",t,t)}},{key:"_keyboardSupport",value:function(t){var e=document.querySelector(".hs-dropdown.open");if(e)return 27===t.keyCode?(t.preventDefault(),this._esc(e)):40===t.keyCode?(t.preventDefault(),this._down(e)):38===t.keyCode?(t.preventDefault(),this._up(e)):36===t.keyCode?(t.preventDefault(),this._start(e)):35===t.keyCode?(t.preventDefault(),this._end(e)):void this._byChar(e,t.key)}},{key:"_esc",value:function(t){this.close(t)}},{key:"_up",value:function(t){var e=t.querySelector(".hs-dropdown-menu"),o=s(e.querySelectorAll("a")).reverse().filter(function(t){return!t.disabled}),r=e.querySelector("a:focus"),n=o.findIndex(function(t){return t===r});n+1{function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function n(t,e){(null==e||e>t.length)&&(e=t.length);for(var o=0,r=Array(e);o=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:i}}throw TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,c=!1;return{s:function(){o=o.call(t)},n:function(){var t=o.next();return s=t.done,t},e:function(t){c=!0,a=t},f:function(){try{s||null==o.return||o.return()}finally{if(c)throw a}}}}(t.classList.values());try{for(s.s();!(e=s.n()).done;){var c=e.value;c.startsWith("hs-overlay-backdrop-open:")&&(a+=" ".concat(c))}}catch(t){s.e(t)}finally{s.f()}var l="static"!==this.getClassProperty(t,"--overlay-backdrop","true");"false"===this.getClassProperty(t,"--overlay-backdrop","true")||(r&&((i=document.querySelector(r).cloneNode(!0)).classList.remove("hidden"),a=i.classList,i.classList=""),l&&i.addEventListener("click",function(){return o.close(t)},!0),i.setAttribute("data-hs-overlay-backdrop-template",""),document.body.appendChild(i),setTimeout(function(){i.classList=a}))}},{key:"_destroyBackdrop",value:function(){var t=document.querySelector("[data-hs-overlay-backdrop-template]");t&&(this.openNextOverlay&&(t.style.transitionDuration="".concat(1.8*parseFloat(window.getComputedStyle(t).transitionDuration.replace(/[^\d.-]/g,"")),"s")),t.classList.add("opacity-0"),this.afterTransition(t,function(){t.remove()}))}},{key:"_focusInput",value:function(t){var e=t.querySelector("[autofocus]");e&&e.focus()}}],function(t,e){for(var o=0;o{function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function n(t,e){return(n=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function i(t){return(i=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var a=function(t){!function(t,e){if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&n(t,e)}(s,t);var e,o,a=(o=function(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(t){return!1}}(),function(){var t,e=i(s);if(o){var n=i(this).constructor;t=Reflect.construct(e,arguments,n)}else t=e.apply(this,arguments);return function(t,e){if(e&&("object"===r(e)||"function"==typeof e))return e;if(void 0!==e)throw TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,t)});function s(){return function(t,e){if(!(t instanceof e))throw TypeError("Cannot call a class as a function")}(this,s),a.call(this,"[data-hs-remove-element]")}return e=[{key:"init",value:function(){var t=this;document.addEventListener("click",function(e){var o=e.target.closest(t.selector);if(o){var r=document.querySelector(o.getAttribute("data-hs-remove-element"));r&&(r.classList.add("hs-removing"),t.afterTransition(r,function(){r.remove()}))}})}}],function(t,e){for(var o=0;o{function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function n(t,e){return(n=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function i(t){return(i=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var a=function(t){!function(t,e){if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&n(t,e)}(s,t);var e,o,a=(o=function(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(t){return!1}}(),function(){var t,e=i(s);if(o){var n=i(this).constructor;t=Reflect.construct(e,arguments,n)}else t=e.apply(this,arguments);return function(t,e){if(e&&("object"===r(e)||"function"==typeof e))return e;if(void 0!==e)throw TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,t)});function s(){var t;return function(t,e){if(!(t instanceof e))throw TypeError("Cannot call a class as a function")}(this,s),(t=a.call(this,"[data-hs-scrollspy] ")).activeSection=null,t}return e=[{key:"init",value:function(){var t=this;document.querySelectorAll(this.selector).forEach(function(e){var o=document.querySelector(e.getAttribute("data-hs-scrollspy")),r=e.querySelectorAll("[href]"),n=o.children,i=e.getAttribute("data-hs-scrollspy-scrollable-parent")?document.querySelector(e.getAttribute("data-hs-scrollspy-scrollable-parent")):document;Array.from(n).forEach(function(a){a.getAttribute("id")&&i.addEventListener("scroll",function(i){return t._update({$scrollspyEl:e,$scrollspyContentEl:o,links:r,$sectionEl:a,sections:n,ev:i})})}),r.forEach(function(o){o.addEventListener("click",function(r){r.preventDefault(),"javascript:;"!==o.getAttribute("href")&&t._scrollTo({$scrollspyEl:e,$scrollableEl:i,$link:o})})})})}},{key:"_update",value:function(t){var e=t.ev,o=t.$scrollspyEl,r=(t.sections,t.links),n=t.$sectionEl,i=parseInt(this.getClassProperty(o,"--scrollspy-offset","0")),a=this.getClassProperty(n,"--scrollspy-offset")||i,s=e.target===document?0:parseInt(e.target.getBoundingClientRect().top),c=parseInt(n.getBoundingClientRect().top)-a-s,l=n.offsetHeight;if(c<=0&&c+l>0){if(this.activeSection===n)return;r.forEach(function(t){t.classList.remove("active")});var u=o.querySelector('[href="#'.concat(n.getAttribute("id"),'"]'));if(u){u.classList.add("active");var f=u.closest("[data-hs-scrollspy-group]");if(f){var p=f.querySelector("[href]");p&&p.classList.add("active")}}this.activeSection=n}}},{key:"_scrollTo",value:function(t){var e=t.$scrollspyEl,o=t.$scrollableEl,r=t.$link,n=document.querySelector(r.getAttribute("href")),i=parseInt(this.getClassProperty(e,"--scrollspy-offset","0")),a=this.getClassProperty(n,"--scrollspy-offset")||i,s=o===document?0:o.offsetTop,c=n.offsetTop-a-s,l=o===document?window:o;this._fireEvent("scroll",e),this._dispatch("scroll.hs.scrollspy",e,e),window.history.replaceState(null,null,r.getAttribute("href")),l.scrollTo({top:c,left:0,behavior:"smooth"})}}],function(t,e){for(var o=0;o{function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function n(t){return function(t){if(Array.isArray(t))return i(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return i(t,e);var o=Object.prototype.toString.call(t).slice(8,-1);return"Object"===o&&t.constructor&&(o=t.constructor.name),"Map"===o||"Set"===o?Array.from(t):"Arguments"===o||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o)?i(t,e):void 0}}(t)||function(){throw TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function i(t,e){(null==e||e>t.length)&&(e=t.length);for(var o=0,r=Array(e);o{var r=o(765),n=o(714);function i(t){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function a(t,e){return(a=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function s(t){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}var c=function(t){!function(t,e){if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&a(t,e)}(c,t);var e,o,r=(o=function(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(t){return!1}}(),function(){var t,e=s(c);if(o){var r=s(this).constructor;t=Reflect.construct(e,arguments,r)}else t=e.apply(this,arguments);return function(t,e){if(e&&("object"===i(e)||"function"==typeof e))return e;if(void 0!==e)throw TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}(this,t)});function c(){return function(t,e){if(!(t instanceof e))throw TypeError("Cannot call a class as a function")}(this,c),r.call(this,".hs-tooltip")}return e=[{key:"init",value:function(){var t=this;document.addEventListener("click",function(e){var o=e.target.closest(t.selector);o&&"focus"===t.getClassProperty(o,"--trigger")&&t._focus(o),o&&"click"===t.getClassProperty(o,"--trigger")&&t._click(o)}),document.addEventListener("mousemove",function(e){var o=e.target.closest(t.selector);o&&"focus"!==t.getClassProperty(o,"--trigger")&&"click"!==t.getClassProperty(o,"--trigger")&&t._hover(o)})}},{key:"_hover",value:function(t){var e=this;if(!t.classList.contains("show")){var o=t.querySelector(".hs-tooltip-toggle"),r=t.querySelector(".hs-tooltip-content"),i=this.getClassProperty(t,"--placement");(0,n.fi)(o,r,{placement:i||"top",strategy:"fixed",modifiers:[{name:"offset",options:{offset:[0,5]}}]}),this.show(t),t.addEventListener("mouseleave",function o(r){r.relatedTarget.closest(e.selector)&&r.relatedTarget.closest(e.selector)==t||(e.hide(t),t.removeEventListener("mouseleave",o,!0))},!0)}}},{key:"_focus",value:function(t){var e=this,o=t.querySelector(".hs-tooltip-toggle"),r=t.querySelector(".hs-tooltip-content"),i=this.getClassProperty(t,"--placement"),a=this.getClassProperty(t,"--strategy");(0,n.fi)(o,r,{placement:i||"top",strategy:a||"fixed",modifiers:[{name:"offset",options:{offset:[0,5]}}]}),this.show(t),t.addEventListener("blur",function o(){e.hide(t),t.removeEventListener("blur",o,!0)},!0)}},{key:"_click",value:function(t){var e=this;if(!t.classList.contains("show")){var o=t.querySelector(".hs-tooltip-toggle"),r=t.querySelector(".hs-tooltip-content"),i=this.getClassProperty(t,"--placement"),a=this.getClassProperty(t,"--strategy");(0,n.fi)(o,r,{placement:i||"top",strategy:a||"fixed",modifiers:[{name:"offset",options:{offset:[0,5]}}]}),this.show(t);var s=function o(r){setTimeout(function(){e.hide(t),t.removeEventListener("click",o,!0),t.removeEventListener("blur",o,!0)})};t.addEventListener("blur",s,!0),t.addEventListener("click",s,!0)}}},{key:"show",value:function(t){var e=this;t.querySelector(".hs-tooltip-content").classList.remove("hidden"),setTimeout(function(){t.classList.add("show"),e._fireEvent("show",t),e._dispatch("show.hs.tooltip",t,t)})}},{key:"hide",value:function(t){var e=t.querySelector(".hs-tooltip-content");t.classList.remove("show"),this._fireEvent("hide",t),this._dispatch("hide.hs.tooltip",t,t),this.afterTransition(e,function(){t.classList.contains("show")||e.classList.add("hidden")})}}],function(t,e){for(var o=0;o{o.d(e,{Z:()=>r});var r=function(){var t;function e(t,o){(function(t,e){if(!(t instanceof e))throw TypeError("Cannot call a class as a function")})(this,e),this.$collection=[],this.selector=t,this.config=o,this.events={}}return t=[{key:"_fireEvent",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this.events.hasOwnProperty(t)&&this.events[t](e)}},{key:"_dispatch",value:function(t,e){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=new CustomEvent(t,{detail:{payload:o},bubbles:!0,cancelable:!0,composed:!1});e.dispatchEvent(r)}},{key:"on",value:function(t,e){this.events[t]=e}},{key:"afterTransition",value:function(t,e){"all 0s ease 0s"!==window.getComputedStyle(t,null).getPropertyValue("transition")?t.addEventListener("transitionend",function o(){e(),t.removeEventListener("transitionend",o,!0)},!0):e()}},{key:"getClassProperty",value:function(t,e){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return(window.getComputedStyle(t).getPropertyValue(e)||o).replace(" ","")}}],function(t,e){for(var o=0;o{function r(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function n(t){return t instanceof r(t).Element||t instanceof Element}function i(t){return t instanceof r(t).HTMLElement||t instanceof HTMLElement}function a(t){return"undefined"!=typeof ShadowRoot&&(t instanceof r(t).ShadowRoot||t instanceof ShadowRoot)}o.d(e,{fi:()=>ta});var s,c,l,u,f,p=Math.max,d=Math.min,y=Math.round;function h(t,e){void 0===e&&(e=!1);var o=t.getBoundingClientRect(),r=1,n=1;if(i(t)&&e){var a=t.offsetHeight,s=t.offsetWidth;s>0&&(r=y(o.width)/s||1),a>0&&(n=y(o.height)/a||1)}return{width:o.width/r,height:o.height/n,top:o.top/n,right:o.right/r,bottom:o.bottom/n,left:o.left/r,x:o.left/r,y:o.top/n}}function v(t){var e=r(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function m(t){return t?(t.nodeName||"").toLowerCase():null}function b(t){return((n(t)?t.ownerDocument:t.document)||window.document).documentElement}function g(t){return h(b(t)).left+v(t).scrollLeft}function w(t){return r(t).getComputedStyle(t)}function O(t){var e=w(t),o=e.overflow,r=e.overflowX,n=e.overflowY;return/auto|scroll|overlay|hidden/.test(o+n+r)}function S(t){var e=h(t),o=t.offsetWidth,r=t.offsetHeight;return 1>=Math.abs(e.width-o)&&(o=e.width),1>=Math.abs(e.height-r)&&(r=e.height),{x:t.offsetLeft,y:t.offsetTop,width:o,height:r}}function _(t){return"html"===m(t)?t:t.assignedSlot||t.parentNode||(a(t)?t.host:null)||b(t)}function x(t,e){void 0===e&&(e=[]);var o,n=function t(e){return["html","body","#document"].indexOf(m(e))>=0?e.ownerDocument.body:i(e)&&O(e)?e:t(_(e))}(t),a=n===(null==(o=t.ownerDocument)?void 0:o.body),s=r(n),c=a?[s].concat(s.visualViewport||[],O(n)?n:[]):n,l=e.concat(c);return a?l:l.concat(x(_(c)))}function E(t){return i(t)&&"fixed"!==w(t).position?t.offsetParent:null}function k(t){for(var e=r(t),o=E(t);o&&["table","td","th"].indexOf(m(o))>=0&&"static"===w(o).position;)o=E(o);return o&&("html"===m(o)||"body"===m(o)&&"static"===w(o).position)?e:o||function(t){var e=-1!==navigator.userAgent.toLowerCase().indexOf("firefox");if(-1!==navigator.userAgent.indexOf("Trident")&&i(t)&&"fixed"===w(t).position)return null;for(var o=_(t);i(o)&&0>["html","body"].indexOf(m(o));){var r=w(o);if("none"!==r.transform||"none"!==r.perspective||"paint"===r.contain||-1!==["transform","perspective"].indexOf(r.willChange)||e&&"filter"===r.willChange||e&&r.filter&&"none"!==r.filter)return o;o=o.parentNode}return null}(t)||e}var j="bottom",P="right",L="left",A="auto",T=["top",j,P,L],C="start",q="viewport",R="popper",D=T.reduce(function(t,e){return t.concat([e+"-"+C,e+"-end"])},[]),H=[].concat(T,[A]).reduce(function(t,e){return t.concat([e,e+"-"+C,e+"-end"])},[]),B=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"],I={placement:"bottom",modifiers:[],strategy:"absolute"};function M(){for(var t=arguments.length,e=Array(t),o=0;o=0?"x":"y"}function Z(t){var e,o=t.reference,r=t.element,n=t.placement,i=n?W(n):null,a=n?$(n):null,s=o.x+o.width/2-r.width/2,c=o.y+o.height/2-r.height/2;switch(i){case"top":e={x:s,y:o.y-r.height};break;case j:e={x:s,y:o.y+o.height};break;case P:e={x:o.x+o.width,y:c};break;case L:e={x:o.x-r.width,y:c};break;default:e={x:o.x,y:o.y}}var l=i?N(i):null;if(null!=l){var u="y"===l?"height":"width";switch(a){case C:e[l]=e[l]-(o[u]/2-r[u]/2);break;case"end":e[l]=e[l]+(o[u]/2-r[u]/2)}}return e}var U={top:"auto",right:"auto",bottom:"auto",left:"auto"};function z(t){var e,o=t.popper,n=t.popperRect,i=t.placement,a=t.variation,s=t.offsets,c=t.position,l=t.gpuAcceleration,u=t.adaptive,f=t.roundOffsets,p=t.isFixed,d=s.x,h=void 0===d?0:d,v=s.y,m=void 0===v?0:v,g="function"==typeof f?f({x:h,y:m}):{x:h,y:m};h=g.x,m=g.y;var O=s.hasOwnProperty("x"),S=s.hasOwnProperty("y"),_=L,x="top",E=window;if(u){var A=k(o),T="clientHeight",C="clientWidth";A===r(o)&&"static"!==w(A=b(o)).position&&"absolute"===c&&(T="scrollHeight",C="scrollWidth"),("top"===i||(i===L||i===P)&&"end"===a)&&(x=j,m-=(p&&E.visualViewport?E.visualViewport.height:A[T])-n.height,m*=l?1:-1),i!==L&&("top"!==i&&i!==j||"end"!==a)||(_=P,h-=(p&&E.visualViewport?E.visualViewport.width:A[C])-n.width,h*=l?1:-1)}var q,R,D,H,B,I=Object.assign({position:c},u&&U),M=!0===f?(R=(q={x:h,y:m}).x,D=q.y,{x:y(R*(H=window.devicePixelRatio||1))/H||0,y:y(D*H)/H||0}):{x:h,y:m};return h=M.x,m=M.y,l?Object.assign({},I,((B={})[x]=S?"0":"",B[_]=O?"0":"",B.transform=1>=(E.devicePixelRatio||1)?"translate("+h+"px, "+m+"px)":"translate3d("+h+"px, "+m+"px, 0)",B)):Object.assign({},I,((e={})[x]=S?m+"px":"",e[_]=O?h+"px":"",e.transform="",e))}var F={left:"right",right:"left",bottom:"top",top:"bottom"};function X(t){return t.replace(/left|right|bottom|top/g,function(t){return F[t]})}var Y={start:"end",end:"start"};function G(t){return t.replace(/start|end/g,function(t){return Y[t]})}function J(t,e){var o=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(o&&a(o)){var r=e;do{if(r&&t.isSameNode(r))return!0;r=r.parentNode||r.host}while(r)}return!1}function K(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Q(t,e){var o,i,a,s,c,l,u,f,d,y,m,O,S,_,x,E,k;return e===q?K((o=r(t),i=b(t),a=o.visualViewport,s=i.clientWidth,c=i.clientHeight,l=0,u=0,a&&(s=a.width,c=a.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(l=a.offsetLeft,u=a.offsetTop)),{width:s,height:c,x:l+g(t),y:u})):n(e)?((f=h(e)).top=f.top+e.clientTop,f.left=f.left+e.clientLeft,f.bottom=f.top+e.clientHeight,f.right=f.left+e.clientWidth,f.width=e.clientWidth,f.height=e.clientHeight,f.x=f.left,f.y=f.top,f):K((d=b(t),m=b(d),O=v(d),S=null==(y=d.ownerDocument)?void 0:y.body,_=p(m.scrollWidth,m.clientWidth,S?S.scrollWidth:0,S?S.clientWidth:0),x=p(m.scrollHeight,m.clientHeight,S?S.scrollHeight:0,S?S.clientHeight:0),E=-O.scrollLeft+g(d),k=-O.scrollTop,"rtl"===w(S||m).direction&&(E+=p(m.clientWidth,S?S.clientWidth:0)-_),{width:_,height:x,x:E,y:k}))}function tt(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function te(t,e){return e.reduce(function(e,o){return e[o]=t,e},{})}function to(t,e){void 0===e&&(e={});var o,r,a,s,c,l,u,f,y=e,v=y.placement,g=void 0===v?t.placement:v,O=y.boundary,S=y.rootBoundary,E=y.elementContext,L=void 0===E?R:E,A=y.altBoundary,C=y.padding,D=void 0===C?0:C,H=tt("number"!=typeof D?D:te(D,T)),B=t.rects.popper,I=t.elements[void 0!==A&&A?L===R?"reference":R:L],M=(o=n(I)?I:I.contextElement||b(t.elements.popper),r=void 0===O?"clippingParents":O,a=void 0===S?q:S,u=(l=[].concat("clippingParents"===r?(s=x(_(o)),n(c=["absolute","fixed"].indexOf(w(o).position)>=0&&i(o)?k(o):o)?s.filter(function(t){return n(t)&&J(t,c)&&"body"!==m(t)}):[]):[].concat(r),[a]))[0],(f=l.reduce(function(t,e){var r=Q(o,e);return t.top=p(r.top,t.top),t.right=d(r.right,t.right),t.bottom=d(r.bottom,t.bottom),t.left=p(r.left,t.left),t},Q(o,u))).width=f.right-f.left,f.height=f.bottom-f.top,f.x=f.left,f.y=f.top,f),V=h(t.elements.reference),W=Z({reference:V,element:B,strategy:"absolute",placement:g}),$=K(Object.assign({},B,W)),N=L===R?$:V,U={top:M.top-N.top+H.top,bottom:N.bottom-M.bottom+H.bottom,left:M.left-N.left+H.left,right:N.right-M.right+H.right},z=t.modifiersData.offset;if(L===R&&z){var F=z[g];Object.keys(U).forEach(function(t){var e=[P,j].indexOf(t)>=0?1:-1,o=["top",j].indexOf(t)>=0?"y":"x";U[t]+=F[o]*e})}return U}function tr(t,e,o){return p(t,d(e,o))}function tn(t,e,o){return void 0===o&&(o={x:0,y:0}),{top:t.top-e.height-o.y,right:t.right-e.width+o.x,bottom:t.bottom-e.height+o.y,left:t.left-e.width-o.x}}function ti(t){return["top",P,j,L].some(function(e){return t[e]>=0})}var ta=(l=void 0===(c=(s={defaultModifiers:[{name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,o=t.instance,n=t.options,i=n.scroll,a=void 0===i||i,s=n.resize,c=void 0===s||s,l=r(e.elements.popper),u=[].concat(e.scrollParents.reference,e.scrollParents.popper);return a&&u.forEach(function(t){t.addEventListener("scroll",o.update,V)}),c&&l.addEventListener("resize",o.update,V),function(){a&&u.forEach(function(t){t.removeEventListener("scroll",o.update,V)}),c&&l.removeEventListener("resize",o.update,V)}},data:{}},{name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,o=t.name;e.modifiersData[o]=Z({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},{name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,o=t.options,r=o.gpuAcceleration,n=o.adaptive,i=o.roundOffsets,a=void 0===i||i,s={placement:W(e.placement),variation:$(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:void 0===r||r,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,z(Object.assign({},s,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:void 0===n||n,roundOffsets:a})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,z(Object.assign({},s,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:a})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}},{name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach(function(t){var o=e.styles[t]||{},r=e.attributes[t]||{},n=e.elements[t];i(n)&&m(n)&&(Object.assign(n.style,o),Object.keys(r).forEach(function(t){var e=r[t];!1===e?n.removeAttribute(t):n.setAttribute(t,!0===e?"":e)}))})},effect:function(t){var e=t.state,o={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,o.popper),e.styles=o,e.elements.arrow&&Object.assign(e.elements.arrow.style,o.arrow),function(){Object.keys(e.elements).forEach(function(t){var r=e.elements[t],n=e.attributes[t]||{},a=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:o[t]).reduce(function(t,e){return t[e]="",t},{});i(r)&&m(r)&&(Object.assign(r.style,a),Object.keys(n).forEach(function(t){r.removeAttribute(t)}))})}},requires:["computeStyles"]},{name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,o=t.options,r=t.name,n=o.offset,i=void 0===n?[0,0]:n,a=H.reduce(function(t,o){var r,n,a,s,c,l;return t[o]=(r=e.rects,a=[L,"top"].indexOf(n=W(o))>=0?-1:1,c=(s="function"==typeof i?i(Object.assign({},r,{placement:o})):i)[0],l=s[1],c=c||0,l=(l||0)*a,[L,P].indexOf(n)>=0?{x:l,y:c}:{x:c,y:l}),t},{}),s=a[e.placement],c=s.x,l=s.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=c,e.modifiersData.popperOffsets.y+=l),e.modifiersData[r]=a}},{name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,o=t.options,r=t.name;if(!e.modifiersData[r]._skip){for(var n=o.mainAxis,i=void 0===n||n,a=o.altAxis,s=void 0===a||a,c=o.fallbackPlacements,l=o.padding,u=o.boundary,f=o.rootBoundary,p=o.altBoundary,d=o.flipVariations,y=void 0===d||d,h=o.allowedAutoPlacements,v=e.options.placement,m=W(v),b=c||(m!==v&&y?function(t){if(W(t)===A)return[];var e=X(t);return[G(t),e,G(e)]}(v):[X(v)]),g=[v].concat(b).reduce(function(t,o){var r,n,i,a,s,c,p,d,v,m,b,g;return t.concat(W(o)===A?(n=(r={placement:o,boundary:u,rootBoundary:f,padding:l,flipVariations:y,allowedAutoPlacements:h}).placement,i=r.boundary,a=r.rootBoundary,s=r.padding,c=r.flipVariations,d=void 0===(p=r.allowedAutoPlacements)?H:p,0===(b=(m=(v=$(n))?c?D:D.filter(function(t){return $(t)===v}):T).filter(function(t){return d.indexOf(t)>=0})).length&&(b=m),Object.keys(g=b.reduce(function(t,o){return t[o]=to(e,{placement:o,boundary:i,rootBoundary:a,padding:s})[W(o)],t},{})).sort(function(t,e){return g[t]-g[e]})):o)},[]),w=e.rects.reference,O=e.rects.popper,S=new Map,_=!0,x=g[0],E=0;E=0,I=B?"width":"height",M=to(e,{placement:k,boundary:u,rootBoundary:f,altBoundary:p,padding:l}),V=B?R?P:L:R?j:"top";w[I]>O[I]&&(V=X(V));var N=X(V),Z=[];if(i&&Z.push(M[q]<=0),s&&Z.push(M[V]<=0,M[N]<=0),Z.every(function(t){return t})){x=k,_=!1;break}S.set(k,Z)}if(_)for(var U=function(t){var e=g.find(function(e){var o=S.get(e);if(o)return o.slice(0,t).every(function(t){return t})});if(e)return x=e,"break"},z=y?3:1;z>0&&"break"!==U(z);z--);e.placement!==x&&(e.modifiersData[r]._skip=!0,e.placement=x,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}},{name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,o=t.options,r=t.name,n=o.mainAxis,i=o.altAxis,a=o.boundary,s=o.rootBoundary,c=o.altBoundary,l=o.padding,u=o.tether,f=void 0===u||u,y=o.tetherOffset,h=void 0===y?0:y,v=to(e,{boundary:a,rootBoundary:s,padding:l,altBoundary:c}),m=W(e.placement),b=$(e.placement),g=!b,w=N(m),O="x"===w?"y":"x",_=e.modifiersData.popperOffsets,x=e.rects.reference,E=e.rects.popper,A="function"==typeof h?h(Object.assign({},e.rects,{placement:e.placement})):h,T="number"==typeof A?{mainAxis:A,altAxis:A}:Object.assign({mainAxis:0,altAxis:0},A),q=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,R={x:0,y:0};if(_){if(void 0===n||n){var D,H="y"===w?"top":L,B="y"===w?j:P,I="y"===w?"height":"width",M=_[w],V=M+v[H],Z=M-v[B],U=f?-E[I]/2:0,z=b===C?x[I]:E[I],F=b===C?-E[I]:-x[I],X=e.elements.arrow,Y=f&&X?S(X):{width:0,height:0},G=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},J=G[H],K=G[B],Q=tr(0,x[I],Y[I]),tt=g?x[I]/2-U-Q-J-T.mainAxis:z-Q-J-T.mainAxis,te=g?-x[I]/2+U+Q+K+T.mainAxis:F+Q+K+T.mainAxis,tn=e.elements.arrow&&k(e.elements.arrow),ti=tn?"y"===w?tn.clientTop||0:tn.clientLeft||0:0,ta=null!=(D=null==q?void 0:q[w])?D:0,ts=M+te-ta,tc=tr(f?d(V,M+tt-ta-ti):V,M,f?p(Z,ts):Z);_[w]=tc,R[w]=tc-M}if(void 0!==i&&i){var tl,tu,tf="x"===w?"top":L,tp="x"===w?j:P,td=_[O],ty="y"===O?"height":"width",th=td+v[tf],tv=td-v[tp],tm=-1!==["top",L].indexOf(m),tb=null!=(tu=null==q?void 0:q[O])?tu:0,tg=tm?th:td-x[ty]-E[ty]-tb+T.altAxis,tw=tm?td+x[ty]+E[ty]-tb-T.altAxis:tv,tO=f&&tm?(tl=tr(tg,td,tw))>tw?tw:tl:tr(f?tg:th,td,f?tw:tv);_[O]=tO,R[O]=tO-td}e.modifiersData[r]=R}},requiresIfExists:["offset"]},{name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,o=t.state,r=t.name,n=t.options,i=o.elements.arrow,a=o.modifiersData.popperOffsets,s=W(o.placement),c=N(s),l=[L,P].indexOf(s)>=0?"height":"width";if(i&&a){var u,f=tt("number"!=typeof(u="function"==typeof(u=n.padding)?u(Object.assign({},o.rects,{placement:o.placement})):u)?u:te(u,T)),p=S(i),d="y"===c?"top":L,y="y"===c?j:P,h=o.rects.reference[l]+o.rects.reference[c]-a[c]-o.rects.popper[l],v=a[c]-o.rects.reference[c],m=k(i),b=m?"y"===c?m.clientHeight||0:m.clientWidth||0:0,g=f[d],w=b-p[l]-f[y],O=b/2-p[l]/2+(h/2-v/2),_=tr(g,O,w);o.modifiersData[r]=((e={})[c]=_,e.centerOffset=_-O,e)}},effect:function(t){var e=t.state,o=t.options.element,r=void 0===o?"[data-popper-arrow]":o;null!=r&&("string"!=typeof r||(r=e.elements.popper.querySelector(r)))&&J(e.elements.popper,r)&&(e.elements.arrow=r)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]},{name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,o=t.name,r=e.rects.reference,n=e.rects.popper,i=e.modifiersData.preventOverflow,a=to(e,{elementContext:"reference"}),s=to(e,{altBoundary:!0}),c=tn(a,r),l=tn(s,n,i),u=ti(c),f=ti(l);e.modifiersData[o]={referenceClippingOffsets:c,popperEscapeOffsets:l,isReferenceHidden:u,hasPopperEscaped:f},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":u,"data-popper-escaped":f})}}]}).defaultModifiers)?[]:c,f=void 0===(u=s.defaultOptions)?I:u,function(t,e,o){void 0===o&&(o=f);var a,s={placement:"bottom",orderedModifiers:[],options:Object.assign({},I,f),modifiersData:{},elements:{reference:t,popper:e},attributes:{},styles:{}},c=[],u=!1,p={state:s,setOptions:function(o){var r="function"==typeof o?o(s.options):o;d(),s.options=Object.assign({},f,s.options,r),s.scrollParents={reference:n(t)?x(t):t.contextElement?x(t.contextElement):[],popper:x(e)};var i,a,u,y,h,v=(i=Object.keys(h=[].concat(l,s.options.modifiers).reduce(function(t,e){var o=t[e.name];return t[e.name]=o?Object.assign({},o,e,{options:Object.assign({},o.options,e.options),data:Object.assign({},o.data,e.data)}):e,t},{})).map(function(t){return h[t]}),a=new Map,u=new Set,y=[],i.forEach(function(t){a.set(t.name,t)}),i.forEach(function(t){u.has(t.name)||function t(e){u.add(e.name),[].concat(e.requires||[],e.requiresIfExists||[]).forEach(function(e){if(!u.has(e)){var o=a.get(e);o&&t(o)}}),y.push(e)}(t)}),B.reduce(function(t,e){return t.concat(y.filter(function(t){return t.phase===e}))},[]));return s.orderedModifiers=v.filter(function(t){return t.enabled}),s.orderedModifiers.forEach(function(t){var e=t.name,o=t.options,r=t.effect;if("function"==typeof r){var n=r({state:s,name:e,instance:p,options:void 0===o?{}:o});c.push(n||function(){})}}),p.update()},forceUpdate:function(){if(!u){var t=s.elements,e=t.reference,o=t.popper;if(M(e,o)){s.rects={reference:(n=k(o),a="fixed"===s.options.strategy,d=i(n),w=i(n)&&(l=y((c=n.getBoundingClientRect()).width)/n.offsetWidth||1,f=y(c.height)/n.offsetHeight||1,1!==l||1!==f),_=b(n),x=h(e,w),E={scrollLeft:0,scrollTop:0},j={x:0,y:0},(d||!d&&!a)&&(("body"!==m(n)||O(_))&&(E=n!==r(n)&&i(n)?{scrollLeft:n.scrollLeft,scrollTop:n.scrollTop}:v(n)),i(n)?((j=h(n,!0)).x+=n.clientLeft,j.y+=n.clientTop):_&&(j.x=g(_))),{x:x.left+E.scrollLeft-j.x,y:x.top+E.scrollTop-j.y,width:x.width,height:x.height}),popper:S(o)},s.reset=!1,s.placement=s.options.placement,s.orderedModifiers.forEach(function(t){return s.modifiersData[t.name]=Object.assign({},t.data)});for(var n,a,c,l,f,d,w,_,x,E,j,P=0;P{for(var r in e)o.o(e,r)&&!o.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),o.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var r={};return o.r(r),o(661),o(795),o(682),o(284),o(181),o(778),o(51),o(185),r})()}}]); \ No newline at end of file diff --git a/crates/rooch/public/dashboard/_next/static/chunks/framework-6698976aa0ea586d.js b/crates/rooch/public/dashboard/_next/static/chunks/framework-6698976aa0ea586d.js new file mode 100644 index 0000000000..621428577c --- /dev/null +++ b/crates/rooch/public/dashboard/_next/static/chunks/framework-6698976aa0ea586d.js @@ -0,0 +1,33 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[774],{3746:function(e,n,t){/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var r,l,a,u,o,i,s=t(959),c=t(2962);function f(e){for(var n="https://reactjs.org/docs/error-decoder.html?invariant="+e,t=1;t